mirror of
https://github.com/PurpleI2P/i2pd.git
synced 2025-03-07 06:09:42 +00:00
Compare commits
8 Commits
static-bui
...
webconsole
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
31936f6025 | ||
|
|
f3dcc5364f | ||
|
|
fbe2e734c2 | ||
|
|
78193fc8f8 | ||
|
|
463d43b0bb | ||
|
|
7197fce349 | ||
|
|
5ba387ba2b | ||
|
|
a843be75f3 |
@@ -1,32 +0,0 @@
|
||||
# editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
# Unix style files
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[Makefile,Makefile.*]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
|
||||
[*.cmd]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = crlf
|
||||
|
||||
[*.{h,cpp}]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
|
||||
[*.rc]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[*.{md,markdown}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
trim_trailing_whitespace = false
|
||||
32
.github/workflows/build-deb.yml
vendored
32
.github/workflows/build-deb.yml
vendored
@@ -1,32 +0,0 @@
|
||||
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
|
||||
13
.github/workflows/build-freebsd.yml
vendored
13
.github/workflows/build-freebsd.yml
vendored
@@ -4,25 +4,18 @@ on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: macos-12
|
||||
runs-on: macos-10.15
|
||||
name: with UPnP
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Test in FreeBSD
|
||||
id: test
|
||||
uses: vmactions/freebsd-vm@v0.3.0
|
||||
uses: vmactions/freebsd-vm@v0.1.5
|
||||
with:
|
||||
usesh: true
|
||||
mem: 2048
|
||||
sync: rsync
|
||||
copyback: true
|
||||
prepare: pkg install -y devel/cmake devel/gmake devel/boost-libs security/openssl net/miniupnpc
|
||||
prepare: pkg install -y devel/cmake devel/gmake devel/boost-libs security/openssl net/miniupnpc
|
||||
run: |
|
||||
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
|
||||
1
.github/workflows/build-osx.yml
vendored
1
.github/workflows/build-osx.yml
vendored
@@ -14,7 +14,6 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
- name: install packages
|
||||
run: |
|
||||
find /usr/local/bin -lname '*/Library/Frameworks/Python.framework/*' -delete
|
||||
brew update
|
||||
brew install boost miniupnpc openssl@1.1
|
||||
- name: build application
|
||||
|
||||
64
.github/workflows/build.yml
vendored
64
.github/workflows/build.yml
vendored
@@ -5,7 +5,7 @@ on: [push, pull_request]
|
||||
jobs:
|
||||
build-make:
|
||||
name: Make with USE_UPNP=${{ matrix.with_upnp }}
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-18.04
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
@@ -14,13 +14,14 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
- name: install packages
|
||||
run: |
|
||||
sudo add-apt-repository ppa:mhier/libboost-latest
|
||||
sudo apt-get update
|
||||
sudo apt-get install build-essential libboost-all-dev libminiupnpc-dev libssl-dev zlib1g-dev
|
||||
sudo apt-get install build-essential libboost1.74-dev libminiupnpc-dev libssl-dev zlib1g-dev
|
||||
- name: build application
|
||||
run: make USE_UPNP=${{ matrix.with_upnp }} -j3
|
||||
build-cmake:
|
||||
name: CMake with -DWITH_UPNP=${{ matrix.with_upnp }}
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-18.04
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
@@ -29,26 +30,59 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
- name: install packages
|
||||
run: |
|
||||
sudo add-apt-repository ppa:mhier/libboost-latest
|
||||
sudo apt-get update
|
||||
sudo apt-get install build-essential cmake libboost-all-dev libminiupnpc-dev libssl-dev zlib1g-dev
|
||||
sudo apt-get install build-essential cmake libboost1.74-dev libminiupnpc-dev libssl-dev zlib1g-dev
|
||||
- name: build application
|
||||
run: |
|
||||
cd build
|
||||
cmake -DWITH_UPNP=${{ matrix.with_upnp }} .
|
||||
make -j3
|
||||
build-static:
|
||||
name: Static build with UPnP
|
||||
runs-on: ubuntu-20.04
|
||||
build-deb-stretch:
|
||||
name: Build package for stretch
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: install packages
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: change debian changelog
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install build-essential libboost-all-dev libminiupnpc-dev libssl-dev zlib1g-dev
|
||||
- name: build application
|
||||
run: make DEBUG=no USE_STATIC=yes USE_UPNP=yes -j`nproc`
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
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:
|
||||
name: i2pd-amd64-static
|
||||
path: i2pd
|
||||
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,137 +4,67 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- openssl
|
||||
- docker
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
docker:
|
||||
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@v2
|
||||
uses: docker/setup-qemu-action@v1
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
uses: docker/setup-buildx-action@v1
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Login to GitHub Container registry
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build container for ${{ matrix.archname }}
|
||||
uses: docker/build-push-action@v3
|
||||
- name: Build and push trunk container
|
||||
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: ./contrib/docker
|
||||
file: ./contrib/docker/Dockerfile
|
||||
platforms: ${{ matrix.platform }}
|
||||
platforms: linux/amd64,linux/386,linux/arm64,linux/arm/v7
|
||||
push: true
|
||||
tags: |
|
||||
purplei2p/i2pd:latest-${{ matrix.archname }}
|
||||
ghcr.io/purplei2p/i2pd:latest-${{ matrix.archname }}
|
||||
purplei2p/i2pd:latest
|
||||
ghcr.io/purplei2p/i2pd:latest
|
||||
|
||||
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
|
||||
- name: Set env
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||
run: echo "RELEASE_VERSION=${GITHUB_REF:10}" >> $GITHUB_ENV
|
||||
|
||||
- name: Create and push release manifest image to Docker Hub
|
||||
- name: Build and push release container
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||
uses: Noelware/docker-manifest-action@master
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
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
|
||||
context: ./contrib/docker
|
||||
file: ./contrib/docker/Dockerfile
|
||||
platforms: linux/amd64,linux/386,linux/arm64,linux/arm/v7
|
||||
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 }}
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -8,12 +8,15 @@ netDb
|
||||
/libi2pd.a
|
||||
/libi2pdclient.a
|
||||
/libi2pdlang.a
|
||||
/libi2pdwebconsole.a
|
||||
/libi2pd.so
|
||||
/libi2pdclient.so
|
||||
/libi2pdlang.so
|
||||
/libi2pdwebconsole.so
|
||||
/libi2pd.dll
|
||||
/libi2pdclient.dll
|
||||
/libi2pdlang.dll
|
||||
/libi2pdwebconsole.dll
|
||||
*.exe
|
||||
|
||||
|
||||
|
||||
108
ChangeLog
108
ChangeLog
@@ -1,110 +1,6 @@
|
||||
# for this file format description,
|
||||
# see https://github.com/olivierlacan/keep-a-changelog
|
||||
|
||||
## [2.45.1] - 2023-01-11
|
||||
### Added
|
||||
- Full Cone NAT status error
|
||||
### Changed
|
||||
- Drop duplicated I2NP messages in SSU2
|
||||
- Set rejection code 30 if tunnel with id already exists
|
||||
- Network status is always OK if peer test msg 5 received
|
||||
### Fixed
|
||||
- UPnP crash if SSU2 or NTCP2 is disabled
|
||||
- Crash on termination for some platforms
|
||||
|
||||
## [2.45.0] - 2023-01-03
|
||||
### Added
|
||||
- Test for Symmetric NAT with peer test msgs 6 and 7
|
||||
- Webconsole "No Descriptors" router error state
|
||||
- 1 and 15 seconds bandwidth calculation for i2pcontrol
|
||||
- Show non-zero send queue size for transports in web console
|
||||
- Compressible padding for I2P addresses
|
||||
- Localization to Czech
|
||||
- Don't accept incoming session from invalid/reserved addresses for NTCP2 and SSU2
|
||||
- Limit simultaneous tunnel build requests by 4 per pool
|
||||
### Changed
|
||||
- Removed SSU support
|
||||
- Reduced bandwidth calculation interval from 60 to 15 seconds
|
||||
- Increased default max transit tunnels number from 2500 to 5000 or 10000 for floodfill
|
||||
- Transit tunnels limit is doubled if floodfill mode is enabled
|
||||
- NTCP2 and SSU2 timestamps are rounded to seconds
|
||||
- Drop RouterInfos and LeaseSets with timestamp from future
|
||||
- Don't delete unreachable routers if tunnel creation success rate is too low
|
||||
- Refuse duplicated incoming pending NTCP2 session from same IP
|
||||
- Don't send SSU2 termination again if termination received block received
|
||||
- Handle standard network error for SSU2 without throwing an exception
|
||||
- Don't select overloaded peer for next tunnel
|
||||
- Remove "X-Requested-With" in HTTP Proxy for non-AJAX requests
|
||||
### Fixed
|
||||
- File descriptors leak
|
||||
- Random crash on AddressBook update
|
||||
- Crash if incorrect LeaseSet size
|
||||
- Spamming to log if no descriptors
|
||||
- ::1 address in RouterInfo
|
||||
- SSU2 network error handling (especially for Windows)
|
||||
- Race condition with pending outgoing SSU2 sessions
|
||||
- RTT self-reduction for long-live streams
|
||||
|
||||
## [2.44.0] - 2022-11-20
|
||||
### Added
|
||||
- SSL connection for server I2P tunnels
|
||||
- Localization to Italian and Spanish
|
||||
- SSU2 through SOCKS5 UDP proxy
|
||||
- Reload tunnels through web console
|
||||
- SSU2 send immediate ack request flag
|
||||
- SSU2 send and verify path challenge
|
||||
- Configurable ssu2.mtu4 and ssu2.mtu6
|
||||
### Changed
|
||||
- SSU2 is enabled and SSU is disabled by default
|
||||
- Separate network status and error
|
||||
- Random selection between NTCP2 and SSU2 priority
|
||||
- Added notbob.i2p to jump services
|
||||
- Remove DoNotTrack flag from HTTP Request header
|
||||
- Skip addresshelper page if destination was not changed
|
||||
- SSU2 allow different ports from RelayReponse and HolePunch
|
||||
- SSU2 resend PeerTest msg 1 and msg 2
|
||||
- SSU2 Send Retry instead SessionCreated if clock skew detected
|
||||
### Fixed
|
||||
- Long HTTP headers for HTTP proxy and HTTP server tunnel
|
||||
- SSU2 resends and resend limits
|
||||
- Crash at startup if addressbook is disabled
|
||||
- NTCP2 ipv6 connection through SOCKS5 proxy
|
||||
- SSU2 SessionRequest with zero token
|
||||
- SSU2 MTU less than 1280
|
||||
- SSU2 port=1
|
||||
- Incorrect addresses from network interfaces
|
||||
- Definitions for Darwin PPC; do not use pthread_setname_np
|
||||
|
||||
## [2.43.0] - 2022-08-22
|
||||
### Added
|
||||
- Complete SSU2 implementation
|
||||
- Localization to Chinese
|
||||
- Send RouterInfo update for long live sessions
|
||||
- Explicit ipv6 ranges of known tunnel brokers for MTU detection
|
||||
- Always send "Connection: close" and strip out Keep-Alive for server HTTP tunnel
|
||||
- Show ports for all transports in web console
|
||||
- Translation of webconsole site title
|
||||
- Support for Windows ProgramData path when running as service
|
||||
- Ability to turn off address book
|
||||
- Handle signals TSTP and CONT to stop and resume network
|
||||
### Changed
|
||||
- Case insensitive headers for server HTTP tunnel
|
||||
- Do not show 'Address registration' line if LeaseSet is encrypted
|
||||
- SSU2 transports have higher priority than SSU
|
||||
- Disable ElGamal precalculated table if no SSU
|
||||
- Deprecate limits.ntcpsoft, limits.ntcphard and limits.ntcpthreads config options
|
||||
- SSU2 is enabled and SSU is disabled by default for new installations
|
||||
### Fixed
|
||||
- Typo with Referer header name in HTTP proxy
|
||||
- Can't handle garlic message from an exploratory tunnel
|
||||
- Incorrect encryption key for exploratory lookup reply
|
||||
- Bound checks issues in LeaseSets code
|
||||
- MTU detection on Windows
|
||||
- Crash on stop of active server tunnel
|
||||
- Send datagram to wrong destination in SAM
|
||||
- Incorrect static key in RouterInfo if the keys were regenerated
|
||||
- Duplicated sessions in BOB
|
||||
|
||||
## [2.42.1] - 2022-05-24
|
||||
### Fixed
|
||||
- Incorrect jump link in HTTP Proxy
|
||||
@@ -485,7 +381,7 @@
|
||||
### Added
|
||||
- Client auth flag for b33 address
|
||||
### Changed
|
||||
- Remove incoming NTCP2 session from pending list when established
|
||||
- Remove incoming NTCP2 session from pending list when established
|
||||
- Handle errors for NTCP2 SessionConfrimed send
|
||||
### Fixed
|
||||
- Failure to start on Windows XP
|
||||
@@ -789,7 +685,7 @@
|
||||
### Added
|
||||
- Datagram i2p tunnels
|
||||
- Unique local addresses for server tunnels
|
||||
- Configurable list of reseed servers and initial addressbook
|
||||
- Configurable list of reseed servers and initial addressbook
|
||||
- Configurable netid
|
||||
- Initial iOS support
|
||||
|
||||
|
||||
66
Makefile
66
Makefile
@@ -12,19 +12,22 @@ endif
|
||||
|
||||
SHLIB := libi2pd.$(SHARED_SUFFIX)
|
||||
ARLIB := libi2pd.a
|
||||
SHLIB_LANG := libi2pdlang.$(SHARED_SUFFIX)
|
||||
ARLIB_LANG := libi2pdlang.a
|
||||
SHLIB_CLIENT := libi2pdclient.$(SHARED_SUFFIX)
|
||||
ARLIB_CLIENT := libi2pdclient.a
|
||||
SHLIB_LANG := libi2pdlang.$(SHARED_SUFFIX)
|
||||
ARLIB_LANG := libi2pdlang.a
|
||||
SHLIB_WEBCONSOLE := libi2pdwebconsole.$(SHARED_SUFFIX)
|
||||
ARLIB_WEBCONSOLE := libi2pdwebconsole.a
|
||||
SHLIB_WRAP := libi2pdwrapper.$(SHARED_SUFFIX)
|
||||
ARLIB_WRAP := libi2pdwrapper.a
|
||||
I2PD := i2pd
|
||||
|
||||
LIB_SRC_DIR := libi2pd
|
||||
LIB_CLIENT_SRC_DIR := libi2pd_client
|
||||
WRAP_SRC_DIR := libi2pd_wrapper
|
||||
WEBCONSOLE_SRC_DIR := libi2pd_webconsole
|
||||
LANG_SRC_DIR := i18n
|
||||
DAEMON_SRC_DIR := daemon
|
||||
WRAP_SRC_DIR := libi2pd_wrapper
|
||||
|
||||
# import source files lists
|
||||
include filelist.mk
|
||||
@@ -47,26 +50,22 @@ else
|
||||
LD_DEBUG = -s
|
||||
endif
|
||||
|
||||
ifneq (, $(DESTDIR))
|
||||
PREFIX = $(DESTDIR)
|
||||
endif
|
||||
|
||||
ifneq (, $(findstring darwin, $(SYS)))
|
||||
DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp
|
||||
DAEMON_SRC += $(DAEMON_SRC_DIR)/DaemonUnix.cpp
|
||||
ifeq ($(HOMEBREW),1)
|
||||
include Makefile.homebrew
|
||||
else
|
||||
include Makefile.osx
|
||||
endif
|
||||
else ifneq (, $(findstring mingw, $(SYS))$(findstring windows-gnu, $(SYS))$(findstring cygwin, $(SYS)))
|
||||
DAEMON_SRC += Win32/DaemonWin32.cpp Win32/Win32App.cpp Win32/Win32Service.cpp Win32/Win32NetState.cpp
|
||||
include Makefile.mingw
|
||||
else ifneq (, $(findstring linux, $(SYS))$(findstring gnu, $(SYS)))
|
||||
DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp
|
||||
DAEMON_SRC += $(DAEMON_SRC_DIR)/DaemonUnix.cpp
|
||||
include Makefile.linux
|
||||
else ifneq (, $(findstring freebsd, $(SYS))$(findstring openbsd, $(SYS)))
|
||||
DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp
|
||||
DAEMON_SRC += $(DAEMON_SRC_DIR)/DaemonUnix.cpp
|
||||
include Makefile.bsd
|
||||
else ifneq (, $(findstring mingw, $(SYS))$(findstring windows-gnu, $(SYS))$(findstring cygwin, $(SYS)))
|
||||
DAEMON_SRC += $(DAEMON_SRC_DIR)/DaemonWin32.cpp Win32/Win32App.cpp Win32/Win32Service.cpp Win32/Win32NetState.cpp
|
||||
include Makefile.mingw
|
||||
else # not supported
|
||||
$(error Not supported platform)
|
||||
endif
|
||||
@@ -76,31 +75,34 @@ ifeq ($(USE_GIT_VERSION),yes)
|
||||
NEEDED_CXXFLAGS += -DGITVER=\"$(GIT_VERSION)\"
|
||||
endif
|
||||
|
||||
NEEDED_CXXFLAGS += -MMD -MP -I$(LIB_SRC_DIR) -I$(LIB_CLIENT_SRC_DIR) -I$(LANG_SRC_DIR) -DOPENSSL_SUPPRESS_DEPRECATED
|
||||
NEEDED_CXXFLAGS += -MMD -MP -I$(LIB_SRC_DIR) -I$(LIB_CLIENT_SRC_DIR) -I$(LANG_SRC_DIR) -I$(WEBCONSOLE_SRC_DIR) -DOPENSSL_SUPPRESS_DEPRECATED
|
||||
|
||||
LIB_OBJS += $(patsubst %.cpp,obj/%.o,$(LIB_SRC))
|
||||
LIB_CLIENT_OBJS += $(patsubst %.cpp,obj/%.o,$(LIB_CLIENT_SRC))
|
||||
LANG_OBJS += $(patsubst %.cpp,obj/%.o,$(LANG_SRC))
|
||||
WEBCONSOLE_OBJS += $(patsubst %.cpp,obj/%.o,$(WEBCONSOLE_SRC))
|
||||
DAEMON_OBJS += $(patsubst %.cpp,obj/%.o,$(DAEMON_SRC))
|
||||
WRAP_LIB_OBJS += $(patsubst %.cpp,obj/%.o,$(WRAP_LIB_SRC))
|
||||
DEPS += $(LIB_OBJS:.o=.d) $(LIB_CLIENT_OBJS:.o=.d) $(LANG_OBJS:.o=.d) $(DAEMON_OBJS:.o=.d) $(WRAP_LIB_OBJS:.o=.d)
|
||||
DEPS += $(LIB_OBJS:.o=.d) $(LIB_CLIENT_OBJS:.o=.d) $(LANG_OBJS:.o=.d) $(WEBCONSOLE_OBJS:.o=.d) $(DAEMON_OBJS:.o=.d) $(WRAP_LIB_OBJS:.o=.d)
|
||||
|
||||
## Build all code (libi2pd, libi2pdclient, libi2pdlang), link it to .a and build binary
|
||||
all: $(ARLIB) $(ARLIB_CLIENT) $(ARLIB_LANG) $(I2PD)
|
||||
all: $(ARLIB) $(ARLIB_CLIENT) $(ARLIB_LANG) $(ARLIB_WEBCONSOLE) $(I2PD)
|
||||
|
||||
mk_obj_dir:
|
||||
@mkdir -p obj/$(LIB_SRC_DIR)
|
||||
@mkdir -p obj/$(LIB_CLIENT_SRC_DIR)
|
||||
@mkdir -p obj/$(LANG_SRC_DIR)
|
||||
@mkdir -p obj/$(WEBCONSOLE_SRC_DIR)
|
||||
@mkdir -p obj/$(DAEMON_SRC_DIR)
|
||||
@mkdir -p obj/$(WRAP_SRC_DIR)
|
||||
@mkdir -p obj/Win32
|
||||
|
||||
api: $(SHLIB) $(ARLIB)
|
||||
client: $(SHLIB_CLIENT) $(ARLIB_CLIENT)
|
||||
lang: $(SHLIB_LANG) $(ARLIB_LANG)
|
||||
api_client: api client lang
|
||||
wrapper: api_client $(SHLIB_WRAP) $(ARLIB_WRAP)
|
||||
api: $(SHLIB) $(ARLIB)
|
||||
client: $(SHLIB_CLIENT) $(ARLIB_CLIENT)
|
||||
lang: $(SHLIB_LANG) $(ARLIB_LANG)
|
||||
webconsole: $(SHLIB_WEBCONSOLE) $(ARLIB_WEBCONSOLE)
|
||||
api_client: api client lang webconsole
|
||||
wrapper: api_client $(SHLIB_WRAP) $(ARLIB_WRAP)
|
||||
|
||||
## NOTE: The NEEDED_CXXFLAGS are here so that CXXFLAGS can be specified at build time
|
||||
## **without** overwriting the CXXFLAGS which we need in order to build.
|
||||
@@ -115,7 +117,7 @@ obj/%.o: %.cpp | mk_obj_dir
|
||||
# '-' is 'ignore if missing' on first run
|
||||
-include $(DEPS)
|
||||
|
||||
$(I2PD): $(DAEMON_OBJS) $(ARLIB) $(ARLIB_CLIENT) $(ARLIB_LANG)
|
||||
$(I2PD): $(DAEMON_OBJS) $(ARLIB_WEBCONSOLE) $(ARLIB) $(ARLIB_CLIENT) $(ARLIB_LANG)
|
||||
$(CXX) -o $@ $(LDFLAGS) $^ $(LDLIBS)
|
||||
|
||||
$(SHLIB): $(LIB_OBJS) $(SHLIB_LANG)
|
||||
@@ -128,12 +130,17 @@ ifneq ($(USE_STATIC),yes)
|
||||
$(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS) $(SHLIB) $(SHLIB_LANG)
|
||||
endif
|
||||
|
||||
$(SHLIB_WRAP): $(WRAP_LIB_OBJS)
|
||||
$(SHLIB_LANG): $(LANG_OBJS)
|
||||
ifneq ($(USE_STATIC),yes)
|
||||
$(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS)
|
||||
endif
|
||||
|
||||
$(SHLIB_LANG): $(LANG_OBJS)
|
||||
$(SHLIB_WEBCONSOLE): $(WEBCONSOLE_OBJS) $(SHLIB) $(SHLIB_CLIENT) $(SHLIB_LANG)
|
||||
ifneq ($(USE_STATIC),yes)
|
||||
$(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS) $(SHLIB) $(SHLIB_CLIENT) $(SHLIB_LANG)
|
||||
endif
|
||||
|
||||
$(SHLIB_WRAP): $(WRAP_LIB_OBJS)
|
||||
ifneq ($(USE_STATIC),yes)
|
||||
$(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS)
|
||||
endif
|
||||
@@ -144,18 +151,21 @@ $(ARLIB): $(LIB_OBJS)
|
||||
$(ARLIB_CLIENT): $(LIB_CLIENT_OBJS)
|
||||
$(AR) -r $@ $^
|
||||
|
||||
$(ARLIB_WRAP): $(WRAP_LIB_OBJS)
|
||||
$(ARLIB_LANG): $(LANG_OBJS)
|
||||
$(AR) -r $@ $^
|
||||
|
||||
$(ARLIB_LANG): $(LANG_OBJS)
|
||||
$(ARLIB_WEBCONSOLE): $(WEBCONSOLE_OBJS)
|
||||
$(AR) -r $@ $^
|
||||
|
||||
$(ARLIB_WRAP): $(WRAP_LIB_OBJS)
|
||||
$(AR) -r $@ $^
|
||||
|
||||
clean:
|
||||
$(RM) -r obj
|
||||
$(RM) -r docs/generated
|
||||
$(RM) $(I2PD) $(SHLIB) $(ARLIB) $(SHLIB_CLIENT) $(ARLIB_CLIENT) $(SHLIB_LANG) $(ARLIB_LANG) $(SHLIB_WRAP) $(ARLIB_WRAP)
|
||||
$(RM) $(I2PD) $(SHLIB) $(ARLIB) $(SHLIB_CLIENT) $(ARLIB_CLIENT) $(SHLIB_LANG) $(ARLIB_LANG) $(SHLIB_WEBCONSOLE) $(ARLIB_WEBCONSOLE) $(SHLIB_WRAP) $(ARLIB_WRAP)
|
||||
|
||||
strip: $(I2PD) $(SHLIB) $(SHLIB_CLIENT) $(SHLIB_LANG)
|
||||
strip: $(I2PD) $(SHLIB) $(SHLIB_CLIENT) $(SHLIB_LANG) $(SHLIB_WEBCONSOLE)
|
||||
strip $^
|
||||
|
||||
LATEST_TAG=$(shell git describe --tags --abbrev=0 openssl)
|
||||
|
||||
@@ -39,18 +39,13 @@ ifeq ($(USE_AESNI),yes)
|
||||
endif
|
||||
|
||||
install: all
|
||||
install -d ${PREFIX}/bin
|
||||
install -m 755 ${I2PD} ${PREFIX}/bin
|
||||
install -d ${PREFIX}/etc ${PREFIX}/etc/i2pd ${PREFIX}/etc/i2pd/tunnels.conf.d
|
||||
install -d ${PREFIX}/bin ${PREFIX}/etc/i2pd ${PREFIX}/etc/i2pd/tunnels.conf.d ${PREFIX}/share/doc/i2pd ${PREFIX}/share/i2pd ${PREFIX}/share/man/man1 ${PREFIX}/var/lib/i2pd
|
||||
install -m 755 ${I2PD} ${PREFIX}/bin/
|
||||
install -m 644 contrib/i2pd.conf contrib/subscriptions.txt contrib/tunnels.conf ${PREFIX}/etc/i2pd
|
||||
install -d ${PREFIX}/share ${PREFIX}/share/doc ${PREFIX}/share/doc/i2pd
|
||||
install -m 644 ChangeLog LICENSE README.md contrib/i2pd.conf contrib/subscriptions.txt contrib/tunnels.conf ${PREFIX}/share/doc/i2pd
|
||||
install -d ${PREFIX}/share/i2pd
|
||||
@cp -R contrib/certificates ${PREFIX}/share/i2pd/
|
||||
install -d ${PREFIX}/share/man ${PREFIX}/share/man/man1
|
||||
install -m 644 ChangeLog LICENSE README.md contrib/i2pd.conf contrib/subscriptions.txt contrib/tunnels.conf ${PREFIX}/share/doc/i2pd
|
||||
@gzip -kf debian/i2pd.1 && install debian/i2pd.1.gz ${PREFIX}/share/man/man1
|
||||
install -d ${PREFIX}/var ${PREFIX}/var/lib ${PREFIX}/var/lib/i2pd
|
||||
@ln -sf ${PREFIX}/share/i2pd/certificates ${PREFIX}/var/lib/i2pd/certificates
|
||||
@ln -sf ${PREFIX}/share/i2pd/certificates ${PREFIX}/var/lib/i2pd/
|
||||
@ln -sf ${PREFIX}/etc/i2pd/tunnels.conf.d ${PREFIX}/var/lib/i2pd/tunnels.d
|
||||
@ln -sf ${PREFIX}/etc/i2pd/i2pd.conf ${PREFIX}/var/lib/i2pd/i2pd.conf
|
||||
@ln -sf ${PREFIX}/etc/i2pd/subscriptions.txt ${PREFIX}/var/lib/i2pd/subscriptions.txt
|
||||
|
||||
@@ -20,11 +20,7 @@ else ifeq ($(shell expr match ${CXXVER} "4\.[8-9]"),3) # gcc 4.8 - 4.9
|
||||
else ifeq ($(shell expr match ${CXXVER} "[5-6]"),1) # gcc 5 - 6
|
||||
NEEDED_CXXFLAGS += -std=c++11
|
||||
LDLIBS = -latomic
|
||||
else ifeq ($(shell expr match ${CXXVER} "[7-9]"),1) # gcc 7 - 9
|
||||
NEEDED_CXXFLAGS += -std=c++17
|
||||
LDLIBS = -latomic
|
||||
else ifeq ($(shell expr match ${CXXVER} "1[0-9]"),2) # gcc 10+
|
||||
# NEEDED_CXXFLAGS += -std=c++20
|
||||
else ifeq ($(shell expr match ${CXXVER} "[1,7-9]"),1) # gcc >= 7
|
||||
NEEDED_CXXFLAGS += -std=c++17
|
||||
LDLIBS = -latomic
|
||||
else # not supported
|
||||
@@ -33,21 +29,32 @@ endif
|
||||
|
||||
NEEDED_CXXFLAGS += -fPIC
|
||||
|
||||
LDLIBS += -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lssl -lcrypto -lz
|
||||
|
||||
# UPNP Support (miniupnpc 1.5 and higher)
|
||||
ifeq ($(USE_UPNP),yes)
|
||||
LDLIBS += -lminiupnpc
|
||||
NEEDED_CXXFLAGS += -DUSE_UPNP
|
||||
endif
|
||||
|
||||
ifeq ($(USE_STATIC),yes)
|
||||
# NOTE: on glibc you will get this warning:
|
||||
# Using 'getaddrinfo' in statically linked applications requires at runtime
|
||||
# the shared libraries from the glibc version used for linking
|
||||
ifeq ($(USE_STATIC),yes)
|
||||
LDLIBS += -static -ldl -lpthread -static-libgcc -static-libstdc++
|
||||
LIBDIR := /usr/lib/$(SYS)
|
||||
LDLIBS += $(LIBDIR)/libboost_system.a
|
||||
LDLIBS += $(LIBDIR)/libboost_date_time.a
|
||||
LDLIBS += $(LIBDIR)/libboost_filesystem.a
|
||||
LDLIBS += $(LIBDIR)/libboost_program_options.a
|
||||
LDLIBS += $(LIBDIR)/libssl.a
|
||||
LDLIBS += $(LIBDIR)/libcrypto.a
|
||||
LDLIBS += $(LIBDIR)/libz.a
|
||||
ifeq ($(USE_UPNP),yes)
|
||||
LDLIBS += $(LIBDIR)/libminiupnpc.a
|
||||
endif
|
||||
LDLIBS += -lpthread -ldl
|
||||
else
|
||||
LDLIBS += -lpthread
|
||||
LDLIBS += -lcrypto -lssl -lz -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread
|
||||
ifeq ($(USE_UPNP),yes)
|
||||
LDLIBS += -lminiupnpc
|
||||
endif
|
||||
endif
|
||||
|
||||
# UPNP Support (miniupnpc 1.5 and higher)
|
||||
ifeq ($(USE_UPNP),yes)
|
||||
NEEDED_CXXFLAGS += -DUSE_UPNP
|
||||
endif
|
||||
|
||||
ifeq ($(USE_AESNI),yes)
|
||||
@@ -57,18 +64,13 @@ endif
|
||||
endif
|
||||
|
||||
install: all
|
||||
install -d ${PREFIX}/bin
|
||||
install -m 755 ${I2PD} ${PREFIX}/bin
|
||||
install -d ${PREFIX}/etc ${PREFIX}/etc/i2pd ${PREFIX}/etc/i2pd/tunnels.conf.d
|
||||
install -d ${PREFIX}/bin ${PREFIX}/etc ${PREFIX}/etc/i2pd ${PREFIX}/etc/i2pd/tunnels.conf.d ${PREFIX}/usr ${PREFIX}/usr/share ${PREFIX}/usr/share/doc/i2pd ${PREFIX}/usr/share/i2pd ${PREFIX}/usr/share/man ${PREFIX}/usr/share/man/man1 ${PREFIX}/var/lib ${PREFIX}/var/lib/i2pd
|
||||
install -m 755 ${I2PD} ${PREFIX}/bin/
|
||||
install -m 644 contrib/i2pd.conf contrib/subscriptions.txt contrib/tunnels.conf ${PREFIX}/etc/i2pd
|
||||
install -d ${PREFIX}/share ${PREFIX}/share/doc ${PREFIX}/share/doc/i2pd
|
||||
install -m 644 ChangeLog LICENSE README.md contrib/i2pd.conf contrib/subscriptions.txt contrib/tunnels.conf ${PREFIX}/share/doc/i2pd
|
||||
install -d ${PREFIX}/share/i2pd
|
||||
@cp -R contrib/certificates ${PREFIX}/share/i2pd/
|
||||
install -d ${PREFIX}/share/man ${PREFIX}/share/man/man1
|
||||
@gzip -kf debian/i2pd.1 && install debian/i2pd.1.gz ${PREFIX}/share/man/man1
|
||||
install -d ${PREFIX}/var ${PREFIX}/var/lib ${PREFIX}/var/lib/i2pd
|
||||
@ln -sf ${PREFIX}/share/i2pd/certificates ${PREFIX}/var/lib/i2pd/certificates
|
||||
@cp -R contrib/certificates ${PREFIX}/usr/share/i2pd/
|
||||
install -m 644 ChangeLog LICENSE README.md contrib/i2pd.conf contrib/subscriptions.txt contrib/tunnels.conf ${PREFIX}/usr/share/doc/i2pd
|
||||
@gzip -kf debian/i2pd.1 && install debian/i2pd.1.gz ${PREFIX}/usr/share/man/man1
|
||||
@ln -sf ${PREFIX}/usr/share/i2pd/certificates ${PREFIX}/var/lib/i2pd/
|
||||
@ln -sf ${PREFIX}/etc/i2pd/tunnels.conf.d ${PREFIX}/var/lib/i2pd/tunnels.d
|
||||
@ln -sf ${PREFIX}/etc/i2pd/i2pd.conf ${PREFIX}/var/lib/i2pd/i2pd.conf
|
||||
@ln -sf ${PREFIX}/etc/i2pd/subscriptions.txt ${PREFIX}/var/lib/i2pd/subscriptions.txt
|
||||
|
||||
@@ -4,7 +4,7 @@ USE_WIN32_APP := yes
|
||||
WINDRES = windres
|
||||
|
||||
CXXFLAGS := $(CXX_DEBUG) -fPIC -msse
|
||||
INCFLAGS = -I$(DAEMON_SRC_DIR) -IWin32
|
||||
INCFLAGS = -IWin32
|
||||
LDFLAGS := ${LD_DEBUG} -static
|
||||
|
||||
NEEDED_CXXFLAGS += -std=c++17 -DWIN32_LEAN_AND_MEAN
|
||||
|
||||
10
README.md
10
README.md
@@ -69,12 +69,12 @@ Build instructions:
|
||||
|
||||
**Supported systems:**
|
||||
|
||||
* GNU/Linux (Debian, Ubuntu, etc) - [](https://github.com/PurpleI2P/i2pd/actions/workflows/build.yml)
|
||||
* CentOS, Fedora, Mageia - [](https://copr.fedorainfracloud.org/coprs/supervillain/i2pd/package/i2pd-git/)
|
||||
* Alpine, ArchLinux, openSUSE, Gentoo, etc.
|
||||
* GNU/Linux - [](https://github.com/PurpleI2P/i2pd/actions/workflows/build.yml)
|
||||
* CentOS / Fedora / Mageia - [](https://copr.fedorainfracloud.org/coprs/supervillain/i2pd/package/i2pd-git/)
|
||||
* Alpine, ArchLinux, openSUSE, Gentoo, Debian, Ubuntu, etc.
|
||||
* Windows - [](https://github.com/PurpleI2P/i2pd/actions/workflows/build-windows.yml)
|
||||
* Mac OS - [](https://github.com/PurpleI2P/i2pd/actions/workflows/build-osx.yml)
|
||||
* Docker image - [](https://github.com/PurpleI2P/i2pd/actions/workflows/docker.yml)
|
||||
* Mac OS X - [](https://github.com/PurpleI2P/i2pd/actions/workflows/build-osx.yml)
|
||||
* Docker image - [](https://hub.docker.com/r/purplei2p/i2pd/builds/) [](https://github.com/PurpleI2P/i2pd/actions/workflows/docker.yml)
|
||||
* Snap - [](https://snapcraft.io/i2pd) [](https://snapcraft.io/i2pd)
|
||||
* FreeBSD - [](https://github.com/PurpleI2P/i2pd/actions/workflows/build-freebsd.yml)
|
||||
* Android - [](https://github.com/PurpleI2P/i2pd-android/actions/workflows/android.yml)
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
#include "Tunnel.h"
|
||||
#include "version.h"
|
||||
#include "resource.h"
|
||||
#include "Daemon.h"
|
||||
|
||||
#include "Win32App.h"
|
||||
#include "Win32NetState.h"
|
||||
|
||||
@@ -55,13 +55,15 @@ namespace win32
|
||||
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_ABOUT, "&About...");
|
||||
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_SEPARATOR, 0, NULL);
|
||||
if(!i2p::context.AcceptsTunnels())
|
||||
InsertMenu (hPopup, -1,
|
||||
i2p::util::DaemonWin32::Instance ().isGraceful ? MF_BYPOSITION | MF_STRING | MF_GRAYED : MF_BYPOSITION | MF_STRING,
|
||||
ID_ACCEPT_TRANSIT, "Accept &transit");
|
||||
if(m_getIsGraceful)
|
||||
if(m_getIsGraceful())
|
||||
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING | MF_GRAYED, ID_ACCEPT_TRANSIT, "Accept &transit");
|
||||
else
|
||||
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_ACCEPT_TRANSIT, "Accept &transit");
|
||||
else
|
||||
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_DECLINE_TRANSIT, "Decline &transit");
|
||||
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_RELOAD, "&Reload tunnels config");
|
||||
if (!i2p::util::DaemonWin32::Instance ().isGraceful)
|
||||
if (!m_getIsGraceful)
|
||||
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_GRACEFUL_SHUTDOWN, "&Graceful shutdown");
|
||||
else
|
||||
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_STOP_GRACEFUL_SHUTDOWN, "Stop &graceful shutdown");
|
||||
@@ -154,23 +156,25 @@ namespace win32
|
||||
case eRouterStatusUnknown: s << "Unk"; break;
|
||||
case eRouterStatusProxy: s << "Proxy"; break;
|
||||
case eRouterStatusMesh: s << "Mesh"; break;
|
||||
default: s << "Unk";
|
||||
};
|
||||
if (i2p::context.GetError () != eRouterErrorNone)
|
||||
{
|
||||
switch (i2p::context.GetError ())
|
||||
case eRouterStatusError:
|
||||
{
|
||||
case eRouterErrorClockSkew:
|
||||
s << " - Clock skew";
|
||||
s << "Err";
|
||||
switch (i2p::context.GetError ())
|
||||
{
|
||||
case eRouterErrorClockSkew:
|
||||
s << " - Clock skew";
|
||||
break;
|
||||
case eRouterErrorOffline:
|
||||
s << " - Offline";
|
||||
break;
|
||||
case eRouterErrorSymmetricNAT:
|
||||
s << " - Symmetric NAT";
|
||||
break;
|
||||
default: ;
|
||||
}
|
||||
break;
|
||||
case eRouterErrorOffline:
|
||||
s << " - Offline";
|
||||
break;
|
||||
case eRouterErrorSymmetricNAT:
|
||||
s << " - Symmetric NAT";
|
||||
break;
|
||||
default: ;
|
||||
}
|
||||
default: s << "Unk";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -268,7 +272,7 @@ namespace win32
|
||||
SetTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER, 10*60*1000, nullptr); // 10 minutes
|
||||
SetTimer (hWnd, IDT_GRACEFUL_TUNNELCHECK_TIMER, 1000, nullptr); // check tunnels every second
|
||||
g_GracefulShutdownEndtime = GetTickCount() + 10*60*1000;
|
||||
i2p::util::DaemonWin32::Instance ().isGraceful = true;
|
||||
if (m_setIsGraceful) m_setIsGraceful(true);
|
||||
return 0;
|
||||
}
|
||||
case ID_STOP_GRACEFUL_SHUTDOWN:
|
||||
@@ -277,7 +281,7 @@ namespace win32
|
||||
KillTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER);
|
||||
KillTimer (hWnd, IDT_GRACEFUL_TUNNELCHECK_TIMER);
|
||||
g_GracefulShutdownEndtime = 0;
|
||||
i2p::util::DaemonWin32::Instance ().isGraceful = false;
|
||||
if (m_setIsGraceful) m_setIsGraceful(false);
|
||||
return 0;
|
||||
}
|
||||
case ID_RELOAD:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2020, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -22,6 +22,15 @@ namespace win32
|
||||
int RunWin32App ();
|
||||
bool GracefulShutdown ();
|
||||
bool StopGracefulShutdown ();
|
||||
|
||||
inline typedef std::function<void (bool)> DaemonSetIsGraceful;
|
||||
inline DaemonSetIsGraceful m_setIsGraceful;
|
||||
inline void SetIsGraceful (const DaemonSetIsGraceful& f) { m_setIsGraceful = f; };
|
||||
|
||||
inline typedef std::function<bool ()> DaemonGetIsGraceful;
|
||||
inline DaemonGetIsGraceful m_getIsGraceful;
|
||||
inline void GetIsGraceful (const DaemonGetIsGraceful& f) { m_getIsGraceful = f; };
|
||||
|
||||
}
|
||||
}
|
||||
#endif // WIN32APP_H__
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include <assert.h>
|
||||
#include <windows.h>
|
||||
|
||||
#include "Daemon.h"
|
||||
#include "Log.h"
|
||||
|
||||
I2PService *I2PService::s_service = NULL;
|
||||
@@ -132,7 +131,13 @@ void I2PService::Start(DWORD dwArgc, PSTR *pszArgv)
|
||||
void I2PService::OnStart(DWORD dwArgc, PSTR *pszArgv)
|
||||
{
|
||||
LogPrint(eLogInfo, "Win32Service: in OnStart (", EVENTLOG_INFORMATION_TYPE, ")");
|
||||
Daemon.start();
|
||||
if(m_daemonStart)
|
||||
m_daemonStart();
|
||||
else
|
||||
{
|
||||
LogPrint(eLogError, "Win32Service: failed to start: Unable to call callback");
|
||||
SetServiceStatus(SERVICE_STOPPED);
|
||||
}
|
||||
_worker = new std::thread(std::bind(&I2PService::WorkerThread, this));
|
||||
}
|
||||
|
||||
@@ -171,7 +176,11 @@ void I2PService::OnStop()
|
||||
{
|
||||
// Log a service stop message to the Application log.
|
||||
LogPrint(eLogInfo, "Win32Service: in OnStop (", EVENTLOG_INFORMATION_TYPE, ")");
|
||||
Daemon.stop();
|
||||
if(m_daemonStop)
|
||||
m_daemonStop();
|
||||
else
|
||||
LogPrint(eLogError, "Win32Service: failed to stop: Unable to call callback");
|
||||
|
||||
m_fStopping = TRUE;
|
||||
if (WaitForSingleObject(m_hStoppedEvent, INFINITE) != WAIT_OBJECT_0)
|
||||
{
|
||||
|
||||
@@ -6,9 +6,10 @@
|
||||
* See full license text in LICENSE file at top of project tree
|
||||
*/
|
||||
|
||||
#ifndef WIN_32_SERVICE_H__
|
||||
#define WIN_32_SERVICE_H__
|
||||
#ifndef WIN32SERVICE_H__
|
||||
#define WIN32SERVICE_H__
|
||||
|
||||
#include <functional>
|
||||
#include <thread>
|
||||
#include <windows.h>
|
||||
|
||||
@@ -29,6 +30,13 @@ class I2PService
|
||||
static BOOL Run(I2PService &service);
|
||||
void Stop();
|
||||
|
||||
typedef std::function<bool ()> DaemonStart;
|
||||
void SetDaemonStart (const DaemonStart& f) { m_daemonStart = f; };
|
||||
|
||||
typedef std::function<bool ()> DaemonStop;
|
||||
void SetDaemonStop (const DaemonStop& f) { m_daemonStop = f; };
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
virtual void OnStart(DWORD dwArgc, PSTR *pszArgv);
|
||||
@@ -58,6 +66,11 @@ class I2PService
|
||||
HANDLE m_hStoppedEvent;
|
||||
|
||||
std::thread* _worker;
|
||||
|
||||
private:
|
||||
|
||||
DaemonStart m_daemonStart;
|
||||
DaemonStop m_daemonStop;
|
||||
};
|
||||
|
||||
#endif // WIN_32_SERVICE_H__
|
||||
#endif // WIN32SERVICE_H__
|
||||
|
||||
@@ -17,20 +17,14 @@ option(WITH_LIBRARY "Build library" ON)
|
||||
option(WITH_BINARY "Build binary" ON)
|
||||
option(WITH_STATIC "Static build" OFF)
|
||||
option(WITH_UPNP "Include support for UPnP client" OFF)
|
||||
option(WITH_GIT_VERSION "Use git commit info as version" OFF)
|
||||
option(WITH_ADDRSANITIZER "Build with address sanitizer unix only" OFF)
|
||||
option(WITH_THREADSANITIZER "Build with thread sanitizer unix only" OFF)
|
||||
option(BUILD_TESTING "Build tests" OFF)
|
||||
|
||||
IF(BUILD_TESTING)
|
||||
enable_testing()
|
||||
ENDIF()
|
||||
|
||||
# paths
|
||||
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules")
|
||||
set(CMAKE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/..")
|
||||
set(CMAKE_SOURCE_DIR "..")
|
||||
|
||||
# Handle paths nicely
|
||||
#Handle paths nicely
|
||||
include(GNUInstallDirs)
|
||||
|
||||
# architecture
|
||||
@@ -39,11 +33,13 @@ target_architecture(ARCHITECTURE)
|
||||
|
||||
set(LIBI2PD_SRC_DIR ../libi2pd)
|
||||
set(LIBI2PD_CLIENT_SRC_DIR ../libi2pd_client)
|
||||
set(WEBCONSOLE_SRC_DIR ../libi2pd_webconsole)
|
||||
set(LANG_SRC_DIR ../i18n)
|
||||
set(DAEMON_SRC_DIR ../daemon)
|
||||
|
||||
include_directories(${LIBI2PD_SRC_DIR})
|
||||
include_directories(${LIBI2PD_CLIENT_SRC_DIR})
|
||||
include_directories(${WEBCONSOLE_SRC_DIR})
|
||||
include_directories(${LANG_SRC_DIR})
|
||||
include_directories(${DAEMON_SRC_DIR})
|
||||
|
||||
@@ -74,6 +70,18 @@ if(WITH_LIBRARY)
|
||||
COMPONENT Libraries)
|
||||
endif()
|
||||
|
||||
FILE(GLOB WEBCONSOLE_SRC ${WEBCONSOLE_SRC_DIR}/*.cpp)
|
||||
add_library(libi2pdwebconsole ${WEBCONSOLE_SRC})
|
||||
set_target_properties(libi2pdwebconsole PROPERTIES PREFIX "")
|
||||
|
||||
if(WITH_LIBRARY)
|
||||
install(TARGETS libi2pdwebconsole
|
||||
EXPORT libi2pdwebconsole
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
COMPONENT Libraries)
|
||||
endif()
|
||||
|
||||
FILE(GLOB LANG_SRC ${LANG_SRC_DIR}/*.cpp)
|
||||
add_library(libi2pdlang ${LANG_SRC})
|
||||
set_target_properties(libi2pdlang PROPERTIES PREFIX "")
|
||||
@@ -88,9 +96,7 @@ endif()
|
||||
|
||||
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"
|
||||
)
|
||||
@@ -99,12 +105,6 @@ if(WITH_UPNP)
|
||||
add_definitions(-DUSE_UPNP)
|
||||
endif()
|
||||
|
||||
if(WITH_GIT_VERSION)
|
||||
include(GetGitRevisionDescription)
|
||||
git_describe(GIT_VERSION)
|
||||
add_definitions(-DGITVER="${GIT_VERSION}")
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
add_definitions(-DMAC_OSX)
|
||||
endif()
|
||||
@@ -148,7 +148,7 @@ endif()
|
||||
|
||||
# compiler flags customization(by system)
|
||||
if(UNIX)
|
||||
list(APPEND DAEMON_SRC "${DAEMON_SRC_DIR}/UnixDaemon.cpp")
|
||||
list(APPEND DAEMON_SRC "${DAEMON_SRC_DIR}/DaemonUnix.cpp")
|
||||
if(NOT(CMAKE_SYSTEM_NAME STREQUAL "OpenBSD" OR APPLE))
|
||||
# "'sleep_for' is not a member of 'std::this_thread'" in gcc 4.7/4.8
|
||||
add_definitions("-D_GLIBCXX_USE_NANOSLEEP=1")
|
||||
@@ -176,11 +176,6 @@ if(WITH_THREADSANITIZER)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Use std::atomic instead of GCC builtins on macOS PowerPC:
|
||||
# For more information refer to: https://github.com/PurpleI2P/i2pd/issues/1726#issuecomment-1306335111
|
||||
if(APPLE AND CMAKE_OSX_ARCHITECTURES MATCHES "ppc")
|
||||
add_definitions(-DBOOST_SP_USE_STD_ATOMIC)
|
||||
endif()
|
||||
|
||||
# libraries
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
@@ -189,7 +184,6 @@ find_package(Threads REQUIRED)
|
||||
if(WITH_STATIC)
|
||||
set(Boost_USE_STATIC_LIBS ON)
|
||||
set(Boost_USE_STATIC_RUNTIME ON)
|
||||
set(OPENSSL_USE_STATIC_LIBS ON)
|
||||
set(BUILD_SHARED_LIBS OFF)
|
||||
if(${CMAKE_CXX_COMPILER} MATCHES ".*-openwrt-.*")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")
|
||||
@@ -204,6 +198,8 @@ else()
|
||||
add_definitions(-DBOOST_SYSTEM_DYN_LINK -DBOOST_FILESYSTEM_DYN_LINK -DBOOST_PROGRAM_OPTIONS_DYN_LINK -DBOOST_DATE_TIME_DYN_LINK -DBOOST_REGEX_DYN_LINK)
|
||||
endif()
|
||||
|
||||
target_link_libraries(libi2pdwebconsole libi2pdclient libi2pd libi2pdlang)
|
||||
|
||||
find_package(Boost COMPONENTS system filesystem program_options date_time REQUIRED)
|
||||
if(NOT DEFINED Boost_INCLUDE_DIRS)
|
||||
message(SEND_ERROR "Boost is not found, or your boost version was below 1.46. Please download Boost!")
|
||||
@@ -252,7 +248,6 @@ message(STATUS " LIBRARY : ${WITH_LIBRARY}")
|
||||
message(STATUS " BINARY : ${WITH_BINARY}")
|
||||
message(STATUS " STATIC BUILD : ${WITH_STATIC}")
|
||||
message(STATUS " UPnP : ${WITH_UPNP}")
|
||||
message(STATUS " GIT VERSION : ${WITH_GIT_VERSION}")
|
||||
message(STATUS " ADDRSANITIZER : ${WITH_ADDRSANITIZER}")
|
||||
message(STATUS " THREADSANITIZER : ${WITH_THREADSANITIZER}")
|
||||
message(STATUS "---------------------------------------")
|
||||
@@ -268,23 +263,25 @@ if(WITH_BINARY)
|
||||
set_target_properties("${PROJECT_NAME}" PROPERTIES LINK_FLAGS "-z relro -z now")
|
||||
endif()
|
||||
|
||||
if(WITH_UPNP)
|
||||
set(UPNP_LIB ${MINIUPNPC_LIBRARY})
|
||||
endif()
|
||||
|
||||
# FindBoost pulls pthread for thread which is broken for static linking at least on Ubuntu 15.04
|
||||
list(GET Boost_LIBRARIES -1 LAST_Boost_LIBRARIES)
|
||||
if(${LAST_Boost_LIBRARIES} MATCHES ".*pthread.*")
|
||||
list(REMOVE_AT Boost_LIBRARIES -1)
|
||||
endif()
|
||||
|
||||
|
||||
if(WITH_STATIC)
|
||||
set(DL_LIB ${CMAKE_DL_LIBS})
|
||||
endif()
|
||||
|
||||
target_link_libraries("${PROJECT_NAME}" libi2pd libi2pdclient libi2pdlang ${Boost_LIBRARIES} OpenSSL::SSL OpenSSL::Crypto ${MINIUPNPC_LIBRARY} ZLIB::ZLIB Threads::Threads ${DL_LIB} ${CMAKE_REQUIRED_LIBRARIES})
|
||||
target_link_libraries(libi2pd ${Boost_LIBRARIES} ${ZLIB_LIBRARY})
|
||||
target_link_libraries("${PROJECT_NAME}" libi2pdwebconsole libi2pd libi2pdclient libi2pdlang ${DL_LIB} ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} ${UPNP_LIB} ${ZLIB_LIBRARY} Threads::Threads ${DL_LIB} ${CMAKE_REQUIRED_LIBRARIES})
|
||||
|
||||
install(TARGETS "${PROJECT_NAME}" RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT Runtime)
|
||||
set(APPS "\${CMAKE_INSTALL_PREFIX}/bin/${PROJECT_NAME}${CMAKE_EXECUTABLE_SUFFIX}")
|
||||
set(DIRS "${Boost_LIBRARY_DIR};${OPENSSL_INCLUDE_DIR}/../bin;${ZLIB_INCLUDE_DIR}/../bin;/mingw32/bin")
|
||||
endif()
|
||||
|
||||
if(BUILD_TESTING)
|
||||
add_subdirectory(${CMAKE_SOURCE_DIR}/tests ${CMAKE_CURRENT_BINARY_DIR}/tests)
|
||||
endif()
|
||||
|
||||
@@ -1,23 +1,18 @@
|
||||
# atomic builtins are required for threading support.
|
||||
|
||||
INCLUDE(CheckCXXSourceCompiles)
|
||||
INCLUDE(CheckLibraryExists)
|
||||
|
||||
# Sometimes linking against libatomic is required for atomic ops, if
|
||||
# the platform doesn't support lock-free atomics.
|
||||
|
||||
function(check_working_cxx_atomics varname)
|
||||
set(OLD_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS})
|
||||
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -std=c++11")
|
||||
set(CMAKE_REQUIRED_FLAGS "-std=c++11")
|
||||
CHECK_CXX_SOURCE_COMPILES("
|
||||
#include <atomic>
|
||||
std::atomic<int> x;
|
||||
std::atomic<short> y;
|
||||
std::atomic<char> z;
|
||||
int main() {
|
||||
++z;
|
||||
++y;
|
||||
return ++x;
|
||||
return x;
|
||||
}
|
||||
" ${varname})
|
||||
set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQUIRED_FLAGS})
|
||||
@@ -32,7 +27,6 @@ function(check_working_cxx_atomics64 varname)
|
||||
std::atomic<uint64_t> x (0);
|
||||
int main() {
|
||||
uint64_t i = x.load(std::memory_order_relaxed);
|
||||
(void)i;
|
||||
return 0;
|
||||
}
|
||||
" ${varname})
|
||||
@@ -40,16 +34,15 @@ int main() {
|
||||
endfunction(check_working_cxx_atomics64)
|
||||
|
||||
|
||||
# Check for (non-64-bit) atomic operations.
|
||||
if(MSVC)
|
||||
set(HAVE_CXX_ATOMICS_WITHOUT_LIB True)
|
||||
else()
|
||||
# This isn't necessary on MSVC, so avoid command-line switch annoyance
|
||||
# by only running on GCC-like hosts.
|
||||
if (LLVM_COMPILER_IS_GCC_COMPATIBLE)
|
||||
# First check if atomics work without the library.
|
||||
check_working_cxx_atomics(HAVE_CXX_ATOMICS_WITHOUT_LIB)
|
||||
# If not, check if the library exists, and atomics work with it.
|
||||
if(NOT HAVE_CXX_ATOMICS_WITHOUT_LIB)
|
||||
check_library_exists(atomic __atomic_fetch_add_4 "" HAVE_LIBATOMIC)
|
||||
if(HAVE_LIBATOMIC)
|
||||
if( HAVE_LIBATOMIC )
|
||||
list(APPEND CMAKE_REQUIRED_LIBRARIES "atomic")
|
||||
check_working_cxx_atomics(HAVE_CXX_ATOMICS_WITH_LIB)
|
||||
if (NOT HAVE_CXX_ATOMICS_WITH_LIB)
|
||||
@@ -65,20 +58,20 @@ endif()
|
||||
if(MSVC)
|
||||
set(HAVE_CXX_ATOMICS64_WITHOUT_LIB True)
|
||||
else()
|
||||
# First check if atomics work without the library.
|
||||
check_working_cxx_atomics64(HAVE_CXX_ATOMICS64_WITHOUT_LIB)
|
||||
# If not, check if the library exists, and atomics work with it.
|
||||
if(NOT HAVE_CXX_ATOMICS64_WITHOUT_LIB)
|
||||
check_library_exists(atomic __atomic_load_8 "" HAVE_CXX_LIBATOMICS64)
|
||||
if(HAVE_CXX_LIBATOMICS64)
|
||||
list(APPEND CMAKE_REQUIRED_LIBRARIES "atomic")
|
||||
check_working_cxx_atomics64(HAVE_CXX_ATOMICS64_WITH_LIB)
|
||||
if (NOT HAVE_CXX_ATOMICS64_WITH_LIB)
|
||||
message(FATAL_ERROR "Host compiler must support 64-bit std::atomic!")
|
||||
endif()
|
||||
else()
|
||||
message(FATAL_ERROR "Host compiler appears to require libatomic for 64-bit operations, but cannot find it.")
|
||||
endif()
|
||||
|
||||
# If not, check if the library exists, and atomics work with it.
|
||||
if(NOT HAVE_CXX_ATOMICS64_WITHOUT_LIB)
|
||||
check_library_exists(atomic __atomic_load_8 "" HAVE_CXX_LIBATOMICS64)
|
||||
if(HAVE_CXX_LIBATOMICS64)
|
||||
list(APPEND CMAKE_REQUIRED_LIBRARIES "atomic")
|
||||
check_working_cxx_atomics64(HAVE_CXX_ATOMICS64_WITH_LIB)
|
||||
if (NOT HAVE_CXX_ATOMICS64_WITH_LIB)
|
||||
message(FATAL_ERROR "Host compiler must support std::atomic!")
|
||||
endif()
|
||||
else()
|
||||
message(FATAL_ERROR "Host compiler appears to require libatomic, but cannot find it.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -87,6 +80,7 @@ endif()
|
||||
## assumes C++11 <atomic> works.
|
||||
CHECK_CXX_SOURCE_COMPILES("
|
||||
#ifdef _MSC_VER
|
||||
#include <Intrin.h> /* Workaround for PR19898. */
|
||||
#include <windows.h>
|
||||
#endif
|
||||
int main() {
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
# - Try to find the CHECK libraries
|
||||
# Once done this will define
|
||||
#
|
||||
# CHECK_FOUND - system has check
|
||||
# CHECK_INCLUDE_DIRS - the check include directory
|
||||
# CHECK_LIBRARIES - check library
|
||||
#
|
||||
# Copyright (c) 2007 Daniel Gollub <gollub@b1-systems.de>
|
||||
# Copyright (c) 2007-2009 Bjoern Ricks <bjoern.ricks@gmail.com>
|
||||
#
|
||||
# Redistribution and use is allowed according to the terms of the New
|
||||
# BSD license.
|
||||
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
|
||||
|
||||
|
||||
INCLUDE( FindPkgConfig )
|
||||
|
||||
IF ( Check_FIND_REQUIRED )
|
||||
SET( _pkgconfig_REQUIRED "REQUIRED" )
|
||||
ELSE( Check_FIND_REQUIRED )
|
||||
SET( _pkgconfig_REQUIRED "" )
|
||||
ENDIF ( Check_FIND_REQUIRED )
|
||||
|
||||
IF ( CHECK_MIN_VERSION )
|
||||
PKG_SEARCH_MODULE( CHECK ${_pkgconfig_REQUIRED} check>=${CHECK_MIN_VERSION} )
|
||||
ELSE ( CHECK_MIN_VERSION )
|
||||
PKG_SEARCH_MODULE( CHECK ${_pkgconfig_REQUIRED} check )
|
||||
ENDIF ( CHECK_MIN_VERSION )
|
||||
|
||||
# Look for CHECK include dir and libraries
|
||||
IF( NOT CHECK_FOUND AND NOT PKG_CONFIG_FOUND )
|
||||
|
||||
FIND_PATH( CHECK_INCLUDE_DIRS check.h )
|
||||
|
||||
FIND_LIBRARY( CHECK_LIBRARIES NAMES check )
|
||||
|
||||
IF ( CHECK_INCLUDE_DIRS AND CHECK_LIBRARIES )
|
||||
SET( CHECK_FOUND 1 )
|
||||
IF ( NOT Check_FIND_QUIETLY )
|
||||
MESSAGE ( STATUS "Found CHECK: ${CHECK_LIBRARIES}" )
|
||||
ENDIF ( NOT Check_FIND_QUIETLY )
|
||||
ELSE ( CHECK_INCLUDE_DIRS AND CHECK_LIBRARIES )
|
||||
IF ( Check_FIND_REQUIRED )
|
||||
MESSAGE( FATAL_ERROR "Could NOT find CHECK" )
|
||||
ELSE ( Check_FIND_REQUIRED )
|
||||
IF ( NOT Check_FIND_QUIETLY )
|
||||
MESSAGE( STATUS "Could NOT find CHECK" )
|
||||
ENDIF ( NOT Check_FIND_QUIETLY )
|
||||
ENDIF ( Check_FIND_REQUIRED )
|
||||
ENDIF ( CHECK_INCLUDE_DIRS AND CHECK_LIBRARIES )
|
||||
ENDIF( NOT CHECK_FOUND AND NOT PKG_CONFIG_FOUND )
|
||||
|
||||
# Hide advanced variables from CMake GUIs
|
||||
MARK_AS_ADVANCED( CHECK_INCLUDE_DIRS CHECK_LIBRARIES )
|
||||
|
||||
@@ -1,284 +0,0 @@
|
||||
# - Returns a version string from Git
|
||||
#
|
||||
# These functions force a re-configure on each git commit so that you can
|
||||
# trust the values of the variables in your build system.
|
||||
#
|
||||
# get_git_head_revision(<refspecvar> <hashvar> [ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR])
|
||||
#
|
||||
# Returns the refspec and sha hash of the current head revision
|
||||
#
|
||||
# git_describe(<var> [<additional arguments to git describe> ...])
|
||||
#
|
||||
# Returns the results of git describe on the source tree, and adjusting
|
||||
# the output so that it tests false if an error occurs.
|
||||
#
|
||||
# git_describe_working_tree(<var> [<additional arguments to git describe> ...])
|
||||
#
|
||||
# Returns the results of git describe on the working tree (--dirty option),
|
||||
# and adjusting the output so that it tests false if an error occurs.
|
||||
#
|
||||
# git_get_exact_tag(<var> [<additional arguments to git describe> ...])
|
||||
#
|
||||
# Returns the results of git describe --exact-match on the source tree,
|
||||
# and adjusting the output so that it tests false if there was no exact
|
||||
# matching tag.
|
||||
#
|
||||
# git_local_changes(<var>)
|
||||
#
|
||||
# Returns either "CLEAN" or "DIRTY" with respect to uncommitted changes.
|
||||
# Uses the return code of "git diff-index --quiet HEAD --".
|
||||
# Does not regard untracked files.
|
||||
#
|
||||
# Requires CMake 2.6 or newer (uses the 'function' command)
|
||||
#
|
||||
# Original Author:
|
||||
# 2009-2020 Ryan Pavlik <ryan.pavlik@gmail.com> <abiryan@ryand.net>
|
||||
# http://academic.cleardefinition.com
|
||||
#
|
||||
# Copyright 2009-2013, Iowa State University.
|
||||
# Copyright 2013-2020, Ryan Pavlik
|
||||
# Copyright 2013-2020, Contributors
|
||||
# SPDX-License-Identifier: BSL-1.0
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE_1_0.txt or copy at
|
||||
# http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
if(__get_git_revision_description)
|
||||
return()
|
||||
endif()
|
||||
set(__get_git_revision_description YES)
|
||||
|
||||
# We must run the following at "include" time, not at function call time,
|
||||
# to find the path to this module rather than the path to a calling list file
|
||||
get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH)
|
||||
|
||||
# Function _git_find_closest_git_dir finds the next closest .git directory
|
||||
# that is part of any directory in the path defined by _start_dir.
|
||||
# The result is returned in the parent scope variable whose name is passed
|
||||
# as variable _git_dir_var. If no .git directory can be found, the
|
||||
# function returns an empty string via _git_dir_var.
|
||||
#
|
||||
# Example: Given a path C:/bla/foo/bar and assuming C:/bla/.git exists and
|
||||
# neither foo nor bar contain a file/directory .git. This wil return
|
||||
# C:/bla/.git
|
||||
#
|
||||
function(_git_find_closest_git_dir _start_dir _git_dir_var)
|
||||
set(cur_dir "${_start_dir}")
|
||||
set(git_dir "${_start_dir}/.git")
|
||||
while(NOT EXISTS "${git_dir}")
|
||||
# .git dir not found, search parent directories
|
||||
set(git_previous_parent "${cur_dir}")
|
||||
get_filename_component(cur_dir "${cur_dir}" DIRECTORY)
|
||||
if(cur_dir STREQUAL git_previous_parent)
|
||||
# We have reached the root directory, we are not in git
|
||||
set(${_git_dir_var}
|
||||
""
|
||||
PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
set(git_dir "${cur_dir}/.git")
|
||||
endwhile()
|
||||
set(${_git_dir_var}
|
||||
"${git_dir}"
|
||||
PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(get_git_head_revision _refspecvar _hashvar)
|
||||
_git_find_closest_git_dir("${CMAKE_CURRENT_SOURCE_DIR}" GIT_DIR)
|
||||
|
||||
if("${ARGN}" STREQUAL "ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR")
|
||||
set(ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR TRUE)
|
||||
else()
|
||||
set(ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR FALSE)
|
||||
endif()
|
||||
if(NOT "${GIT_DIR}" STREQUAL "")
|
||||
file(RELATIVE_PATH _relative_to_source_dir "${CMAKE_SOURCE_DIR}"
|
||||
"${GIT_DIR}")
|
||||
if("${_relative_to_source_dir}" MATCHES "[.][.]" AND NOT ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR)
|
||||
# We've gone above the CMake root dir.
|
||||
set(GIT_DIR "")
|
||||
endif()
|
||||
endif()
|
||||
if("${GIT_DIR}" STREQUAL "")
|
||||
set(${_refspecvar}
|
||||
"GITDIR-NOTFOUND"
|
||||
PARENT_SCOPE)
|
||||
set(${_hashvar}
|
||||
"GITDIR-NOTFOUND"
|
||||
PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
# Check if the current source dir is a git submodule or a worktree.
|
||||
# In both cases .git is a file instead of a directory.
|
||||
#
|
||||
if(NOT IS_DIRECTORY ${GIT_DIR})
|
||||
# The following git command will return a non empty string that
|
||||
# points to the super project working tree if the current
|
||||
# source dir is inside a git submodule.
|
||||
# Otherwise the command will return an empty string.
|
||||
#
|
||||
execute_process(
|
||||
COMMAND "${GIT_EXECUTABLE}" rev-parse
|
||||
--show-superproject-working-tree
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
OUTPUT_VARIABLE out
|
||||
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
if(NOT "${out}" STREQUAL "")
|
||||
# If out is empty, GIT_DIR/CMAKE_CURRENT_SOURCE_DIR is in a submodule
|
||||
file(READ ${GIT_DIR} submodule)
|
||||
string(REGEX REPLACE "gitdir: (.*)$" "\\1" GIT_DIR_RELATIVE
|
||||
${submodule})
|
||||
string(STRIP ${GIT_DIR_RELATIVE} GIT_DIR_RELATIVE)
|
||||
get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH)
|
||||
get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE}
|
||||
ABSOLUTE)
|
||||
set(HEAD_SOURCE_FILE "${GIT_DIR}/HEAD")
|
||||
else()
|
||||
# GIT_DIR/CMAKE_CURRENT_SOURCE_DIR is in a worktree
|
||||
file(READ ${GIT_DIR} worktree_ref)
|
||||
# The .git directory contains a path to the worktree information directory
|
||||
# inside the parent git repo of the worktree.
|
||||
#
|
||||
string(REGEX REPLACE "gitdir: (.*)$" "\\1" git_worktree_dir
|
||||
${worktree_ref})
|
||||
string(STRIP ${git_worktree_dir} git_worktree_dir)
|
||||
_git_find_closest_git_dir("${git_worktree_dir}" GIT_DIR)
|
||||
set(HEAD_SOURCE_FILE "${git_worktree_dir}/HEAD")
|
||||
endif()
|
||||
else()
|
||||
set(HEAD_SOURCE_FILE "${GIT_DIR}/HEAD")
|
||||
endif()
|
||||
set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data")
|
||||
if(NOT EXISTS "${GIT_DATA}")
|
||||
file(MAKE_DIRECTORY "${GIT_DATA}")
|
||||
endif()
|
||||
|
||||
if(NOT EXISTS "${HEAD_SOURCE_FILE}")
|
||||
return()
|
||||
endif()
|
||||
set(HEAD_FILE "${GIT_DATA}/HEAD")
|
||||
configure_file("${HEAD_SOURCE_FILE}" "${HEAD_FILE}" COPYONLY)
|
||||
|
||||
configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in"
|
||||
"${GIT_DATA}/grabRef.cmake" @ONLY)
|
||||
include("${GIT_DATA}/grabRef.cmake")
|
||||
|
||||
set(${_refspecvar}
|
||||
"${HEAD_REF}"
|
||||
PARENT_SCOPE)
|
||||
set(${_hashvar}
|
||||
"${HEAD_HASH}"
|
||||
PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(git_describe _var)
|
||||
if(NOT GIT_FOUND)
|
||||
find_package(Git QUIET)
|
||||
endif()
|
||||
get_git_head_revision(refspec hash)
|
||||
if(NOT GIT_FOUND)
|
||||
set(${_var}
|
||||
"GIT-NOTFOUND"
|
||||
PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
if(NOT hash)
|
||||
set(${_var}
|
||||
"HEAD-HASH-NOTFOUND"
|
||||
PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
# TODO sanitize
|
||||
#if((${ARGN}" MATCHES "&&") OR
|
||||
# (ARGN MATCHES "||") OR
|
||||
# (ARGN MATCHES "\\;"))
|
||||
# message("Please report the following error to the project!")
|
||||
# message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}")
|
||||
#endif()
|
||||
|
||||
#message(STATUS "Arguments to execute_process: ${ARGN}")
|
||||
|
||||
execute_process(
|
||||
COMMAND "${GIT_EXECUTABLE}" describe --tags --always ${hash} ${ARGN}
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
RESULT_VARIABLE res
|
||||
OUTPUT_VARIABLE out
|
||||
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
if(NOT res EQUAL 0)
|
||||
set(out "${out}-${res}-NOTFOUND")
|
||||
endif()
|
||||
|
||||
set(${_var}
|
||||
"${out}"
|
||||
PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(git_describe_working_tree _var)
|
||||
if(NOT GIT_FOUND)
|
||||
find_package(Git QUIET)
|
||||
endif()
|
||||
if(NOT GIT_FOUND)
|
||||
set(${_var}
|
||||
"GIT-NOTFOUND"
|
||||
PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
execute_process(
|
||||
COMMAND "${GIT_EXECUTABLE}" describe --dirty ${ARGN}
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
RESULT_VARIABLE res
|
||||
OUTPUT_VARIABLE out
|
||||
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
if(NOT res EQUAL 0)
|
||||
set(out "${out}-${res}-NOTFOUND")
|
||||
endif()
|
||||
|
||||
set(${_var}
|
||||
"${out}"
|
||||
PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(git_get_exact_tag _var)
|
||||
git_describe(out --exact-match ${ARGN})
|
||||
set(${_var}
|
||||
"${out}"
|
||||
PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(git_local_changes _var)
|
||||
if(NOT GIT_FOUND)
|
||||
find_package(Git QUIET)
|
||||
endif()
|
||||
get_git_head_revision(refspec hash)
|
||||
if(NOT GIT_FOUND)
|
||||
set(${_var}
|
||||
"GIT-NOTFOUND"
|
||||
PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
if(NOT hash)
|
||||
set(${_var}
|
||||
"HEAD-HASH-NOTFOUND"
|
||||
PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
execute_process(
|
||||
COMMAND "${GIT_EXECUTABLE}" diff-index --quiet HEAD --
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
RESULT_VARIABLE res
|
||||
OUTPUT_VARIABLE out
|
||||
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
if(res EQUAL 0)
|
||||
set(${_var}
|
||||
"CLEAN"
|
||||
PARENT_SCOPE)
|
||||
else()
|
||||
set(${_var}
|
||||
"DIRTY"
|
||||
PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
@@ -1,43 +0,0 @@
|
||||
#
|
||||
# Internal file for GetGitRevisionDescription.cmake
|
||||
#
|
||||
# Requires CMake 2.6 or newer (uses the 'function' command)
|
||||
#
|
||||
# Original Author:
|
||||
# 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
|
||||
# http://academic.cleardefinition.com
|
||||
# Iowa State University HCI Graduate Program/VRAC
|
||||
#
|
||||
# Copyright 2009-2012, Iowa State University
|
||||
# Copyright 2011-2015, Contributors
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE_1_0.txt or copy at
|
||||
# http://www.boost.org/LICENSE_1_0.txt)
|
||||
# SPDX-License-Identifier: BSL-1.0
|
||||
|
||||
set(HEAD_HASH)
|
||||
|
||||
file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024)
|
||||
|
||||
string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS)
|
||||
if(HEAD_CONTENTS MATCHES "ref")
|
||||
# named branch
|
||||
string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}")
|
||||
if(EXISTS "@GIT_DIR@/${HEAD_REF}")
|
||||
configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY)
|
||||
else()
|
||||
configure_file("@GIT_DIR@/packed-refs" "@GIT_DATA@/packed-refs" COPYONLY)
|
||||
file(READ "@GIT_DATA@/packed-refs" PACKED_REFS)
|
||||
if(${PACKED_REFS} MATCHES "([0-9a-z]*) ${HEAD_REF}")
|
||||
set(HEAD_HASH "${CMAKE_MATCH_1}")
|
||||
endif()
|
||||
endif()
|
||||
else()
|
||||
# detached HEAD
|
||||
configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY)
|
||||
endif()
|
||||
|
||||
if(NOT HEAD_HASH)
|
||||
file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024)
|
||||
string(STRIP "${HEAD_HASH}" HEAD_HASH)
|
||||
endif()
|
||||
@@ -61,7 +61,7 @@ set(archdetect_c_code "
|
||||
#else
|
||||
#error cmake_ARCH mips
|
||||
#endif
|
||||
#elif defined(__ppc__) || defined(__ppc) || defined(__powerpc__) || defined(__POWERPC__) \\
|
||||
#elif defined(__ppc__) || defined(__ppc) || 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,18 +1,5 @@
|
||||
#
|
||||
# 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 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
|
||||
LABEL authors "Mikal Villa <mikal@sigterm.no>, Darknet Villain <supervillain@riseup.net>"
|
||||
|
||||
# Expose git branch, tag and URL variables as arguments
|
||||
ARG GIT_BRANCH="openssl"
|
||||
@@ -24,28 +11,27 @@ ENV REPO_URL=${REPO_URL}
|
||||
|
||||
ENV I2PD_HOME="/home/i2pd"
|
||||
ENV DATA_DIR="${I2PD_HOME}/data"
|
||||
ENV DEFAULT_ARGS=" --datadir=$DATA_DIR"
|
||||
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"
|
||||
|
||||
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.
|
||||
# Each RUN is a layer, adding the dependencies and building i2pd in one layer takes around 8-900Mb, so to keep the
|
||||
# image under 20mb we need to remove all the build dependencies in the same "RUN" / layer.
|
||||
#
|
||||
|
||||
# 1. install deps, clone and build.
|
||||
# 2. strip binaries.
|
||||
# 3. Purge all dependencies and other unrelated packages, including build directory.
|
||||
RUN apk update \
|
||||
&& apk --no-cache --virtual build-dependendencies add make gcc g++ libtool zlib-dev boost-dev build-base openssl-dev openssl miniupnpc-dev git \
|
||||
&& mkdir -p /tmp/build \
|
||||
&& cd /tmp/build && git clone -b ${GIT_BRANCH} ${REPO_URL} \
|
||||
&& cd i2pd \
|
||||
&& if [ -n "${GIT_TAG}" ]; then git checkout tags/${GIT_TAG}; fi \
|
||||
&& make -j$(nproc) USE_UPNP=yes \
|
||||
&& make USE_UPNP=yes \
|
||||
&& cp -R contrib/certificates /i2pd_certificates \
|
||||
&& mkdir -p /usr/local/bin \
|
||||
&& mv i2pd /usr/local/bin \
|
||||
@@ -59,9 +45,6 @@ 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 "$DATA_DIR/i2pd.conf"
|
||||
RUN chown i2pd:nobody "$DATA_DIR/i2pd.conf"
|
||||
COPY entrypoint.sh /entrypoint.sh
|
||||
RUN chmod a+x /entrypoint.sh
|
||||
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
## Preconfigured i2pd configuration file for a Docker container
|
||||
## See https://i2pd.readthedocs.io/en/latest/user-guide/configuration/
|
||||
## for more options you can use in this file.
|
||||
|
||||
## Note that for exposing ports outside of container you need to bind all services to 0.0.0.0
|
||||
|
||||
log = file
|
||||
loglevel = none
|
||||
|
||||
ipv4 = true
|
||||
ipv6 = false
|
||||
|
||||
# bandwidth = L
|
||||
# notransit = false
|
||||
# floodfill = false
|
||||
|
||||
[ntcp2]
|
||||
enabled = true
|
||||
published = true
|
||||
|
||||
[ssu2]
|
||||
enabled = true
|
||||
published = true
|
||||
|
||||
[http]
|
||||
enabled = true
|
||||
address = 0.0.0.0
|
||||
port = 7070
|
||||
|
||||
[httpproxy]
|
||||
enabled = true
|
||||
address = 0.0.0.0
|
||||
port = 4444
|
||||
|
||||
[socksproxy]
|
||||
enabled = true
|
||||
address = 0.0.0.0
|
||||
port = 4447
|
||||
|
||||
[sam]
|
||||
enabled = true
|
||||
address = 0.0.0.0
|
||||
port = 7656
|
||||
|
||||
[upnp]
|
||||
enabled = false
|
||||
|
||||
[reseed]
|
||||
verify = true
|
||||
|
||||
[limits]
|
||||
# transittunnels = 2500
|
||||
@@ -1,13 +1,13 @@
|
||||
# i2pd
|
||||
# Copyright (C) 2021-2023 PurpleI2P team
|
||||
# Copyright (C) 2021 PurpleI2P team
|
||||
# This file is distributed under the same license as the i2pd package.
|
||||
# R4SAS <r4sas@i2pmail.org>, 2021-2023.
|
||||
# R4SAS <r4sas@i2pmail.org>, 2021.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: i2pd\n"
|
||||
"Report-Msgid-Bugs-To: https://github.com/PurpleI2P/i2pd/issues\n"
|
||||
"POT-Creation-Date: 2023-01-19 04:18\n"
|
||||
"POT-Creation-Date: 2021-08-06 17:12\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
@@ -18,748 +18,706 @@ msgstr ""
|
||||
"X-Poedit-SearchPath-0: daemon/HTTPServer.cpp\n"
|
||||
"X-Poedit-SearchPath-1: libi2pd_client/HTTPProxy.cpp\n"
|
||||
|
||||
#: daemon/HTTPServer.cpp:106
|
||||
#, c-format
|
||||
msgid "%d day"
|
||||
msgid_plural "%d days"
|
||||
#: daemon/HTTPServer.cpp:177
|
||||
msgid "day"
|
||||
msgid_plural "days"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:110
|
||||
#, c-format
|
||||
msgid "%d hour"
|
||||
msgid_plural "%d hours"
|
||||
#: daemon/HTTPServer.cpp:181
|
||||
msgid "hour"
|
||||
msgid_plural "hours"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:114
|
||||
#, c-format
|
||||
msgid "%d minute"
|
||||
msgid_plural "%d minutes"
|
||||
#: daemon/HTTPServer.cpp:185
|
||||
msgid "minute"
|
||||
msgid_plural "minutes"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:117
|
||||
#, c-format
|
||||
msgid "%d second"
|
||||
msgid_plural "%d seconds"
|
||||
#: daemon/HTTPServer.cpp:188
|
||||
msgid "second"
|
||||
msgid_plural "seconds"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#. tr: Kibibyte
|
||||
#: daemon/HTTPServer.cpp:125 daemon/HTTPServer.cpp:153
|
||||
#, c-format
|
||||
msgid "%.2f KiB"
|
||||
#. tr: Kibibit
|
||||
#: daemon/HTTPServer.cpp:196 daemon/HTTPServer.cpp:224
|
||||
msgid "KiB"
|
||||
msgstr ""
|
||||
|
||||
#. tr: Mebibyte
|
||||
#: daemon/HTTPServer.cpp:127
|
||||
#, c-format
|
||||
msgid "%.2f MiB"
|
||||
#. tr: Mebibit
|
||||
#: daemon/HTTPServer.cpp:198
|
||||
msgid "MiB"
|
||||
msgstr ""
|
||||
|
||||
#. tr: Gibibyte
|
||||
#: daemon/HTTPServer.cpp:129
|
||||
#, c-format
|
||||
msgid "%.2f GiB"
|
||||
#. tr: Gibibit
|
||||
#: daemon/HTTPServer.cpp:200
|
||||
msgid "GiB"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:146
|
||||
#: daemon/HTTPServer.cpp:217
|
||||
msgid "building"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:147
|
||||
#: daemon/HTTPServer.cpp:218
|
||||
msgid "failed"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:148
|
||||
#: daemon/HTTPServer.cpp:219
|
||||
msgid "expiring"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:149
|
||||
#: daemon/HTTPServer.cpp:220
|
||||
msgid "established"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:150
|
||||
#: daemon/HTTPServer.cpp:221
|
||||
msgid "unknown"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:152
|
||||
#: daemon/HTTPServer.cpp:223
|
||||
msgid "exploratory"
|
||||
msgstr ""
|
||||
|
||||
#. tr: Webconsole page title
|
||||
#: daemon/HTTPServer.cpp:183
|
||||
msgid "Purple I2P Webconsole"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:188
|
||||
#: daemon/HTTPServer.cpp:259
|
||||
msgid "<b>i2pd</b> webconsole"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:191
|
||||
#: daemon/HTTPServer.cpp:262
|
||||
msgid "Main page"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:192 daemon/HTTPServer.cpp:712
|
||||
#: daemon/HTTPServer.cpp:263 daemon/HTTPServer.cpp:725
|
||||
msgid "Router commands"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:193 daemon/HTTPServer.cpp:387
|
||||
#: daemon/HTTPServer.cpp:399
|
||||
#: daemon/HTTPServer.cpp:264 daemon/HTTPServer.cpp:448
|
||||
#: daemon/HTTPServer.cpp:460
|
||||
msgid "Local Destinations"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:195 daemon/HTTPServer.cpp:357
|
||||
#: daemon/HTTPServer.cpp:443 daemon/HTTPServer.cpp:449
|
||||
#: daemon/HTTPServer.cpp:609 daemon/HTTPServer.cpp:652
|
||||
#: daemon/HTTPServer.cpp:656
|
||||
#: 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
|
||||
msgid "LeaseSets"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:197 daemon/HTTPServer.cpp:662
|
||||
#: daemon/HTTPServer.cpp:268 daemon/HTTPServer.cpp:694
|
||||
msgid "Tunnels"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:199 daemon/HTTPServer.cpp:364
|
||||
#: daemon/HTTPServer.cpp:781 daemon/HTTPServer.cpp:797
|
||||
#: daemon/HTTPServer.cpp:269 daemon/HTTPServer.cpp:425
|
||||
#: daemon/HTTPServer.cpp:787 daemon/HTTPServer.cpp:803
|
||||
msgid "Transit Tunnels"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:201 daemon/HTTPServer.cpp:855
|
||||
#: daemon/HTTPServer.cpp:270 daemon/HTTPServer.cpp:852
|
||||
msgid "Transports"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:202
|
||||
#: daemon/HTTPServer.cpp:271
|
||||
msgid "I2P tunnels"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:204 daemon/HTTPServer.cpp:884
|
||||
#: daemon/HTTPServer.cpp:894
|
||||
#: daemon/HTTPServer.cpp:273 daemon/HTTPServer.cpp:914
|
||||
#: daemon/HTTPServer.cpp:924
|
||||
msgid "SAM sessions"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:220 daemon/HTTPServer.cpp:1278
|
||||
#: daemon/HTTPServer.cpp:1281 daemon/HTTPServer.cpp:1284
|
||||
#: daemon/HTTPServer.cpp:1298 daemon/HTTPServer.cpp:1343
|
||||
#: daemon/HTTPServer.cpp:1346 daemon/HTTPServer.cpp:1349
|
||||
#: 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
|
||||
msgid "ERROR"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:227
|
||||
#: daemon/HTTPServer.cpp:296
|
||||
msgid "OK"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:228
|
||||
#: daemon/HTTPServer.cpp:297
|
||||
msgid "Testing"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:229
|
||||
#: daemon/HTTPServer.cpp:298
|
||||
msgid "Firewalled"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:230 daemon/HTTPServer.cpp:233
|
||||
#: daemon/HTTPServer.cpp:329
|
||||
#: daemon/HTTPServer.cpp:299 daemon/HTTPServer.cpp:320
|
||||
#: daemon/HTTPServer.cpp:406
|
||||
msgid "Unknown"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:231 daemon/HTTPServer.cpp:374
|
||||
#: daemon/HTTPServer.cpp:375 daemon/HTTPServer.cpp:952
|
||||
#: daemon/HTTPServer.cpp:961
|
||||
#: daemon/HTTPServer.cpp:300 daemon/HTTPServer.cpp:435
|
||||
#: daemon/HTTPServer.cpp:436 daemon/HTTPServer.cpp:982
|
||||
#: daemon/HTTPServer.cpp:991
|
||||
msgid "Proxy"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:232
|
||||
#: daemon/HTTPServer.cpp:301
|
||||
msgid "Mesh"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:240
|
||||
#: daemon/HTTPServer.cpp:304
|
||||
msgid "Error"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:308
|
||||
msgid "Clock skew"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:243
|
||||
#: daemon/HTTPServer.cpp:311
|
||||
msgid "Offline"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:246
|
||||
#: daemon/HTTPServer.cpp:314
|
||||
msgid "Symmetric NAT"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:249
|
||||
msgid "Full cone NAT"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:252
|
||||
msgid "No Descriptors"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:261
|
||||
#: daemon/HTTPServer.cpp:326
|
||||
msgid "Uptime"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:264
|
||||
#: daemon/HTTPServer.cpp:329
|
||||
msgid "Network status"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:269
|
||||
#: daemon/HTTPServer.cpp:334
|
||||
msgid "Network status v6"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:275 daemon/HTTPServer.cpp:282
|
||||
#: daemon/HTTPServer.cpp:340 daemon/HTTPServer.cpp:347
|
||||
msgid "Stopping in"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:289
|
||||
#: daemon/HTTPServer.cpp:354
|
||||
msgid "Family"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:290
|
||||
#: daemon/HTTPServer.cpp:355
|
||||
msgid "Tunnel creation success rate"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:291
|
||||
#: daemon/HTTPServer.cpp:356
|
||||
msgid "Received"
|
||||
msgstr ""
|
||||
|
||||
#. tr: Kibibyte/s
|
||||
#: daemon/HTTPServer.cpp:293 daemon/HTTPServer.cpp:296
|
||||
#: daemon/HTTPServer.cpp:299
|
||||
#, c-format
|
||||
msgid "%.2f KiB/s"
|
||||
#. tr: Kibibit/s
|
||||
#: daemon/HTTPServer.cpp:358 daemon/HTTPServer.cpp:361
|
||||
#: daemon/HTTPServer.cpp:364
|
||||
msgid "KiB/s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:294
|
||||
#: daemon/HTTPServer.cpp:359
|
||||
msgid "Sent"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:297
|
||||
#: daemon/HTTPServer.cpp:362
|
||||
msgid "Transit"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:300
|
||||
#: daemon/HTTPServer.cpp:365
|
||||
msgid "Data path"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:303
|
||||
#: daemon/HTTPServer.cpp:368
|
||||
msgid "Hidden content. Press on text to see."
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:307
|
||||
#: daemon/HTTPServer.cpp:371
|
||||
msgid "Router Ident"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:309
|
||||
#: daemon/HTTPServer.cpp:373
|
||||
msgid "Router Family"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:310
|
||||
#: daemon/HTTPServer.cpp:374
|
||||
msgid "Router Caps"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:311
|
||||
#: daemon/HTTPServer.cpp:375
|
||||
msgid "Version"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:312
|
||||
#: daemon/HTTPServer.cpp:376
|
||||
msgid "Our external address"
|
||||
msgstr ""
|
||||
|
||||
#. tr: Shown when router doesn't publish itself and have "Firewalled" state
|
||||
#: daemon/HTTPServer.cpp:341
|
||||
#: daemon/HTTPServer.cpp:384
|
||||
msgid "supported"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:355
|
||||
#: daemon/HTTPServer.cpp:416
|
||||
msgid "Routers"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:356
|
||||
#: daemon/HTTPServer.cpp:417
|
||||
msgid "Floodfills"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:363 daemon/HTTPServer.cpp:938
|
||||
#: daemon/HTTPServer.cpp:424 daemon/HTTPServer.cpp:968
|
||||
msgid "Client Tunnels"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:373
|
||||
#: daemon/HTTPServer.cpp:434
|
||||
msgid "Services"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:374 daemon/HTTPServer.cpp:375
|
||||
#: daemon/HTTPServer.cpp:376 daemon/HTTPServer.cpp:377
|
||||
#: daemon/HTTPServer.cpp:378 daemon/HTTPServer.cpp:379
|
||||
#: daemon/HTTPServer.cpp:435 daemon/HTTPServer.cpp:436
|
||||
#: daemon/HTTPServer.cpp:437 daemon/HTTPServer.cpp:438
|
||||
#: daemon/HTTPServer.cpp:439 daemon/HTTPServer.cpp:440
|
||||
msgid "Enabled"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:374 daemon/HTTPServer.cpp:375
|
||||
#: daemon/HTTPServer.cpp:376 daemon/HTTPServer.cpp:377
|
||||
#: daemon/HTTPServer.cpp:378 daemon/HTTPServer.cpp:379
|
||||
#: daemon/HTTPServer.cpp:435 daemon/HTTPServer.cpp:436
|
||||
#: daemon/HTTPServer.cpp:437 daemon/HTTPServer.cpp:438
|
||||
#: daemon/HTTPServer.cpp:439 daemon/HTTPServer.cpp:440
|
||||
msgid "Disabled"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:422
|
||||
#: daemon/HTTPServer.cpp:483
|
||||
msgid "Encrypted B33 address"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:431
|
||||
#: daemon/HTTPServer.cpp:492
|
||||
msgid "Address registration line"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:436
|
||||
#: daemon/HTTPServer.cpp:497
|
||||
msgid "Domain"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:437
|
||||
#: daemon/HTTPServer.cpp:498
|
||||
msgid "Generate"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:438
|
||||
#: daemon/HTTPServer.cpp:499
|
||||
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:444
|
||||
#: daemon/HTTPServer.cpp:505
|
||||
msgid "Address"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:444
|
||||
#: daemon/HTTPServer.cpp:505
|
||||
msgid "Type"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:444
|
||||
#: daemon/HTTPServer.cpp:505
|
||||
msgid "EncType"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:454 daemon/HTTPServer.cpp:667
|
||||
#: daemon/HTTPServer.cpp:515 daemon/HTTPServer.cpp:699
|
||||
msgid "Inbound tunnels"
|
||||
msgstr ""
|
||||
|
||||
#. tr: Milliseconds
|
||||
#: daemon/HTTPServer.cpp:469 daemon/HTTPServer.cpp:489
|
||||
#: daemon/HTTPServer.cpp:681 daemon/HTTPServer.cpp:701
|
||||
#, c-format
|
||||
msgid "%dms"
|
||||
#: daemon/HTTPServer.cpp:520 daemon/HTTPServer.cpp:530
|
||||
#: daemon/HTTPServer.cpp:704 daemon/HTTPServer.cpp:714
|
||||
msgid "ms"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:474 daemon/HTTPServer.cpp:686
|
||||
#: daemon/HTTPServer.cpp:525 daemon/HTTPServer.cpp:709
|
||||
msgid "Outbound tunnels"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:496
|
||||
#: daemon/HTTPServer.cpp:537
|
||||
msgid "Tags"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:497
|
||||
#: daemon/HTTPServer.cpp:537
|
||||
msgid "Incoming"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:504 daemon/HTTPServer.cpp:510
|
||||
#: daemon/HTTPServer.cpp:544 daemon/HTTPServer.cpp:547
|
||||
msgid "Outgoing"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:507 daemon/HTTPServer.cpp:526
|
||||
#: daemon/HTTPServer.cpp:545 daemon/HTTPServer.cpp:561
|
||||
msgid "Destination"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:507
|
||||
#: daemon/HTTPServer.cpp:545
|
||||
msgid "Amount"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:515
|
||||
#: daemon/HTTPServer.cpp:552
|
||||
msgid "Incoming Tags"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:523 daemon/HTTPServer.cpp:529
|
||||
#: daemon/HTTPServer.cpp:560 daemon/HTTPServer.cpp:563
|
||||
msgid "Tags sessions"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:526
|
||||
#: daemon/HTTPServer.cpp:561
|
||||
msgid "Status"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:536 daemon/HTTPServer.cpp:594
|
||||
#: daemon/HTTPServer.cpp:570 daemon/HTTPServer.cpp:626
|
||||
msgid "Local Destination"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:547 daemon/HTTPServer.cpp:917
|
||||
#: daemon/HTTPServer.cpp:580 daemon/HTTPServer.cpp:947
|
||||
msgid "Streams"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:570
|
||||
#: daemon/HTTPServer.cpp:602
|
||||
msgid "Close stream"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:599
|
||||
#: daemon/HTTPServer.cpp:631
|
||||
msgid "I2CP session not found"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:602
|
||||
#: daemon/HTTPServer.cpp:634
|
||||
msgid "I2CP is not enabled"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:628
|
||||
#: daemon/HTTPServer.cpp:660
|
||||
msgid "Invalid"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:631
|
||||
#: daemon/HTTPServer.cpp:663
|
||||
msgid "Store type"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:632
|
||||
#: daemon/HTTPServer.cpp:664
|
||||
msgid "Expires"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:637
|
||||
#: daemon/HTTPServer.cpp:669
|
||||
msgid "Non Expired Leases"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:640
|
||||
#: daemon/HTTPServer.cpp:672
|
||||
msgid "Gateway"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:641
|
||||
#: daemon/HTTPServer.cpp:673
|
||||
msgid "TunnelID"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:642
|
||||
#: daemon/HTTPServer.cpp:674
|
||||
msgid "EndDate"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:652
|
||||
msgid "floodfill mode is disabled"
|
||||
#: daemon/HTTPServer.cpp:684
|
||||
msgid "not floodfill"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:663
|
||||
#: daemon/HTTPServer.cpp:695
|
||||
msgid "Queue size"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:713
|
||||
#: daemon/HTTPServer.cpp:726
|
||||
msgid "Run peer test"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:714
|
||||
msgid "Reload tunnels configuration"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:717
|
||||
#: daemon/HTTPServer.cpp:731
|
||||
msgid "Decline transit tunnels"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:719
|
||||
#: daemon/HTTPServer.cpp:733
|
||||
msgid "Accept transit tunnels"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:723 daemon/HTTPServer.cpp:728
|
||||
#: daemon/HTTPServer.cpp:737 daemon/HTTPServer.cpp:742
|
||||
msgid "Cancel graceful shutdown"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:725 daemon/HTTPServer.cpp:730
|
||||
#: daemon/HTTPServer.cpp:739 daemon/HTTPServer.cpp:744
|
||||
msgid "Start graceful shutdown"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:733
|
||||
#: daemon/HTTPServer.cpp:747
|
||||
msgid "Force shutdown"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:734
|
||||
#: daemon/HTTPServer.cpp:748
|
||||
msgid "Reload external CSS styles"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:737
|
||||
#: daemon/HTTPServer.cpp:751
|
||||
msgid ""
|
||||
"<b>Note:</b> any action done here are not persistent and not changes your "
|
||||
"config files."
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:739
|
||||
#: daemon/HTTPServer.cpp:753
|
||||
msgid "Logging level"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:747
|
||||
#: daemon/HTTPServer.cpp:761
|
||||
msgid "Transit tunnels limit"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:752 daemon/HTTPServer.cpp:771
|
||||
#: daemon/HTTPServer.cpp:766 daemon/HTTPServer.cpp:778
|
||||
msgid "Change"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:759
|
||||
#: daemon/HTTPServer.cpp:770
|
||||
msgid "Change language"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:797
|
||||
#: daemon/HTTPServer.cpp:803
|
||||
msgid "no transit tunnels currently built"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:878 daemon/HTTPServer.cpp:901
|
||||
#: daemon/HTTPServer.cpp:908 daemon/HTTPServer.cpp:931
|
||||
msgid "SAM disabled"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:894
|
||||
#: daemon/HTTPServer.cpp:924
|
||||
msgid "no sessions currently running"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:907
|
||||
#: daemon/HTTPServer.cpp:937
|
||||
msgid "SAM session not found"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:912
|
||||
#: daemon/HTTPServer.cpp:942
|
||||
msgid "SAM Session"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:969
|
||||
#: daemon/HTTPServer.cpp:999
|
||||
msgid "Server Tunnels"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:985
|
||||
#: daemon/HTTPServer.cpp:1015
|
||||
msgid "Client Forwards"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:999
|
||||
#: daemon/HTTPServer.cpp:1029
|
||||
msgid "Server Forwards"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1199
|
||||
#: daemon/HTTPServer.cpp:1227
|
||||
msgid "Unknown page"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1218
|
||||
#: daemon/HTTPServer.cpp:1246
|
||||
msgid "Invalid token"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1276 daemon/HTTPServer.cpp:1333
|
||||
#: daemon/HTTPServer.cpp:1373
|
||||
#: daemon/HTTPServer.cpp:1304 daemon/HTTPServer.cpp:1361
|
||||
#: daemon/HTTPServer.cpp:1401
|
||||
msgid "SUCCESS"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1276
|
||||
#: daemon/HTTPServer.cpp:1304
|
||||
msgid "Stream closed"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1278
|
||||
#: daemon/HTTPServer.cpp:1306
|
||||
msgid "Stream not found or already was closed"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1281
|
||||
#: daemon/HTTPServer.cpp:1309
|
||||
msgid "Destination not found"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1284
|
||||
#: daemon/HTTPServer.cpp:1312
|
||||
msgid "StreamID can't be null"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1286 daemon/HTTPServer.cpp:1351
|
||||
#: daemon/HTTPServer.cpp:1314 daemon/HTTPServer.cpp:1379
|
||||
msgid "Return to destination page"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1287 daemon/HTTPServer.cpp:1300
|
||||
#: daemon/HTTPServer.cpp:1375
|
||||
#, c-format
|
||||
msgid "You will be redirected in %d seconds"
|
||||
#: daemon/HTTPServer.cpp:1315 daemon/HTTPServer.cpp:1328
|
||||
#: daemon/HTTPServer.cpp:1403
|
||||
msgid "You will be redirected in 5 seconds"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1298
|
||||
#, c-format
|
||||
msgid "Transit tunnels count must not exceed %d"
|
||||
#: daemon/HTTPServer.cpp:1326
|
||||
msgid "Transit tunnels count must not exceed 65535"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1299 daemon/HTTPServer.cpp:1374
|
||||
#: daemon/HTTPServer.cpp:1327 daemon/HTTPServer.cpp:1402
|
||||
msgid "Back to commands list"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1335
|
||||
#: daemon/HTTPServer.cpp:1363
|
||||
msgid "Register at reg.i2p"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1336
|
||||
#: daemon/HTTPServer.cpp:1364
|
||||
msgid "Description"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1336
|
||||
#: daemon/HTTPServer.cpp:1364
|
||||
msgid "A bit information about service on domain"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1337
|
||||
#: daemon/HTTPServer.cpp:1365
|
||||
msgid "Submit"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1343
|
||||
#: daemon/HTTPServer.cpp:1371
|
||||
msgid "Domain can't end with .b32.i2p"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1346
|
||||
#: daemon/HTTPServer.cpp:1374
|
||||
msgid "Domain must end with .i2p"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1349
|
||||
#: daemon/HTTPServer.cpp:1377
|
||||
msgid "Such destination is not found"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1369
|
||||
#: daemon/HTTPServer.cpp:1397
|
||||
msgid "Unknown command"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1373
|
||||
#: daemon/HTTPServer.cpp:1401
|
||||
msgid "Command accepted"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:166
|
||||
#: libi2pd_client/HTTPProxy.cpp:157
|
||||
msgid "Proxy error"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:174
|
||||
#: libi2pd_client/HTTPProxy.cpp:165
|
||||
msgid "Proxy info"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:182
|
||||
#: libi2pd_client/HTTPProxy.cpp:173
|
||||
msgid "Proxy error: Host not found"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:183
|
||||
#: libi2pd_client/HTTPProxy.cpp:174
|
||||
msgid "Remote host not found in router's addressbook"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:184
|
||||
#: libi2pd_client/HTTPProxy.cpp:175
|
||||
msgid "You may try to find this host on jump services below"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:309 libi2pd_client/HTTPProxy.cpp:324
|
||||
#: libi2pd_client/HTTPProxy.cpp:392 libi2pd_client/HTTPProxy.cpp:435
|
||||
#: libi2pd_client/HTTPProxy.cpp:273 libi2pd_client/HTTPProxy.cpp:288
|
||||
#: libi2pd_client/HTTPProxy.cpp:322 libi2pd_client/HTTPProxy.cpp:365
|
||||
msgid "Invalid request"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:309
|
||||
#: libi2pd_client/HTTPProxy.cpp:273
|
||||
msgid "Proxy unable to parse your request"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:324
|
||||
msgid "Addresshelper is not supported"
|
||||
#: libi2pd_client/HTTPProxy.cpp:288
|
||||
msgid "addresshelper is not supported"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:349
|
||||
#, c-format
|
||||
msgid ""
|
||||
"Host %s is <font color=red>already in router's addressbook</font>. <b>Be "
|
||||
"careful: source of this URL may be harmful!</b> Click here to update record: "
|
||||
"<a href=\"%s%s%s&update=true\">Continue</a>."
|
||||
#: libi2pd_client/HTTPProxy.cpp:297 libi2pd_client/HTTPProxy.cpp:306
|
||||
#: libi2pd_client/HTTPProxy.cpp:385
|
||||
msgid "Host"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:351
|
||||
msgid "Addresshelper forced update rejected"
|
||||
#: libi2pd_client/HTTPProxy.cpp:297
|
||||
msgid "added to router's addressbook from helper"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:358
|
||||
#, c-format
|
||||
msgid ""
|
||||
"To add host <b>%s</b> in router's addressbook, click here: <a href=\"%s%s%s"
|
||||
"\">Continue</a>."
|
||||
#: libi2pd_client/HTTPProxy.cpp:298
|
||||
msgid "Click here to proceed:"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:360
|
||||
msgid "Addresshelper request"
|
||||
#: libi2pd_client/HTTPProxy.cpp:298 libi2pd_client/HTTPProxy.cpp:308
|
||||
msgid "Continue"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:369
|
||||
#, c-format
|
||||
msgid ""
|
||||
"Host %s added to router's addressbook from helper. Click here to proceed: <a "
|
||||
"href=\"%s\">Continue</a>."
|
||||
#: libi2pd_client/HTTPProxy.cpp:299 libi2pd_client/HTTPProxy.cpp:309
|
||||
msgid "Addresshelper found"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:370
|
||||
msgid "Addresshelper adding"
|
||||
#: libi2pd_client/HTTPProxy.cpp:306
|
||||
msgid "already in router's addressbook"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:377
|
||||
#, c-format
|
||||
msgid ""
|
||||
"Host %s is <font color=red>already in router's addressbook</font>. Click "
|
||||
"here to update record: <a href=\"%s%s%s&update=true\">Continue</a>."
|
||||
#: libi2pd_client/HTTPProxy.cpp:307
|
||||
msgid "Click here to update record:"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:379
|
||||
msgid "Addresshelper update"
|
||||
#: libi2pd_client/HTTPProxy.cpp:322
|
||||
msgid "invalid request uri"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:392
|
||||
msgid "Invalid request URI"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:435
|
||||
#: libi2pd_client/HTTPProxy.cpp:365
|
||||
msgid "Can't detect destination host from request"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:452 libi2pd_client/HTTPProxy.cpp:456
|
||||
#: libi2pd_client/HTTPProxy.cpp:382 libi2pd_client/HTTPProxy.cpp:386
|
||||
msgid "Outproxy failure"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:452
|
||||
msgid "Bad outproxy settings"
|
||||
#: libi2pd_client/HTTPProxy.cpp:382
|
||||
msgid "bad outproxy settings"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:455
|
||||
#, c-format
|
||||
msgid "Host %s is not inside I2P network, but outproxy is not enabled"
|
||||
#: libi2pd_client/HTTPProxy.cpp:385
|
||||
msgid "not inside I2P network, but outproxy is not enabled"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:544
|
||||
msgid "Unknown outproxy URL"
|
||||
#: libi2pd_client/HTTPProxy.cpp:474
|
||||
msgid "unknown outproxy url"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:550
|
||||
msgid "Cannot resolve upstream proxy"
|
||||
#: libi2pd_client/HTTPProxy.cpp:480
|
||||
msgid "cannot resolve upstream proxy"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:558
|
||||
msgid "Hostname is too long"
|
||||
#: libi2pd_client/HTTPProxy.cpp:488
|
||||
msgid "hostname too long"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:585
|
||||
msgid "Cannot connect to upstream SOCKS proxy"
|
||||
#: libi2pd_client/HTTPProxy.cpp:515
|
||||
msgid "cannot connect to upstream socks proxy"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:591
|
||||
msgid "Cannot negotiate with SOCKS proxy"
|
||||
#: libi2pd_client/HTTPProxy.cpp:521
|
||||
msgid "Cannot negotiate with socks proxy"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:633
|
||||
#: libi2pd_client/HTTPProxy.cpp:563
|
||||
msgid "CONNECT error"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:633
|
||||
msgid "Failed to connect"
|
||||
#: libi2pd_client/HTTPProxy.cpp:563
|
||||
msgid "Failed to Connect"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:644 libi2pd_client/HTTPProxy.cpp:670
|
||||
msgid "SOCKS proxy error"
|
||||
#: libi2pd_client/HTTPProxy.cpp:574 libi2pd_client/HTTPProxy.cpp:600
|
||||
msgid "socks proxy error"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:652
|
||||
msgid "Failed to send request to upstream"
|
||||
#: libi2pd_client/HTTPProxy.cpp:582
|
||||
msgid "failed to send request to upstream"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:673
|
||||
msgid "No reply from SOCKS proxy"
|
||||
#: libi2pd_client/HTTPProxy.cpp:603
|
||||
msgid "No Reply From socks proxy"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:680
|
||||
msgid "Cannot connect"
|
||||
#: libi2pd_client/HTTPProxy.cpp:610
|
||||
msgid "cannot connect"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:680
|
||||
msgid "HTTP out proxy not implemented"
|
||||
#: libi2pd_client/HTTPProxy.cpp:610
|
||||
msgid "http out proxy not implemented"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:681
|
||||
msgid "Cannot connect to upstream HTTP proxy"
|
||||
#: libi2pd_client/HTTPProxy.cpp:611
|
||||
msgid "cannot connect to upstream http proxy"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:714
|
||||
#: libi2pd_client/HTTPProxy.cpp:644
|
||||
msgid "Host is down"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:714
|
||||
#: libi2pd_client/HTTPProxy.cpp:644
|
||||
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\]\ \"(.*)\"\n(msgstr\[1\]\ \"(.*)\"\n)?(msgstr\[2\]\ \"(.*)\"\n)?(msgstr\[3\]\ \"(.*)\"\n)?(msgstr\[4\]\ \"(.*)\"\n)?(msgstr\[5\]\ \"(.*)\"\n)?
|
||||
out: #{"$2", {"$3", "$5", "$7", "$9", "$11"}},\n
|
||||
in: msgid\ \"(.*)\"\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
|
||||
```
|
||||
|
||||
```
|
||||
|
||||
@@ -75,8 +75,8 @@ ipv4 = true
|
||||
## Enable communication through ipv6
|
||||
ipv6 = false
|
||||
|
||||
## Enable SSU transport
|
||||
ssu = false
|
||||
## Enable SSU transport (default = true)
|
||||
# ssu = true
|
||||
|
||||
## Bandwidth configuration
|
||||
## L limit bandwidth to 32KBs/sec, O - to 256KBs/sec, P - to 2048KBs/sec,
|
||||
@@ -96,22 +96,6 @@ ssu = 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
|
||||
@@ -122,14 +106,12 @@ port = 7070
|
||||
## Path to web console, default "/"
|
||||
# webroot = /
|
||||
## Uncomment following lines to enable Web Console authentication
|
||||
## You should not use Web Console via public networks without additional encryption.
|
||||
## HTTP authentication is not encryption layer!
|
||||
# auth = true
|
||||
# user = i2pd
|
||||
# pass = changeme
|
||||
## Select webconsole language
|
||||
## Currently supported english (default), afrikaans, armenian, chinese, czech, french,
|
||||
## german, italian, russian, spanish, turkmen, ukrainian and uzbek languages
|
||||
## Currently supported english (default), afrikaans, armenian, french, german,
|
||||
## russian, turkmen, ukrainian and uzbek languages
|
||||
# lang = english
|
||||
|
||||
[httpproxy]
|
||||
@@ -141,8 +123,6 @@ port = 4444
|
||||
## Optional keys file for proxy local destination
|
||||
# keys = http-proxy-keys.dat
|
||||
## Enable address helper for adding .i2p domains with "jump URLs" (default: true)
|
||||
## You should disable this feature if your i2pd HTTP Proxy is public,
|
||||
## because anyone could spoof the short domain via addresshelper and forward other users to phishing links
|
||||
# addresshelper = true
|
||||
## Address of a proxy server inside I2P, which is used to visit regular Internet
|
||||
# outproxy = http://false.i2p
|
||||
@@ -241,9 +221,8 @@ verify = true
|
||||
# subscriptions = http://reg.i2p/hosts.txt,http://identiguy.i2p/hosts.txt,http://stats.i2p/cgi-bin/newhosts.txt,http://rus.i2p/hosts.txt
|
||||
|
||||
[limits]
|
||||
## Maximum active transit sessions (default: 5000)
|
||||
## This value is doubled if floodfill mode is enabled!
|
||||
# transittunnels = 5000
|
||||
## Maximum active transit sessions (default:2500)
|
||||
# transittunnels = 2500
|
||||
## Limit number of open file descriptors (0 - use system limit)
|
||||
# openfiles = 0
|
||||
## Maximum size of corefile in Kb (0 - use system limit)
|
||||
|
||||
@@ -29,7 +29,7 @@ SendSIGKILL=yes
|
||||
#TimeoutStopSec=10m
|
||||
|
||||
# If you have problems with hanging i2pd, you can try increase this
|
||||
LimitNOFILE=8192
|
||||
LimitNOFILE=4096
|
||||
# To enable write of coredump uncomment this
|
||||
#LimitCORE=infinity
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
%define git_hash %(git rev-parse HEAD | cut -c -7)
|
||||
|
||||
Name: i2pd-git
|
||||
Version: 2.45.1
|
||||
Version: 2.42.1
|
||||
Release: git%{git_hash}%{?dist}
|
||||
Summary: I2P router written in C++
|
||||
Conflicts: i2pd
|
||||
@@ -62,7 +62,9 @@ pushd redhat-linux-build
|
||||
%endif
|
||||
|
||||
%if 0%{?fedora} >= 35
|
||||
%if 0%{?fedora} < 37
|
||||
pushd redhat-linux-build
|
||||
%endif
|
||||
%else
|
||||
%if 0%{?fedora} >= 33
|
||||
pushd %{_target_platform}
|
||||
@@ -80,8 +82,10 @@ popd
|
||||
%endif
|
||||
|
||||
%if 0%{?fedora} >= 33
|
||||
%if 0%{?fedora} < 37
|
||||
popd
|
||||
%endif
|
||||
%endif
|
||||
|
||||
%if 0%{?mageia} > 7
|
||||
popd
|
||||
@@ -95,7 +99,9 @@ pushd redhat-linux-build
|
||||
%endif
|
||||
|
||||
%if 0%{?fedora} >= 35
|
||||
%if 0%{?fedora} < 37
|
||||
pushd redhat-linux-build
|
||||
%endif
|
||||
%else
|
||||
%if 0%{?fedora} >= 33
|
||||
pushd %{_target_platform}
|
||||
@@ -158,18 +164,6 @@ getent passwd i2pd >/dev/null || \
|
||||
|
||||
|
||||
%changelog
|
||||
* Wed Jan 11 2023 orignal <orignal@i2pmail.org> - 2.45.1
|
||||
- update to 2.45.1
|
||||
|
||||
* Tue Jan 3 2023 orignal <orignal@i2pmail.org> - 2.45.0
|
||||
- update to 2.45.0
|
||||
|
||||
* 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.45.1
|
||||
Version: 2.42.1
|
||||
Release: 1%{?dist}
|
||||
Summary: I2P router written in C++
|
||||
Conflicts: i2pd-git
|
||||
@@ -59,7 +59,9 @@ pushd redhat-linux-build
|
||||
%endif
|
||||
|
||||
%if 0%{?fedora} >= 35
|
||||
%if 0%{?fedora} < 37
|
||||
pushd redhat-linux-build
|
||||
%endif
|
||||
%else
|
||||
%if 0%{?fedora} >= 33
|
||||
pushd %{_target_platform}
|
||||
@@ -77,8 +79,10 @@ popd
|
||||
%endif
|
||||
|
||||
%if 0%{?fedora} >= 33
|
||||
%if 0%{?fedora} < 37
|
||||
popd
|
||||
%endif
|
||||
%endif
|
||||
|
||||
%if 0%{?mageia} > 7
|
||||
popd
|
||||
@@ -92,7 +96,9 @@ pushd redhat-linux-build
|
||||
%endif
|
||||
|
||||
%if 0%{?fedora} >= 35
|
||||
%if 0%{?fedora} < 37
|
||||
pushd redhat-linux-build
|
||||
%endif
|
||||
%else
|
||||
%if 0%{?fedora} >= 33
|
||||
pushd %{_target_platform}
|
||||
@@ -155,18 +161,6 @@ getent passwd i2pd >/dev/null || \
|
||||
|
||||
|
||||
%changelog
|
||||
* Wed Jan 11 2023 orignal <orignal@i2pmail.org> - 2.45.1
|
||||
- update to 2.45.1
|
||||
|
||||
* Tue Jan 3 2023 orignal <orignal@i2pmail.org> - 2.45.0
|
||||
- update to 2.45.0
|
||||
|
||||
* 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,293 +1,293 @@
|
||||
/*
|
||||
* Copyright (c) 2021-2023, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
* See full license text in LICENSE file at top of project tree
|
||||
*
|
||||
******************************************************************
|
||||
*
|
||||
* This is style sheet for webconsole, with @media selectors for adaptive
|
||||
* view on desktop and mobile devices, respecting preferred user's color
|
||||
* scheme used in system/browser.
|
||||
*
|
||||
* Minified copy of that style sheet is bundled inside i2pd sources.
|
||||
*/
|
||||
|
||||
:root {
|
||||
--main-bg-color: #fafafa;
|
||||
--main-text-color: #103456;
|
||||
--main-link-color: #894c84;
|
||||
--main-link-hover-color: #fafafa;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--main-bg-color: #242424;
|
||||
--main-text-color: #17ab5c;
|
||||
--main-link-color: #bf64b7;
|
||||
--main-link-hover-color: #000000;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
font: 100%/1.5em sans-serif;
|
||||
margin: 0;
|
||||
padding: 1.5em;
|
||||
background: var(--main-bg-color);
|
||||
color: var(--main-text-color);
|
||||
}
|
||||
|
||||
a, .slide label {
|
||||
text-decoration: none;
|
||||
color: var(--main-link-color);
|
||||
}
|
||||
|
||||
a:hover, a.button.selected, .slide label:hover, button[type=submit]:hover {
|
||||
color: var(--main-link-hover-color);
|
||||
background: var(--main-link-color);
|
||||
}
|
||||
|
||||
a.button {
|
||||
appearance: button;
|
||||
text-decoration: none;
|
||||
padding: 0 5px;
|
||||
border: 1px solid var(--main-link-color);
|
||||
}
|
||||
|
||||
.header {
|
||||
font-size: 2.5em;
|
||||
text-align: center;
|
||||
margin: 1em 0;
|
||||
color: var(--main-link-color);
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
margin: 0 auto;
|
||||
padding: 1em;
|
||||
max-width: 64em;
|
||||
}
|
||||
|
||||
.menu {
|
||||
display: block;
|
||||
float: left;
|
||||
overflow: hidden;
|
||||
padding: 4px;
|
||||
max-width: 12em;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.listitem {
|
||||
display: block;
|
||||
font-family: monospace;
|
||||
font-size: 1.2em;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.tableitem {
|
||||
font-family: monospace;
|
||||
font-size: 1.2em;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.content {
|
||||
float: left;
|
||||
font-size: 1em;
|
||||
margin-left: 2em;
|
||||
padding: 4px;
|
||||
max-width: 50em;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.tunnel.established {
|
||||
color: #56B734;
|
||||
}
|
||||
|
||||
.tunnel.expiring {
|
||||
color: #D3AE3F;
|
||||
}
|
||||
|
||||
.tunnel.failed {
|
||||
color: #D33F3F;
|
||||
}
|
||||
|
||||
.tunnel.building {
|
||||
color: #434343;
|
||||
}
|
||||
|
||||
caption {
|
||||
font-size: 1.5em;
|
||||
text-align: center;
|
||||
color: var(--main-link-color);
|
||||
}
|
||||
|
||||
table {
|
||||
display: table;
|
||||
border-collapse: collapse;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
table.extaddr {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
table.services {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
textarea {
|
||||
background-color: var(--main-bg-color);
|
||||
color: var(--main-text-color);
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.streamdest {
|
||||
width: 120px;
|
||||
max-width: 240px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.slide div.slidecontent, .slide [type="checkbox"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.slide [type="checkbox"]:checked ~ div.slidecontent {
|
||||
display: block;
|
||||
margin-top: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.disabled {
|
||||
color: #D33F3F;
|
||||
}
|
||||
|
||||
.enabled {
|
||||
color: #56B734;
|
||||
}
|
||||
|
||||
button[type=submit] {
|
||||
background-color: transparent;
|
||||
color: var(--main-link-color);
|
||||
text-decoration: none;
|
||||
padding: 5px;
|
||||
border: 1px solid var(--main-link-color);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
input, select, select option {
|
||||
background-color: var(--main-bg-color);
|
||||
color: var(--main-link-color);
|
||||
padding: 5px;
|
||||
border: 1px solid var(--main-link-color);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
input:focus, select:focus, select option:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
input[type=number]::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1150px) { /* adaptive style */
|
||||
.wrapper {
|
||||
max-width: 58em;
|
||||
}
|
||||
|
||||
.content {
|
||||
max-width: 40em;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 980px) {
|
||||
body {
|
||||
font: 100%/1.2em sans-serif;
|
||||
padding: 1.2em 0 0 0;
|
||||
}
|
||||
|
||||
.menu {
|
||||
width: 100%;
|
||||
max-width: unset;
|
||||
display: block;
|
||||
float: none;
|
||||
position: unset;
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.menu a, .commands a {
|
||||
display: inline-block;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.content {
|
||||
float: none;
|
||||
margin-left: unset;
|
||||
margin-top: 16px;
|
||||
max-width: 100%;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
a, .slide label {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.header {
|
||||
margin: unset;
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
small {
|
||||
display: block
|
||||
}
|
||||
|
||||
a.button {
|
||||
appearance: button;
|
||||
text-decoration: none;
|
||||
margin-top: 10px;
|
||||
padding: 6px;
|
||||
border: 2px solid var(--main-link-color);
|
||||
border-radius: 5px;
|
||||
width: -webkit-fill-available;
|
||||
}
|
||||
|
||||
input, select {
|
||||
width: 35%;
|
||||
text-align: center;
|
||||
padding: 5px;
|
||||
border: 2px solid var(--main-link-color);
|
||||
border-radius: 5px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
table.extaddr {
|
||||
margin: auto;
|
||||
text-align: unset;
|
||||
}
|
||||
|
||||
textarea {
|
||||
width: -webkit-fill-available;
|
||||
height: auto;
|
||||
padding: 5px;
|
||||
border: 2px solid var(--main-link-color);
|
||||
border-radius: 5px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
button[type=submit] {
|
||||
padding: 5px 15px;
|
||||
background: transparent;
|
||||
border: 2px solid var(--main-link-color);
|
||||
cursor: pointer;
|
||||
-webkit-border-radius: 5px;
|
||||
border-radius: 5px;
|
||||
position: relative;
|
||||
height: 36px;
|
||||
display: -webkit-inline-box;
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2021-2022, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
* See full license text in LICENSE file at top of project tree
|
||||
*
|
||||
******************************************************************
|
||||
*
|
||||
* This is style sheet for webconsole, with @media selectors for adaptive
|
||||
* view on desktop and mobile devices, respecting preferred user's color
|
||||
* scheme used in system/browser.
|
||||
*
|
||||
* Minified copy of that style sheet is bundled inside i2pd sources.
|
||||
*/
|
||||
|
||||
:root {
|
||||
--main-bg-color: #fafafa;
|
||||
--main-text-color: #103456;
|
||||
--main-link-color: #894c84;
|
||||
--main-link-hover-color: #fafafa;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--main-bg-color: #242424;
|
||||
--main-text-color: #17ab5c;
|
||||
--main-link-color: #bf64b7;
|
||||
--main-link-hover-color: #000000;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
font: 100%/1.5em sans-serif;
|
||||
margin: 0;
|
||||
padding: 1.5em;
|
||||
background: var(--main-bg-color);
|
||||
color: var(--main-text-color);
|
||||
}
|
||||
|
||||
a, .slide label {
|
||||
text-decoration: none;
|
||||
color: var(--main-link-color);
|
||||
}
|
||||
|
||||
a:hover, .slide label:hover, button[type=submit]:hover {
|
||||
color: var(--main-link-hover-color);
|
||||
background: var(--main-link-color);
|
||||
}
|
||||
|
||||
a.button {
|
||||
appearance: button;
|
||||
text-decoration: none;
|
||||
padding: 0 5px;
|
||||
border: 1px solid var(--main-link-color);
|
||||
}
|
||||
|
||||
.header {
|
||||
font-size: 2.5em;
|
||||
text-align: center;
|
||||
margin: 1em 0;
|
||||
color: var(--main-link-color);
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
margin: 0 auto;
|
||||
padding: 1em;
|
||||
max-width: 64em;
|
||||
}
|
||||
|
||||
.menu {
|
||||
display: block;
|
||||
float: left;
|
||||
overflow: hidden;
|
||||
padding: 4px;
|
||||
max-width: 12em;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.listitem {
|
||||
display: block;
|
||||
font-family: monospace;
|
||||
font-size: 1.2em;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.tableitem {
|
||||
font-family: monospace;
|
||||
font-size: 1.2em;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.content {
|
||||
float: left;
|
||||
font-size: 1em;
|
||||
margin-left: 2em;
|
||||
padding: 4px;
|
||||
max-width: 50em;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.tunnel.established {
|
||||
color: #56B734;
|
||||
}
|
||||
|
||||
.tunnel.expiring {
|
||||
color: #D3AE3F;
|
||||
}
|
||||
|
||||
.tunnel.failed {
|
||||
color: #D33F3F;
|
||||
}
|
||||
|
||||
.tunnel.building {
|
||||
color: #434343;
|
||||
}
|
||||
|
||||
caption {
|
||||
font-size: 1.5em;
|
||||
text-align: center;
|
||||
color: var(--main-link-color);
|
||||
}
|
||||
|
||||
table {
|
||||
display: table;
|
||||
border-collapse: collapse;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
table.extaddr {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
table.services {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
textarea {
|
||||
background-color: var(--main-bg-color);
|
||||
color: var(--main-text-color);
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.streamdest {
|
||||
width: 120px;
|
||||
max-width: 240px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.slide div.slidecontent, .slide [type="checkbox"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.slide [type="checkbox"]:checked ~ div.slidecontent {
|
||||
display: block;
|
||||
margin-top: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.disabled {
|
||||
color: #D33F3F;
|
||||
}
|
||||
|
||||
.enabled {
|
||||
color: #56B734;
|
||||
}
|
||||
|
||||
button[type=submit] {
|
||||
background-color: transparent;
|
||||
color: var(--main-link-color);
|
||||
text-decoration: none;
|
||||
padding: 5px;
|
||||
border: 1px solid var(--main-link-color);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
input, select, select option {
|
||||
background-color: var(--main-bg-color);
|
||||
color: var(--main-link-color);
|
||||
padding: 5px;
|
||||
border: 1px solid var(--main-link-color);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
input:focus, select:focus, select option:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
input[type=number]::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1150px) { /* adaptive style */
|
||||
.wrapper {
|
||||
max-width: 58em;
|
||||
}
|
||||
|
||||
.content {
|
||||
max-width: 40em;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 980px) {
|
||||
body {
|
||||
font: 100%/1.2em sans-serif;
|
||||
padding: 1.2em 0 0 0;
|
||||
}
|
||||
|
||||
.menu {
|
||||
width: 100%;
|
||||
max-width: unset;
|
||||
display: block;
|
||||
float: none;
|
||||
position: unset;
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.menu a, .commands a {
|
||||
display: inline-block;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.content {
|
||||
float: none;
|
||||
margin-left: unset;
|
||||
margin-top: 16px;
|
||||
max-width: 100%;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
a, .slide label {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.header {
|
||||
margin: unset;
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
small {
|
||||
display: block
|
||||
}
|
||||
|
||||
a.button {
|
||||
appearance: button;
|
||||
text-decoration: none;
|
||||
margin-top: 10px;
|
||||
padding: 6px;
|
||||
border: 2px solid var(--main-link-color);
|
||||
border-radius: 5px;
|
||||
width: -webkit-fill-available;
|
||||
}
|
||||
|
||||
input, select {
|
||||
width: 35%;
|
||||
text-align: center;
|
||||
padding: 5px;
|
||||
border: 2px solid var(--main-link-color);
|
||||
border-radius: 5px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
table.extaddr {
|
||||
margin: auto;
|
||||
text-align: unset;
|
||||
}
|
||||
|
||||
textarea {
|
||||
width: -webkit-fill-available;
|
||||
height: auto;
|
||||
padding: 5px;
|
||||
border: 2px solid var(--main-link-color);
|
||||
border-radius: 5px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
button[type=submit] {
|
||||
padding: 5px 15px;
|
||||
background: transparent;
|
||||
border: 2px solid var(--main-link-color);
|
||||
cursor: pointer;
|
||||
-webkit-border-radius: 5px;
|
||||
border-radius: 5px;
|
||||
position: relative;
|
||||
height: 36px;
|
||||
display: -webkit-inline-box;
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2023, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "Crypto.h"
|
||||
#include "UPnP.h"
|
||||
#include "Timestamp.h"
|
||||
#include "util.h"
|
||||
#include "I18N.h"
|
||||
|
||||
namespace i2p
|
||||
@@ -152,35 +153,131 @@ 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 ();
|
||||
|
||||
i2p::transport::InitTransports ();
|
||||
bool ipv6; i2p::config::GetOption("ipv6", ipv6);
|
||||
bool ipv4; i2p::config::GetOption("ipv4", ipv4);
|
||||
|
||||
bool isFloodfill; i2p::config::GetOption("floodfill", isFloodfill);
|
||||
if (isFloodfill)
|
||||
// ifname -> address
|
||||
std::string ifname; i2p::config::GetOption("ifname", ifname);
|
||||
if (ipv4 && i2p::config::IsDefault ("address4"))
|
||||
{
|
||||
LogPrint(eLogInfo, "Daemon: Router configured as floodfill");
|
||||
i2p::context.SetFloodfill (true);
|
||||
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
|
||||
}
|
||||
else
|
||||
i2p::context.SetFloodfill (false);
|
||||
|
||||
bool transit; i2p::config::GetOption("notransit", transit);
|
||||
i2p::context.SetAcceptsTunnels (!transit);
|
||||
uint16_t transitTunnels; i2p::config::GetOption("limits.transittunnels", transitTunnels);
|
||||
if (isFloodfill && i2p::config::IsDefault ("limits.transittunnels"))
|
||||
transitTunnels *= 2; // double default number of transit tunnels for floodfill
|
||||
SetMaxNumTransitTunnels (transitTunnels);
|
||||
|
||||
bool isFloodfill; i2p::config::GetOption("floodfill", isFloodfill);
|
||||
if (isFloodfill) {
|
||||
LogPrint(eLogInfo, "Daemon: Router configured as floodfill");
|
||||
i2p::context.SetFloodfill (true);
|
||||
}
|
||||
else
|
||||
{
|
||||
i2p::context.SetFloodfill (false);
|
||||
}
|
||||
|
||||
/* this section also honors 'floodfill' flag, if set above */
|
||||
std::string bandwidth; i2p::config::GetOption("bandwidth", bandwidth);
|
||||
if (bandwidth.length () > 0)
|
||||
@@ -299,14 +396,15 @@ namespace util
|
||||
|
||||
bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2);
|
||||
bool ssu2; i2p::config::GetOption("ssu2.enabled", ssu2);
|
||||
bool ssu; i2p::config::GetOption("ssu", ssu);
|
||||
bool checkInReserved; i2p::config::GetOption("reservedrange", checkInReserved);
|
||||
LogPrint(eLogInfo, "Daemon: Starting Transports");
|
||||
if(!ssu2) LogPrint(eLogInfo, "Daemon: SSU2 disabled");
|
||||
if(!ssu) LogPrint(eLogInfo, "Daemon: SSU disabled");
|
||||
if(!ntcp2) LogPrint(eLogInfo, "Daemon: NTCP2 disabled");
|
||||
|
||||
i2p::transport::transports.SetCheckReserved(checkInReserved);
|
||||
i2p::transport::transports.Start(ntcp2, ssu2);
|
||||
if (i2p::transport::transports.IsBoundSSU2() || i2p::transport::transports.IsBoundNTCP2())
|
||||
i2p::transport::transports.Start(ntcp2, ssu, ssu2);
|
||||
if (i2p::transport::transports.IsBoundSSU() || i2p::transport::transports.IsBoundNTCP2())
|
||||
LogPrint(eLogInfo, "Daemon: Transports started");
|
||||
else
|
||||
{
|
||||
@@ -325,6 +423,10 @@ namespace util
|
||||
try
|
||||
{
|
||||
d.httpServer = std::unique_ptr<i2p::http::HTTPServer>(new i2p::http::HTTPServer(httpAddr, httpPort));
|
||||
d.httpServer->SetDaemonStop (std::bind (Daemon_Singleton::stop, this));
|
||||
#if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY))
|
||||
d.httpServer->SetDaemonGracefulTimer (std::bind (gracefulShutdownInterval, this));
|
||||
#endif
|
||||
d.httpServer->Start();
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
|
||||
@@ -75,14 +75,17 @@ namespace util
|
||||
return instance;
|
||||
}
|
||||
|
||||
bool init(int argc, char* argv[]);
|
||||
bool start();
|
||||
bool stop();
|
||||
bool init (int argc, char* argv[]);
|
||||
bool start ();
|
||||
bool stop ();
|
||||
void run ();
|
||||
|
||||
bool isGraceful;
|
||||
void SetIsGraceful (bool state) { m_isGraceful = state; };
|
||||
const bool GetIsGraceful () { return m_isGraceful; };
|
||||
|
||||
DaemonWin32 ():isGraceful(false) {}
|
||||
private:
|
||||
|
||||
bool m_isGraceful;
|
||||
};
|
||||
#elif (defined(ANDROID) && !defined(ANDROID_BINARY))
|
||||
#define Daemon i2p::util::DaemonAndroid::Instance()
|
||||
@@ -109,8 +112,8 @@ namespace util
|
||||
return instance;
|
||||
}
|
||||
|
||||
bool start();
|
||||
bool stop();
|
||||
bool start ();
|
||||
bool stop ();
|
||||
void run ();
|
||||
|
||||
private:
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
#include "Tunnel.h"
|
||||
#include "RouterContext.h"
|
||||
#include "ClientContext.h"
|
||||
#include "Transports.h"
|
||||
|
||||
void handle_signal(int sig)
|
||||
{
|
||||
@@ -55,14 +54,6 @@ void handle_signal(int sig)
|
||||
case SIGPIPE:
|
||||
LogPrint(eLogInfo, "SIGPIPE received");
|
||||
break;
|
||||
case SIGTSTP:
|
||||
LogPrint(eLogInfo, "Daemon: Got SIGTSTP, disconnecting from network...");
|
||||
i2p::transport::transports.SetOnline(false);
|
||||
break;
|
||||
case SIGCONT:
|
||||
LogPrint(eLogInfo, "Daemon: Got SIGCONT, restoring connection to network...");
|
||||
i2p::transport::transports.SetOnline(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,9 +171,6 @@ namespace i2p
|
||||
}
|
||||
gracefulShutdownInterval = 0; // not specified
|
||||
|
||||
// handle signal TSTP
|
||||
bool handleTSTP; i2p::config::GetOption("unix.handle_sigtstp", handleTSTP);
|
||||
|
||||
// Signal handler
|
||||
struct sigaction sa;
|
||||
sa.sa_handler = handle_signal;
|
||||
@@ -194,11 +182,6 @@ namespace i2p
|
||||
sigaction(SIGTERM, &sa, 0);
|
||||
sigaction(SIGINT, &sa, 0);
|
||||
sigaction(SIGPIPE, &sa, 0);
|
||||
if (handleTSTP)
|
||||
{
|
||||
sigaction(SIGTSTP, &sa, 0);
|
||||
sigaction(SIGCONT, &sa, 0);
|
||||
}
|
||||
|
||||
return Daemon_Singleton::start();
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2023, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
* See full license text in LICENSE file at top of project tree
|
||||
*/
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include <thread>
|
||||
#include <clocale>
|
||||
#include "Config.h"
|
||||
@@ -13,7 +15,6 @@
|
||||
#include "util.h"
|
||||
#include "Log.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "Win32Service.h"
|
||||
#ifdef WIN32_APP
|
||||
#include <windows.h>
|
||||
@@ -29,7 +30,7 @@ namespace util
|
||||
setlocale(LC_CTYPE, "");
|
||||
SetConsoleCP(1251);
|
||||
SetConsoleOutputCP(1251);
|
||||
//setlocale(LC_ALL, "Russian");
|
||||
setlocale(LC_ALL, "Russian");
|
||||
setlocale(LC_TIME, "C");
|
||||
|
||||
i2p::log::SetThrowFunction ([](const std::string& s)
|
||||
@@ -38,6 +39,9 @@ namespace util
|
||||
}
|
||||
);
|
||||
|
||||
i2p::win32::SetIsGraceful (std::bind (&DaemonWin32::SetIsGraceful, this, std::placeholders::_1));
|
||||
i2p::win32::GetIsGraceful (std::bind (&DaemonWin32::GetIsGraceful, this));
|
||||
|
||||
if (!Daemon_Singleton::init(argc, argv))
|
||||
return false;
|
||||
|
||||
@@ -45,6 +49,8 @@ namespace util
|
||||
{
|
||||
LogPrint(eLogDebug, "Daemon: running as service");
|
||||
I2PService service((PSTR)SERVICE_NAME);
|
||||
service.SetDaemonStart (std::bind (&DaemonWin32::start, this));
|
||||
service.SetDaemonStop (std::bind (&DaemonWin32::stop, this));
|
||||
if (!I2PService::Run(service))
|
||||
{
|
||||
LogPrint(eLogError, "Daemon: Service failed to run w/err 0x%08lx\n", GetLastError());
|
||||
@@ -61,7 +67,7 @@ namespace util
|
||||
setlocale(LC_CTYPE, "");
|
||||
SetConsoleCP(1251);
|
||||
SetConsoleOutputCP(1251);
|
||||
//setlocale(LC_ALL, "Russian");
|
||||
setlocale(LC_ALL, "Russian");
|
||||
setlocale(LC_TIME, "C");
|
||||
#ifdef WIN32_APP
|
||||
if (!i2p::win32::StartWin32App ()) return false;
|
||||
@@ -14,17 +14,25 @@
|
||||
// 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 "Tunnel.h"
|
||||
#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 "I2PControl.h"
|
||||
|
||||
namespace i2p
|
||||
@@ -61,18 +69,44 @@ namespace client
|
||||
m_MethodHandlers["Authenticate"] = &I2PControlService::AuthenticateHandler;
|
||||
m_MethodHandlers["Echo"] = &I2PControlService::EchoHandler;
|
||||
m_MethodHandlers["I2PControl"] = &I2PControlService::I2PControlHandler;
|
||||
m_MethodHandlers["RouterInfo"] = &I2PControlHandlers::RouterInfoHandler;
|
||||
m_MethodHandlers["RouterInfo"] = &I2PControlService::RouterInfoHandler;
|
||||
m_MethodHandlers["RouterManager"] = &I2PControlService::RouterManagerHandler;
|
||||
m_MethodHandlers["NetworkSetting"] = &I2PControlHandlers::NetworkSettingHandler;
|
||||
m_MethodHandlers["ClientServicesInfo"] = &I2PControlHandlers::ClientServicesInfoHandler;
|
||||
m_MethodHandlers["NetworkSetting"] = &I2PControlService::NetworkSettingHandler;
|
||||
m_MethodHandlers["ClientServicesInfo"] = &I2PControlService::ClientServicesInfoHandler;
|
||||
|
||||
// I2PControl
|
||||
m_I2PControlHandlers["i2pcontrol.password"] = &I2PControlService::PasswordHandler;
|
||||
|
||||
// RouterInfo
|
||||
m_RouterInfoHandlers["i2p.router.uptime"] = &I2PControlService::UptimeHandler;
|
||||
m_RouterInfoHandlers["i2p.router.version"] = &I2PControlService::VersionHandler;
|
||||
m_RouterInfoHandlers["i2p.router.status"] = &I2PControlService::StatusHandler;
|
||||
m_RouterInfoHandlers["i2p.router.netdb.knownpeers"] = &I2PControlService::NetDbKnownPeersHandler;
|
||||
m_RouterInfoHandlers["i2p.router.netdb.activepeers"] = &I2PControlService::NetDbActivePeersHandler;
|
||||
m_RouterInfoHandlers["i2p.router.net.bw.inbound.1s"] = &I2PControlService::InboundBandwidth1S;
|
||||
m_RouterInfoHandlers["i2p.router.net.bw.outbound.1s"] = &I2PControlService::OutboundBandwidth1S;
|
||||
m_RouterInfoHandlers["i2p.router.net.status"] = &I2PControlService::NetStatusHandler;
|
||||
m_RouterInfoHandlers["i2p.router.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 ()
|
||||
@@ -246,6 +280,37 @@ 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)
|
||||
{
|
||||
@@ -331,6 +396,91 @@ 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
|
||||
|
||||
@@ -382,6 +532,37 @@ 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)
|
||||
{
|
||||
@@ -430,5 +611,178 @@ 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,7 +20,6 @@
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/asio/ssl.hpp>
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
#include "I2PControlHandlers.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
@@ -33,7 +32,7 @@ namespace client
|
||||
const char I2P_CONTROL_CERTIFICATE_COMMON_NAME[] = "i2pd.i2pcontrol";
|
||||
const char I2P_CONTROL_CERTIFICATE_ORGANIZATION[] = "Purple I2P";
|
||||
|
||||
class I2PControlService: public I2PControlHandlers
|
||||
class I2PControlService
|
||||
{
|
||||
typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> ssl_socket;
|
||||
|
||||
@@ -64,24 +63,61 @@ 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;
|
||||
@@ -96,7 +132,10 @@ 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;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,390 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
* See full license text in LICENSE file at top of project tree
|
||||
*/
|
||||
|
||||
#include <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.inbound.15s"] = &I2PControlHandlers::InboundBandwidth15S;
|
||||
m_RouterInfoHandlers["i2p.router.net.bw.outbound.1s"] = &I2PControlHandlers::OutboundBandwidth1S;
|
||||
m_RouterInfoHandlers["i2p.router.net.bw.outbound.15s"] = &I2PControlHandlers::OutboundBandwidth15S;
|
||||
m_RouterInfoHandlers["i2p.router.net.status"] = &I2PControlHandlers::NetStatusHandler;
|
||||
m_RouterInfoHandlers["i2p.router.net.tunnels.participating"] = &I2PControlHandlers::TunnelsParticipatingHandler;
|
||||
m_RouterInfoHandlers["i2p.router.net.tunnels.successrate"] = &I2PControlHandlers::TunnelsSuccessRateHandler;
|
||||
m_RouterInfoHandlers["i2p.router.net.total.received.bytes"] = &I2PControlHandlers::NetTotalReceivedBytes;
|
||||
m_RouterInfoHandlers["i2p.router.net.total.sent.bytes"] = &I2PControlHandlers::NetTotalSentBytes;
|
||||
|
||||
// NetworkSetting
|
||||
m_NetworkSettingHandlers["i2p.router.net.bw.in"] = &I2PControlHandlers::InboundBandwidthLimit;
|
||||
m_NetworkSettingHandlers["i2p.router.net.bw.out"] = &I2PControlHandlers::OutboundBandwidthLimit;
|
||||
|
||||
// ClientServicesInfo
|
||||
m_ClientServicesInfoHandlers["I2PTunnel"] = &I2PControlHandlers::I2PTunnelInfoHandler;
|
||||
m_ClientServicesInfoHandlers["HTTPProxy"] = &I2PControlHandlers::HTTPProxyInfoHandler;
|
||||
m_ClientServicesInfoHandlers["SOCKS"] = &I2PControlHandlers::SOCKSInfoHandler;
|
||||
m_ClientServicesInfoHandlers["SAM"] = &I2PControlHandlers::SAMInfoHandler;
|
||||
m_ClientServicesInfoHandlers["BOB"] = &I2PControlHandlers::BOBInfoHandler;
|
||||
m_ClientServicesInfoHandlers["I2CP"] = &I2PControlHandlers::I2CPInfoHandler;
|
||||
}
|
||||
|
||||
void I2PControlHandlers::InsertParam (std::ostringstream& ss, const std::string& name, int value) const
|
||||
{
|
||||
ss << "\"" << name << "\":" << value;
|
||||
}
|
||||
|
||||
void I2PControlHandlers::InsertParam (std::ostringstream& ss, const std::string& name, const std::string& value, bool quotes) const
|
||||
{
|
||||
ss << "\"" << name << "\":";
|
||||
if (value.length () > 0)
|
||||
{
|
||||
if (quotes)
|
||||
ss << "\"" << value << "\"";
|
||||
else
|
||||
ss << value;
|
||||
}
|
||||
else
|
||||
ss << "null";
|
||||
}
|
||||
|
||||
void I2PControlHandlers::InsertParam (std::ostringstream& ss, const std::string& name, double value) const
|
||||
{
|
||||
ss << "\"" << name << "\":" << std::fixed << std::setprecision(2) << value;
|
||||
}
|
||||
|
||||
void I2PControlHandlers::InsertParam (std::ostringstream& ss, const std::string& name, const boost::property_tree::ptree& value) const
|
||||
{
|
||||
std::ostringstream buf;
|
||||
boost::property_tree::write_json (buf, value, false);
|
||||
ss << "\"" << name << "\":" << buf.str();
|
||||
}
|
||||
|
||||
// RouterInfo
|
||||
|
||||
void I2PControlHandlers::RouterInfoHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
|
||||
{
|
||||
bool first = true;
|
||||
for (auto it = params.begin (); it != params.end (); it++)
|
||||
{
|
||||
LogPrint (eLogDebug, "I2PControl: RouterInfo request: ", it->first);
|
||||
auto it1 = m_RouterInfoHandlers.find (it->first);
|
||||
if (it1 != m_RouterInfoHandlers.end ())
|
||||
{
|
||||
if (!first) results << ",";
|
||||
else first = false;
|
||||
(this->*(it1->second))(results);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "I2PControl: RouterInfo unknown request ", it->first);
|
||||
}
|
||||
}
|
||||
|
||||
void I2PControlHandlers::UptimeHandler (std::ostringstream& results)
|
||||
{
|
||||
InsertParam (results, "i2p.router.uptime", std::to_string (i2p::context.GetUptime ()*1000LL), false);
|
||||
}
|
||||
|
||||
void I2PControlHandlers::VersionHandler (std::ostringstream& results)
|
||||
{
|
||||
InsertParam (results, "i2p.router.version", VERSION);
|
||||
}
|
||||
|
||||
void I2PControlHandlers::StatusHandler (std::ostringstream& results)
|
||||
{
|
||||
auto dest = i2p::client::context.GetSharedLocalDestination ();
|
||||
InsertParam (results, "i2p.router.status", (dest && dest->IsReady ()) ? "1" : "0");
|
||||
}
|
||||
|
||||
void I2PControlHandlers::NetDbKnownPeersHandler (std::ostringstream& results)
|
||||
{
|
||||
InsertParam (results, "i2p.router.netdb.knownpeers", i2p::data::netdb.GetNumRouters ());
|
||||
}
|
||||
|
||||
void I2PControlHandlers::NetDbActivePeersHandler (std::ostringstream& results)
|
||||
{
|
||||
InsertParam (results, "i2p.router.netdb.activepeers", (int)i2p::transport::transports.GetPeers ().size ());
|
||||
}
|
||||
|
||||
void I2PControlHandlers::NetStatusHandler (std::ostringstream& results)
|
||||
{
|
||||
InsertParam (results, "i2p.router.net.status", (int)i2p::context.GetStatus ());
|
||||
}
|
||||
|
||||
void I2PControlHandlers::TunnelsParticipatingHandler (std::ostringstream& results)
|
||||
{
|
||||
int transit = i2p::tunnel::tunnels.GetTransitTunnels ().size ();
|
||||
InsertParam (results, "i2p.router.net.tunnels.participating", transit);
|
||||
}
|
||||
|
||||
void I2PControlHandlers::TunnelsSuccessRateHandler (std::ostringstream& results)
|
||||
{
|
||||
int rate = i2p::tunnel::tunnels.GetTunnelCreationSuccessRate ();
|
||||
InsertParam (results, "i2p.router.net.tunnels.successrate", rate);
|
||||
}
|
||||
|
||||
void I2PControlHandlers::InboundBandwidth1S (std::ostringstream& results)
|
||||
{
|
||||
double bw = i2p::transport::transports.GetInBandwidth ();
|
||||
InsertParam (results, "i2p.router.net.bw.inbound.1s", bw);
|
||||
}
|
||||
|
||||
void I2PControlHandlers::InboundBandwidth15S (std::ostringstream& results)
|
||||
{
|
||||
double bw = i2p::transport::transports.GetInBandwidth15s ();
|
||||
InsertParam (results, "i2p.router.net.bw.inbound.15s", bw);
|
||||
}
|
||||
|
||||
void I2PControlHandlers::OutboundBandwidth1S (std::ostringstream& results)
|
||||
{
|
||||
double bw = i2p::transport::transports.GetOutBandwidth ();
|
||||
InsertParam (results, "i2p.router.net.bw.outbound.1s", bw);
|
||||
}
|
||||
|
||||
void I2PControlHandlers::OutboundBandwidth15S (std::ostringstream& results)
|
||||
{
|
||||
double bw = i2p::transport::transports.GetOutBandwidth15s ();
|
||||
InsertParam (results, "i2p.router.net.bw.outbound.15s", bw);
|
||||
}
|
||||
|
||||
void I2PControlHandlers::NetTotalReceivedBytes (std::ostringstream& results)
|
||||
{
|
||||
InsertParam (results, "i2p.router.net.total.received.bytes", (double)i2p::transport::transports.GetTotalReceivedBytes ());
|
||||
}
|
||||
|
||||
void I2PControlHandlers::NetTotalSentBytes (std::ostringstream& results)
|
||||
{
|
||||
InsertParam (results, "i2p.router.net.total.sent.bytes", (double)i2p::transport::transports.GetTotalSentBytes ());
|
||||
}
|
||||
|
||||
// network setting
|
||||
void I2PControlHandlers::NetworkSettingHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
|
||||
{
|
||||
for (auto it = params.begin (); it != params.end (); it++)
|
||||
{
|
||||
LogPrint (eLogDebug, "I2PControl: NetworkSetting request: ", it->first);
|
||||
auto it1 = m_NetworkSettingHandlers.find (it->first);
|
||||
if (it1 != m_NetworkSettingHandlers.end ()) {
|
||||
if (it != params.begin ()) results << ",";
|
||||
(this->*(it1->second))(it->second.data (), results);
|
||||
} else
|
||||
LogPrint (eLogError, "I2PControl: NetworkSetting unknown request: ", it->first);
|
||||
}
|
||||
}
|
||||
|
||||
void I2PControlHandlers::InboundBandwidthLimit (const std::string& value, std::ostringstream& results)
|
||||
{
|
||||
if (value != "null")
|
||||
i2p::context.SetBandwidth (std::atoi(value.c_str()));
|
||||
int bw = i2p::context.GetBandwidthLimit();
|
||||
InsertParam (results, "i2p.router.net.bw.in", bw);
|
||||
}
|
||||
|
||||
void I2PControlHandlers::OutboundBandwidthLimit (const std::string& value, std::ostringstream& results)
|
||||
{
|
||||
if (value != "null")
|
||||
i2p::context.SetBandwidth (std::atoi(value.c_str()));
|
||||
int bw = i2p::context.GetBandwidthLimit();
|
||||
InsertParam (results, "i2p.router.net.bw.out", bw);
|
||||
}
|
||||
|
||||
// ClientServicesInfo
|
||||
|
||||
void I2PControlHandlers::ClientServicesInfoHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
|
||||
{
|
||||
for (auto it = params.begin (); it != params.end (); it++)
|
||||
{
|
||||
LogPrint (eLogDebug, "I2PControl: ClientServicesInfo request: ", it->first);
|
||||
auto it1 = m_ClientServicesInfoHandlers.find (it->first);
|
||||
if (it1 != m_ClientServicesInfoHandlers.end ())
|
||||
{
|
||||
if (it != params.begin ()) results << ",";
|
||||
(this->*(it1->second))(results);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "I2PControl: ClientServicesInfo unknown request ", it->first);
|
||||
}
|
||||
}
|
||||
|
||||
void I2PControlHandlers::I2PTunnelInfoHandler (std::ostringstream& results)
|
||||
{
|
||||
boost::property_tree::ptree pt;
|
||||
boost::property_tree::ptree client_tunnels, server_tunnels;
|
||||
|
||||
for (auto& it: i2p::client::context.GetClientTunnels ())
|
||||
{
|
||||
auto& ident = it.second->GetLocalDestination ()->GetIdentHash();
|
||||
boost::property_tree::ptree ct;
|
||||
ct.put("address", i2p::client::context.GetAddressBook ().ToAddress(ident));
|
||||
client_tunnels.add_child(it.second->GetName (), ct);
|
||||
}
|
||||
|
||||
auto& serverTunnels = i2p::client::context.GetServerTunnels ();
|
||||
if (!serverTunnels.empty ()) {
|
||||
for (auto& it: serverTunnels)
|
||||
{
|
||||
auto& ident = it.second->GetLocalDestination ()->GetIdentHash();
|
||||
boost::property_tree::ptree st;
|
||||
st.put("address", i2p::client::context.GetAddressBook ().ToAddress(ident));
|
||||
st.put("port", it.second->GetLocalPort ());
|
||||
server_tunnels.add_child(it.second->GetName (), st);
|
||||
}
|
||||
}
|
||||
|
||||
auto& clientForwards = i2p::client::context.GetClientForwards ();
|
||||
if (!clientForwards.empty ())
|
||||
{
|
||||
for (auto& it: clientForwards)
|
||||
{
|
||||
auto& ident = it.second->GetLocalDestination ()->GetIdentHash();
|
||||
boost::property_tree::ptree ct;
|
||||
ct.put("address", i2p::client::context.GetAddressBook ().ToAddress(ident));
|
||||
client_tunnels.add_child(it.second->GetName (), ct);
|
||||
}
|
||||
}
|
||||
|
||||
auto& serverForwards = i2p::client::context.GetServerForwards ();
|
||||
if (!serverForwards.empty ())
|
||||
{
|
||||
for (auto& it: serverForwards)
|
||||
{
|
||||
auto& ident = it.second->GetLocalDestination ()->GetIdentHash();
|
||||
boost::property_tree::ptree st;
|
||||
st.put("address", i2p::client::context.GetAddressBook ().ToAddress(ident));
|
||||
server_tunnels.add_child(it.second->GetName (), st);
|
||||
}
|
||||
}
|
||||
|
||||
pt.add_child("client", client_tunnels);
|
||||
pt.add_child("server", server_tunnels);
|
||||
|
||||
InsertParam (results, "I2PTunnel", pt);
|
||||
}
|
||||
|
||||
void I2PControlHandlers::HTTPProxyInfoHandler (std::ostringstream& results)
|
||||
{
|
||||
boost::property_tree::ptree pt;
|
||||
|
||||
auto httpProxy = i2p::client::context.GetHttpProxy ();
|
||||
if (httpProxy)
|
||||
{
|
||||
auto& ident = httpProxy->GetLocalDestination ()->GetIdentHash();
|
||||
pt.put("enabled", true);
|
||||
pt.put("address", i2p::client::context.GetAddressBook ().ToAddress(ident));
|
||||
}
|
||||
else
|
||||
pt.put("enabled", false);
|
||||
|
||||
InsertParam (results, "HTTPProxy", pt);
|
||||
}
|
||||
|
||||
void I2PControlHandlers::SOCKSInfoHandler (std::ostringstream& results)
|
||||
{
|
||||
boost::property_tree::ptree pt;
|
||||
|
||||
auto socksProxy = i2p::client::context.GetSocksProxy ();
|
||||
if (socksProxy)
|
||||
{
|
||||
auto& ident = socksProxy->GetLocalDestination ()->GetIdentHash();
|
||||
pt.put("enabled", true);
|
||||
pt.put("address", i2p::client::context.GetAddressBook ().ToAddress(ident));
|
||||
}
|
||||
else
|
||||
pt.put("enabled", false);
|
||||
|
||||
InsertParam (results, "SOCKS", pt);
|
||||
}
|
||||
|
||||
void I2PControlHandlers::SAMInfoHandler (std::ostringstream& results)
|
||||
{
|
||||
boost::property_tree::ptree pt;
|
||||
auto sam = i2p::client::context.GetSAMBridge ();
|
||||
if (sam)
|
||||
{
|
||||
pt.put("enabled", true);
|
||||
boost::property_tree::ptree sam_sessions;
|
||||
for (auto& it: sam->GetSessions ())
|
||||
{
|
||||
boost::property_tree::ptree sam_session, sam_session_sockets;
|
||||
auto& name = it.second->GetLocalDestination ()->GetNickname ();
|
||||
auto& ident = it.second->GetLocalDestination ()->GetIdentHash();
|
||||
sam_session.put("name", name);
|
||||
sam_session.put("address", i2p::client::context.GetAddressBook ().ToAddress(ident));
|
||||
|
||||
for (const auto& socket: sam->ListSockets(it.first))
|
||||
{
|
||||
boost::property_tree::ptree stream;
|
||||
stream.put("type", socket->GetSocketType ());
|
||||
stream.put("peer", socket->GetSocket ().remote_endpoint());
|
||||
|
||||
sam_session_sockets.push_back(std::make_pair("", stream));
|
||||
}
|
||||
sam_session.add_child("sockets", sam_session_sockets);
|
||||
sam_sessions.add_child(it.first, sam_session);
|
||||
}
|
||||
|
||||
pt.add_child("sessions", sam_sessions);
|
||||
}
|
||||
else
|
||||
pt.put("enabled", false);
|
||||
|
||||
InsertParam (results, "SAM", pt);
|
||||
}
|
||||
|
||||
void I2PControlHandlers::BOBInfoHandler (std::ostringstream& results)
|
||||
{
|
||||
boost::property_tree::ptree pt;
|
||||
auto bob = i2p::client::context.GetBOBCommandChannel ();
|
||||
if (bob)
|
||||
{
|
||||
/* TODO more info */
|
||||
pt.put("enabled", true);
|
||||
}
|
||||
else
|
||||
pt.put("enabled", false);
|
||||
|
||||
InsertParam (results, "BOB", pt);
|
||||
}
|
||||
|
||||
void I2PControlHandlers::I2CPInfoHandler (std::ostringstream& results)
|
||||
{
|
||||
boost::property_tree::ptree pt;
|
||||
auto i2cp = i2p::client::context.GetI2CPServer ();
|
||||
if (i2cp)
|
||||
{
|
||||
/* TODO more info */
|
||||
pt.put("enabled", true);
|
||||
}
|
||||
else
|
||||
pt.put("enabled", false);
|
||||
|
||||
InsertParam (results, "I2CP", pt);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
* See full license text in LICENSE file at top of project tree
|
||||
*/
|
||||
|
||||
#ifndef I2P_CONTROL_HANDLERS_H__
|
||||
#define I2P_CONTROL_HANDLERS_H__
|
||||
|
||||
#include <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 InboundBandwidth15S (std::ostringstream& results);
|
||||
void OutboundBandwidth1S (std::ostringstream& results);
|
||||
void OutboundBandwidth15S (std::ostringstream& results);
|
||||
void NetTotalReceivedBytes (std::ostringstream& results);
|
||||
void NetTotalSentBytes (std::ostringstream& results);
|
||||
|
||||
// NetworkSetting
|
||||
typedef void (I2PControlHandlers::*NetworkSettingRequestHandler)(const std::string& value, std::ostringstream& results);
|
||||
void InboundBandwidthLimit (const std::string& value, std::ostringstream& results);
|
||||
void OutboundBandwidthLimit (const std::string& value, std::ostringstream& results);
|
||||
|
||||
// ClientServicesInfo
|
||||
typedef void (I2PControlHandlers::*ClientServicesInfoRequestHandler)(std::ostringstream& results);
|
||||
void I2PTunnelInfoHandler (std::ostringstream& results);
|
||||
void HTTPProxyInfoHandler (std::ostringstream& results);
|
||||
void SOCKSInfoHandler (std::ostringstream& results);
|
||||
void SAMInfoHandler (std::ostringstream& results);
|
||||
void BOBInfoHandler (std::ostringstream& results);
|
||||
void I2CPInfoHandler (std::ostringstream& results);
|
||||
|
||||
private:
|
||||
|
||||
std::map<std::string, RouterInfoRequestHandler> m_RouterInfoHandlers;
|
||||
std::map<std::string, NetworkSettingRequestHandler> m_NetworkSettingHandlers;
|
||||
std::map<std::string, ClientServicesInfoRequestHandler> m_ClientServicesInfoHandlers;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -159,11 +159,10 @@ namespace transport
|
||||
|
||||
void UPnP::PortMapping ()
|
||||
{
|
||||
auto a = context.GetRouterInfo().GetAddresses();
|
||||
if (!a) return;
|
||||
for (const auto& address : *a)
|
||||
const auto& a = context.GetRouterInfo().GetAddresses();
|
||||
for (const auto& address : a)
|
||||
{
|
||||
if (address && !address->host.is_v6 () && address->port)
|
||||
if (!address->host.is_v6 () && address->port)
|
||||
TryPortMapping (address);
|
||||
}
|
||||
m_Timer.expires_from_now (boost::posix_time::minutes(20)); // every 20 minutes
|
||||
@@ -211,11 +210,10 @@ namespace transport
|
||||
|
||||
void UPnP::CloseMapping ()
|
||||
{
|
||||
auto a = context.GetRouterInfo().GetAddresses();
|
||||
if (!a) return;
|
||||
for (const auto& address : *a)
|
||||
const auto& a = context.GetRouterInfo().GetAddresses();
|
||||
for (const auto& address : a)
|
||||
{
|
||||
if (address && !address->host.is_v6 () && address->port)
|
||||
if (!address->host.is_v6 () && address->port)
|
||||
CloseMapping (address);
|
||||
}
|
||||
}
|
||||
@@ -250,10 +248,10 @@ namespace transport
|
||||
{
|
||||
switch (address->transportStyle)
|
||||
{
|
||||
case i2p::data::RouterInfo::eTransportNTCP2:
|
||||
case i2p::data::RouterInfo::eTransportNTCP:
|
||||
return "TCP";
|
||||
break;
|
||||
case i2p::data::RouterInfo::eTransportSSU2:
|
||||
case i2p::data::RouterInfo::eTransportSSU:
|
||||
default:
|
||||
return "UDP";
|
||||
}
|
||||
|
||||
29
debian/changelog
vendored
29
debian/changelog
vendored
@@ -1,32 +1,3 @@
|
||||
i2pd (2.45.1-1) unstable; urgency=medium
|
||||
|
||||
* updated to version 2.45.1/0.9.57
|
||||
|
||||
-- orignal <orignal@i2pmail.org> Wed, 11 Jan 2023 19:00:00 +0000
|
||||
|
||||
i2pd (2.45.0-1) unstable; urgency=high
|
||||
|
||||
* updated to version 2.45.0/0.9.57
|
||||
* compat level 12
|
||||
* standards version 4.3.0
|
||||
* increased nofile limit in service and init.d to 8192
|
||||
* added conffiles
|
||||
* removed #1210 patch
|
||||
|
||||
-- r4sas <r4sas@i2pmail.org> Tue, 3 Jan 2023 18:00:00 +0000
|
||||
|
||||
i2pd (2.44.0-1) unstable; urgency=medium
|
||||
|
||||
* updated to version 2.44.0/0.9.56
|
||||
|
||||
-- orignal <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
|
||||
|
||||
2
debian/compat
vendored
2
debian/compat
vendored
@@ -1 +1 @@
|
||||
12
|
||||
9
|
||||
|
||||
2
debian/conffiles
vendored
2
debian/conffiles
vendored
@@ -1,2 +0,0 @@
|
||||
/etc/i2pd/i2pd.conf
|
||||
/etc/i2pd/tunnels.conf
|
||||
4
debian/control
vendored
4
debian/control
vendored
@@ -2,8 +2,8 @@ Source: i2pd
|
||||
Section: net
|
||||
Priority: optional
|
||||
Maintainer: r4sas <r4sas@i2pmail.org>
|
||||
Build-Depends: debhelper (>= 12~), libboost-system-dev (>= 1.46), libboost-date-time-dev (>= 1.46), libboost-filesystem-dev (>= 1.46), libboost-program-options-dev (>= 1.46), libminiupnpc-dev, libssl-dev, zlib1g-dev
|
||||
Standards-Version: 4.3.0
|
||||
Build-Depends: debhelper (>= 9), dpkg-dev (>= 1.17.2~), gcc (>= 4.7) | clang (>= 3.3), libboost-system-dev (>= 1.46), libboost-date-time-dev (>= 1.46), libboost-filesystem-dev (>= 1.46), libboost-program-options-dev (>= 1.46), libminiupnpc-dev, libssl-dev, zlib1g-dev
|
||||
Standards-Version: 3.9.8
|
||||
Homepage: http://i2pd.website/
|
||||
Vcs-Git: git://github.com/PurpleI2P/i2pd.git
|
||||
Vcs-Browser: https://github.com/PurpleI2P/i2pd
|
||||
|
||||
6
debian/copyright
vendored
6
debian/copyright
vendored
@@ -3,18 +3,18 @@ Upstream-Name: i2pd
|
||||
Source: https://github.com/PurpleI2P
|
||||
|
||||
Files: *
|
||||
Copyright: 2013-2023 PurpleI2P
|
||||
Copyright: 2013-2020 PurpleI2P
|
||||
License: BSD-3-clause
|
||||
|
||||
Files: debian/*
|
||||
Copyright: 2013-2015 Kill Your TV <killyourtv@i2pmail.org>
|
||||
2014-2016 hagen <hagen@i2pmail.org>
|
||||
2016-2023 R4SAS <r4sas@i2pmail.org>
|
||||
2016-2020 R4SAS <r4sas@i2pmail.org>
|
||||
2017-2020 Yangfl <mmyangfl@gmail.com>
|
||||
License: GPL-2+
|
||||
|
||||
License: BSD-3-clause
|
||||
Copyright (c) 2013-2023, The PurpleI2P Project
|
||||
Copyright (c) 2013-2017, The PurpleI2P Project
|
||||
.
|
||||
All rights reserved.
|
||||
.
|
||||
|
||||
2
debian/i2pd.default
vendored
2
debian/i2pd.default
vendored
@@ -8,4 +8,4 @@ I2PD_ENABLED="yes"
|
||||
DAEMON_OPTS=""
|
||||
|
||||
# If you have problems with hunging i2pd, you can try enable this
|
||||
ulimit -n 8192
|
||||
ulimit -n 4096
|
||||
|
||||
1
debian/i2pd.install
vendored
1
debian/i2pd.install
vendored
@@ -1,6 +1,7 @@
|
||||
i2pd usr/sbin/
|
||||
contrib/i2pd.conf etc/i2pd/
|
||||
contrib/tunnels.conf etc/i2pd/
|
||||
contrib/subscriptions.txt etc/i2pd/
|
||||
contrib/certificates/ usr/share/i2pd/
|
||||
contrib/tunnels.d/README etc/i2pd/tunnels.conf.d/
|
||||
contrib/apparmor/usr.sbin.i2pd etc/apparmor.d
|
||||
|
||||
3
debian/i2pd.links
vendored
3
debian/i2pd.links
vendored
@@ -1,4 +1,5 @@
|
||||
etc/i2pd/i2pd.conf var/lib/i2pd/i2pd.conf
|
||||
etc/i2pd/i2pd.conf var/lib/i2pd/i2pd.conf
|
||||
etc/i2pd/tunnels.conf var/lib/i2pd/tunnels.conf
|
||||
etc/i2pd/subscriptions.txt var/lib/i2pd/subscriptions.txt
|
||||
etc/i2pd/tunnels.conf.d var/lib/i2pd/tunnels.d
|
||||
usr/share/i2pd/certificates var/lib/i2pd/certificates
|
||||
|
||||
27
debian/patches/01-fix-1210.patch
vendored
Normal file
27
debian/patches/01-fix-1210.patch
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
Description: fix #1210
|
||||
Disables two options, which not presented in old systemd versions
|
||||
Author: r4sas <r4sas@i2pmail.org>
|
||||
|
||||
Bug: https://github.com/PurpleI2P/i2pd/issues/1210
|
||||
Reviewed-By: r4sas <r4sas@i2pmail.org>
|
||||
Last-Update: 2020-05-25
|
||||
|
||||
Index: i2pd/contrib/i2pd.service
|
||||
===================================================================
|
||||
--- i2pd.orig/contrib/i2pd.service
|
||||
+++ i2pd/contrib/i2pd.service
|
||||
@@ -6,10 +6,10 @@ After=network.target
|
||||
[Service]
|
||||
User=i2pd
|
||||
Group=i2pd
|
||||
-RuntimeDirectory=i2pd
|
||||
-RuntimeDirectoryMode=0700
|
||||
-LogsDirectory=i2pd
|
||||
-LogsDirectoryMode=0700
|
||||
+#RuntimeDirectory=i2pd
|
||||
+#RuntimeDirectoryMode=0700
|
||||
+#LogsDirectory=i2pd
|
||||
+#LogsDirectoryMode=0700
|
||||
Type=forking
|
||||
ExecStart=/usr/sbin/i2pd --conf=/etc/i2pd/i2pd.conf --tunconf=/etc/i2pd/tunnels.conf --tunnelsdir=/etc/i2pd/tunnels.conf.d --pidfile=/run/i2pd/i2pd.pid --logfile=/var/log/i2pd/i2pd.log --daemon --service
|
||||
ExecReload=/bin/sh -c "kill -HUP $MAINPID"
|
||||
3
debian/patches/series
vendored
3
debian/patches/series
vendored
@@ -1 +1,2 @@
|
||||
01-upnp.patch
|
||||
01-fix-1210.patch
|
||||
02-upnp.patch
|
||||
|
||||
3
debian/watch
vendored
3
debian/watch
vendored
@@ -1,4 +1,3 @@
|
||||
version=4
|
||||
opts="filenamemangle=s%(?:.*?)?v?(\d[\d.]*)\.tar\.gz%i2pd-$1.tar.gz%" \
|
||||
version=4 opts="filenamemangle=s%(?:.*?)?v?(\d[\d.]*)\.tar\.gz%i2pd-$1.tar.gz%" \
|
||||
https://github.com/PurpleI2P/i2pd/tags \
|
||||
(?:.*?/)?(\d[\d.]*)\.tar\.gz debian uupdate
|
||||
|
||||
23
filelist.mk
23
filelist.mk
@@ -1,26 +1,7 @@
|
||||
#LIB_SRC = \
|
||||
# BloomFilter.cpp Gzip.cpp Crypto.cpp Datagram.cpp Garlic.cpp I2NPProtocol.cpp LeaseSet.cpp \
|
||||
# Log.cpp NTCPSession.cpp NetDb.cpp NetDbRequests.cpp Profiling.cpp \
|
||||
# Reseed.cpp RouterContext.cpp RouterInfo.cpp Signature.cpp SSU.cpp \
|
||||
# SSUSession.cpp SSUData.cpp Streaming.cpp Identity.cpp TransitTunnel.cpp \
|
||||
# Transports.cpp Tunnel.cpp TunnelEndpoint.cpp TunnelPool.cpp TunnelGateway.cpp \
|
||||
# Destination.cpp Base.cpp I2PEndian.cpp FS.cpp Config.cpp Family.cpp \
|
||||
# Config.cpp HTTP.cpp Timestamp.cpp util.cpp api.cpp Gost.cpp
|
||||
|
||||
LIB_SRC = $(wildcard $(LIB_SRC_DIR)/*.cpp)
|
||||
|
||||
#LIB_CLIENT_SRC = \
|
||||
# AddressBook.cpp BOB.cpp ClientContext.cpp I2PTunnel.cpp I2PService.cpp MatchedDestination.cpp \
|
||||
# SAM.cpp SOCKS.cpp HTTPProxy.cpp I2CP.cpp
|
||||
|
||||
LIB_CLIENT_SRC = $(wildcard $(LIB_CLIENT_SRC_DIR)/*.cpp)
|
||||
|
||||
# also: Daemon{Linux,Win32}.cpp will be added later
|
||||
#DAEMON_SRC = \
|
||||
# HTTPServer.cpp I2PControl.cpp UPnP.cpp Daemon.cpp i2pd.cpp
|
||||
|
||||
LANG_SRC = $(wildcard $(LANG_SRC_DIR)/*.cpp)
|
||||
WEBCONSOLE_SRC = $(wildcard $(WEBCONSOLE_SRC_DIR)/*.cpp)
|
||||
|
||||
WRAP_LIB_SRC = $(wildcard $(WRAP_SRC_DIR)/*.cpp)
|
||||
|
||||
DAEMON_SRC = $(wildcard $(DAEMON_SRC_DIR)/*.cpp)
|
||||
DAEMON_SRC = $(DAEMON_SRC_DIR)/Daemon.cpp $(DAEMON_SRC_DIR)/I2PControl.cpp $(DAEMON_SRC_DIR)/i2pd.cpp $(DAEMON_SRC_DIR)/UPnP.cpp
|
||||
|
||||
@@ -64,10 +64,10 @@ namespace afrikaans // language namespace
|
||||
|
||||
static std::map<std::string, std::vector<std::string>> plurals
|
||||
{
|
||||
{"%d days", {"%d dag", "%d dae"}},
|
||||
{"%d hours", {"%d uur", "%d ure"}},
|
||||
{"%d minutes", {"%d minuut", "%d minute"}},
|
||||
{"%d seconds", {"%d seconde", "%d sekondes"}},
|
||||
{"days", {"dag", "dae"}},
|
||||
{"hours", {"uur", "ure"}},
|
||||
{"minutes", {"minuut", "minute"}},
|
||||
{"seconds", {"seconde", "sekondes"}},
|
||||
{"", {"", ""}},
|
||||
};
|
||||
|
||||
|
||||
@@ -31,9 +31,9 @@ namespace armenian // language namespace
|
||||
|
||||
static std::map<std::string, std::string> strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f ԿիԲ"},
|
||||
{"%.2f MiB", "%.2f ՄիԲ"},
|
||||
{"%.2f GiB", "%.2f ԳիԲ"},
|
||||
{"KiB", "ԿիԲ"},
|
||||
{"MiB", "ՄիԲ"},
|
||||
{"GiB", "ԳիԲ"},
|
||||
{"building", "կառուցվում է"},
|
||||
{"failed", "Անհաջող"},
|
||||
{"expiring", "Լրանում է"},
|
||||
@@ -68,7 +68,7 @@ namespace armenian // language namespace
|
||||
{"Family", "Խմբատեսակ"},
|
||||
{"Tunnel creation success rate", "Հաջողությամբ կառուցված թունելներ"},
|
||||
{"Received", "Ստացվել է"},
|
||||
{"%.2f KiB/s", "%.2f ԿիԲ/վ"},
|
||||
{"KiB/s", "ԿիԲ/վ"},
|
||||
{"Sent", "Ուղարկվել է"},
|
||||
{"Transit", "Տարանցում"},
|
||||
{"Data path", "Տվյալների ուղին"},
|
||||
@@ -94,7 +94,7 @@ namespace armenian // language namespace
|
||||
{"Type", "Տեսակը"},
|
||||
{"EncType", "Գաղտնագրի տեսակը"},
|
||||
{"Inbound tunnels", "Մուտքային թունելներ"},
|
||||
{"%dms", "%dմլվ"},
|
||||
{"ms", "մլվ"},
|
||||
{"Outbound tunnels", "Ելքային թունելներ"},
|
||||
{"Tags", "Թեգեր"},
|
||||
{"Incoming", "Մուտքային"},
|
||||
@@ -198,10 +198,10 @@ namespace armenian // language namespace
|
||||
|
||||
static std::map<std::string, std::vector<std::string>> plurals
|
||||
{
|
||||
{"%d days", {"%d օր", "%d օր"}},
|
||||
{"%d hours", {"%d ժամ", "%d ժամ"}},
|
||||
{"%d minutes", {"%d րոպե", "%d րոպե"}},
|
||||
{"%d seconds", {"%d վարկյան", "%d վարկյան"}},
|
||||
{"days", {"օր", "օր"}},
|
||||
{"hours", {"ժամ", "ժամ"}},
|
||||
{"minutes", {"րոպե", "րոպե"}},
|
||||
{"seconds", {"վարկյան", "վարկյան"}},
|
||||
{"", {"", ""}},
|
||||
};
|
||||
|
||||
|
||||
217
i18n/Chinese.cpp
217
i18n/Chinese.cpp
@@ -1,217 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
{
|
||||
{"%.2f KiB", "%.2f KiB"},
|
||||
{"%.2f MiB", "%.2f MiB"},
|
||||
{"%.2f GiB", "%.2f 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", "已接收"},
|
||||
{"%.2f KiB/s", "%.2f 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", "入站隧道"},
|
||||
{"%dms", "%d毫秒"},
|
||||
{"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
|
||||
{
|
||||
{"%d days", {"%d 日"}},
|
||||
{"%d hours", {"%d 时"}},
|
||||
{"%d minutes", {"%d 分"}},
|
||||
{"%d seconds", {"%d 秒"}},
|
||||
{"", {""}},
|
||||
};
|
||||
|
||||
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/Czech.cpp
216
i18n/Czech.cpp
@@ -1,216 +0,0 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
// Czech localization file
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace i18n
|
||||
{
|
||||
namespace czech // language namespace
|
||||
{
|
||||
// language name in lowercase
|
||||
static std::string language = "czech";
|
||||
|
||||
// See for language plural forms here:
|
||||
// https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
|
||||
static int plural (int n) {
|
||||
return (n == 1) ? 0 : (n >= 2 && n <= 4) ? 1 : 2;
|
||||
}
|
||||
|
||||
static std::map<std::string, std::string> strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f KiB"},
|
||||
{"%.2f MiB", "%.2f MiB"},
|
||||
{"%.2f GiB", "%.2f GiB"},
|
||||
{"building", "vytváří se"},
|
||||
{"failed", "selhalo"},
|
||||
{"expiring", "končící"},
|
||||
{"established", "vytvořeno"},
|
||||
{"unknown", "neznámý"},
|
||||
{"exploratory", "průzkumné"},
|
||||
{"Purple I2P Webconsole", "Purple I2P Webkonsole"},
|
||||
{"<b>i2pd</b> webconsole", "<b>i2pd</b> webkonsole"},
|
||||
{"Main page", "Hlavní stránka"},
|
||||
{"Router commands", "Router příkazy"},
|
||||
{"Local Destinations", "Lokální destinace"},
|
||||
{"LeaseSets", "LeaseSety"},
|
||||
{"Tunnels", "Tunely"},
|
||||
{"Transit Tunnels", "Transitní tunely"},
|
||||
{"Transports", "Transporty"},
|
||||
{"I2P tunnels", "I2P tunely"},
|
||||
{"SAM sessions", "SAM relace"},
|
||||
{"ERROR", "CHYBA"},
|
||||
{"OK", "OK"},
|
||||
{"Testing", "Testuji"},
|
||||
{"Firewalled", "Za Firewallem"},
|
||||
{"Unknown", "Neznámý"},
|
||||
{"Proxy", "Proxy"},
|
||||
{"Mesh", "Síť"},
|
||||
{"Error", "Chyba"},
|
||||
{"Clock skew", "Časová nesrovnalost"},
|
||||
{"Offline", "Offline"},
|
||||
{"Symmetric NAT", "Symetrický NAT"},
|
||||
{"Uptime", "Doba provozu"},
|
||||
{"Network status", "Status sítě"},
|
||||
{"Network status v6", "Status sítě v6"},
|
||||
{"Stopping in", "Zastavuji za"},
|
||||
{"Family", "Rodina"},
|
||||
{"Tunnel creation success rate", "Úspěšnost vytváření tunelů"},
|
||||
{"Received", "Přijato"},
|
||||
{"%.2f KiB/s", "%.2f KiB/s"},
|
||||
{"Sent", "Odesláno"},
|
||||
{"Transit", "Tranzit"},
|
||||
{"Data path", "Cesta k data souborům"},
|
||||
{"Hidden content. Press on text to see.", "Skrytý kontent. Pro zobrazení, klikni na text."},
|
||||
{"Router Ident", "Routerová Identita"},
|
||||
{"Router Family", "Rodina routerů"},
|
||||
{"Router Caps", "Omezení Routerů"},
|
||||
{"Version", "Verze"},
|
||||
{"Our external address", "Naše externí adresa"},
|
||||
{"supported", "podporováno"},
|
||||
{"Routers", "Routery"},
|
||||
{"Floodfills", "Floodfilly"},
|
||||
{"Client Tunnels", "Klientské tunely"},
|
||||
{"Services", "Služby"},
|
||||
{"Enabled", "Zapnuto"},
|
||||
{"Disabled", "Vypnuto"},
|
||||
{"Encrypted B33 address", "Šifrovaná adresa B33"},
|
||||
{"Address registration line", "Registrační řádek adresy"},
|
||||
{"Domain", "Doména"},
|
||||
{"Generate", "Vygenerovat"},
|
||||
{"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "<b>Poznámka:</b> výsledný řetězec může být použit pouze pro registraci 2LD domén (example.i2p). Pro registraci subdomén použijte prosím i2pd-tools."},
|
||||
{"Address", "Adresa"},
|
||||
{"Type", "Typ"},
|
||||
{"EncType", "EncType"},
|
||||
{"Inbound tunnels", "Příchozí tunely"},
|
||||
{"%dms", "%dms"},
|
||||
{"Outbound tunnels", "Odchozí tunely"},
|
||||
{"Tags", "Štítky"},
|
||||
{"Incoming", "Příchozí"},
|
||||
{"Outgoing", "Odchozí"},
|
||||
{"Destination", "Destinace"},
|
||||
{"Amount", "Množství"},
|
||||
{"Incoming Tags", "Příchozí štítky"},
|
||||
{"Tags sessions", "Relace štítků"},
|
||||
{"Status", "Status"},
|
||||
{"Local Destination", "Lokální Destinace"},
|
||||
{"Streams", "Toky"},
|
||||
{"Close stream", "Uzavřít tok"},
|
||||
{"I2CP session not found", "I2CP relace nenalezena"},
|
||||
{"I2CP is not enabled", "I2CP není zapnuto"},
|
||||
{"Invalid", "Neplatný"},
|
||||
{"Store type", "Druh uložení"},
|
||||
{"Expires", "Vyprší"},
|
||||
{"Non Expired Leases", "Nevypršené Leasy"},
|
||||
{"Gateway", "Brána"},
|
||||
{"TunnelID", "ID tunelu"},
|
||||
{"EndDate", "Datum ukončení"},
|
||||
{"not floodfill", "není floodfill"},
|
||||
{"Queue size", "Velikost fronty"},
|
||||
{"Run peer test", "Spustit peer test"},
|
||||
{"Decline transit tunnels", "Odmítnout tranzitní tunely"},
|
||||
{"Accept transit tunnels", "Přijmout tranzitní tunely"},
|
||||
{"Cancel graceful shutdown", "Zrušit hladké vypnutí"},
|
||||
{"Start graceful shutdown", "Zahájit hladké vypnutí"},
|
||||
{"Force shutdown", "Vynutit vypnutí"},
|
||||
{"Reload external CSS styles", "Znovu načíst externí CSS"},
|
||||
{"<b>Note:</b> any action done here are not persistent and not changes your config files.", "<b>Poznámka:</b> žádná vykonaná akce zde není trvalá a nemění konfigurační soubory."},
|
||||
{"Logging level", "Úroveň logování"},
|
||||
{"Transit tunnels limit", "Limit tranzitních tunelů"},
|
||||
{"Change", "Změnit"},
|
||||
{"Change language", "Změnit jazyk"},
|
||||
{"no transit tunnels currently built", "Žádný tranzitní tunel není momentálně vytvořen"},
|
||||
{"SAM disabled", "SAM vypnutý"},
|
||||
{"no sessions currently running", "Momentálně nejsou spuštěné žádné relace"},
|
||||
{"SAM session not found", "SAM relace nenalezena"},
|
||||
{"SAM Session", "SAM Relace"},
|
||||
{"Server Tunnels", "Server Tunely"},
|
||||
{"Client Forwards", "Přesměrování Klienta"},
|
||||
{"Server Forwards", "Přesměrování Serveru"},
|
||||
{"Unknown page", "Neznámá stránka"},
|
||||
{"Invalid token", "Neplatný token"},
|
||||
{"SUCCESS", "ÚSPĚCH"},
|
||||
{"Stream closed", "Tok uzavřen"},
|
||||
{"Stream not found or already was closed", "Tok nenalezen nebo byl již uzavřen"},
|
||||
{"Destination not found", "Destinace nenalezena"},
|
||||
{"StreamID can't be null", "StreamID nemůže být null"},
|
||||
{"Return to destination page", "Zpět na stránku destinací"},
|
||||
{"You will be redirected in 5 seconds", "Budete přesměrováni za 5 vteřin"},
|
||||
{"Transit tunnels count must not exceed 65535", "Počet tranzitních tunelů nesmí přesáhnout 65535"},
|
||||
{"Back to commands list", "Zpět na list příkazů"},
|
||||
{"Register at reg.i2p", "Zaregistrovat na reg.i2p"},
|
||||
{"Description", "Popis"},
|
||||
{"A bit information about service on domain", "Trochu informací o službě na doméně"},
|
||||
{"Submit", "Odeslat"},
|
||||
{"Domain can't end with .b32.i2p", "Doména nesmí končit na .b32.i2p"},
|
||||
{"Domain must end with .i2p", "Doména musí končit s .i2p"},
|
||||
{"Such destination is not found", "Takováto destinace nebyla nalezena"},
|
||||
{"Unknown command", "Neznámý příkaz"},
|
||||
{"Command accepted", "Příkaz přijat"},
|
||||
{"Proxy error", "Chyba proxy serveru"},
|
||||
{"Proxy info", "Proxy informace"},
|
||||
{"Proxy error: Host not found", "Chyba proxy serveru: Hostitel nenalezen"},
|
||||
{"Remote host not found in router's addressbook", "Vzdálený hostitel nebyl nalezen v adresáři routeru"},
|
||||
{"You may try to find this host on jump services below", "Můžete se pokusit najít tohoto hostitele na startovacích službách níže"},
|
||||
{"Invalid request", "Neplatný požadavek"},
|
||||
{"Proxy unable to parse your request", "Proxy server nemohl zpracovat váš požadavek"},
|
||||
{"addresshelper is not supported", "Adresshelper není podporován"},
|
||||
{"Host", "Hostitel"},
|
||||
{"added to router's addressbook from helper", "přidáno do adresáře routeru od pomocníka"},
|
||||
{"Click here to proceed:", "Pro pokračování, klikněte zde:"},
|
||||
{"Continue", "Pokračovat"},
|
||||
{"Addresshelper found", "Adresář nalezen"},
|
||||
{"already in router's addressbook", "je již v adresáři routeru"},
|
||||
{"Click here to update record:", "Pro aktualizaci záznamu, klikněte zde:"},
|
||||
{"invalid request uri", "neplatný URI požadavek"},
|
||||
{"Can't detect destination host from request", "Nelze zjistit cílového hostitele z požadavku"},
|
||||
{"Outproxy failure", "Outproxy selhání"},
|
||||
{"bad outproxy settings", "špatné outproxy nastavení"},
|
||||
{"not inside I2P network, but outproxy is not enabled", "není uvnitř I2P sítě a outproxy není nastavena"},
|
||||
{"unknown outproxy url", "neznámá outproxy URL"},
|
||||
{"cannot resolve upstream proxy", "nelze rozluštit upstream proxy server"},
|
||||
{"hostname too long", "Název hostitele je příliš dlouhý"},
|
||||
{"cannot connect to upstream socks proxy", "nelze se připojit k upstream socks proxy serveru"},
|
||||
{"Cannot negotiate with socks proxy", "Nelze vyjednávat se socks proxy serverem"},
|
||||
{"CONNECT error", "Chyba PŘIPOJENÍ"},
|
||||
{"Failed to Connect", "Připojení se nezdařilo"},
|
||||
{"socks proxy error", "chyba socks proxy serveru"},
|
||||
{"failed to send request to upstream", "odeslání žádosti upstream serveru se nezdařilo"},
|
||||
{"No Reply From socks proxy", "Žádná odpověď od socks proxy serveru"},
|
||||
{"cannot connect", "nelze se připojit"},
|
||||
{"http out proxy not implemented", "http out proxy není implementován"},
|
||||
{"cannot connect to upstream http proxy", "nelze se připojit k upstream socks proxy serveru"},
|
||||
{"Host is down", "Hostitel je nedostupný"},
|
||||
{"Can't create connection to requested host, it may be down. Please try again later.", "Připojení k požadovanému hostiteli nelze vytvořit, může být nedostupný. Zkuste to, prosím, znovu později."},
|
||||
{"", ""},
|
||||
};
|
||||
|
||||
static std::map<std::string, std::vector<std::string>> plurals
|
||||
{
|
||||
{"%d days", {"%d den", "%d dny", "%d dní", "%d dní"}},
|
||||
{"%d hours", {"%d hodina", "%d hodiny", "%d hodin", "%d hodin"}},
|
||||
{"%d minutes", {"%d minuta", "%d minuty", "%d minut", "%d minut"}},
|
||||
{"%d seconds", {"%d vteřina", "%d vteřiny", "%d vteřin", "%d vteřin"}},
|
||||
{"", {"", "", "", ""}},
|
||||
};
|
||||
|
||||
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
|
||||
133
i18n/French.cpp
133
i18n/French.cpp
@@ -31,37 +31,28 @@ namespace french // language namespace
|
||||
|
||||
static std::map<std::string, std::string> strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f Kio"},
|
||||
{"%.2f MiB", "%.2f Mio"},
|
||||
{"%.2f GiB", "%.2f Gio"},
|
||||
{"KiB", "Kio"},
|
||||
{"MiB", "Mio"},
|
||||
{"GiB", "Gio"},
|
||||
{"building", "En construction"},
|
||||
{"failed", "échoué"},
|
||||
{"failed", "echoué"},
|
||||
{"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"},
|
||||
@@ -69,135 +60,35 @@ namespace french // language namespace
|
||||
{"Family", "Famille"},
|
||||
{"Tunnel creation success rate", "Taux de succès de création de tunnels"},
|
||||
{"Received", "Reçu"},
|
||||
{"%.2f KiB/s", "%.2f kio/s"},
|
||||
{"KiB/s", "kio/s"},
|
||||
{"Sent", "Envoyé"},
|
||||
{"Transit", "Transité"},
|
||||
{"Data path", "Emplacement des données"},
|
||||
{"Hidden content. Press on text to see.", "Contenu caché. Cliquez sur le texte pour afficher."},
|
||||
{"Transit", "Transit"},
|
||||
{"Hidden content. Press on text to see.", "Contenu caché. Cliquez sur le texte pour regarder."},
|
||||
{"Router Ident", "Identifiant du routeur"},
|
||||
{"Router Family", "Famille du routeur"},
|
||||
{"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"},
|
||||
{"%dms", "%dms"},
|
||||
{"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."},
|
||||
{"", ""},
|
||||
};
|
||||
|
||||
static std::map<std::string, std::vector<std::string>> plurals
|
||||
{
|
||||
{"%d days", {"%d jour", "%d jours"}},
|
||||
{"%d hours", {"%d heure", "%d heures"}},
|
||||
{"%d minutes", {"%d minute", "%d minutes"}},
|
||||
{"%d seconds", {"%d seconde", "%d secondes"}},
|
||||
{"days", {"jour", "jours"}},
|
||||
{"hours", {"heure", "heures"}},
|
||||
{"minutes", {"minute", "minutes"}},
|
||||
{"seconds", {"seconde", "secondes"}},
|
||||
{"", {"", ""}},
|
||||
};
|
||||
|
||||
|
||||
@@ -31,20 +31,19 @@ namespace german // language namespace
|
||||
|
||||
static std::map<std::string, std::string> strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f KiB"},
|
||||
{"%.2f MiB", "%.2f MiB"},
|
||||
{"%.2f GiB", "%.2f GiB"},
|
||||
{"KiB", "KiB"},
|
||||
{"MiB", "MiB"},
|
||||
{"GiB", "GiB"},
|
||||
{"building", "In Bau"},
|
||||
{"failed", "fehlgeschlagen"},
|
||||
{"expiring", "läuft ab"},
|
||||
{"expiring", "läuft ab in"},
|
||||
{"established", "hergestellt"},
|
||||
{"unknown", "Unbekannt"},
|
||||
{"exploratory", "erforschend"},
|
||||
{"Purple I2P Webconsole", "Purple I2P-Webkonsole"},
|
||||
{"<b>i2pd</b> webconsole", "<b>i2pd</b>-Webkonsole"},
|
||||
{"exploratory", "erforschende"},
|
||||
{"<b>i2pd</b> webconsole", "<b>i2pd</b> Webkonsole"},
|
||||
{"Main page", "Startseite"},
|
||||
{"Router commands", "Routerbefehle"},
|
||||
{"Local Destinations", "Lokale Ziele"},
|
||||
{"Router commands", "Router Befehle"},
|
||||
{"Local Destinations", "Lokale Destination"},
|
||||
{"LeaseSets", "LeaseSets"},
|
||||
{"Tunnels", "Tunnel"},
|
||||
{"Transit Tunnels", "Transittunnel"},
|
||||
@@ -54,7 +53,7 @@ namespace german // language namespace
|
||||
{"ERROR", "FEHLER"},
|
||||
{"OK", "OK"},
|
||||
{"Testing", "Testen"},
|
||||
{"Firewalled", "Hinter einer Firewall"},
|
||||
{"Firewalled", "Hinter eine Firewall"},
|
||||
{"Unknown", "Unbekannt"},
|
||||
{"Proxy", "Proxy"},
|
||||
{"Mesh", "Mesh"},
|
||||
@@ -69,7 +68,7 @@ namespace german // language namespace
|
||||
{"Family", "Familie"},
|
||||
{"Tunnel creation success rate", "Erfolgsrate der Tunnelerstellung"},
|
||||
{"Received", "Eingegangen"},
|
||||
{"%.2f KiB/s", "%.2f KiB/s"},
|
||||
{"KiB/s", "KiB/s"},
|
||||
{"Sent", "Gesendet"},
|
||||
{"Transit", "Transit"},
|
||||
{"Data path", "Datenpfad"},
|
||||
@@ -82,33 +81,33 @@ namespace german // language namespace
|
||||
{"supported", "unterstützt"},
|
||||
{"Routers", "Router"},
|
||||
{"Floodfills", "Floodfills"},
|
||||
{"Client Tunnels", "Clienttunnel"},
|
||||
{"Client Tunnels", "Klienttunnel"},
|
||||
{"Services", "Services"},
|
||||
{"Enabled", "Aktiviert"},
|
||||
{"Disabled", "Deaktiviert"},
|
||||
{"Encrypted B33 address", "Verschlüsselte B33-Adresse"},
|
||||
{"Address registration line", "Adressregistrierungszeile"},
|
||||
{"Encrypted B33 address", "Verschlüsselte B33 Adresse"},
|
||||
{"Address registration line", "Adresseregistrierungszeile"},
|
||||
{"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"},
|
||||
{"Inbound tunnels", "Eingehende Tunnel"},
|
||||
{"%dms", "%dms"},
|
||||
{"ms", "ms"},
|
||||
{"Outbound tunnels", "Ausgehende Tunnel"},
|
||||
{"Tags", "Tags"},
|
||||
{"Incoming", "Eingehend"},
|
||||
{"Outgoing", "Ausgehend"},
|
||||
{"Destination", "Ziel"},
|
||||
{"Destination", "Destination"},
|
||||
{"Amount", "Anzahl"},
|
||||
{"Incoming Tags", "Eingehende Tags"},
|
||||
{"Tags sessions", "Tags-Sitzungen"},
|
||||
{"Tags sessions", "Tags Sitzungen"},
|
||||
{"Status", "Status"},
|
||||
{"Local Destination", "Lokales Ziel"},
|
||||
{"Local Destination", "Lokale Destination"},
|
||||
{"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"},
|
||||
@@ -118,67 +117,67 @@ namespace german // language namespace
|
||||
{"TunnelID", "TunnelID"},
|
||||
{"EndDate", "Enddatum"},
|
||||
{"not floodfill", "kein Floodfill"},
|
||||
{"Queue size", "Größe der Warteschlange"},
|
||||
{"Run peer test", "Peer-Test durchführen"},
|
||||
{"Queue size", "Warteschlangengröße"},
|
||||
{"Run peer test", "Peer-Test ausführen"},
|
||||
{"Decline transit tunnels", "Transittunnel ablehnen"},
|
||||
{"Accept transit tunnels", "Transittunnel akzeptieren"},
|
||||
{"Cancel graceful shutdown", "Beende das kontrollierte Herunterfahren"},
|
||||
{"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-Stile neu"},
|
||||
{"Reload external CSS styles", "Lade externe CSS-Styles 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", "Ändern"},
|
||||
{"Change", "Verändern"},
|
||||
{"Change language", "Sprache ändern"},
|
||||
{"no transit tunnels currently built", "derzeit keine Transittunnel aufgebaut"},
|
||||
{"SAM disabled", "SAM deaktiviert"},
|
||||
{"no sessions currently running", "Derzeit keine laufenden Sitzungen"},
|
||||
{"SAM session not found", "SAM-Sitzung nicht gefunden"},
|
||||
{"SAM Session", "SAM-Sitzung"},
|
||||
{"SAM session not found", "SAM Sitzung nicht gefunden"},
|
||||
{"SAM Session", "SAM Sitzung"},
|
||||
{"Server Tunnels", "Servertunnel"},
|
||||
{"Client Forwards", "Client-Weiterleitungen"},
|
||||
{"Client Forwards", "Klient-Weiterleitungen"},
|
||||
{"Server Forwards", "Server-Weiterleitungen"},
|
||||
{"Unknown page", "Unbekannte Seite"},
|
||||
{"Invalid token", "Ungültiger Token"},
|
||||
{"SUCCESS", "ERFOLGREICH"},
|
||||
{"Stream closed", "Stream geschlossen"},
|
||||
{"Stream not found or already was closed", "Stream nicht gefunden oder bereits geschlossen"},
|
||||
{"Destination not found", "Ziel nicht gefunden"},
|
||||
{"Destination not found", "Destination nicht gefunden"},
|
||||
{"StreamID can't be null", "StreamID kann nicht null sein"},
|
||||
{"Return to destination page", "Zurück zur Ziel-Seite"},
|
||||
{"Return to destination page", "Zurück zur Destination-Seite"},
|
||||
{"You will be redirected in 5 seconds", "Du wirst in 5 Sekunden weitergeleitet"},
|
||||
{"Transit tunnels count must not exceed 65535", "Es darf maximal 65535 Transittunnel geben"},
|
||||
{"Back to commands list", "Zurück zur Befehlsliste"},
|
||||
{"Back to commands list", "Zurück zur Kommandoliste"},
|
||||
{"Register at reg.i2p", "Auf reg.i2p registrieren"},
|
||||
{"Description", "Beschreibung"},
|
||||
{"A bit information about service on domain", "Ein 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"},
|
||||
{"A bit information about service on domain", "Ein bisschen Informationen über den Service auf der Domain"},
|
||||
{"Submit", "Einreichen"},
|
||||
{"Domain can't end with .b32.i2p", "Domain kann nicht mit .b32.i2p enden"},
|
||||
{"Domain must end with .i2p", "Domain muss mit .i2p enden"},
|
||||
{"Such destination is not found", "Eine solche Destination konnte nicht gefunden werden"},
|
||||
{"Unknown command", "Unbekannter Befehl"},
|
||||
{"Command accepted", "Befehl akzeptiert"},
|
||||
{"Proxy error", "Proxy-Fehler"},
|
||||
{"Proxy info", "Proxy-Info"},
|
||||
{"Proxy error: Host not found", "Proxy-Fehler: Host nicht gefunden"},
|
||||
{"Remote host not found in router's addressbook", "Remote-Host nicht im Router-Adressbuch gefunden"},
|
||||
{"You may try to find this host on jump services below", "Vielleicht kannst du diesen Host auf einem der nachfolgenden Jump-Services 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 einen der Jump-Services unten finden"},
|
||||
{"Invalid request", "Ungültige Anfrage"},
|
||||
{"Proxy unable to parse your request", "Proxy konnte die Anfrage nicht verarbeiten"},
|
||||
{"addresshelper is not supported", "Addresshelfer wird nicht unterstützt"},
|
||||
{"Proxy unable to parse your request", "Proxy konnte die Anfrage nicht interpretieren"},
|
||||
{"addresshelper is not supported", "addresshelper wird nicht unterstützt"},
|
||||
{"Host", "Host"},
|
||||
{"added to router's addressbook from helper", "vom Helfer zum Router-Adressbuch hinzugefügt"},
|
||||
{"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 den Ziel-Host von der Anfrage nicht erkennen"},
|
||||
{"Can't detect destination host from request", "Kann Anhand der Anfrage den Destination-Host nicht erkennen"},
|
||||
{"Outproxy failure", "Outproxy-Fehler"},
|
||||
{"bad outproxy settings", "ungültige Outproxy-Einstellungen"},
|
||||
{"not inside I2P network, but outproxy is not enabled", "außerhalb des I2P-Netzwerks, aber Outproxy ist nicht aktiviert"},
|
||||
{"not inside I2P network, but outproxy is not enabled", "nicht innerhalb des I2P-Netzwerks, aber Outproxy ist nicht aktiviert"},
|
||||
{"unknown outproxy url", "unbekannte Outproxy-URL"},
|
||||
{"cannot resolve upstream proxy", "kann den Upstream-Proxy nicht auflösen"},
|
||||
{"hostname too long", "Hostname zu lang"},
|
||||
@@ -193,16 +192,16 @@ 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 aufbauen, vielleicht ist er offline. Versuche es später noch mal."},
|
||||
{"Can't create connection to requested host, it may be down. Please try again later.", "Konnte keine Verbindung zum angefragten Host aufbaunen, vielleicht ist es offline. Versuche es später noch einmal."},
|
||||
{"", ""},
|
||||
};
|
||||
|
||||
static std::map<std::string, std::vector<std::string>> plurals
|
||||
{
|
||||
{"%d days", {"%d Tag", "%d Tage"}},
|
||||
{"%d hours", {"%d Stunde", "%d Stunden"}},
|
||||
{"%d minutes", {"%d Minute", "%d Minuten"}},
|
||||
{"%d seconds", {"%d Sekunde", "%d Sekunden"}},
|
||||
{"days", {"Tag", "Tage"}},
|
||||
{"hours", {"Stunde", "Stunden"}},
|
||||
{"minutes", {"Minute", "Minuten"}},
|
||||
{"seconds", {"Sekunde", "Sekunden"}},
|
||||
{"", {"", ""}},
|
||||
};
|
||||
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2021-2023, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
* See full license text in LICENSE file at top of project tree
|
||||
*/
|
||||
|
||||
#include <clocale>
|
||||
#include "ClientContext.h"
|
||||
#include "I18N_langs.h"
|
||||
#include "I18N.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace i18n
|
||||
{
|
||||
void SetLanguage(const std::string &lang)
|
||||
{
|
||||
const auto it = i2p::i18n::languages.find(lang);
|
||||
if (it == i2p::i18n::languages.end()) // fallback
|
||||
{
|
||||
i2p::client::context.SetLanguage (i2p::i18n::english::GetLocale());
|
||||
setlocale(LC_NUMERIC, "english");
|
||||
}
|
||||
else
|
||||
{
|
||||
i2p::client::context.SetLanguage (it->second.LocaleFunc());
|
||||
setlocale(LC_NUMERIC, lang.c_str()); // set decimal point based on language
|
||||
}
|
||||
}
|
||||
|
||||
std::string translate (const std::string& arg)
|
||||
{
|
||||
return i2p::client::context.GetLanguage ()->GetString (arg);
|
||||
}
|
||||
|
||||
std::string translate (const std::string& arg, const std::string& arg2, const int& n)
|
||||
{
|
||||
return i2p::client::context.GetLanguage ()->GetPlural (arg, arg2, n);
|
||||
}
|
||||
} // i18n
|
||||
} // i2p
|
||||
132
i18n/I18N.h
132
i18n/I18N.h
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2021-2023, The PurpleI2P Project
|
||||
* Copyright (c) 2021-2022, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -9,129 +9,37 @@
|
||||
#ifndef __I18N_H__
|
||||
#define __I18N_H__
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <utility>
|
||||
#include <functional>
|
||||
#include "ClientContext.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace i18n
|
||||
{
|
||||
class Locale
|
||||
inline void SetLanguage(const std::string &lang)
|
||||
{
|
||||
public:
|
||||
Locale (
|
||||
const std::string& language,
|
||||
const std::map<std::string, std::string>& strings,
|
||||
const std::map<std::string, std::vector<std::string>>& plurals,
|
||||
std::function<int(int)> formula
|
||||
): m_Language (language), m_Strings (strings), m_Plurals (plurals), m_Formula (formula) { };
|
||||
const auto it = i2p::i18n::languages.find(lang);
|
||||
if (it == i2p::i18n::languages.end()) // fallback
|
||||
i2p::client::context.SetLanguage (i2p::i18n::english::GetLocale());
|
||||
else
|
||||
i2p::client::context.SetLanguage (it->second.LocaleFunc());
|
||||
}
|
||||
|
||||
// Get activated language name for webconsole
|
||||
std::string GetLanguage() const
|
||||
{
|
||||
return m_Language;
|
||||
}
|
||||
inline std::string translate (const std::string& arg)
|
||||
{
|
||||
return i2p::client::context.GetLanguage ()->GetString (arg);
|
||||
}
|
||||
|
||||
std::string GetString (const std::string& arg) const
|
||||
{
|
||||
const auto it = m_Strings.find(arg);
|
||||
if (it == m_Strings.end())
|
||||
{
|
||||
return arg;
|
||||
}
|
||||
else
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
|
||||
std::string GetPlural (const std::string& arg, const std::string& arg2, const int& n) const
|
||||
{
|
||||
const auto it = m_Plurals.find(arg2);
|
||||
if (it == m_Plurals.end()) // not found, fallback to english
|
||||
{
|
||||
return n == 1 ? arg : arg2;
|
||||
}
|
||||
else
|
||||
{
|
||||
int form = m_Formula(n);
|
||||
return it->second[form];
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const std::string m_Language;
|
||||
const std::map<std::string, std::string> m_Strings;
|
||||
const std::map<std::string, std::vector<std::string>> m_Plurals;
|
||||
std::function<int(int)> m_Formula;
|
||||
};
|
||||
|
||||
void SetLanguage(const std::string &lang);
|
||||
std::string translate (const std::string& arg);
|
||||
std::string translate (const std::string& arg, const std::string& arg2, const int& n);
|
||||
inline std::string translate (const std::string& arg, const std::string& arg2, const int& n)
|
||||
{
|
||||
return i2p::client::context.GetLanguage ()->GetPlural (arg, arg2, n);
|
||||
}
|
||||
} // i18n
|
||||
} // i2p
|
||||
|
||||
/**
|
||||
* @brief Get translation of string
|
||||
* @param arg String with message
|
||||
*/
|
||||
template<typename TValue>
|
||||
std::string tr (TValue&& arg)
|
||||
template<typename... TArgs>
|
||||
std::string tr (TArgs&&... args)
|
||||
{
|
||||
return i2p::i18n::translate(std::forward<TValue>(arg));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get translation of string and format it
|
||||
* @param arg String with message
|
||||
* @param args Array of arguments for string formatting
|
||||
*/
|
||||
template<typename TValue, typename... TArgs>
|
||||
std::string tr (TValue&& arg, TArgs&&... args)
|
||||
{
|
||||
std::string tr_str = i2p::i18n::translate(std::forward<TValue>(arg));
|
||||
|
||||
size_t size = std::snprintf(NULL, 0, tr_str.c_str(), std::forward<TArgs>(args)...);
|
||||
size = size + 1;
|
||||
std::string str(size, 0);
|
||||
std::snprintf(&str.front(), size, tr_str.c_str(), std::forward<TArgs>(args)...);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get translation of string with plural forms
|
||||
* @param arg String with message in singular form
|
||||
* @param arg2 String with message in plural form
|
||||
* @param n Integer, used for selection of form
|
||||
*/
|
||||
template<typename TValue, typename TValue2>
|
||||
std::string ntr (TValue&& arg, TValue2&& arg2, int& n)
|
||||
{
|
||||
return i2p::i18n::translate(std::forward<TValue>(arg), std::forward<TValue2>(arg2), std::forward<int>(n));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get translation of string with plural forms and format it
|
||||
* @param arg String with message in singular form
|
||||
* @param arg2 String with message in plural form
|
||||
* @param n Integer, used for selection of form
|
||||
* @param args Array of arguments for string formatting
|
||||
*/
|
||||
template<typename TValue, typename TValue2, typename... TArgs>
|
||||
std::string ntr (TValue&& arg, TValue2&& arg2, int& n, TArgs&&... args)
|
||||
{
|
||||
std::string tr_str = i2p::i18n::translate(std::forward<TValue>(arg), std::forward<TValue2>(arg2), std::forward<int>(n));
|
||||
|
||||
size_t size = std::snprintf(NULL, 0, tr_str.c_str(), std::forward<TArgs>(args)...);
|
||||
size = size + 1;
|
||||
std::string str(size, 0);
|
||||
std::snprintf(&str.front(), size, tr_str.c_str(), std::forward<TArgs>(args)...);
|
||||
|
||||
return str;
|
||||
return i2p::i18n::translate(std::forward<TArgs>(args)...);
|
||||
}
|
||||
|
||||
#endif // __I18N_H__
|
||||
|
||||
@@ -9,12 +9,60 @@
|
||||
#ifndef __I18N_LANGS_H__
|
||||
#define __I18N_LANGS_H__
|
||||
|
||||
#include "I18N.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace i18n
|
||||
{
|
||||
class Locale
|
||||
{
|
||||
public:
|
||||
Locale (
|
||||
const std::string& language,
|
||||
const std::map<std::string, std::string>& strings,
|
||||
const std::map<std::string, std::vector<std::string>>& plurals,
|
||||
std::function<int(int)> formula
|
||||
): m_Language (language), m_Strings (strings), m_Plurals (plurals), m_Formula (formula) { };
|
||||
|
||||
// Get activated language name for webconsole
|
||||
std::string GetLanguage() const
|
||||
{
|
||||
return m_Language;
|
||||
}
|
||||
|
||||
std::string GetString (const std::string& arg) const
|
||||
{
|
||||
const auto it = m_Strings.find(arg);
|
||||
if (it == m_Strings.end())
|
||||
{
|
||||
return arg;
|
||||
}
|
||||
else
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
|
||||
std::string GetPlural (const std::string& arg, const std::string& arg2, const int& n) const
|
||||
{
|
||||
const auto it = m_Plurals.find(arg2);
|
||||
if (it == m_Plurals.end()) // not found, fallback to english
|
||||
{
|
||||
return n == 1 ? arg : arg2;
|
||||
}
|
||||
else
|
||||
{
|
||||
int form = m_Formula(n);
|
||||
return it->second[form];
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const std::string m_Language;
|
||||
const std::map<std::string, std::string> m_Strings;
|
||||
const std::map<std::string, std::vector<std::string>> m_Plurals;
|
||||
std::function<int(int)> m_Formula;
|
||||
};
|
||||
|
||||
struct langData
|
||||
{
|
||||
std::string LocaleName; // localized name
|
||||
@@ -25,15 +73,10 @@ 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 czech { 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 swedish { 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 (); }
|
||||
@@ -44,18 +87,13 @@ namespace i18n
|
||||
static std::map<std::string, langData> languages
|
||||
{
|
||||
{ "afrikaans", {"Afrikaans", "af", i2p::i18n::afrikaans::GetLocale} },
|
||||
{ "armenian", {"hայերէն", "hy", i2p::i18n::armenian::GetLocale} },
|
||||
{ "chinese", {"简体字", "zh-CN", i2p::i18n::chinese::GetLocale} },
|
||||
{ "czech", {"čeština", "cs", i2p::i18n::czech::GetLocale} },
|
||||
{ "armenian", {"հայերէն", "hy", i2p::i18n::armenian::GetLocale} },
|
||||
{ "english", {"English", "en", i2p::i18n::english::GetLocale} },
|
||||
{ "french", {"Français", "fr", i2p::i18n::french::GetLocale} },
|
||||
{ "german", {"Deutsch", "de", i2p::i18n::german::GetLocale} },
|
||||
{ "italian", {"Italiano", "it", i2p::i18n::italian::GetLocale} },
|
||||
{ "russian", {"Русский язык", "ru", i2p::i18n::russian::GetLocale} },
|
||||
{ "spanish", {"Español", "es", i2p::i18n::spanish::GetLocale} },
|
||||
{ "swedish", {"Svenska", "sv", i2p::i18n::swedish::GetLocale} },
|
||||
{ "turkmen", {"Türkmen dili", "tk", i2p::i18n::turkmen::GetLocale} },
|
||||
{ "ukrainian", {"Украї́нська мо́ва", "uk", i2p::i18n::ukrainian::GetLocale} },
|
||||
{ "russian", {"русский язык", "ru", i2p::i18n::russian::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
216
i18n/Italian.cpp
@@ -1,216 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
{
|
||||
{"%.2f KiB", "%.2f KiB"},
|
||||
{"%.2f MiB", "%.2f MiB"},
|
||||
{"%.2f GiB", "%.2f 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"},
|
||||
{"%.2f KiB/s", "%.2f 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"},
|
||||
{"%dms", "%dms"},
|
||||
{"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
|
||||
{
|
||||
{"%d days", {"%d giorno", "%d giorni"}},
|
||||
{"%d hours", {"%d ora", "%d ore"}},
|
||||
{"%d minutes", {"%d minuto", "%d minuti"}},
|
||||
{"%d seconds", {"%d secondo", "%d 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
|
||||
@@ -31,9 +31,9 @@ namespace russian // language namespace
|
||||
|
||||
static std::map<std::string, std::string> strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f КиБ"},
|
||||
{"%.2f MiB", "%.2f МиБ"},
|
||||
{"%.2f GiB", "%.2f ГиБ"},
|
||||
{"KiB", "КиБ"},
|
||||
{"MiB", "МиБ"},
|
||||
{"GiB", "ГиБ"},
|
||||
{"building", "строится"},
|
||||
{"failed", "неудачный"},
|
||||
{"expiring", "истекает"},
|
||||
@@ -68,7 +68,7 @@ namespace russian // language namespace
|
||||
{"Family", "Семейство"},
|
||||
{"Tunnel creation success rate", "Успешно построенных туннелей"},
|
||||
{"Received", "Получено"},
|
||||
{"%.2f KiB/s", "%.2f КиБ/с"},
|
||||
{"KiB/s", "КиБ/с"},
|
||||
{"Sent", "Отправлено"},
|
||||
{"Transit", "Транзит"},
|
||||
{"Data path", "Путь к данным"},
|
||||
@@ -94,7 +94,7 @@ namespace russian // language namespace
|
||||
{"Type", "Тип"},
|
||||
{"EncType", "ТипШифр"},
|
||||
{"Inbound tunnels", "Входящие туннели"},
|
||||
{"%dms", "%dмс"},
|
||||
{"ms", "мс"},
|
||||
{"Outbound tunnels", "Исходящие туннели"},
|
||||
{"Tags", "Теги"},
|
||||
{"Incoming", "Входящие"},
|
||||
@@ -198,10 +198,10 @@ namespace russian // language namespace
|
||||
|
||||
static std::map<std::string, std::vector<std::string>> plurals
|
||||
{
|
||||
{"%d days", {"%d день", "%d дня", "%d дней"}},
|
||||
{"%d hours", {"%d час", "%d часа", "%d часов"}},
|
||||
{"%d minutes", {"%d минуту", "%d минуты", "%d минут"}},
|
||||
{"%d seconds", {"%d секунду", "%d секунды", "%d секунд"}},
|
||||
{"days", {"день", "дня", "дней"}},
|
||||
{"hours", {"час", "часа", "часов"}},
|
||||
{"minutes", {"минуту", "минуты", "минут"}},
|
||||
{"seconds", {"секунду", "секунды", "секунд"}},
|
||||
{"", {"", "", ""}},
|
||||
};
|
||||
|
||||
|
||||
216
i18n/Spanish.cpp
216
i18n/Spanish.cpp
@@ -1,216 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
{
|
||||
{"%.2f KiB", "%.2f KiB"},
|
||||
{"%.2f MiB", "%.2f MiB"},
|
||||
{"%.2f GiB", "%.2f 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"},
|
||||
{"%.2f KiB/s", "%.2f 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"},
|
||||
{"%dms", "%dms"},
|
||||
{"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
|
||||
{
|
||||
{"%d days", {"%d día", "%d días"}},
|
||||
{"%d hours", {"%d hora", "%d horas"}},
|
||||
{"%d minutes", {"%d minuto", "%d minutos"}},
|
||||
{"%d seconds", {"%d segundo", "%d 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
|
||||
217
i18n/Swedish.cpp
217
i18n/Swedish.cpp
@@ -1,217 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
* See full license text in LICENSE file at top of project tree
|
||||
*/
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include "I18N.h"
|
||||
|
||||
// Swedish localization file
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace i18n
|
||||
{
|
||||
namespace swedish // language namespace
|
||||
{
|
||||
// language name in lowercase
|
||||
static std::string language = "swedish";
|
||||
|
||||
// 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
|
||||
{
|
||||
{"%.2f KiB", "%.2f KiB"},
|
||||
{"%.2f MiB", "%.2f MiB"},
|
||||
{"%.2f GiB", "%.2f GiB"},
|
||||
{"building", "bygger"},
|
||||
{"failed", "misslyckad"},
|
||||
{"expiring", "utgår"},
|
||||
{"established", "upprättad"},
|
||||
{"unknown", "okänt"},
|
||||
{"exploratory", "utforskande"},
|
||||
{"Purple I2P Webconsole", "Purple I2P Webbkonsoll"},
|
||||
{"<b>i2pd</b> webbkonsoll", "<b>i2pd</b>-Webbkonsoll"},
|
||||
{"Main page", "Huvudsida"},
|
||||
{"Router commands", "Routerkommandon"},
|
||||
{"Local Destinations", "Lokala Platser"},
|
||||
{"LeaseSets", "Hyresuppsättningar"},
|
||||
{"Tunnels", "Tunnlar"},
|
||||
{"Transit Tunnels", "Förmedlande Tunnlar"},
|
||||
{"Transports", "Transporter"},
|
||||
{"I2P tunnels", "I2P-tunnlar"},
|
||||
{"SAM sessions", "SAM-perioder"},
|
||||
{"ERROR", "FEL"},
|
||||
{"OK", "OK"},
|
||||
{"Testing", "Prövar"},
|
||||
{"Firewalled", "Bakom Brandvägg"},
|
||||
{"Unknown", "Okänt"},
|
||||
{"Proxy", "Proxy"},
|
||||
{"Mesh", "Mesh"},
|
||||
{"Error", "Fel"},
|
||||
{"Clock skew", "Tidsförskjutning"},
|
||||
{"Offline", "Nedkopplad"},
|
||||
{"Symmetric NAT", "Symmetrisk NAT"},
|
||||
{"Uptime", "Upptid"},
|
||||
{"Network status", "Nätverkstillstånd"},
|
||||
{"Network status v6", "Nätverkstillstånd v6"},
|
||||
{"Stopping in", "Avstängd om"},
|
||||
{"Family", "Familj"},
|
||||
{"Tunnel creation success rate", "Andel framgångsrika tunnlar"},
|
||||
{"Received", "Mottaget"},
|
||||
{"%.2f KiB/s", "%.2f KiB/s"},
|
||||
{"Sent", "Skickat"},
|
||||
{"Transit", "Förmedlat"},
|
||||
{"Data path", "Sökväg"},
|
||||
{"Hidden content. Press on text to see.", "Dolt innehåll. Tryck för att visa."},
|
||||
{"Router Ident", "Routeridentitet"},
|
||||
{"Router Family", "Routerfamilj"},
|
||||
{"Router Caps", "Routerbegränsningar"},
|
||||
{"Version", "Version"},
|
||||
{"Our external address", "Vår externa adress"},
|
||||
{"supported", "stöds"},
|
||||
{"Routers", "Routrar"},
|
||||
{"Floodfills", "Översvämningsfyllare"},
|
||||
{"Client Tunnels", "Klienttunnlar"},
|
||||
{"Services", "Tjänster"},
|
||||
{"Enabled", "Påslaget"},
|
||||
{"Disabled", "Avslaget"},
|
||||
{"Encrypted B33 address", "Krypterad B33-Adress"},
|
||||
{"Address registration line", "Adressregistreringsrad"},
|
||||
{"Domain", "Domän"},
|
||||
{"Generate", "Skapa"},
|
||||
{"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "<b>Uppmärksamma:</b> den resulterande strängen kan enbart användas för att registrera 2LD-domäner (exempel.i2p). För att registrera underdomäner, vänligen använd i2pd-tools."},
|
||||
{"Address", "Adress"},
|
||||
{"Type", "Typ"},
|
||||
{"EncType", "EncTyp"},
|
||||
{"Inbound tunnels", "Ingående Tunnlar"},
|
||||
{"%dms", "%dms"},
|
||||
{"Outbound tunnels", "Utgående Tunnlar"},
|
||||
{"Tags", "Taggar"},
|
||||
{"Incoming", "Ingående"},
|
||||
{"Outgoing", "Utgående"},
|
||||
{"Destination", "Plats"},
|
||||
{"Amount", "Mängd"},
|
||||
{"Incoming Tags", "Ingående Taggar"},
|
||||
{"Tags sessions", "Tagg-perioder"},
|
||||
{"Status", "Tillstånd"},
|
||||
{"Local Destination", "Lokal Plats"},
|
||||
{"Streams", "Strömmar"},
|
||||
{"Close stream", "Stäng strömmen"},
|
||||
{"I2CP session not found", "I2CP-period hittades inte"},
|
||||
{"I2CP is not enabled", "I2CP är inte påslaget"},
|
||||
{"Invalid", "Ogiltig"},
|
||||
{"Store type", "Lagringstyp"},
|
||||
{"Expires", "Utgångsdatum"},
|
||||
{"Non Expired Leases", "Ickeutgångna Hyresuppsättningar"},
|
||||
{"Gateway", "Gateway"},
|
||||
{"TunnelID", "TunnelID"},
|
||||
{"EndDate", "EndDate"},
|
||||
{"not floodfill", "inte Översvämningsfyllare"},
|
||||
{"Queue size", "Köstorlek"},
|
||||
{"Run peer test", "Utför utsiktstest"},
|
||||
{"Decline transit tunnels", "Avvisa förmedlande tunnlar"},
|
||||
{"Accept transit tunnels", "Tillåt förmedlande tunnlar"},
|
||||
{"Cancel graceful shutdown", "Avbryt välvillig avstängning"},
|
||||
{"Start graceful shutdown", "Påbörja välvillig avstängning"},
|
||||
{"Force shutdown", "Tvingad avstängning"},
|
||||
{"Reload external CSS styles", "Ladda om externa CSS-stilar"},
|
||||
{"<b>Note:</b> any action done here are not persistent and not changes your config files.", "<b>Uppmärksamma:</b> inga ändringar här är beständiga eller påverkar dina inställningsfiler."},
|
||||
{"Logging level", "Protokollförningsnivå"},
|
||||
{"Transit tunnels limit", "Begränsa förmedlande tunnlar"},
|
||||
{"Change", "Ändra"},
|
||||
{"Change language", "Ändra språk"},
|
||||
{"no transit tunnels currently built", "inga förmedlande tunnlar har byggts"},
|
||||
{"SAM disabled", "SAM avslaget"},
|
||||
{"no sessions currently running", "inga perioder igång"},
|
||||
{"SAM session not found", "SAM-perioder hittades ej"},
|
||||
{"SAM Session", "SAM-period"},
|
||||
{"Server Tunnels", "Värdtunnlar"},
|
||||
{"Client Forwards", "Klientförpassningar"},
|
||||
{"Server Forwards", "Värdförpassningar"},
|
||||
{"Unknown page", "Okänd sida"},
|
||||
{"Invalid token", "Ogiltig polett"},
|
||||
{"SUCCESS", "FRAMGÅNG"},
|
||||
{"Stream closed", "Ström stängd"},
|
||||
{"Stream not found or already was closed", "Strömmen hittades inte eller var redan avslutad"},
|
||||
{"Destination not found", "Plats hittades ej"},
|
||||
{"StreamID can't be null", "Ström-ID kan inte vara null"},
|
||||
{"Return to destination page", "Återvänd till platssidan"},
|
||||
{"You will be redirected in 5 seconds", "Du omdirigeras inom fem sekunder"},
|
||||
{"Transit tunnels count must not exceed 65535", "Förmedlande tunnlar får inte överstiga 65535"},
|
||||
{"Back to commands list", "Tillbaka till kommandolistan"},
|
||||
{"Register at reg.i2p", "Registrera vid reg.i2p"},
|
||||
{"Description", "Beskrivning"},
|
||||
{"A bit information about service on domain", "Ett stycke information om domänens tjänst"},
|
||||
{"Submit", "Skicka"},
|
||||
{"Domain can't end with .b32.i2p", "Domänen får inte sluta med .b32.i2p"},
|
||||
{"Domain must end with .i2p", "Domänen måste sluta med .i2p"},
|
||||
{"Such destination is not found", "En sådan plats hittas ej"},
|
||||
{"Unknown command", "Okänt kommando"},
|
||||
{"Command accepted", "Kommando accepterades"},
|
||||
{"Proxy error", "Proxyfel"},
|
||||
{"Proxy info", "Proxyinfo"},
|
||||
{"Proxy error: Host not found", "Proxyfel: Värden hittades ej"},
|
||||
{"Remote host not found in router's addressbook", "Främmande värd hittades inte i routerns adressbok"},
|
||||
{"You may try to find this host on jump services below", "Du kan försöka att hitta värden genom hopptjänsterna nedan"},
|
||||
{"Invalid request", "Ogiltig förfrågan"},
|
||||
{"Proxy unable to parse your request", "Proxyt kan inte behandla din förfrågan"},
|
||||
{"addresshelper is not supported", "adresshjälparen stöds ej"},
|
||||
{"Host", "Värd"},
|
||||
{"added to router's addressbook from helper", "tillagd i routerns adressbok från adresshjälparen"},
|
||||
{"Click here to proceed:", "Tryck här för att fortsätta:"},
|
||||
{"Continue", "Fortsätt"},
|
||||
{"Addresshelper found", "Adresshjälpare hittad"},
|
||||
{"already in router's addressbook", "finns redan i routerns adressbok"},
|
||||
{"Click here to update record:", "Tryck här för att uppdatera:"},
|
||||
{"invalid request uri", "ogiltig förfrågnings-URI"},
|
||||
{"Can't detect destination host from request", "Kan inte upptäcka platsvärden från förfrågan"},
|
||||
{"Outproxy failure", "Utproxyfel"},
|
||||
{"bad outproxy settings", "ogiltig utproxyinställning"},
|
||||
{"not inside I2P network, but outproxy is not enabled", "adressen är inte inom I2P-näverket, men utproxy är inte påslaget"},
|
||||
{"unknown outproxy url", "okänt Utproxy-URL"},
|
||||
{"cannot resolve upstream proxy", "hittar inte uppströmsproxyt"},
|
||||
{"hostname too long", "värdnamnet är för långt"},
|
||||
{"cannot connect to upstream socks proxy", "kan inte ansluta till uppströmsproxyt"},
|
||||
{"Cannot negotiate with socks proxy", "Kan inte förhandla med socksproxyt"},
|
||||
{"CONNECT error", "CONNECT-fel"},
|
||||
{"Failed to Connect", "Anslutningen misslyckades"},
|
||||
{"socks proxy error", "Socksproxyfel"},
|
||||
{"failed to send request to upstream", "förfrågan uppströms kunde ej skickas"},
|
||||
{"No Reply From socks proxy", "Fick inget svar från socksproxyt"},
|
||||
{"cannot connect", "kan inte ansluta"},
|
||||
{"http out proxy not implemented", "HTTP-Utproxy ej implementerat"},
|
||||
{"cannot connect to upstream http proxy", "Kan inte ansluta till uppströms HTTP-proxy"},
|
||||
{"Host is down", "Värden är nere"},
|
||||
{"Can't create connection to requested host, it may be down. Please try again later.", "Kan inte ansluta till värden, den kan vara nere. Vänligen försök senare."},
|
||||
{"", ""},
|
||||
};
|
||||
|
||||
static std::map<std::string, std::vector<std::string>> plurals
|
||||
{
|
||||
{"%d days", {"%d Dag", "%d Dagar"}},
|
||||
{"%d hours", {"%d Timme", "%d Timmar"}},
|
||||
{"%d minutes", {"%d Minut", "%d Minuter"}},
|
||||
{"%d seconds", {"%d Sekund", "%d Sekunder"}},
|
||||
{"", {"", ""}},
|
||||
};
|
||||
|
||||
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
|
||||
|
||||
@@ -31,9 +31,9 @@ namespace turkmen // language namespace
|
||||
|
||||
static std::map<std::string, std::string> strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f KiB"},
|
||||
{"%.2f MiB", "%.2f MiB"},
|
||||
{"%.2f GiB", "%.2f GiB"},
|
||||
{"KiB", "KiB"},
|
||||
{"MiB", "MiB"},
|
||||
{"GiB", "GiB"},
|
||||
{"building", "bina"},
|
||||
{"failed", "şowsuz"},
|
||||
{"expiring", "möhleti gutarýar"},
|
||||
@@ -68,7 +68,7 @@ namespace turkmen // language namespace
|
||||
{"Family", "Maşgala"},
|
||||
{"Tunnel creation success rate", "Gurlan teneller üstünlikli gurlan teneller"},
|
||||
{"Received", "Alnan"},
|
||||
{"%.2f KiB/s", "%.2f KiB/s"},
|
||||
{"KiB/s", "KiB/s"},
|
||||
{"Sent", "Ýerleşdirildi"},
|
||||
{"Transit", "Tranzit"},
|
||||
{"Data path", "Maglumat ýoly"},
|
||||
@@ -94,7 +94,7 @@ namespace turkmen // language namespace
|
||||
{"Type", "Görnüş"},
|
||||
{"EncType", "Şifrlemek görnüşi"},
|
||||
{"Inbound tunnels", "Gelýän tuneller"},
|
||||
{"%dms", "%dms"},
|
||||
{"ms", "ms"},
|
||||
{"Outbound tunnels", "Çykýan tuneller"},
|
||||
{"Tags", "Bellikler"},
|
||||
{"Incoming", "Gelýän"},
|
||||
@@ -198,10 +198,10 @@ namespace turkmen // language namespace
|
||||
|
||||
static std::map<std::string, std::vector<std::string>> plurals
|
||||
{
|
||||
{"%d days", {"%d gün", "%d gün"}},
|
||||
{"%d hours", {"%d sagat", "%d sagat"}},
|
||||
{"%d minutes", {"%d minut", "%d minut"}},
|
||||
{"%d seconds", {"%d sekunt", "%d sekunt"}},
|
||||
{"days", {"gün", "gün"}},
|
||||
{"hours", {"sagat", "sagat"}},
|
||||
{"minutes", {"minut", "minut"}},
|
||||
{"seconds", {"sekunt", "sekunt"}},
|
||||
{"", {"", ""}},
|
||||
};
|
||||
|
||||
|
||||
@@ -31,9 +31,9 @@ namespace ukrainian // language namespace
|
||||
|
||||
static std::map<std::string, std::string> strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f КіБ"},
|
||||
{"%.2f MiB", "%.2f МіБ"},
|
||||
{"%.2f GiB", "%.2f ГіБ"},
|
||||
{"KiB", "КіБ"},
|
||||
{"MiB", "МіБ"},
|
||||
{"GiB", "ГіБ"},
|
||||
{"building", "будується"},
|
||||
{"failed", "невдалий"},
|
||||
{"expiring", "завершується"},
|
||||
@@ -68,7 +68,7 @@ namespace ukrainian // language namespace
|
||||
{"Family", "Сімейство"},
|
||||
{"Tunnel creation success rate", "Успішно побудованих тунелів"},
|
||||
{"Received", "Отримано"},
|
||||
{"%.2f KiB/s", "%.2f КіБ/с"},
|
||||
{"KiB/s", "КіБ/с"},
|
||||
{"Sent", "Відправлено"},
|
||||
{"Transit", "Транзит"},
|
||||
{"Data path", "Шлях до даних"},
|
||||
@@ -94,7 +94,7 @@ namespace ukrainian // language namespace
|
||||
{"Type", "Тип"},
|
||||
{"EncType", "ТипШифр"},
|
||||
{"Inbound tunnels", "Вхідні тунелі"},
|
||||
{"%dms", "%dмс"},
|
||||
{"ms", "мс"},
|
||||
{"Outbound tunnels", "Вихідні тунелі"},
|
||||
{"Tags", "Теги"},
|
||||
{"Incoming", "Вхідні"},
|
||||
@@ -198,10 +198,10 @@ namespace ukrainian // language namespace
|
||||
|
||||
static std::map<std::string, std::vector<std::string>> plurals
|
||||
{
|
||||
{"%d days", {"%d день", "%d дня", "%d днів"}},
|
||||
{"%d hours", {"%d годину", "%d години", "%d годин"}},
|
||||
{"%d minutes", {"%d хвилину", "%d хвилини", "%d хвилин"}},
|
||||
{"%d seconds", {"%d секунду", "%d секунди", "%d секунд"}},
|
||||
{"days", {"день", "дня", "днів"}},
|
||||
{"hours", {"годину", "години", "годин"}},
|
||||
{"minutes", {"хвилину", "хвилини", "хвилин"}},
|
||||
{"seconds", {"секунду", "секунди", "секунд"}},
|
||||
{"", {"", "", ""}},
|
||||
};
|
||||
|
||||
|
||||
@@ -31,9 +31,9 @@ namespace uzbek // language namespace
|
||||
|
||||
static std::map<std::string, std::string> strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f KiB"},
|
||||
{"%.2f MiB", "%.2f MiB"},
|
||||
{"%.2f GiB", "%.2f GiB"},
|
||||
{"KiB", "KiB"},
|
||||
{"MiB", "MiB"},
|
||||
{"GiB", "GiB"},
|
||||
{"building", "yaratilmoqda"},
|
||||
{"failed", "muvaffaqiyatsiz"},
|
||||
{"expiring", "muddati tugaydi"},
|
||||
@@ -68,7 +68,7 @@ namespace uzbek // language namespace
|
||||
{"Family", "Oila"},
|
||||
{"Tunnel creation success rate", "Tunnel yaratish muvaffaqiyat darajasi"},
|
||||
{"Received", "Qabul qilindi"},
|
||||
{"%.2f KiB/s", "%.2f KiB/s"},
|
||||
{"KiB/s", "KiB/s"},
|
||||
{"Sent", "Yuborilgan"},
|
||||
{"Transit", "Tranzit"},
|
||||
{"Data path", "Ma'lumotlar joylanishi"},
|
||||
@@ -94,7 +94,7 @@ namespace uzbek // language namespace
|
||||
{"Type", "Turi"},
|
||||
{"EncType", "ShifrlashTuri"},
|
||||
{"Inbound tunnels", "Kirish tunnellari"},
|
||||
{"%dms", "%dms"},
|
||||
{"ms", "ms"},
|
||||
{"Outbound tunnels", "Chiquvchi tunnellar"},
|
||||
{"Tags", "Teglar"},
|
||||
{"Incoming", "Kiruvchi"},
|
||||
@@ -198,10 +198,10 @@ namespace uzbek // language namespace
|
||||
|
||||
static std::map<std::string, std::vector<std::string>> plurals
|
||||
{
|
||||
{"%d days", {"%d kun", "%d kun"}},
|
||||
{"%d hours", {"%d soat", "%d soat"}},
|
||||
{"%d minutes", {"%d daqiqa", "%d daqiqa"}},
|
||||
{"%d seconds", {"%d soniya", "%d soniya"}},
|
||||
{"days", {"kun", "kun"}},
|
||||
{"hours", {"soat", "soat"}},
|
||||
{"minutes", {"daqiqa", "daqiqa"}},
|
||||
{"seconds", {"soniya", "soniya"}},
|
||||
{"", {"", ""}},
|
||||
};
|
||||
|
||||
|
||||
@@ -135,7 +135,7 @@ namespace data
|
||||
//----------------------------------------------------------
|
||||
|
||||
const uint8_t B33_TWO_BYTES_SIGTYPE_FLAG = 0x01;
|
||||
// const uint8_t B33_PER_SECRET_FLAG = 0x02; // not used for now
|
||||
const uint8_t B33_PER_SECRET_FLAG = 0x02; // not used for now
|
||||
const uint8_t B33_PER_CLIENT_AUTH_FLAG = 0x04;
|
||||
|
||||
BlindedPublicKey::BlindedPublicKey (std::shared_ptr<const IdentityEx> identity, bool clientAuth):
|
||||
|
||||
77
libi2pd/BloomFilter.cpp
Normal file
77
libi2pd/BloomFilter.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2020, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
* See full license text in LICENSE file at top of project tree
|
||||
*/
|
||||
|
||||
#include "BloomFilter.h"
|
||||
#include "I2PEndian.h"
|
||||
#include <array>
|
||||
#include <openssl/sha.h>
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
|
||||
/** @brief decaying bloom filter implementation */
|
||||
class DecayingBloomFilter : public IBloomFilter
|
||||
{
|
||||
public:
|
||||
|
||||
DecayingBloomFilter(const std::size_t size)
|
||||
{
|
||||
m_Size = size;
|
||||
m_Data = new uint8_t[size];
|
||||
}
|
||||
|
||||
/** @brief implements IBloomFilter::~IBloomFilter */
|
||||
~DecayingBloomFilter()
|
||||
{
|
||||
delete [] m_Data;
|
||||
}
|
||||
|
||||
/** @brief implements IBloomFilter::Add */
|
||||
bool Add(const uint8_t * data, std::size_t len)
|
||||
{
|
||||
std::size_t idx;
|
||||
uint8_t mask;
|
||||
Get(data, len, idx, mask);
|
||||
if(m_Data[idx] & mask) return false; // filter hit
|
||||
m_Data[idx] |= mask;
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @brief implements IBloomFilter::Decay */
|
||||
void Decay()
|
||||
{
|
||||
// reset bloom filter buffer
|
||||
memset(m_Data, 0, m_Size);
|
||||
}
|
||||
|
||||
private:
|
||||
/** @brief get bit index for for data */
|
||||
void Get(const uint8_t * data, std::size_t len, std::size_t & idx, uint8_t & bm)
|
||||
{
|
||||
bm = 1;
|
||||
uint8_t digest[32];
|
||||
// TODO: use blake2 because it's faster
|
||||
SHA256(data, len, digest);
|
||||
uint64_t i = buf64toh(digest);
|
||||
idx = i % m_Size;
|
||||
bm <<= (i % 8);
|
||||
}
|
||||
|
||||
uint8_t * m_Data;
|
||||
std::size_t m_Size;
|
||||
};
|
||||
|
||||
|
||||
BloomFilterPtr BloomFilter(std::size_t capacity)
|
||||
{
|
||||
return std::make_shared<DecayingBloomFilter>(capacity);
|
||||
}
|
||||
}
|
||||
}
|
||||
39
libi2pd/BloomFilter.h
Normal file
39
libi2pd/BloomFilter.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2020, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
* See full license text in LICENSE file at top of project tree
|
||||
*/
|
||||
|
||||
#ifndef BLOOM_FILTER_H_
|
||||
#define BLOOM_FILTER_H_
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
|
||||
/** @brief interface for bloom filter */
|
||||
struct IBloomFilter
|
||||
{
|
||||
|
||||
/** @brief destructor */
|
||||
virtual ~IBloomFilter() {};
|
||||
/** @brief add entry to bloom filter, return false if filter hit otherwise return true */
|
||||
virtual bool Add(const uint8_t * data, std::size_t len) = 0;
|
||||
/** @brief optionally decay old entries */
|
||||
virtual void Decay() = 0;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<IBloomFilter> BloomFilterPtr;
|
||||
|
||||
/** @brief create bloom filter */
|
||||
BloomFilterPtr BloomFilter(std::size_t capacity = 1024 * 8);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -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(false), "Ignored. Always false")
|
||||
("ssu", bool_switch()->default_value(true), "Enable SSU transport (default: enabled)")
|
||||
("ntcpproxy", value<std::string>()->default_value(""), "Ignored")
|
||||
#ifdef _WIN32
|
||||
("svcctl", value<std::string>()->default_value(""), "Ignored")
|
||||
@@ -77,7 +77,7 @@ namespace config {
|
||||
limits.add_options()
|
||||
("limits.coresize", value<uint32_t>()->default_value(0), "Maximum size of corefile in Kb (0 - use system limit)")
|
||||
("limits.openfiles", value<uint16_t>()->default_value(0), "Maximum number of open files (0 - use system default)")
|
||||
("limits.transittunnels", value<uint16_t>()->default_value(5000), "Maximum active transit tunnels (default:5000)")
|
||||
("limits.transittunnels", value<uint16_t>()->default_value(2500), "Maximum active transit sessions (default:2500)")
|
||||
("limits.ntcpsoft", value<uint16_t>()->default_value(0), "Ignored")
|
||||
("limits.ntcphard", value<uint16_t>()->default_value(0), "Ignored")
|
||||
("limits.ntcpthreads", value<uint16_t>()->default_value(1), "Ignored")
|
||||
@@ -274,12 +274,9 @@ namespace config {
|
||||
|
||||
options_description ssu2("SSU2 Options");
|
||||
ssu2.add_options()
|
||||
("ssu2.enabled", value<bool>()->default_value(true), "Enable SSU2 (default: enabled)")
|
||||
("ssu2.published", value<bool>()->default_value(true), "Publish SSU2 (default: enabled)")
|
||||
("ssu2.enabled", value<bool>()->default_value(false), "Enable SSU2 (default: disabled)")
|
||||
("ssu2.published", value<bool>()->default_value(false), "Publish SSU2 (default: disabled)")
|
||||
("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");
|
||||
@@ -314,13 +311,6 @@ namespace config {
|
||||
("meshnets.yggaddress", value<std::string>()->default_value(""), "Yggdrasil address to publish")
|
||||
;
|
||||
|
||||
#ifdef __linux__
|
||||
options_description unix_specific("UNIX-specific options");
|
||||
unix_specific.add_options()
|
||||
("unix.handle_sigtstp", bool_switch()->default_value(false), "Handle SIGTSTP and SIGCONT signals (default: disabled)")
|
||||
;
|
||||
#endif
|
||||
|
||||
m_OptionsDesc
|
||||
.add(general)
|
||||
.add(limits)
|
||||
@@ -344,9 +334,6 @@ namespace config {
|
||||
.add(persist)
|
||||
.add(cpuext)
|
||||
.add(meshnets)
|
||||
#ifdef __linux__
|
||||
.add(unix_specific)
|
||||
#endif
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
@@ -159,10 +159,8 @@ namespace crypto
|
||||
|
||||
// DH/ElGamal
|
||||
|
||||
#if !defined(__x86_64__)
|
||||
const int ELGAMAL_SHORT_EXPONENT_NUM_BITS = 226;
|
||||
const int ELGAMAL_SHORT_EXPONENT_NUM_BYTES = ELGAMAL_SHORT_EXPONENT_NUM_BITS/8+1;
|
||||
#endif
|
||||
const int ELGAMAL_FULL_EXPONENT_NUM_BITS = 2048;
|
||||
const int ELGAMAL_FULL_EXPONENT_NUM_BYTES = ELGAMAL_FULL_EXPONENT_NUM_BITS/8;
|
||||
|
||||
@@ -241,6 +239,55 @@ namespace crypto
|
||||
|
||||
static BIGNUM * (* g_ElggTable)[255] = nullptr;
|
||||
|
||||
// DH
|
||||
|
||||
DHKeys::DHKeys ()
|
||||
{
|
||||
m_DH = DH_new ();
|
||||
DH_set0_pqg (m_DH, BN_dup (elgp), NULL, BN_dup (elgg));
|
||||
DH_set0_key (m_DH, NULL, NULL);
|
||||
}
|
||||
|
||||
DHKeys::~DHKeys ()
|
||||
{
|
||||
DH_free (m_DH);
|
||||
}
|
||||
|
||||
void DHKeys::GenerateKeys ()
|
||||
{
|
||||
BIGNUM * priv_key = NULL, * pub_key = NULL;
|
||||
#if !defined(__x86_64__) // use short exponent for non x64
|
||||
priv_key = BN_new ();
|
||||
BN_rand (priv_key, ELGAMAL_SHORT_EXPONENT_NUM_BITS, 0, 1);
|
||||
#endif
|
||||
if (g_ElggTable)
|
||||
{
|
||||
#if defined(__x86_64__)
|
||||
priv_key = BN_new ();
|
||||
BN_rand (priv_key, ELGAMAL_FULL_EXPONENT_NUM_BITS, 0, 1);
|
||||
#endif
|
||||
auto ctx = BN_CTX_new ();
|
||||
pub_key = ElggPow (priv_key, g_ElggTable, ctx);
|
||||
DH_set0_key (m_DH, pub_key, priv_key);
|
||||
BN_CTX_free (ctx);
|
||||
}
|
||||
else
|
||||
{
|
||||
DH_set0_key (m_DH, NULL, priv_key);
|
||||
DH_generate_key (m_DH);
|
||||
DH_get0_key (m_DH, (const BIGNUM **)&pub_key, (const BIGNUM **)&priv_key);
|
||||
}
|
||||
|
||||
bn2buf (pub_key, m_PublicKey, 256);
|
||||
}
|
||||
|
||||
void DHKeys::Agree (const uint8_t * pub, uint8_t * shared)
|
||||
{
|
||||
BIGNUM * pk = BN_bin2bn (pub, 256, NULL);
|
||||
DH_compute_key (shared, pk, m_DH);
|
||||
BN_free (pk);
|
||||
}
|
||||
|
||||
// x25519
|
||||
X25519Keys::X25519Keys ()
|
||||
{
|
||||
@@ -554,6 +601,77 @@ namespace crypto
|
||||
BN_CTX_free (ctx);
|
||||
}
|
||||
|
||||
// HMAC
|
||||
const uint64_t IPAD = 0x3636363636363636;
|
||||
const uint64_t OPAD = 0x5C5C5C5C5C5C5C5C;
|
||||
|
||||
|
||||
static const uint64_t ipads[] = { IPAD, IPAD, IPAD, IPAD };
|
||||
static const uint64_t opads[] = { OPAD, OPAD, OPAD, OPAD };
|
||||
|
||||
void HMACMD5Digest (uint8_t * msg, size_t len, const MACKey& key, uint8_t * digest)
|
||||
// key is 32 bytes
|
||||
// digest is 16 bytes
|
||||
// block size is 64 bytes
|
||||
{
|
||||
uint64_t buf[256];
|
||||
uint64_t hash[12]; // 96 bytes
|
||||
#if (defined(__x86_64__) || defined(__i386__)) && defined(__AVX__) // not all X86 targets supports AVX (like old Pentium, see #1600)
|
||||
if(i2p::cpu::avx)
|
||||
{
|
||||
__asm__
|
||||
(
|
||||
"vmovups %[key], %%ymm0 \n"
|
||||
"vmovups %[ipad], %%ymm1 \n"
|
||||
"vmovups %%ymm1, 32(%[buf]) \n"
|
||||
"vxorps %%ymm0, %%ymm1, %%ymm1 \n"
|
||||
"vmovups %%ymm1, (%[buf]) \n"
|
||||
"vmovups %[opad], %%ymm1 \n"
|
||||
"vmovups %%ymm1, 32(%[hash]) \n"
|
||||
"vxorps %%ymm0, %%ymm1, %%ymm1 \n"
|
||||
"vmovups %%ymm1, (%[hash]) \n"
|
||||
"vzeroall \n" // end of AVX
|
||||
"movups %%xmm0, 80(%[hash]) \n" // zero last 16 bytes
|
||||
:
|
||||
: [key]"m"(*(const uint8_t *)key), [ipad]"m"(*ipads), [opad]"m"(*opads),
|
||||
[buf]"r"(buf), [hash]"r"(hash)
|
||||
: "memory", "%xmm0" // TODO: change to %ymm0 later
|
||||
);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
// ikeypad
|
||||
buf[0] = key.GetLL ()[0] ^ IPAD;
|
||||
buf[1] = key.GetLL ()[1] ^ IPAD;
|
||||
buf[2] = key.GetLL ()[2] ^ IPAD;
|
||||
buf[3] = key.GetLL ()[3] ^ IPAD;
|
||||
buf[4] = IPAD;
|
||||
buf[5] = IPAD;
|
||||
buf[6] = IPAD;
|
||||
buf[7] = IPAD;
|
||||
// okeypad
|
||||
hash[0] = key.GetLL ()[0] ^ OPAD;
|
||||
hash[1] = key.GetLL ()[1] ^ OPAD;
|
||||
hash[2] = key.GetLL ()[2] ^ OPAD;
|
||||
hash[3] = key.GetLL ()[3] ^ OPAD;
|
||||
hash[4] = OPAD;
|
||||
hash[5] = OPAD;
|
||||
hash[6] = OPAD;
|
||||
hash[7] = OPAD;
|
||||
// fill last 16 bytes with zeros (first hash size assumed 32 bytes in I2P)
|
||||
memset (hash + 10, 0, 16);
|
||||
}
|
||||
|
||||
// concatenate with msg
|
||||
memcpy (buf + 8, msg, len);
|
||||
// calculate first hash
|
||||
MD5((uint8_t *)buf, len + 64, (uint8_t *)(hash + 8)); // 16 bytes
|
||||
|
||||
// calculate digest
|
||||
MD5((uint8_t *)hash, 96, digest);
|
||||
}
|
||||
|
||||
// AES
|
||||
#ifdef __AES__
|
||||
#define KeyExpansion256(round0,round1) \
|
||||
|
||||
@@ -62,6 +62,24 @@ namespace crypto
|
||||
// RSA
|
||||
const BIGNUM * GetRSAE ();
|
||||
|
||||
// DH
|
||||
class DHKeys
|
||||
{
|
||||
public:
|
||||
|
||||
DHKeys ();
|
||||
~DHKeys ();
|
||||
|
||||
void GenerateKeys ();
|
||||
const uint8_t * GetPublicKey () const { return m_PublicKey; };
|
||||
void Agree (const uint8_t * pub, uint8_t * shared);
|
||||
|
||||
private:
|
||||
|
||||
DH * m_DH;
|
||||
uint8_t m_PublicKey[256];
|
||||
};
|
||||
|
||||
// x25519
|
||||
class X25519Keys
|
||||
{
|
||||
@@ -103,6 +121,10 @@ namespace crypto
|
||||
bool ECIESDecrypt (const EC_GROUP * curve, const BIGNUM * key, const uint8_t * encrypted, uint8_t * data); // 514 bytes encrypted, 222 data
|
||||
void GenerateECIESKeyPair (const EC_GROUP * curve, BIGNUM *& priv, EC_POINT *& pub);
|
||||
|
||||
// HMAC
|
||||
typedef i2p::data::Tag<32> MACKey;
|
||||
void HMACMD5Digest (uint8_t * msg, size_t len, const MACKey& key, uint8_t * digest);
|
||||
|
||||
// AES
|
||||
struct ChipherBlock
|
||||
{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2021, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -15,7 +15,6 @@
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include "Base.h"
|
||||
#include "Gzip.h"
|
||||
#include "Identity.h"
|
||||
#include "LeaseSet.h"
|
||||
#include "I2NPProtocol.h"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2023, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -1096,13 +1096,7 @@ namespace client
|
||||
}
|
||||
auto leaseSet = FindLeaseSet (dest);
|
||||
if (leaseSet)
|
||||
{
|
||||
auto stream = CreateStream (leaseSet, port);
|
||||
GetService ().post ([streamRequestComplete, stream]()
|
||||
{
|
||||
streamRequestComplete(stream);
|
||||
});
|
||||
}
|
||||
streamRequestComplete(CreateStream (leaseSet, port));
|
||||
else
|
||||
{
|
||||
auto s = GetSharedFromThis ();
|
||||
@@ -1135,41 +1129,6 @@ namespace client
|
||||
});
|
||||
}
|
||||
|
||||
template<typename Dest>
|
||||
std::shared_ptr<i2p::stream::Stream> ClientDestination::CreateStreamSync (const Dest& dest, int port)
|
||||
{
|
||||
volatile bool done = false;
|
||||
std::shared_ptr<i2p::stream::Stream> stream;
|
||||
std::condition_variable streamRequestComplete;
|
||||
std::mutex streamRequestCompleteMutex;
|
||||
CreateStream (
|
||||
[&done, &streamRequestComplete, &streamRequestCompleteMutex, &stream](std::shared_ptr<i2p::stream::Stream> s)
|
||||
{
|
||||
stream = s;
|
||||
std::unique_lock<std::mutex> l(streamRequestCompleteMutex);
|
||||
streamRequestComplete.notify_all ();
|
||||
done = true;
|
||||
},
|
||||
dest, port);
|
||||
while (!done)
|
||||
{
|
||||
std::unique_lock<std::mutex> l(streamRequestCompleteMutex);
|
||||
if (!done)
|
||||
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,8 +247,6 @@ 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);
|
||||
@@ -284,9 +282,6 @@ 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-2022, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2020, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -57,8 +57,7 @@ namespace data
|
||||
if ((err = inflate (&m_Inflator, Z_NO_FLUSH)) == Z_STREAM_END)
|
||||
return outLen - m_Inflator.avail_out;
|
||||
// else
|
||||
if (err)
|
||||
LogPrint (eLogError, "Gzip: Inflate error ", err);
|
||||
LogPrint (eLogError, "Gzip: Inflate error ", err);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -129,8 +128,7 @@ namespace data
|
||||
return outLen - m_Deflator.avail_out;
|
||||
}
|
||||
// else
|
||||
if (err)
|
||||
LogPrint (eLogError, "Gzip: Deflate error ", err);
|
||||
LogPrint (eLogError, "Gzip: Deflate error ", err);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -160,8 +158,7 @@ namespace data
|
||||
offset = outLen - m_Deflator.avail_out;
|
||||
}
|
||||
// else
|
||||
if (err)
|
||||
LogPrint (eLogError, "Gzip: Deflate error ", err);
|
||||
LogPrint (eLogError, "Gzip: Deflate error ", err);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2021, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -135,7 +135,6 @@ 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-2023, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -392,8 +392,7 @@ namespace i2p
|
||||
clearText + ECIES_BUILD_REQUEST_RECORD_IV_KEY_OFFSET,
|
||||
clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_GATEWAY_FLAG,
|
||||
clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG);
|
||||
if (!i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel))
|
||||
retCode = 30;
|
||||
i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel);
|
||||
}
|
||||
else
|
||||
retCode = 30; // always reject with bandwidth reason (30)
|
||||
@@ -559,8 +558,7 @@ namespace i2p
|
||||
return;
|
||||
}
|
||||
auto& noiseState = i2p::context.GetCurrentNoiseState ();
|
||||
uint8_t replyKey[32]; // AEAD/Chacha20/Poly1305
|
||||
i2p::crypto::AESKey layerKey, ivKey; // AES
|
||||
uint8_t replyKey[32], layerKey[32], ivKey[32];
|
||||
i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "SMTunnelReplyKey", noiseState.m_CK);
|
||||
memcpy (replyKey, noiseState.m_CK + 32, 32);
|
||||
i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "SMTunnelLayerKey", noiseState.m_CK);
|
||||
@@ -591,8 +589,7 @@ namespace i2p
|
||||
layerKey, ivKey,
|
||||
clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_GATEWAY_FLAG,
|
||||
clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG);
|
||||
if (!i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel))
|
||||
retCode = 30;
|
||||
i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel);
|
||||
}
|
||||
|
||||
// encrypt reply
|
||||
|
||||
@@ -309,7 +309,7 @@ namespace tunnel
|
||||
std::vector<std::shared_ptr<I2NPMessage> > m_TunnelMsgs, m_TunnelGatewayMsgs;
|
||||
};
|
||||
|
||||
const uint16_t DEFAULT_MAX_NUM_TRANSIT_TUNNELS = 5000;
|
||||
const uint16_t DEFAULT_MAX_NUM_TRANSIT_TUNNELS = 2500;
|
||||
void SetMaxNumTransitTunnels (uint16_t maxNumTransitTunnels);
|
||||
uint16_t GetMaxNumTransitTunnels ();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2020, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -49,22 +49,13 @@ 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);
|
||||
memcpy (m_StandardIdentity.publicKey, publicKey, 32);
|
||||
RAND_bytes (m_StandardIdentity.publicKey + 32, 224);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (publicKey)
|
||||
memcpy (m_StandardIdentity.publicKey, publicKey, 256);
|
||||
else
|
||||
for (int i = 0; i < 8; i++) // 256 bytes
|
||||
memcpy (m_StandardIdentity.publicKey + 32*i, randomPaddingBlock, 32);
|
||||
}
|
||||
memcpy (m_StandardIdentity.publicKey, publicKey, 256);
|
||||
if (type != SIGNING_KEY_TYPE_DSA_SHA1)
|
||||
{
|
||||
size_t excessLen = 0;
|
||||
@@ -102,8 +93,7 @@ namespace data
|
||||
case SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519:
|
||||
{
|
||||
size_t padding = 128 - i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH; // 96 = 128 - 32
|
||||
for (int i = 0; i < 3; i++) // 96 bytes
|
||||
memcpy (m_StandardIdentity.signingKey + 32*i, randomPaddingBlock, 32);
|
||||
RAND_bytes (m_StandardIdentity.signingKey, padding);
|
||||
memcpy (m_StandardIdentity.signingKey + padding, signingKey, i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH);
|
||||
break;
|
||||
}
|
||||
@@ -705,7 +695,7 @@ namespace data
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PrivateKeys PrivateKeys::CreateRandomKeys (SigningKeyType type, CryptoKeyType cryptoType, bool isDestination)
|
||||
PrivateKeys PrivateKeys::CreateRandomKeys (SigningKeyType type, CryptoKeyType cryptoType)
|
||||
{
|
||||
if (type != SIGNING_KEY_TYPE_DSA_SHA1)
|
||||
{
|
||||
@@ -715,12 +705,9 @@ 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);
|
||||
GenerateCryptoKeyPair (cryptoType, keys.m_PrivateKey, publicKey);
|
||||
// identity
|
||||
keys.m_Public = std::make_shared<IdentityEx> (isDestination ? nullptr : publicKey, signingPublicKey, type, cryptoType);
|
||||
keys.m_Public = std::make_shared<IdentityEx> (publicKey, signingPublicKey, type, cryptoType);
|
||||
|
||||
keys.CreateSigner ();
|
||||
return keys;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2021, 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, bool isDestination = false);
|
||||
static PrivateKeys CreateRandomKeys (SigningKeyType type = SIGNING_KEY_TYPE_DSA_SHA1, CryptoKeyType cryptoType = CRYPTO_KEY_TYPE_ELGAMAL);
|
||||
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-2022, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2021, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -37,7 +37,14 @@ namespace data
|
||||
|
||||
void LeaseSet::Update (const uint8_t * buf, size_t len, bool verifySignature)
|
||||
{
|
||||
SetBuffer (buf, len);
|
||||
if (len > m_BufferLen)
|
||||
{
|
||||
auto oldBuffer = m_Buffer;
|
||||
m_Buffer = new uint8_t[len];
|
||||
delete[] oldBuffer;
|
||||
}
|
||||
memcpy (m_Buffer, buf, len);
|
||||
m_BufferLen = len;
|
||||
ReadFromBuffer (false, verifySignature);
|
||||
}
|
||||
|
||||
@@ -52,9 +59,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 + 256 > m_BufferLen)
|
||||
if (size > m_BufferLen)
|
||||
{
|
||||
LogPrint (eLogError, "LeaseSet: Identity length ", int(size), " exceeds buffer size ", int(m_BufferLen));
|
||||
LogPrint (eLogError, "LeaseSet: Identity length ", size, " exceeds buffer size ", m_BufferLen);
|
||||
m_IsValid = false;
|
||||
return;
|
||||
}
|
||||
@@ -67,7 +74,7 @@ namespace data
|
||||
size += m_Identity->GetSigningPublicKeyLen (); // unused signing key
|
||||
if (size + 1 > m_BufferLen)
|
||||
{
|
||||
LogPrint (eLogError, "LeaseSet: ", int(size), " exceeds buffer size ", int(m_BufferLen));
|
||||
LogPrint (eLogError, "LeaseSet: ", size, " exceeds buffer size ", m_BufferLen);
|
||||
m_IsValid = false;
|
||||
return;
|
||||
}
|
||||
@@ -82,7 +89,7 @@ namespace data
|
||||
}
|
||||
if (size + num*LEASE_SIZE > m_BufferLen)
|
||||
{
|
||||
LogPrint (eLogError, "LeaseSet: ", int(size), " exceeds buffer size ", int(m_BufferLen));
|
||||
LogPrint (eLogError, "LeaseSet: ", size, " exceeds buffer size ", m_BufferLen);
|
||||
m_IsValid = false;
|
||||
return;
|
||||
}
|
||||
@@ -118,7 +125,7 @@ namespace data
|
||||
auto signedSize = leases - m_Buffer;
|
||||
if (signedSize + m_Identity->GetSignatureLen () > m_BufferLen)
|
||||
{
|
||||
LogPrint (eLogError, "LeaseSet: Signature exceeds buffer size ", int(m_BufferLen));
|
||||
LogPrint (eLogError, "LeaseSet: Signature exceeds buffer size ", m_BufferLen);
|
||||
m_IsValid = false;
|
||||
}
|
||||
else if (!m_Identity->Verify (m_Buffer, signedSize, leases))
|
||||
@@ -165,7 +172,7 @@ namespace data
|
||||
m_ExpirationTime = lease.endDate;
|
||||
if (m_StoreLeases)
|
||||
{
|
||||
auto ret = m_Leases.insert (i2p::data::netdb.NewLease (lease));
|
||||
auto ret = m_Leases.insert (std::make_shared<Lease>(lease));
|
||||
if (!ret.second) (*ret.first)->endDate = lease.endDate; // update existing
|
||||
(*ret.first)->isUpdated = true;
|
||||
}
|
||||
@@ -257,18 +264,8 @@ namespace data
|
||||
|
||||
void LeaseSet::SetBuffer (const uint8_t * buf, size_t len)
|
||||
{
|
||||
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];
|
||||
if (m_Buffer) delete[] m_Buffer;
|
||||
m_Buffer = new uint8_t[len];
|
||||
m_BufferLen = len;
|
||||
memcpy (m_Buffer, buf, len);
|
||||
}
|
||||
@@ -277,7 +274,7 @@ namespace data
|
||||
{
|
||||
if (len <= m_BufferLen) m_BufferLen = len;
|
||||
else
|
||||
LogPrint (eLogError, "LeaseSet2: Actual buffer size ", int(len) , " exceeds full buffer size ", int(m_BufferLen));
|
||||
LogPrint (eLogError, "LeaseSet2: Actual buffer size ", len , " exceeds full buffer size ", m_BufferLen);
|
||||
}
|
||||
|
||||
LeaseSet2::LeaseSet2 (uint8_t storeType, const uint8_t * buf, size_t len, bool storeLeases, CryptoKeyType preferredCrypto):
|
||||
@@ -323,7 +320,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
|
||||
@@ -367,10 +364,6 @@ 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);
|
||||
}
|
||||
|
||||
@@ -395,17 +388,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
|
||||
@@ -420,7 +413,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 ())
|
||||
@@ -439,8 +432,7 @@ namespace data
|
||||
}
|
||||
else
|
||||
offset += numLeases*LEASE2_SIZE; // 40 bytes per lease
|
||||
|
||||
return (offset > len ? 0 : offset);
|
||||
return offset;
|
||||
}
|
||||
|
||||
size_t LeaseSet2::ReadMetaLS2TypeSpecificPart (const uint8_t * buf, size_t len)
|
||||
@@ -450,18 +442,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 + LEASE2_SIZE > len) return 0;
|
||||
if (offset + 40 >= 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++)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2020, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -18,11 +18,11 @@ namespace log {
|
||||
/**
|
||||
* @brief Maps our loglevel to their symbolic name
|
||||
*/
|
||||
static const char *g_LogLevelStr[eNumLogLevels] =
|
||||
static const char * g_LogLevelStr[eNumLogLevels] =
|
||||
{
|
||||
"none", // eLogNone
|
||||
"error", // eLogError
|
||||
"warn", // eLogWarning
|
||||
"warn", // eLogWarn
|
||||
"info", // eLogInfo
|
||||
"debug" // eLogDebug
|
||||
};
|
||||
@@ -35,12 +35,12 @@ namespace log {
|
||||
static const char *LogMsgColors[] = { "", "", "", "", "", "" };
|
||||
#else /* UNIX */
|
||||
static const char *LogMsgColors[] = {
|
||||
"\033[1;32m", /* none: green */
|
||||
"\033[1;31m", /* error: red */
|
||||
"\033[1;33m", /* warning: yellow */
|
||||
"\033[1;36m", /* info: cyan */
|
||||
"\033[1;34m", /* debug: blue */
|
||||
"\033[0m" /* reset */
|
||||
[eLogNone] = "\033[0m", /* reset */
|
||||
[eLogError] = "\033[1;31m", /* red */
|
||||
[eLogWarning] = "\033[1;33m", /* yellow */
|
||||
[eLogInfo] = "\033[1;36m", /* cyan */
|
||||
[eLogDebug] = "\033[1;34m", /* blue */
|
||||
[eNumLogLevels] = "\033[0m", /* reset */
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -126,7 +126,7 @@ namespace log {
|
||||
if (level == "none") { m_MinLevel = eLogNone; }
|
||||
else if (level == "error") { m_MinLevel = eLogError; }
|
||||
else if (level == "warn") { m_MinLevel = eLogWarning; }
|
||||
else if (level == "info") { m_MinLevel = eLogInfo; }
|
||||
else if (level == "info") { m_MinLevel = eLogInfo; }
|
||||
else if (level == "debug") { m_MinLevel = eLogDebug; }
|
||||
else {
|
||||
LogPrint(eLogError, "Log: Unknown loglevel: ", level);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2023, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -139,7 +139,7 @@ namespace transport
|
||||
m3p2[3] = 0; // flag
|
||||
memcpy (m3p2 + 4, i2p::context.GetRouterInfo ().GetBuffer (), bufLen); // TODO: own RI should be protected by mutex
|
||||
// 2 bytes reserved
|
||||
htobe32buf (options + 8, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000); // tsA, rounded to seconds
|
||||
htobe32buf (options + 8, i2p::util::GetSecondsSinceEpoch ()); // tsA
|
||||
// 4 bytes reserved
|
||||
// sign and encrypt options, use m_H as AD
|
||||
uint8_t nonce[12];
|
||||
@@ -162,7 +162,7 @@ namespace transport
|
||||
uint8_t options[16];
|
||||
memset (options, 0, 16);
|
||||
htobe16buf (options + 2, paddingLen); // padLen
|
||||
htobe32buf (options + 8, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000); // tsB, rounded to seconds
|
||||
htobe32buf (options + 8, i2p::util::GetSecondsSinceEpoch ()); // tsB
|
||||
// sign and encrypt options, use m_H as AD
|
||||
uint8_t nonce[12];
|
||||
memset (nonce, 0, 12); // set nonce to zero
|
||||
@@ -309,7 +309,7 @@ namespace transport
|
||||
|
||||
KDF3Bob ();
|
||||
if (i2p::crypto::AEADChaCha20Poly1305 (m_SessionConfirmedBuffer + 48, m3p2Len - 16, GetH (), 32, GetK (), nonce, m3p2Buf, m3p2Len - 16, false)) // decrypt
|
||||
// calculate new h again for KDF data
|
||||
// caclulate new h again for KDF data
|
||||
MixHash (m_SessionConfirmedBuffer + 48, m3p2Len); // h = SHA256(h || ciphertext)
|
||||
else
|
||||
{
|
||||
@@ -374,16 +374,10 @@ namespace transport
|
||||
transports.PeerDisconnected (shared_from_this ());
|
||||
m_Server.RemoveNTCP2Session (shared_from_this ());
|
||||
m_SendQueue.clear ();
|
||||
m_SendQueueSize = 0;
|
||||
LogPrint (eLogDebug, "NTCP2: Session terminated");
|
||||
}
|
||||
}
|
||||
|
||||
void NTCP2Session::Close ()
|
||||
{
|
||||
m_Socket.close ();
|
||||
}
|
||||
|
||||
void NTCP2Session::TerminateByTimeout ()
|
||||
{
|
||||
SendTerminationAndTerminate (eNTCP2IdleTimeout);
|
||||
@@ -752,8 +746,6 @@ namespace transport
|
||||
|
||||
void NTCP2Session::ServerLogin ()
|
||||
{
|
||||
SetTerminationTimeout (NTCP2_ESTABLISH_TIMEOUT);
|
||||
m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch ();
|
||||
m_Establisher->CreateEphemeralKey ();
|
||||
boost::asio::async_read (m_Socket, boost::asio::buffer(m_Establisher->m_SessionRequestBuffer, 64), boost::asio::transfer_all (),
|
||||
std::bind(&NTCP2Session::HandleSessionRequestReceived, shared_from_this (),
|
||||
@@ -1058,10 +1050,7 @@ namespace transport
|
||||
SendRouterInfo ();
|
||||
}
|
||||
else
|
||||
{
|
||||
SendQueue ();
|
||||
m_SendQueueSize = m_SendQueue.size ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1169,7 +1158,7 @@ namespace transport
|
||||
{
|
||||
if (m_IsTerminated) return;
|
||||
for (auto it: msgs)
|
||||
m_SendQueue.push_back (std::move (it));
|
||||
m_SendQueue.push_back (it);
|
||||
if (!m_IsSending)
|
||||
SendQueue ();
|
||||
else if (m_SendQueue.size () > NTCP2_MAX_OUTGOING_QUEUE_SIZE)
|
||||
@@ -1178,12 +1167,11 @@ namespace transport
|
||||
GetIdentHashBase64(), " exceeds ", NTCP2_MAX_OUTGOING_QUEUE_SIZE);
|
||||
Terminate ();
|
||||
}
|
||||
m_SendQueueSize = m_SendQueue.size ();
|
||||
}
|
||||
|
||||
void NTCP2Session::SendLocalRouterInfo (bool update)
|
||||
void NTCP2Session::SendLocalRouterInfo ()
|
||||
{
|
||||
if (update || !IsOutgoing ()) // we send it in SessionConfirmed for ougoing session
|
||||
if (!IsOutgoing ()) // we send it in SessionConfirmed
|
||||
m_Server.GetService ().post (std::bind (&NTCP2Session::SendRouterInfo, shared_from_this ()));
|
||||
}
|
||||
|
||||
@@ -1222,9 +1210,8 @@ namespace transport
|
||||
else
|
||||
LogPrint(eLogInfo, "NTCP2: Proxy is not used");
|
||||
// start acceptors
|
||||
auto addresses = context.GetRouterInfo ().GetAddresses ();
|
||||
if (!addresses) return;
|
||||
for (const auto& address: *addresses)
|
||||
auto& addresses = context.GetRouterInfo ().GetAddresses ();
|
||||
for (const auto& address: addresses)
|
||||
{
|
||||
if (!address) continue;
|
||||
if (address->IsPublishedNTCP2 () && address->port)
|
||||
@@ -1301,7 +1288,7 @@ namespace transport
|
||||
for (auto& it: ntcpSessions)
|
||||
it.second->Terminate ();
|
||||
for (auto& it: m_PendingIncomingSessions)
|
||||
it.second->Terminate ();
|
||||
it->Terminate ();
|
||||
}
|
||||
m_NTCP2Sessions.clear ();
|
||||
|
||||
@@ -1317,32 +1304,20 @@ namespace transport
|
||||
{
|
||||
if (!session) return false;
|
||||
if (incoming)
|
||||
m_PendingIncomingSessions.erase (session->GetRemoteEndpoint ().address ());
|
||||
if (!session->GetRemoteIdentity ())
|
||||
{
|
||||
LogPrint (eLogWarning, "NTCP2: Unknown identity for ", session->GetRemoteEndpoint ());
|
||||
session->Terminate ();
|
||||
return false;
|
||||
}
|
||||
m_PendingIncomingSessions.remove (session);
|
||||
if (!session->GetRemoteIdentity ()) return false;
|
||||
auto& ident = session->GetRemoteIdentity ()->GetIdentHash ();
|
||||
auto it = m_NTCP2Sessions.find (ident);
|
||||
if (it != m_NTCP2Sessions.end ())
|
||||
{
|
||||
LogPrint (eLogWarning, "NTCP2: Session with ", ident.ToBase64 (), " already exists. ", incoming ? "Replaced" : "Dropped");
|
||||
LogPrint (eLogWarning, "NTCP2: Session to ", ident.ToBase64 (), " already exists");
|
||||
if (incoming)
|
||||
{
|
||||
// replace by new session
|
||||
auto s = it->second;
|
||||
m_NTCP2Sessions.erase (it);
|
||||
s->Terminate ();
|
||||
}
|
||||
it->second->Terminate ();
|
||||
else
|
||||
{
|
||||
session->Terminate ();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
m_NTCP2Sessions.emplace (ident, session);
|
||||
m_NTCP2Sessions.insert (std::make_pair (ident, session));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1437,35 +1412,19 @@ namespace transport
|
||||
if (!ec)
|
||||
{
|
||||
LogPrint (eLogDebug, "NTCP2: Connected from ", ep);
|
||||
if (!i2p::util::net::IsInReservedRange(ep.address ()))
|
||||
if (conn)
|
||||
{
|
||||
if (conn)
|
||||
{
|
||||
if (m_PendingIncomingSessions.emplace (ep.address (), conn).second)
|
||||
{
|
||||
conn->SetRemoteEndpoint (ep);
|
||||
conn->ServerLogin ();
|
||||
conn = nullptr;
|
||||
}
|
||||
else
|
||||
LogPrint (eLogInfo, "NTCP2: Incoming session from ", ep.address (), " is already pending");
|
||||
}
|
||||
conn->SetRemoteEndpoint (ep);
|
||||
conn->ServerLogin ();
|
||||
m_PendingIncomingSessions.push_back (conn);
|
||||
conn = nullptr;
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "NTCP2: Incoming connection from invalid IP ", ep.address ());
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "NTCP2: Connected from error ", ec.message ());
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "NTCP2: Accept error ", error.message ());
|
||||
if (error == boost::asio::error::no_descriptors)
|
||||
{
|
||||
i2p::context.SetError (eRouterErrorNoDescriptors);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (error != boost::asio::error::operation_aborted)
|
||||
{
|
||||
@@ -1487,43 +1446,20 @@ namespace transport
|
||||
if (!ec)
|
||||
{
|
||||
LogPrint (eLogDebug, "NTCP2: Connected from ", ep);
|
||||
if (!i2p::util::net::IsInReservedRange(ep.address ()) ||
|
||||
i2p::util::net::IsYggdrasilAddress (ep.address ()))
|
||||
if (conn)
|
||||
{
|
||||
if (conn)
|
||||
{
|
||||
if (m_PendingIncomingSessions.emplace (ep.address (), conn).second)
|
||||
{
|
||||
conn->SetRemoteEndpoint (ep);
|
||||
conn->ServerLogin ();
|
||||
conn = nullptr;
|
||||
}
|
||||
else
|
||||
LogPrint (eLogInfo, "NTCP2: Incoming session from ", ep.address (), " is already pending");
|
||||
}
|
||||
conn->SetRemoteEndpoint (ep);
|
||||
conn->ServerLogin ();
|
||||
m_PendingIncomingSessions.push_back (conn);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "NTCP2: Incoming connection from invalid IP ", ep.address ());
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "NTCP2: Connected from error ", ec.message ());
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "NTCP2: Accept ipv6 error ", error.message ());
|
||||
if (error == boost::asio::error::no_descriptors)
|
||||
{
|
||||
i2p::context.SetErrorV6 (eRouterErrorNoDescriptors);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (error != boost::asio::error::operation_aborted)
|
||||
{
|
||||
if (!conn) // connection is used, create new one
|
||||
conn = std::make_shared<NTCP2Session> (*this);
|
||||
else // reuse failed
|
||||
conn->Close ();
|
||||
conn = std::make_shared<NTCP2Session> (*this);
|
||||
m_NTCP2V6Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCP2Server::HandleAcceptV6, this,
|
||||
conn, std::placeholders::_1));
|
||||
}
|
||||
@@ -1554,34 +1490,18 @@ namespace transport
|
||||
// pending
|
||||
for (auto it = m_PendingIncomingSessions.begin (); it != m_PendingIncomingSessions.end ();)
|
||||
{
|
||||
if (it->second->IsEstablished () || it->second->IsTerminationTimeoutExpired (ts))
|
||||
if ((*it)->IsEstablished () || (*it)->IsTerminationTimeoutExpired (ts))
|
||||
{
|
||||
it->second->Terminate ();
|
||||
(*it)->Terminate ();
|
||||
it = m_PendingIncomingSessions.erase (it); // established of expired
|
||||
}
|
||||
else if (it->second->IsTerminated ())
|
||||
else if ((*it)->IsTerminated ())
|
||||
it = m_PendingIncomingSessions.erase (it); // already terminated
|
||||
else
|
||||
it++;
|
||||
}
|
||||
ScheduleTermination ();
|
||||
|
||||
// try to restart acceptors if no description
|
||||
// we do it after timer to let timer take descriptor first
|
||||
if (i2p::context.GetError () == eRouterErrorNoDescriptors)
|
||||
{
|
||||
i2p::context.SetError (eRouterErrorNone);
|
||||
auto conn = std::make_shared<NTCP2Session> (*this);
|
||||
m_NTCP2Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCP2Server::HandleAccept, this,
|
||||
conn, std::placeholders::_1));
|
||||
}
|
||||
if (i2p::context.GetErrorV6 () == eRouterErrorNoDescriptors)
|
||||
{
|
||||
i2p::context.SetErrorV6 (eRouterErrorNone);
|
||||
auto conn = std::make_shared<NTCP2Session> (*this);
|
||||
m_NTCP2V6Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCP2Server::HandleAcceptV6, this,
|
||||
conn, std::placeholders::_1));
|
||||
}
|
||||
ScheduleTermination ();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1638,7 +1558,7 @@ namespace transport
|
||||
case eSocksProxy:
|
||||
{
|
||||
// TODO: support username/password auth etc
|
||||
static const uint8_t buff[3] = {SOCKS5_VER, 0x01, 0x00};
|
||||
static const uint8_t buff[3] = {0x05, 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)
|
||||
{
|
||||
@@ -1752,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] = SOCKS5_VER;
|
||||
(*buff)[1] = SOCKS5_CMD_CONNECT;
|
||||
(*buff)[0] = 0x05;
|
||||
(*buff)[1] = 0x01;
|
||||
(*buff)[2] = 0x00;
|
||||
|
||||
auto& ep = conn->GetRemoteEndpoint ();
|
||||
if(ep.address ().is_v4 ())
|
||||
{
|
||||
(*buff)[3] = SOCKS5_ATYP_IPV4;
|
||||
(*buff)[3] = 0x01;
|
||||
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] = SOCKS5_ATYP_IPV6;
|
||||
(*buff)[3] = 0x04;
|
||||
auto addrbytes = ep.address ().to_v6().to_bytes();
|
||||
sz += 16;
|
||||
memcpy(buff->data () + 4, addrbytes.data(), 16);
|
||||
@@ -1788,24 +1708,22 @@ namespace transport
|
||||
}
|
||||
});
|
||||
|
||||
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, readbuff](const boost::system::error_code & e, std::size_t transferred)
|
||||
boost::asio::async_read(conn->GetSocket(), boost::asio::buffer(readbuff->data (), 10),
|
||||
[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 (!(*readbuff)[1]) // succeeded
|
||||
if(e)
|
||||
{
|
||||
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;
|
||||
LogPrint(eLogError, "NTCP2: SOCKS proxy read error ", e.message());
|
||||
}
|
||||
else if(transferred == sz)
|
||||
{
|
||||
if((*readbuff)[1] == 0x00)
|
||||
{
|
||||
timer->cancel();
|
||||
conn->ClientLogin();
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
LogPrint(eLogError, "NTCP2: Proxy reply error ", (int)(*readbuff)[1]);
|
||||
timer->cancel();
|
||||
conn->Terminate();
|
||||
});
|
||||
|
||||
@@ -134,22 +134,22 @@ namespace transport
|
||||
~NTCP2Session ();
|
||||
void Terminate ();
|
||||
void TerminateByTimeout ();
|
||||
void Done () override;
|
||||
void Close (); // for accept
|
||||
void Done ();
|
||||
void Close () { m_Socket.close (); }; // for accept
|
||||
void DeleteNextReceiveBuffer (uint64_t ts);
|
||||
|
||||
boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; };
|
||||
const boost::asio::ip::tcp::endpoint& GetRemoteEndpoint () { return m_RemoteEndpoint; };
|
||||
void SetRemoteEndpoint (const boost::asio::ip::tcp::endpoint& ep) { m_RemoteEndpoint = ep; };
|
||||
|
||||
bool IsEstablished () const override { return m_IsEstablished; };
|
||||
bool IsEstablished () const { return m_IsEstablished; };
|
||||
bool IsTerminated () const { return m_IsTerminated; };
|
||||
|
||||
void ClientLogin (); // Alice
|
||||
void ServerLogin (); // Bob
|
||||
|
||||
void SendLocalRouterInfo (bool update) override; // after handshake or by update
|
||||
void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs) override;
|
||||
void SendLocalRouterInfo (); // after handshake
|
||||
void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs);
|
||||
|
||||
private:
|
||||
|
||||
@@ -277,7 +277,7 @@ namespace transport
|
||||
boost::asio::deadline_timer m_TerminationTimer;
|
||||
std::unique_ptr<boost::asio::ip::tcp::acceptor> m_NTCP2Acceptor, m_NTCP2V6Acceptor;
|
||||
std::map<i2p::data::IdentHash, std::shared_ptr<NTCP2Session> > m_NTCP2Sessions;
|
||||
std::map<boost::asio::ip::address, std::shared_ptr<NTCP2Session> > m_PendingIncomingSessions;
|
||||
std::list<std::shared_ptr<NTCP2Session> > m_PendingIncomingSessions;
|
||||
|
||||
ProxyType m_ProxyType;
|
||||
std::string m_ProxyAddress, m_ProxyAuthorization;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2023, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -240,10 +240,11 @@ namespace data
|
||||
m_HiddenMode = hide;
|
||||
}
|
||||
|
||||
std::shared_ptr<const RouterInfo> NetDb::AddRouterInfo (const uint8_t * buf, int len)
|
||||
bool NetDb::AddRouterInfo (const uint8_t * buf, int len)
|
||||
{
|
||||
bool updated;
|
||||
return AddRouterInfo (buf, len, updated);
|
||||
AddRouterInfo (buf, len, updated);
|
||||
return updated;
|
||||
}
|
||||
|
||||
std::shared_ptr<const RouterInfo> NetDb::AddRouterInfo (const uint8_t * buf, int len, bool& updated)
|
||||
@@ -271,10 +272,7 @@ namespace data
|
||||
if (r->IsNewer (buf, len))
|
||||
{
|
||||
bool wasFloodfill = r->IsFloodfill ();
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_RouterInfosMutex);
|
||||
r->Update (buf, len);
|
||||
}
|
||||
r->Update (buf, len);
|
||||
LogPrint (eLogInfo, "NetDb: RouterInfo updated: ", ident.ToBase64());
|
||||
if (wasFloodfill != r->IsFloodfill ()) // if floodfill status updated
|
||||
{
|
||||
@@ -295,8 +293,7 @@ namespace data
|
||||
else
|
||||
{
|
||||
r = std::make_shared<RouterInfo> (buf, len);
|
||||
if (!r->IsUnreachable () && r->HasValidAddresses () &&
|
||||
i2p::util::GetMillisecondsSinceEpoch () + NETDB_EXPIRATION_TIMEOUT_THRESHOLD*1000LL > r->GetTimestamp ())
|
||||
if (!r->IsUnreachable () && r->HasValidAddresses ())
|
||||
{
|
||||
bool inserted = false;
|
||||
{
|
||||
@@ -366,16 +363,15 @@ namespace data
|
||||
|
||||
bool NetDb::AddLeaseSet2 (const IdentHash& ident, const uint8_t * buf, int len, uint8_t storeType)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_LeaseSetsMutex);
|
||||
auto leaseSet = std::make_shared<LeaseSet2> (storeType, buf, len, false); // we don't need leases in netdb
|
||||
if (leaseSet->IsValid ())
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_LeaseSetsMutex);
|
||||
auto it = m_LeaseSets.find(ident);
|
||||
if (it == m_LeaseSets.end () || it->second->GetStoreType () != storeType ||
|
||||
leaseSet->GetPublishedTimestamp () > it->second->GetPublishedTimestamp ())
|
||||
{
|
||||
if (leaseSet->IsPublic () && !leaseSet->IsExpired () &&
|
||||
i2p::util::GetSecondsSinceEpoch () + NETDB_EXPIRATION_TIMEOUT_THRESHOLD > leaseSet->GetPublishedTimestamp ())
|
||||
if (leaseSet->IsPublic () && !leaseSet->IsExpired ())
|
||||
{
|
||||
// TODO: implement actual update
|
||||
LogPrint (eLogInfo, "NetDb: LeaseSet2 updated: ", ident.ToBase32());
|
||||
@@ -384,7 +380,7 @@ namespace data
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "NetDb: Unpublished or expired or future LeaseSet2 received: ", ident.ToBase32());
|
||||
LogPrint (eLogWarning, "NetDb: Unpublished or expired LeaseSet2 received: ", ident.ToBase32());
|
||||
m_LeaseSets.erase (ident);
|
||||
}
|
||||
}
|
||||
@@ -440,15 +436,12 @@ 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;
|
||||
@@ -496,7 +489,7 @@ namespace data
|
||||
{
|
||||
auto r = std::make_shared<RouterInfo>(path);
|
||||
if (r->GetRouterIdentity () && !r->IsUnreachable () && r->HasValidAddresses () &&
|
||||
ts < r->GetTimestamp () + 24*60*60*NETDB_MAX_OFFLINE_EXPIRATION_TIMEOUT*1000LL) // too old
|
||||
ts < r->GetTimestamp () + 24*60*60*NETDB_MAX_OFFLINE_EXPIRATION_TIMEOUT*1000LL)
|
||||
{
|
||||
r->DeleteBuffer ();
|
||||
if (m_RouterInfos.emplace (r->GetIdentHash (), r).second)
|
||||
@@ -605,7 +598,6 @@ namespace data
|
||||
uint64_t expirationTimeout = NETDB_MAX_EXPIRATION_TIMEOUT*1000LL;
|
||||
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch();
|
||||
auto uptime = i2p::context.GetUptime ();
|
||||
bool isLowRate = i2p::tunnel::tunnels.GetTunnelCreationSuccessRate () < NETDB_MIN_TUNNEL_CREATION_SUCCESS_RATE;
|
||||
// routers don't expire if less than 90 or uptime is less than 1 hour
|
||||
bool checkForExpiration = total > NETDB_MIN_ROUTERS && uptime > 600; // 10 minutes
|
||||
if (checkForExpiration && uptime > 3600) // 1 hour
|
||||
@@ -627,24 +619,18 @@ namespace data
|
||||
continue;
|
||||
}
|
||||
// make router reachable back if too few routers or floodfills
|
||||
if (it.second->IsUnreachable () && (total - deletedCount < NETDB_MIN_ROUTERS || isLowRate ||
|
||||
if (it.second->IsUnreachable () && (total - deletedCount < NETDB_MIN_ROUTERS ||
|
||||
(it.second->IsFloodfill () && totalFloodfills - deletedFloodfillsCount < NETDB_MIN_FLOODFILLS)))
|
||||
it.second->SetUnreachable (false);
|
||||
// find & mark expired routers
|
||||
if (!it.second->IsReachable () && (it.second->GetCompatibleTransports (true) & RouterInfo::eSSU2V4))
|
||||
// non-reachable router, but reachable by ipv4 SSU2 means introducers
|
||||
if (!it.second->IsReachable () && it.second->IsSSU (false))
|
||||
{
|
||||
if (ts > it.second->GetTimestamp () + NETDB_INTRODUCEE_EXPIRATION_TIMEOUT*1000LL)
|
||||
// RouterInfo expires after 1 hour if uses introducer
|
||||
it.second->SetUnreachable (true);
|
||||
}
|
||||
else if (checkForExpiration && ts > it.second->GetTimestamp () + expirationTimeout)
|
||||
it.second->SetUnreachable (true);
|
||||
else if (ts + NETDB_EXPIRATION_TIMEOUT_THRESHOLD*1000LL < it.second->GetTimestamp ())
|
||||
{
|
||||
LogPrint (eLogWarning, "NetDb: RouterInfo is from future for ", (it.second->GetTimestamp () - ts)/1000LL, " seconds");
|
||||
it.second->SetUnreachable (true);
|
||||
}
|
||||
it.second->SetUnreachable (true);
|
||||
|
||||
if (it.second->IsUnreachable ())
|
||||
{
|
||||
@@ -657,8 +643,6 @@ namespace data
|
||||
} // m_RouterInfos iteration
|
||||
|
||||
m_RouterInfoBuffersPool.CleanUpMt ();
|
||||
m_RouterInfoAddressesPool.CleanUpMt ();
|
||||
m_RouterInfoAddressVectorsPool.CleanUpMt ();
|
||||
|
||||
if (updatedCount > 0)
|
||||
LogPrint (eLogInfo, "NetDb: Saved ", updatedCount, " new/updated routers");
|
||||
@@ -759,11 +743,6 @@ 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 ())
|
||||
{
|
||||
@@ -774,11 +753,6 @@ 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;
|
||||
@@ -807,11 +781,6 @@ namespace data
|
||||
uint8_t storeType = buf[DATABASE_STORE_TYPE_OFFSET];
|
||||
if (storeType) // LeaseSet or LeaseSet2
|
||||
{
|
||||
if (len > MAX_LS_BUFFER_SIZE + offset)
|
||||
{
|
||||
LogPrint (eLogError, "NetDb: Database store message is too long ", len);
|
||||
return;
|
||||
}
|
||||
if (!m->from) // unsolicited LS must be received directly
|
||||
{
|
||||
if (storeType == NETDB_STORE_TYPE_LEASESET) // 1
|
||||
@@ -971,9 +940,9 @@ namespace data
|
||||
}
|
||||
uint16_t numExcluded = bufbe16toh (excluded);
|
||||
excluded += 2;
|
||||
if (numExcluded > 512 || (excluded - buf) + numExcluded*32 > (int)msg->GetPayloadLength ())
|
||||
if (numExcluded > 512)
|
||||
{
|
||||
LogPrint (eLogWarning, "NetDb: Number of excluded peers", numExcluded, " is too much");
|
||||
LogPrint (eLogWarning, "NetDb: Number of excluded peers", numExcluded, " exceeds 512");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -982,11 +951,10 @@ 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_ident);
|
||||
excluded_ident += 32;
|
||||
excludedRouters.insert (excluded);
|
||||
excluded += 32;
|
||||
}
|
||||
std::vector<IdentHash> routers;
|
||||
for (int i = 0; i < 3; i++)
|
||||
@@ -1009,7 +977,8 @@ namespace data
|
||||
if (router)
|
||||
{
|
||||
LogPrint (eLogDebug, "NetDb: Requested RouterInfo ", key, " found");
|
||||
PopulateRouterInfoBuffer (router);
|
||||
if (!router->GetBuffer ())
|
||||
router->LoadBuffer (m_Storage.Path (router->GetIdentHashBase64 ()));
|
||||
if (router->GetBuffer ())
|
||||
replyMsg = CreateDatabaseStoreMsg (router);
|
||||
}
|
||||
@@ -1044,7 +1013,7 @@ namespace data
|
||||
if (closestFloodfills.empty ())
|
||||
LogPrint (eLogWarning, "NetDb: Requested ", key, " not found, ", numExcluded, " peers excluded");
|
||||
replyMsg = CreateDatabaseSearchReply (ident, closestFloodfills);
|
||||
}
|
||||
}
|
||||
}
|
||||
excluded += numExcluded * 32;
|
||||
if (replyMsg)
|
||||
@@ -1224,6 +1193,16 @@ namespace data
|
||||
});
|
||||
}
|
||||
|
||||
std::shared_ptr<const RouterInfo> NetDb::GetRandomPeerTestRouter (bool v4, const std::set<IdentHash>& excluded) const
|
||||
{
|
||||
return GetRandomRouter (
|
||||
[v4, &excluded](std::shared_ptr<const RouterInfo> router)->bool
|
||||
{
|
||||
return !router->IsHidden () && router->IsECIES () &&
|
||||
router->IsPeerTesting (v4) && !excluded.count (router->GetIdentHash ());
|
||||
});
|
||||
}
|
||||
|
||||
std::shared_ptr<const RouterInfo> NetDb::GetRandomSSU2PeerTestRouter (bool v4, const std::set<IdentHash>& excluded) const
|
||||
{
|
||||
return GetRandomRouter (
|
||||
@@ -1233,14 +1212,23 @@ namespace data
|
||||
router->IsSSU2PeerTesting (v4) && !excluded.count (router->GetIdentHash ());
|
||||
});
|
||||
}
|
||||
|
||||
std::shared_ptr<const RouterInfo> NetDb::GetRandomSSUV6Router () const
|
||||
{
|
||||
return GetRandomRouter (
|
||||
[](std::shared_ptr<const RouterInfo> router)->bool
|
||||
{
|
||||
return !router->IsHidden () && router->IsECIES () && router->IsSSUV6 ();
|
||||
});
|
||||
}
|
||||
|
||||
std::shared_ptr<const RouterInfo> NetDb::GetRandomSSU2Introducer (bool v4, const std::set<IdentHash>& excluded) const
|
||||
std::shared_ptr<const RouterInfo> NetDb::GetRandomIntroducer (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 ());
|
||||
return !router->IsHidden () && router->IsECIES () && !router->IsFloodfill () && // floodfills don't send relay tag
|
||||
router->IsIntroducer (v4) && !excluded.count (router->GetIdentHash ());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1444,13 +1432,6 @@ namespace data
|
||||
else
|
||||
++it;
|
||||
}
|
||||
m_LeasesPool.CleanUpMt ();
|
||||
}
|
||||
|
||||
void NetDb::PopulateRouterInfoBuffer (std::shared_ptr<RouterInfo> r)
|
||||
{
|
||||
if (!r || r->GetBuffer ()) return;
|
||||
r->LoadBuffer (m_Storage.Path (r->GetIdentHashBase64 ()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,13 +38,11 @@ namespace data
|
||||
{
|
||||
const int NETDB_MIN_ROUTERS = 90;
|
||||
const int NETDB_MIN_FLOODFILLS = 5;
|
||||
const int NETDB_MIN_TUNNEL_CREATION_SUCCESS_RATE = 8; // in percents
|
||||
const int NETDB_FLOODFILL_EXPIRATION_TIMEOUT = 60 * 60; // 1 hour, in seconds
|
||||
const int NETDB_INTRODUCEE_EXPIRATION_TIMEOUT = 65 * 60;
|
||||
const int NETDB_MIN_EXPIRATION_TIMEOUT = 90 * 60; // 1.5 hours
|
||||
const int NETDB_MAX_EXPIRATION_TIMEOUT = 27 * 60 * 60; // 27 hours
|
||||
const int NETDB_MAX_OFFLINE_EXPIRATION_TIMEOUT = 180; // in days
|
||||
const int NETDB_EXPIRATION_TIMEOUT_THRESHOLD = 2*60; // 2 minutes
|
||||
const int NETDB_PUBLISH_INTERVAL = 60 * 40;
|
||||
const int NETDB_PUBLISH_CONFIRMATION_TIMEOUT = 5; // in seconds
|
||||
const int NETDB_MAX_PUBLISH_EXCLUDED_FLOODFILLS = 15;
|
||||
@@ -71,7 +69,7 @@ namespace data
|
||||
void Start ();
|
||||
void Stop ();
|
||||
|
||||
std::shared_ptr<const RouterInfo> AddRouterInfo (const uint8_t * buf, int len);
|
||||
bool 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);
|
||||
@@ -91,8 +89,10 @@ namespace data
|
||||
std::shared_ptr<const RouterInfo> GetRandomRouter () const;
|
||||
std::shared_ptr<const RouterInfo> GetRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith, bool reverse) const;
|
||||
std::shared_ptr<const RouterInfo> GetHighBandwidthRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith, bool reverse) const;
|
||||
std::shared_ptr<const RouterInfo> GetRandomPeerTestRouter (bool v4, const std::set<IdentHash>& excluded) const;
|
||||
std::shared_ptr<const RouterInfo> GetRandomSSU2PeerTestRouter (bool v4, const std::set<IdentHash>& excluded) const;
|
||||
std::shared_ptr<const RouterInfo> GetRandomSSU2Introducer (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> 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;
|
||||
@@ -124,16 +124,6 @@ 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<RouterInfo::Address> NewRouterInfoAddress () { return m_RouterInfoAddressesPool.AcquireSharedMt (); };
|
||||
boost::shared_ptr<RouterInfo::Addresses> NewRouterInfoAddresses ()
|
||||
{
|
||||
return boost::shared_ptr<RouterInfo::Addresses>(m_RouterInfoAddressVectorsPool.AcquireMt (),
|
||||
std::bind <void (i2p::util::MemoryPoolMt<RouterInfo::Addresses>::*)(RouterInfo::Addresses *)>
|
||||
(&i2p::util::MemoryPoolMt<RouterInfo::Addresses>::ReleaseMt,
|
||||
&m_RouterInfoAddressVectorsPool, std::placeholders::_1));
|
||||
};
|
||||
std::shared_ptr<Lease> NewLease (const Lease& lease) { return m_LeasesPool.AcquireSharedMt (lease); };
|
||||
|
||||
uint32_t GetPublishReplyToken () const { return m_PublishReplyToken; };
|
||||
|
||||
@@ -190,9 +180,6 @@ namespace data
|
||||
uint32_t m_PublishReplyToken = 0;
|
||||
|
||||
i2p::util::MemoryPoolMt<RouterInfo::Buffer> m_RouterInfoBuffersPool;
|
||||
i2p::util::MemoryPoolMt<RouterInfo::Address> m_RouterInfoAddressesPool;
|
||||
i2p::util::MemoryPoolMt<RouterInfo::Addresses> m_RouterInfoAddressVectorsPool;
|
||||
i2p::util::MemoryPoolMt<Lease> m_LeasesPool;
|
||||
};
|
||||
|
||||
extern NetDb netdb;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2023, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2020, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -12,7 +12,6 @@
|
||||
#include "Base.h"
|
||||
#include "FS.h"
|
||||
#include "Log.h"
|
||||
#include "Timestamp.h"
|
||||
#include "Profiling.h"
|
||||
|
||||
namespace i2p
|
||||
@@ -23,7 +22,6 @@ namespace data
|
||||
|
||||
RouterProfile::RouterProfile ():
|
||||
m_LastUpdateTime (boost::posix_time::second_clock::local_time()),
|
||||
m_LastDeclineTime (0),
|
||||
m_NumTunnelsAgreed (0), m_NumTunnelsDeclined (0), m_NumTunnelsNonReplied (0),
|
||||
m_NumTimesTaken (0), m_NumTimesRejected (0)
|
||||
{
|
||||
@@ -133,23 +131,15 @@ namespace data
|
||||
{
|
||||
UpdateTime ();
|
||||
if (ret > 0)
|
||||
{
|
||||
m_NumTunnelsDeclined++;
|
||||
m_LastDeclineTime = i2p::util::GetSecondsSinceEpoch ();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_NumTunnelsAgreed++;
|
||||
m_LastDeclineTime = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void RouterProfile::TunnelNonReplied ()
|
||||
{
|
||||
m_NumTunnelsNonReplied++;
|
||||
UpdateTime ();
|
||||
if (m_NumTunnelsNonReplied > 2*m_NumTunnelsAgreed && m_NumTunnelsNonReplied > 3)
|
||||
m_LastDeclineTime = i2p::util::GetSecondsSinceEpoch ();
|
||||
}
|
||||
|
||||
bool RouterProfile::IsLowPartcipationRate () const
|
||||
@@ -163,18 +153,8 @@ namespace data
|
||||
return m_NumTunnelsNonReplied > 10*(total + 1);
|
||||
}
|
||||
|
||||
bool RouterProfile::IsDeclinedRecently ()
|
||||
{
|
||||
if (!m_LastDeclineTime) return false;
|
||||
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
if (ts > m_LastDeclineTime + PEER_PROFILE_DECLINED_RECENTLY_INTERVAL)
|
||||
m_LastDeclineTime = 0;
|
||||
return m_LastDeclineTime;
|
||||
}
|
||||
|
||||
bool RouterProfile::IsBad ()
|
||||
{
|
||||
if (IsDeclinedRecently ()) return true;
|
||||
auto isBad = IsAlwaysDeclining () || IsLowPartcipationRate () /*|| IsLowReplyRate ()*/;
|
||||
if (isBad && m_NumTimesRejected > 10*(m_NumTimesTaken + 1))
|
||||
{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2023, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -31,7 +31,6 @@ namespace data
|
||||
const int PEER_PROFILE_EXPIRATION_TIMEOUT = 72; // in hours (3 days)
|
||||
const int PEER_PROFILE_AUTOCLEAN_TIMEOUT = 24 * 3600; // in seconds (1 day)
|
||||
const int PEER_PROFILE_AUTOCLEAN_VARIANCE = 3 * 3600; // in seconds (3 hours)
|
||||
const int PEER_PROFILE_DECLINED_RECENTLY_INTERVAL = 150; // in seconds (2.5 minutes)
|
||||
|
||||
class RouterProfile
|
||||
{
|
||||
@@ -56,12 +55,10 @@ namespace data
|
||||
bool IsAlwaysDeclining () const { return !m_NumTunnelsAgreed && m_NumTunnelsDeclined >= 5; };
|
||||
bool IsLowPartcipationRate () const;
|
||||
bool IsLowReplyRate () const;
|
||||
bool IsDeclinedRecently ();
|
||||
|
||||
private:
|
||||
|
||||
boost::posix_time::ptime m_LastUpdateTime; // TODO: use std::chrono
|
||||
uint64_t m_LastDeclineTime; // in seconds
|
||||
boost::posix_time::ptime m_LastUpdateTime;
|
||||
// participation
|
||||
uint32_t m_NumTunnelsAgreed;
|
||||
uint32_t m_NumTunnelsDeclined;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2023, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -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_ErrorV6 (eRouterErrorNone), m_NetID (I2PD_NET_ID)
|
||||
m_Error (eRouterErrorNone), m_NetID (I2PD_NET_ID)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -60,9 +60,14 @@ namespace i2p
|
||||
i2p::data::LocalRouterInfo routerInfo;
|
||||
routerInfo.SetRouterIdentity (GetIdentity ());
|
||||
uint16_t port; i2p::config::GetOption("port", port);
|
||||
if (!port) port = SelectRandomPort ();
|
||||
if (!port)
|
||||
{
|
||||
port = rand () % (30777 - 9111) + 9111; // I2P network ports range
|
||||
if (port == 9150) port = 9151; // Tor browser
|
||||
}
|
||||
bool ipv4; i2p::config::GetOption("ipv4", ipv4);
|
||||
bool ipv6; i2p::config::GetOption("ipv6", ipv6);
|
||||
bool ssu; i2p::config::GetOption("ssu", ssu);
|
||||
bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2);
|
||||
bool ssu2; i2p::config::GetOption("ssu2.enabled", ssu2);
|
||||
bool ygg; i2p::config::GetOption("meshnets.yggdrasil", ygg);
|
||||
@@ -108,14 +113,15 @@ namespace i2p
|
||||
routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv);
|
||||
}
|
||||
}
|
||||
if (ssu)
|
||||
{
|
||||
routerInfo.AddSSUAddress (host.c_str(), port, nullptr);
|
||||
caps |= i2p::data::RouterInfo::eReachable; // R
|
||||
}
|
||||
if (ssu2)
|
||||
{
|
||||
if (ssu2Published)
|
||||
{
|
||||
uint16_t ssu2Port; i2p::config::GetOption ("ssu2.port", ssu2Port);
|
||||
if (!ssu2Port) ssu2Port = port;
|
||||
routerInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, boost::asio::ip::address_v4::from_string (host), ssu2Port);
|
||||
}
|
||||
routerInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, boost::asio::ip::address_v4::from_string (host), port);
|
||||
else
|
||||
{
|
||||
addressCaps |= i2p::data::RouterInfo::AddressCaps::eV4;
|
||||
@@ -125,7 +131,7 @@ namespace i2p
|
||||
}
|
||||
if (ipv6)
|
||||
{
|
||||
std::string host;
|
||||
std::string host = "::1";
|
||||
if (!i2p::config::IsDefault("host") && !ipv4) // override if v6 only
|
||||
i2p::config::GetOption("host", host);
|
||||
else
|
||||
@@ -136,7 +142,6 @@ namespace i2p
|
||||
|
||||
if (ntcp2)
|
||||
{
|
||||
bool added = false;
|
||||
if (ntcp2Published)
|
||||
{
|
||||
std::string ntcp2Host;
|
||||
@@ -144,33 +149,25 @@ namespace i2p
|
||||
i2p::config::GetOption ("ntcp2.addressv6", ntcp2Host);
|
||||
else
|
||||
ntcp2Host = host;
|
||||
if (!ntcp2Host.empty () && port)
|
||||
{
|
||||
routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address_v6::from_string (ntcp2Host), port);
|
||||
added = true;
|
||||
}
|
||||
routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address_v6::from_string (ntcp2Host), port);
|
||||
}
|
||||
if (!added)
|
||||
else
|
||||
{
|
||||
if (!ipv4) // no other ntcp2 addresses yet
|
||||
routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv);
|
||||
addressCaps |= i2p::data::RouterInfo::AddressCaps::eV6;
|
||||
}
|
||||
}
|
||||
if (ssu)
|
||||
{
|
||||
routerInfo.AddSSUAddress (host.c_str(), port, nullptr);
|
||||
caps |= i2p::data::RouterInfo::eReachable; // R
|
||||
}
|
||||
if (ssu2)
|
||||
{
|
||||
bool added = false;
|
||||
if (ssu2Published)
|
||||
{
|
||||
uint16_t ssu2Port; i2p::config::GetOption ("ssu2.port", ssu2Port);
|
||||
if (!ssu2Port) ssu2Port = port;
|
||||
if (!host.empty () && ssu2Port)
|
||||
{
|
||||
routerInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, boost::asio::ip::address_v6::from_string (host), ssu2Port);
|
||||
added = true;
|
||||
}
|
||||
}
|
||||
if (!added)
|
||||
routerInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, boost::asio::ip::address_v6::from_string (host), port);
|
||||
else
|
||||
{
|
||||
if (!ipv4) // no other ssu2 addresses yet
|
||||
routerInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro);
|
||||
@@ -195,13 +192,6 @@ 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);
|
||||
@@ -240,6 +230,7 @@ namespace i2p
|
||||
if (status != m_Status)
|
||||
{
|
||||
m_Status = status;
|
||||
m_Error = eRouterErrorNone;
|
||||
switch (m_Status)
|
||||
{
|
||||
case eRouterStatusOK:
|
||||
@@ -248,9 +239,6 @@ namespace i2p
|
||||
case eRouterStatusFirewalled:
|
||||
SetUnreachable (true, false); // ipv4
|
||||
break;
|
||||
case eRouterStatusTesting:
|
||||
m_Error = eRouterErrorNone;
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
@@ -270,9 +258,6 @@ namespace i2p
|
||||
case eRouterStatusFirewalled:
|
||||
SetUnreachable (false, true); // ipv6
|
||||
break;
|
||||
case eRouterStatusTesting:
|
||||
m_ErrorV6 = eRouterErrorNone;
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
@@ -281,12 +266,10 @@ namespace i2p
|
||||
|
||||
void RouterContext::UpdatePort (int port)
|
||||
{
|
||||
auto addresses = m_RouterInfo.GetAddresses ();
|
||||
if (!addresses) return;
|
||||
bool updated = false;
|
||||
for (auto& address : *addresses)
|
||||
for (auto& address : m_RouterInfo.GetAddresses ())
|
||||
{
|
||||
if (address && address->port != port && address->transportStyle == i2p::data::RouterInfo::eTransportSSU2)
|
||||
if (!address->IsNTCP2 () && !address->IsSSU2 () && address->port != port)
|
||||
{
|
||||
address->port = port;
|
||||
updated = true;
|
||||
@@ -299,12 +282,10 @@ namespace i2p
|
||||
void RouterContext::PublishNTCP2Address (int port, bool publish, bool v4, bool v6, bool ygg)
|
||||
{
|
||||
if (!m_NTCP2Keys) return;
|
||||
auto addresses = m_RouterInfo.GetAddresses ();
|
||||
if (!addresses) return;
|
||||
bool updated = false;
|
||||
for (auto& address : *addresses)
|
||||
for (auto& address : m_RouterInfo.GetAddresses ())
|
||||
{
|
||||
if (address && address->IsNTCP2 () && (address->port != port || address->published != publish))
|
||||
if (address->IsNTCP2 () && (address->port != port || address->published != publish))
|
||||
{
|
||||
bool isAddr = v4 && address->IsV4 ();
|
||||
if (!isAddr && (v6 || ygg))
|
||||
@@ -316,7 +297,12 @@ namespace i2p
|
||||
}
|
||||
if (isAddr)
|
||||
{
|
||||
if (!port && !address->port) port = SelectRandomPort ();
|
||||
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;
|
||||
address->published = publish;
|
||||
memcpy (address->i, m_NTCP2Keys->iv, 16);
|
||||
@@ -330,22 +316,19 @@ namespace i2p
|
||||
|
||||
void RouterContext::UpdateNTCP2Address (bool enable)
|
||||
{
|
||||
auto addresses = m_RouterInfo.GetAddresses ();
|
||||
if (!addresses) return;
|
||||
auto& addresses = m_RouterInfo.GetAddresses ();
|
||||
bool found = false, updated = false;
|
||||
for (auto& it: *addresses)
|
||||
for (auto it = addresses.begin (); it != addresses.end (); ++it)
|
||||
{
|
||||
if (it && it->IsNTCP2 ())
|
||||
if ((*it)->IsNTCP2 ())
|
||||
{
|
||||
found = true;
|
||||
if (enable)
|
||||
if (!enable)
|
||||
{
|
||||
it->s = m_NTCP2Keys->staticPublicKey;
|
||||
memcpy (it->i, m_NTCP2Keys->iv, 16);
|
||||
addresses.erase (it);
|
||||
updated= true;
|
||||
}
|
||||
else
|
||||
it.reset ();
|
||||
updated = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (enable && !found)
|
||||
@@ -359,33 +342,19 @@ namespace i2p
|
||||
|
||||
void RouterContext::PublishSSU2Address (int port, bool publish, bool v4, bool v6)
|
||||
{
|
||||
if (!m_SSU2Keys) return;
|
||||
auto addresses = m_RouterInfo.GetAddresses ();
|
||||
if (!addresses) return;
|
||||
int newPort = 0;
|
||||
if (!port)
|
||||
{
|
||||
for (const auto& address : *addresses)
|
||||
if (address && address->port)
|
||||
{
|
||||
newPort = address->port;
|
||||
break;
|
||||
}
|
||||
if (!newPort) newPort = SelectRandomPort ();
|
||||
}
|
||||
if (!m_SSU2Keys || (publish && !port)) return;
|
||||
bool updated = false;
|
||||
for (auto& address : *addresses)
|
||||
for (auto& address : m_RouterInfo.GetAddresses ())
|
||||
{
|
||||
if (address && address->IsSSU2 () && (!address->port || address->port != port || address->published != publish) &&
|
||||
if (address->IsSSU2 () && (address->port != port || address->published != publish) &&
|
||||
((v4 && address->IsV4 ()) || (v6 && address->IsV6 ())))
|
||||
{
|
||||
if (port) address->port = port;
|
||||
else if (!address->port) address->port = newPort;
|
||||
address->port = port;
|
||||
address->published = publish;
|
||||
if (publish)
|
||||
address->caps |= (i2p::data::RouterInfo::eSSUIntroducer | i2p::data::RouterInfo::eSSUTesting);
|
||||
address->caps |= i2p::data::RouterInfo::eSSUIntroducer;
|
||||
else
|
||||
address->caps &= ~(i2p::data::RouterInfo::eSSUIntroducer | i2p::data::RouterInfo::eSSUTesting);
|
||||
address->caps &= ~i2p::data::RouterInfo::eSSUIntroducer;
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
@@ -395,41 +364,29 @@ namespace i2p
|
||||
|
||||
void RouterContext::UpdateSSU2Address (bool enable)
|
||||
{
|
||||
auto addresses = m_RouterInfo.GetAddresses ();
|
||||
if (!addresses) return;
|
||||
auto& addresses = m_RouterInfo.GetAddresses ();
|
||||
bool found = false, updated = false;
|
||||
for (auto& it : *addresses)
|
||||
for (auto it = addresses.begin (); it != addresses.end (); ++it)
|
||||
{
|
||||
if (it && it->IsSSU2 ())
|
||||
if ((*it)->IsSSU2 ())
|
||||
{
|
||||
found = true;
|
||||
if (enable)
|
||||
if (!enable)
|
||||
{
|
||||
it->s = m_SSU2Keys->staticPublicKey;
|
||||
it->i = m_SSU2Keys->intro;
|
||||
addresses.erase (it);
|
||||
updated= true;
|
||||
}
|
||||
else
|
||||
it.reset ();
|
||||
updated = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
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)
|
||||
@@ -438,85 +395,49 @@ namespace i2p
|
||||
|
||||
void RouterContext::UpdateAddress (const boost::asio::ip::address& host)
|
||||
{
|
||||
auto addresses = m_RouterInfo.GetAddresses ();
|
||||
if (!addresses) return;
|
||||
bool updated = false;
|
||||
if (host.is_v4 ())
|
||||
for (auto& address : m_RouterInfo.GetAddresses ())
|
||||
{
|
||||
auto addr = (*addresses)[i2p::data::RouterInfo::eNTCP2V4Idx];
|
||||
if (addr && addr->host != host)
|
||||
if (address->host != host && address->IsCompatible (host) &&
|
||||
!i2p::util::net::IsYggdrasilAddress (address->host))
|
||||
{
|
||||
addr->host = host;
|
||||
updated = true;
|
||||
}
|
||||
addr = (*addresses)[i2p::data::RouterInfo::eSSU2V4Idx];
|
||||
if (addr && addr->host != host)
|
||||
{
|
||||
addr->host = host;
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
else if (host.is_v6 ())
|
||||
{
|
||||
auto addr = (*addresses)[i2p::data::RouterInfo::eNTCP2V6Idx];
|
||||
if (addr && addr->host != host)
|
||||
{
|
||||
addr->host = host;
|
||||
updated = true;
|
||||
}
|
||||
addr = (*addresses)[i2p::data::RouterInfo::eSSU2V6Idx];
|
||||
if (addr && (addr->host != host || !addr->ssu->mtu))
|
||||
{
|
||||
addr->host = host;
|
||||
if (m_StatusV6 != eRouterStatusProxy)
|
||||
address->host = host;
|
||||
if (host.is_v6 () && address->transportStyle == i2p::data::RouterInfo::eTransportSSU)
|
||||
{
|
||||
// update MTU
|
||||
auto mtu = i2p::util::net::GetMTU (host);
|
||||
if (mtu)
|
||||
{
|
||||
LogPrint (eLogDebug, "Router: Our v6 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 > 1472) { // TODO: magic constant
|
||||
mtu = 1472;
|
||||
LogPrint(eLogWarning, "Router: MTU dropped to upper limit of 1472 bytes");
|
||||
}
|
||||
addr->ssu->mtu = mtu;
|
||||
if (address->ssu) address->ssu->mtu = mtu;
|
||||
}
|
||||
}
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
|
||||
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
if (updated || ts > m_LastUpdateTime + ROUTER_INFO_UPDATE_INTERVAL)
|
||||
UpdateRouterInfo ();
|
||||
}
|
||||
|
||||
bool RouterContext::AddSSU2Introducer (const i2p::data::RouterInfo::Introducer& introducer, bool v4)
|
||||
bool RouterContext::AddIntroducer (const i2p::data::RouterInfo::Introducer& introducer)
|
||||
{
|
||||
bool ret = m_RouterInfo.AddSSU2Introducer (introducer, v4);
|
||||
bool ret = m_RouterInfo.AddIntroducer (introducer);
|
||||
if (ret)
|
||||
UpdateRouterInfo ();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void RouterContext::RemoveSSU2Introducer (const i2p::data::IdentHash& h, bool v4)
|
||||
void RouterContext::RemoveIntroducer (const boost::asio::ip::udp::endpoint& e)
|
||||
{
|
||||
if (m_RouterInfo.RemoveSSU2Introducer (h, v4))
|
||||
if (m_RouterInfo.RemoveIntroducer (e))
|
||||
UpdateRouterInfo ();
|
||||
}
|
||||
|
||||
void RouterContext::ClearSSU2Introducers (bool v4)
|
||||
{
|
||||
auto addr = m_RouterInfo.GetSSU2Address (v4);
|
||||
if (addr && !addr->ssu->introducers.empty ())
|
||||
{
|
||||
addr->ssu->introducers.clear ();
|
||||
UpdateRouterInfo ();
|
||||
}
|
||||
}
|
||||
|
||||
void RouterContext::SetFloodfill (bool floodfill)
|
||||
{
|
||||
m_IsFloodfill = floodfill;
|
||||
@@ -615,6 +536,22 @@ namespace i2p
|
||||
return m_RouterInfo.GetCaps () & i2p::data::RouterInfo::eUnreachable;
|
||||
}
|
||||
|
||||
void RouterContext::RemoveNTCPAddress (bool v4only)
|
||||
{
|
||||
auto& addresses = m_RouterInfo.GetAddresses ();
|
||||
for (auto it = addresses.begin (); it != addresses.end ();)
|
||||
{
|
||||
if ((*it)->transportStyle == i2p::data::RouterInfo::eTransportNTCP && !(*it)->IsNTCP2 () &&
|
||||
(!v4only || (*it)->host.is_v4 ()))
|
||||
{
|
||||
it = addresses.erase (it);
|
||||
if (v4only) break; // otherwise might be more than one address
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
void RouterContext::SetUnreachable (bool v4, bool v6)
|
||||
{
|
||||
if (v4 || (v6 && !SupportsV4 ()))
|
||||
@@ -629,18 +566,15 @@ namespace i2p
|
||||
}
|
||||
uint16_t port = 0;
|
||||
// delete previous introducers
|
||||
auto addresses = m_RouterInfo.GetAddresses ();
|
||||
if (addresses)
|
||||
{
|
||||
for (auto& addr : *addresses)
|
||||
if (addr && addr->ssu && ((v4 && addr->IsV4 ()) || (v6 && addr->IsV6 ())))
|
||||
{
|
||||
addr->published = false;
|
||||
addr->caps &= ~i2p::data::RouterInfo::eSSUIntroducer; // can't be introducer
|
||||
addr->ssu->introducers.clear ();
|
||||
port = addr->port;
|
||||
}
|
||||
}
|
||||
auto& addresses = m_RouterInfo.GetAddresses ();
|
||||
for (auto& addr : addresses)
|
||||
if (addr->ssu && !addr->IsSSU2 () && ((v4 && addr->IsV4 ()) || (v6 && addr->IsV6 ())))
|
||||
{
|
||||
addr->published = false;
|
||||
addr->caps &= ~i2p::data::RouterInfo::eSSUIntroducer; // can't be introducer
|
||||
addr->ssu->introducers.clear ();
|
||||
port = addr->port;
|
||||
}
|
||||
// unpublish NTCP2 addreeses
|
||||
bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2);
|
||||
if (ntcp2)
|
||||
@@ -664,19 +598,15 @@ namespace i2p
|
||||
}
|
||||
uint16_t port = 0;
|
||||
// delete previous introducers
|
||||
bool isSSU2Published; i2p::config::GetOption ("ssu2.published", isSSU2Published);
|
||||
auto addresses = m_RouterInfo.GetAddresses ();
|
||||
if (addresses)
|
||||
{
|
||||
for (auto& addr : *addresses)
|
||||
if (addr && addr->ssu && isSSU2Published && ((v4 && addr->IsV4 ()) || (v6 && addr->IsV6 ())))
|
||||
{
|
||||
addr->published = true;
|
||||
addr->caps |= i2p::data::RouterInfo::eSSUIntroducer;
|
||||
addr->ssu->introducers.clear ();
|
||||
if (addr->port) port = addr->port;
|
||||
}
|
||||
}
|
||||
auto& addresses = m_RouterInfo.GetAddresses ();
|
||||
for (auto& addr : addresses)
|
||||
if (addr->ssu && !addr->IsSSU2 () && ((v4 && addr->IsV4 ()) || (v6 && addr->IsV6 ())))
|
||||
{
|
||||
addr->published = true;
|
||||
addr->caps |= i2p::data::RouterInfo::eSSUIntroducer;
|
||||
addr->ssu->introducers.clear ();
|
||||
port = addr->port;
|
||||
}
|
||||
// publish NTCP2
|
||||
bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2);
|
||||
if (ntcp2)
|
||||
@@ -699,33 +629,39 @@ namespace i2p
|
||||
if (supportsV6)
|
||||
{
|
||||
// insert v6 addresses if necessary
|
||||
bool foundNTCP2 = false, foundSSU2 = false;
|
||||
bool foundSSU = false, foundNTCP2 = false, foundSSU2 = false;
|
||||
uint16_t port = 0;
|
||||
auto addresses = m_RouterInfo.GetAddresses ();
|
||||
if (addresses)
|
||||
auto& addresses = m_RouterInfo.GetAddresses ();
|
||||
for (auto& addr: addresses)
|
||||
{
|
||||
for (auto& addr: *addresses)
|
||||
if (addr->IsV6 () && !i2p::util::net::IsYggdrasilAddress (addr->host))
|
||||
{
|
||||
if (addr && addr->IsV6 () && !i2p::util::net::IsYggdrasilAddress (addr->host))
|
||||
switch (addr->transportStyle)
|
||||
{
|
||||
switch (addr->transportStyle)
|
||||
{
|
||||
case i2p::data::RouterInfo::eTransportNTCP2:
|
||||
foundNTCP2 = true;
|
||||
break;
|
||||
case i2p::data::RouterInfo::eTransportSSU2:
|
||||
foundSSU2 = true;
|
||||
break;
|
||||
default: ;
|
||||
}
|
||||
case i2p::data::RouterInfo::eTransportSSU:
|
||||
foundSSU = true;
|
||||
break;
|
||||
case i2p::data::RouterInfo::eTransportNTCP:
|
||||
foundNTCP2 = true;
|
||||
break;
|
||||
case i2p::data::RouterInfo::eTransportSSU2:
|
||||
foundSSU2 = true;
|
||||
break;
|
||||
default: ;
|
||||
}
|
||||
if (addr) port = addr->port;
|
||||
}
|
||||
port = addr->port;
|
||||
}
|
||||
if (!port)
|
||||
if (!port) i2p::config::GetOption("port", port);
|
||||
// SSU
|
||||
if (!foundSSU)
|
||||
{
|
||||
i2p::config::GetOption("port", port);
|
||||
if (!port) port = SelectRandomPort ();
|
||||
bool ssu; i2p::config::GetOption("ssu", ssu);
|
||||
if (ssu)
|
||||
{
|
||||
std::string host = "::1"; // TODO: read host
|
||||
m_RouterInfo.AddSSUAddress (host.c_str (), port, nullptr);
|
||||
}
|
||||
}
|
||||
// NTCP2
|
||||
if (!foundNTCP2)
|
||||
@@ -759,7 +695,6 @@ namespace i2p
|
||||
if (ssu2Published)
|
||||
{
|
||||
uint16_t ssu2Port; i2p::config::GetOption ("ssu2.port", ssu2Port);
|
||||
if (!ssu2Port) ssu2Port = port;
|
||||
m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, boost::asio::ip::address::from_string ("::1"), ssu2Port);
|
||||
}
|
||||
else
|
||||
@@ -781,34 +716,37 @@ namespace i2p
|
||||
// update
|
||||
if (supportsV4)
|
||||
{
|
||||
bool foundNTCP2 = false, foundSSU2 = false;
|
||||
bool foundSSU = false, foundNTCP2 = false, foundSSU2 = false;
|
||||
std::string host = "127.0.0.1";
|
||||
uint16_t port = 0;
|
||||
auto addresses = m_RouterInfo.GetAddresses ();
|
||||
if (addresses)
|
||||
auto& addresses = m_RouterInfo.GetAddresses ();
|
||||
for (auto& addr: addresses)
|
||||
{
|
||||
for (auto& addr: *addresses)
|
||||
if (addr->IsV4 ())
|
||||
{
|
||||
if (addr && addr->IsV4 ())
|
||||
switch (addr->transportStyle)
|
||||
{
|
||||
switch (addr->transportStyle)
|
||||
{
|
||||
case i2p::data::RouterInfo::eTransportNTCP2:
|
||||
foundNTCP2 = true;
|
||||
break;
|
||||
case i2p::data::RouterInfo::eTransportSSU2:
|
||||
foundSSU2 = true;
|
||||
break;
|
||||
default: ;
|
||||
}
|
||||
case i2p::data::RouterInfo::eTransportSSU:
|
||||
foundSSU = true;
|
||||
break;
|
||||
case i2p::data::RouterInfo::eTransportNTCP:
|
||||
foundNTCP2 = true;
|
||||
break;
|
||||
case i2p::data::RouterInfo::eTransportSSU2:
|
||||
foundSSU2 = true;
|
||||
break;
|
||||
default: ;
|
||||
}
|
||||
if (addr && addr->port) port = addr->port;
|
||||
}
|
||||
if (addr->port) port = addr->port;
|
||||
}
|
||||
if (!port)
|
||||
if (!port) i2p::config::GetOption("port", port);
|
||||
// SSU
|
||||
if (!foundSSU)
|
||||
{
|
||||
i2p::config::GetOption("port", port);
|
||||
if (!port) port = SelectRandomPort ();
|
||||
bool ssu; i2p::config::GetOption("ssu", ssu);
|
||||
if (ssu)
|
||||
m_RouterInfo.AddSSUAddress (host.c_str (), port, nullptr);
|
||||
}
|
||||
// NTCP2
|
||||
if (!foundNTCP2)
|
||||
@@ -837,11 +775,10 @@ namespace i2p
|
||||
if (ssu2Published)
|
||||
{
|
||||
uint16_t ssu2Port; i2p::config::GetOption ("ssu2.port", ssu2Port);
|
||||
if (!ssu2Port) ssu2Port = 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::eV4);
|
||||
m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, i2p::data::RouterInfo::eV6);
|
||||
}
|
||||
}
|
||||
m_RouterInfo.EnableV4 ();
|
||||
@@ -855,56 +792,37 @@ namespace i2p
|
||||
{
|
||||
if (supportsmesh)
|
||||
{
|
||||
auto addresses = m_RouterInfo.GetAddresses ();
|
||||
if (!addresses) return;
|
||||
m_RouterInfo.EnableMesh ();
|
||||
if ((*addresses)[i2p::data::RouterInfo::eNTCP2V6MeshIdx]) return; // we have mesh address already
|
||||
uint16_t port = 0;
|
||||
i2p::config::GetOption ("ntcp2.port", port);
|
||||
if (!port) i2p::config::GetOption("port", port);
|
||||
if (!port)
|
||||
bool foundMesh = false;
|
||||
auto& addresses = m_RouterInfo.GetAddresses ();
|
||||
for (auto& addr: addresses)
|
||||
{
|
||||
for (auto& addr: *addresses)
|
||||
if (!port) port = addr->port;
|
||||
if (i2p::util::net::IsYggdrasilAddress (addr->host))
|
||||
{
|
||||
if (addr && addr->port)
|
||||
{
|
||||
port = addr->port;
|
||||
break;
|
||||
}
|
||||
foundMesh = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!port) port = SelectRandomPort ();
|
||||
m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, host, port);
|
||||
if (!foundMesh)
|
||||
m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, host, port);
|
||||
}
|
||||
else
|
||||
m_RouterInfo.DisableMesh ();
|
||||
UpdateRouterInfo ();
|
||||
}
|
||||
|
||||
void RouterContext::SetMTU (int mtu, bool v4)
|
||||
{
|
||||
if (mtu < 1280 || mtu > 1500) return;
|
||||
auto addresses = m_RouterInfo.GetAddresses ();
|
||||
if (!addresses) return;
|
||||
for (auto& addr: *addresses)
|
||||
{
|
||||
if (addr && addr->ssu && ((v4 && addr->IsV4 ()) || (!v4 && addr->IsV6 ())))
|
||||
{
|
||||
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);
|
||||
bool updated = false;
|
||||
auto addresses = m_RouterInfo.GetAddresses ();
|
||||
if (!addresses) return;
|
||||
for (auto& addr: *addresses)
|
||||
auto& addresses = m_RouterInfo.GetAddresses ();
|
||||
for (auto& addr: addresses)
|
||||
{
|
||||
if (addr && addr->IsPublishedNTCP2 ())
|
||||
if (addr->IsPublishedNTCP2 ())
|
||||
{
|
||||
bool isYgg1 = i2p::util::net::IsYggdrasilAddress (addr->host);
|
||||
if (addr->IsV6 () && ((isYgg && isYgg1) || (!isYgg && !isYgg1)))
|
||||
|
||||
@@ -37,9 +37,10 @@ namespace garlic
|
||||
eRouterStatusOK = 0,
|
||||
eRouterStatusTesting = 1,
|
||||
eRouterStatusFirewalled = 2,
|
||||
eRouterStatusUnknown = 3,
|
||||
eRouterStatusProxy = 4,
|
||||
eRouterStatusMesh = 5
|
||||
eRouterStatusError = 3,
|
||||
eRouterStatusUnknown = 4,
|
||||
eRouterStatusProxy = 5,
|
||||
eRouterStatusMesh = 6
|
||||
};
|
||||
|
||||
enum RouterError
|
||||
@@ -47,9 +48,7 @@ namespace garlic
|
||||
eRouterErrorNone = 0,
|
||||
eRouterErrorClockSkew = 1,
|
||||
eRouterErrorOffline = 2,
|
||||
eRouterErrorSymmetricNAT = 3,
|
||||
eRouterErrorFullConeNAT = 4,
|
||||
eRouterErrorNoDescriptors = 5
|
||||
eRouterErrorSymmetricNAT = 3
|
||||
};
|
||||
|
||||
class RouterContext: public i2p::garlic::GarlicDestination
|
||||
@@ -105,11 +104,9 @@ namespace garlic
|
||||
RouterStatus GetStatus () const { return m_Status; };
|
||||
void SetStatus (RouterStatus status);
|
||||
RouterError GetError () const { return m_Error; };
|
||||
void SetError (RouterError error) { m_Error = error; };
|
||||
void SetError (RouterError error) { m_Status = eRouterStatusError; m_Error = error; };
|
||||
RouterStatus GetStatusV6 () const { return m_StatusV6; };
|
||||
void SetStatusV6 (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);
|
||||
@@ -121,9 +118,9 @@ namespace garlic
|
||||
void UpdateNTCP2Address (bool enable);
|
||||
void PublishSSU2Address (int port, bool publish, bool v4, bool v6);
|
||||
void UpdateSSU2Address (bool enable);
|
||||
bool AddSSU2Introducer (const i2p::data::RouterInfo::Introducer& introducer, bool v4);
|
||||
void RemoveSSU2Introducer (const i2p::data::IdentHash& h, bool v4);
|
||||
void ClearSSU2Introducers (bool v4);
|
||||
void RemoveNTCPAddress (bool v4only = true); // delete NTCP address for older routers. TODO: remove later
|
||||
bool AddIntroducer (const i2p::data::RouterInfo::Introducer& introducer);
|
||||
void RemoveIntroducer (const boost::asio::ip::udp::endpoint& e);
|
||||
bool IsUnreachable () const;
|
||||
void SetUnreachable (bool v4, bool v6);
|
||||
void SetReachable (bool v4, bool v6);
|
||||
@@ -142,7 +139,6 @@ 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
|
||||
@@ -177,10 +173,8 @@ 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);
|
||||
|
||||
@@ -196,7 +190,7 @@ namespace garlic
|
||||
uint64_t m_BandwidthLimit; // allowed bandwidth
|
||||
int m_ShareRatio;
|
||||
RouterStatus m_Status, m_StatusV6;
|
||||
RouterError m_Error, m_ErrorV6;
|
||||
RouterError m_Error;
|
||||
int m_NetID;
|
||||
std::mutex m_GarlicMutex;
|
||||
std::unique_ptr<NTCP2PrivateKeys> m_NTCP2Keys;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2023, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -206,24 +206,26 @@ namespace data
|
||||
s.read ((char *)&m_Timestamp, sizeof (m_Timestamp));
|
||||
m_Timestamp = be64toh (m_Timestamp);
|
||||
// read addresses
|
||||
auto addresses = NewAddresses ();
|
||||
auto addresses = boost::make_shared<Addresses>();
|
||||
uint8_t numAddresses;
|
||||
s.read ((char *)&numAddresses, sizeof (numAddresses));
|
||||
addresses->reserve (numAddresses);
|
||||
for (int i = 0; i < numAddresses; i++)
|
||||
{
|
||||
uint8_t supportedTransports = 0;
|
||||
auto address = NewAddress ();
|
||||
auto address = std::make_shared<Address> ();
|
||||
uint8_t cost; // ignore
|
||||
s.read ((char *)&cost, sizeof (cost));
|
||||
s.read ((char *)&address->date, sizeof (address->date));
|
||||
bool isHost = false, isStaticKey = false, isV2 = false;
|
||||
bool isHost = false, isIntroKey = false, isStaticKey = false, isV2 = false;
|
||||
Tag<32> iV2; // for 'i' field in SSU, TODO: remove later
|
||||
char transportStyle[6];
|
||||
ReadString (transportStyle, 6, s);
|
||||
if (!strncmp (transportStyle, "NTCP", 4)) // NTCP or NTCP2
|
||||
address->transportStyle = eTransportNTCP2;
|
||||
address->transportStyle = eTransportNTCP;
|
||||
else if (!strncmp (transportStyle, "SSU", 3)) // SSU or SSU2
|
||||
{
|
||||
address->transportStyle = eTransportSSU2;
|
||||
address->transportStyle = (transportStyle[3] == '2') ? eTransportSSU2 : eTransportSSU;
|
||||
address->ssu.reset (new SSUExt ());
|
||||
address->ssu->mtu = 0;
|
||||
}
|
||||
@@ -255,31 +257,20 @@ 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 ());
|
||||
}
|
||||
}
|
||||
address->port = boost::lexical_cast<int>(value);
|
||||
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 ());
|
||||
}
|
||||
}
|
||||
address->ssu->mtu = boost::lexical_cast<int>(value);
|
||||
else
|
||||
LogPrint (eLogWarning, "RouterInfo: Unexpected field 'mtu' for NTCP2");
|
||||
LogPrint (eLogWarning, "RouterInfo: Unexpected field 'mtu' for NTCP");
|
||||
}
|
||||
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");
|
||||
}
|
||||
else if (!strcmp (key, "caps"))
|
||||
address->caps = ExtractAddressCaps (value);
|
||||
@@ -297,6 +288,8 @@ namespace data
|
||||
}
|
||||
else if (address->IsSSU2 ())
|
||||
Base64ToByteStream (value, strlen (value), address->i, 32);
|
||||
else
|
||||
Base64ToByteStream (value, strlen (value), iV2, 32);
|
||||
}
|
||||
else if (!strcmp (key, "v"))
|
||||
{
|
||||
@@ -329,40 +322,22 @@ namespace data
|
||||
}
|
||||
Introducer& introducer = address->ssu->introducers.at (index);
|
||||
if (!strcmp (key, "ihost"))
|
||||
introducer.isH = false; // SSU1
|
||||
{
|
||||
boost::system::error_code ecode;
|
||||
introducer.iHost = boost::asio::ip::address::from_string (value, ecode);
|
||||
}
|
||||
else if (!strcmp (key, "iport"))
|
||||
introducer.isH = false; // SSU1
|
||||
introducer.iPort = boost::lexical_cast<int>(value);
|
||||
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, "ih"))
|
||||
{
|
||||
Base64ToByteStream (value, strlen (value), introducer.iH, 32);
|
||||
introducer.isH = true;
|
||||
}
|
||||
introducer.iTag = boost::lexical_cast<uint32_t>(value);
|
||||
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 ());
|
||||
}
|
||||
}
|
||||
introducer.iExp = boost::lexical_cast<uint32_t>(value);
|
||||
}
|
||||
if (!s) return;
|
||||
}
|
||||
if (address->transportStyle == eTransportNTCP2)
|
||||
if (address->transportStyle == eTransportNTCP)
|
||||
{
|
||||
if (isStaticKey)
|
||||
{
|
||||
@@ -386,7 +361,46 @@ namespace data
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (address->transportStyle == eTransportSSU2 && isV2)
|
||||
else if (address->transportStyle == eTransportSSU)
|
||||
{
|
||||
if (isIntroKey)
|
||||
{
|
||||
if (isHost)
|
||||
supportedTransports |= address->host.is_v4 () ? eSSUV4 : eSSUV6;
|
||||
else if (address->caps & AddressCaps::eV6)
|
||||
{
|
||||
supportedTransports |= eSSUV6;
|
||||
if (address->caps & AddressCaps::eV4) supportedTransports |= eSSUV4; // in additional to v6
|
||||
}
|
||||
else
|
||||
supportedTransports |= eSSUV4; // in case if host or 6 caps is not preasented, we assume 4
|
||||
if (address->ssu && !address->ssu->introducers.empty ())
|
||||
{
|
||||
// exclude invalid introducers
|
||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
int numValid = 0;
|
||||
for (auto& it: address->ssu->introducers)
|
||||
{
|
||||
if (!it.iExp) it.iExp = m_Timestamp/1000 + NETDB_INTRODUCEE_EXPIRATION_TIMEOUT;
|
||||
if (ts <= it.iExp && it.iPort > 0 &&
|
||||
((it.iHost.is_v4 () && address->IsV4 ()) || (it.iHost.is_v6 () && address->IsV6 ())))
|
||||
numValid++;
|
||||
else
|
||||
it.iPort = 0;
|
||||
}
|
||||
if (numValid)
|
||||
m_ReachableTransports |= supportedTransports;
|
||||
else
|
||||
address->ssu->introducers.resize (0);
|
||||
}
|
||||
else if (isHost && address->port)
|
||||
{
|
||||
address->published = true;
|
||||
m_ReachableTransports |= supportedTransports;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (address->transportStyle == eTransportSSU2 || (isV2 && address->transportStyle == eTransportSSU))
|
||||
{
|
||||
if (address->IsV4 ()) supportedTransports |= eSSU2V4;
|
||||
if (address->IsV6 ()) supportedTransports |= eSSU2V6;
|
||||
@@ -394,38 +408,32 @@ namespace data
|
||||
{
|
||||
if (address->host.is_v4 ()) m_ReachableTransports |= eSSU2V4;
|
||||
if (address->host.is_v6 ()) m_ReachableTransports |= eSSU2V6;
|
||||
address->published = true;
|
||||
}
|
||||
if (address->ssu && !address->ssu->introducers.empty ())
|
||||
{
|
||||
// exclude invalid introducers
|
||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
int numValid = 0;
|
||||
for (auto& it: address->ssu->introducers)
|
||||
{
|
||||
if (it.iTag && ts < it.iExp && it.isH)
|
||||
numValid++;
|
||||
else
|
||||
it.iTag = 0;
|
||||
}
|
||||
if (numValid)
|
||||
m_ReachableTransports |= supportedTransports;
|
||||
else
|
||||
address->ssu->introducers.resize (0);
|
||||
}
|
||||
}
|
||||
if (supportedTransports)
|
||||
{
|
||||
if (!(m_SupportedTransports & supportedTransports)) // avoid duplicates
|
||||
{
|
||||
for (uint8_t i = 0; i < eNumTransports; i++)
|
||||
if ((1 << i) & supportedTransports)
|
||||
(*addresses)[i] = address;
|
||||
addresses->push_back(address);
|
||||
if (address->transportStyle == eTransportSSU && isV2)
|
||||
{
|
||||
// create additional SSU2 address. TODO: remove later
|
||||
auto ssu2addr = std::make_shared<Address> ();
|
||||
ssu2addr->transportStyle = eTransportSSU2;
|
||||
ssu2addr->host = address->host; ssu2addr->port = address->port;
|
||||
ssu2addr->s = address->s; ssu2addr->i = iV2;
|
||||
ssu2addr->date = address->date; ssu2addr->caps = address->caps;
|
||||
ssu2addr->published = address->published;
|
||||
ssu2addr->ssu.reset (new SSUExt ()); ssu2addr->ssu->mtu = address->ssu->mtu;
|
||||
for (const auto& introducer: address->ssu->introducers)
|
||||
if (!introducer.iPort) // SSU2
|
||||
ssu2addr->ssu->introducers.push_back (introducer);
|
||||
addresses->push_back(ssu2addr);
|
||||
}
|
||||
}
|
||||
m_SupportedTransports |= supportedTransports;
|
||||
}
|
||||
}
|
||||
// update addresses
|
||||
#if (BOOST_VERSION >= 105300)
|
||||
boost::atomic_store (&m_Addresses, addresses);
|
||||
#else
|
||||
@@ -554,10 +562,10 @@ namespace data
|
||||
case CAPS_FLAG_V6:
|
||||
caps |= AddressCaps::eV6;
|
||||
break;
|
||||
case CAPS_FLAG_SSU2_TESTING:
|
||||
case CAPS_FLAG_SSU_TESTING:
|
||||
caps |= AddressCaps::eSSUTesting;
|
||||
break;
|
||||
case CAPS_FLAG_SSU2_INTRODUCER:
|
||||
case CAPS_FLAG_SSU_INTRODUCER:
|
||||
caps |= AddressCaps::eSSUIntroducer;
|
||||
break;
|
||||
default: ;
|
||||
@@ -620,13 +628,36 @@ namespace data
|
||||
return l+1;
|
||||
}
|
||||
|
||||
|
||||
void RouterInfo::AddSSUAddress (const char * host, int port, const uint8_t * key, int mtu)
|
||||
{
|
||||
auto addr = std::make_shared<Address>();
|
||||
addr->host = boost::asio::ip::address::from_string (host);
|
||||
addr->port = port;
|
||||
addr->transportStyle = eTransportSSU;
|
||||
addr->published = true;
|
||||
addr->caps = i2p::data::RouterInfo::eSSUTesting | i2p::data::RouterInfo::eSSUIntroducer; // BC;
|
||||
addr->date = 0;
|
||||
addr->ssu.reset (new SSUExt ());
|
||||
addr->ssu->mtu = mtu;
|
||||
if (key)
|
||||
memcpy (addr->i, key, 32);
|
||||
else
|
||||
RAND_bytes (addr->i, 32);
|
||||
for (const auto& it: *m_Addresses) // don't insert same address twice
|
||||
if (*it == *addr) return;
|
||||
m_SupportedTransports |= addr->host.is_v6 () ? eSSUV6 : eSSUV4;
|
||||
m_ReachableTransports |= addr->host.is_v6 () ? eSSUV6 : eSSUV4;
|
||||
m_Addresses->push_back(std::move(addr));
|
||||
}
|
||||
|
||||
void RouterInfo::AddNTCP2Address (const uint8_t * staticKey, const uint8_t * iv,
|
||||
const boost::asio::ip::address& host, int port, uint8_t caps)
|
||||
{
|
||||
auto addr = std::make_shared<Address>();
|
||||
addr->host = host;
|
||||
addr->port = port;
|
||||
addr->transportStyle = eTransportNTCP2;
|
||||
addr->transportStyle = eTransportNTCP;
|
||||
addr->caps = caps;
|
||||
addr->date = 0;
|
||||
if (port) addr->published = true;
|
||||
@@ -636,46 +667,28 @@ namespace data
|
||||
{
|
||||
m_SupportedTransports |= eNTCP2V4;
|
||||
if (addr->published) m_ReachableTransports |= eNTCP2V4;
|
||||
(*m_Addresses)[eNTCP2V4Idx] = addr;
|
||||
}
|
||||
if (addr->IsV6 ())
|
||||
{
|
||||
if (i2p::util::net::IsYggdrasilAddress (addr->host))
|
||||
{
|
||||
m_SupportedTransports |= eNTCP2V6Mesh;
|
||||
m_ReachableTransports |= eNTCP2V6Mesh;
|
||||
(*m_Addresses)[eNTCP2V6MeshIdx] = addr;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_SupportedTransports |= eNTCP2V6;
|
||||
if (addr->published) m_ReachableTransports |= eNTCP2V6;
|
||||
(*m_Addresses)[eNTCP2V6Idx] = addr;
|
||||
}
|
||||
m_SupportedTransports |= eNTCP2V6;
|
||||
if (addr->published) m_ReachableTransports |= eNTCP2V6;
|
||||
}
|
||||
m_Addresses->push_back(std::move(addr));
|
||||
}
|
||||
|
||||
void RouterInfo::AddSSU2Address (const uint8_t * staticKey, const uint8_t * introKey, uint8_t caps)
|
||||
{
|
||||
auto addr = std::make_shared<Address>();
|
||||
addr->transportStyle = eTransportSSU2;
|
||||
addr->port = 0;
|
||||
addr->caps = caps;
|
||||
addr->date = 0;
|
||||
addr->ssu.reset (new SSUExt ());
|
||||
addr->ssu->mtu = 0;
|
||||
memcpy (addr->s, staticKey, 32);
|
||||
memcpy (addr->i, introKey, 32);
|
||||
if (addr->IsV4 ())
|
||||
{
|
||||
m_SupportedTransports |= eSSU2V4;
|
||||
(*m_Addresses)[eSSU2V4Idx] = addr;
|
||||
}
|
||||
if (addr->IsV6 ())
|
||||
{
|
||||
m_SupportedTransports |= eSSU2V6;
|
||||
(*m_Addresses)[eSSU2V6Idx] = addr;
|
||||
}
|
||||
if (addr->IsV4 ()) m_SupportedTransports |= eSSU2V4;
|
||||
if (addr->IsV6 ()) m_SupportedTransports |= eSSU2V6;
|
||||
m_Addresses->push_back(std::move(addr));
|
||||
}
|
||||
|
||||
void RouterInfo::AddSSU2Address (const uint8_t * staticKey, const uint8_t * introKey,
|
||||
@@ -686,7 +699,7 @@ namespace data
|
||||
addr->host = host;
|
||||
addr->port = port;
|
||||
addr->published = true;
|
||||
addr->caps = i2p::data::RouterInfo::eSSUTesting | i2p::data::RouterInfo::eSSUIntroducer; // BC;
|
||||
addr->caps = 0;
|
||||
addr->date = 0;
|
||||
addr->ssu.reset (new SSUExt ());
|
||||
addr->ssu->mtu = 0;
|
||||
@@ -696,14 +709,58 @@ namespace data
|
||||
{
|
||||
m_SupportedTransports |= eSSU2V4;
|
||||
m_ReachableTransports |= eSSU2V4;
|
||||
(*m_Addresses)[eSSU2V4Idx] = addr;
|
||||
}
|
||||
if (addr->IsV6 ())
|
||||
{
|
||||
m_SupportedTransports |= eSSU2V6;
|
||||
m_ReachableTransports |= eSSU2V6;
|
||||
(*m_Addresses)[eSSU2V6Idx] = addr;
|
||||
}
|
||||
m_Addresses->push_back(std::move(addr));
|
||||
}
|
||||
|
||||
bool RouterInfo::AddIntroducer (const Introducer& introducer)
|
||||
{
|
||||
for (auto& addr : *m_Addresses)
|
||||
{
|
||||
if (addr->transportStyle == eTransportSSU &&
|
||||
((addr->IsV4 () && introducer.iHost.is_v4 ()) || (addr->IsV6 () && introducer.iHost.is_v6 ())))
|
||||
{
|
||||
for (auto& intro: addr->ssu->introducers)
|
||||
if (intro.iTag == introducer.iTag) return false; // already presented
|
||||
addr->ssu->introducers.push_back (introducer);
|
||||
m_ReachableTransports |= (addr->IsV4 () ? eSSUV4 : eSSUV6);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RouterInfo::RemoveIntroducer (const boost::asio::ip::udp::endpoint& e)
|
||||
{
|
||||
for (auto& addr: *m_Addresses)
|
||||
{
|
||||
if (addr->transportStyle == eTransportSSU &&
|
||||
((addr->IsV4 () && e.address ().is_v4 ()) || (addr->IsV6 () && e.address ().is_v6 ())))
|
||||
{
|
||||
for (auto it = addr->ssu->introducers.begin (); it != addr->ssu->introducers.end (); ++it)
|
||||
if (boost::asio::ip::udp::endpoint (it->iHost, it->iPort) == e)
|
||||
{
|
||||
addr->ssu->introducers.erase (it);
|
||||
if (addr->ssu->introducers.empty ())
|
||||
m_ReachableTransports &= ~(addr->IsV4 () ? eSSUV4 : eSSUV6);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RouterInfo::IsSSU (bool v4only) const
|
||||
{
|
||||
if (v4only)
|
||||
return m_SupportedTransports & eSSUV4;
|
||||
else
|
||||
return m_SupportedTransports & (eSSUV4 | eSSUV6);
|
||||
}
|
||||
|
||||
bool RouterInfo::IsNTCP2 (bool v4only) const
|
||||
@@ -742,17 +799,21 @@ namespace data
|
||||
{
|
||||
if (IsV6 ())
|
||||
{
|
||||
if ((*m_Addresses)[eNTCP2V6Idx])
|
||||
for (auto it = m_Addresses->begin (); it != m_Addresses->end ();)
|
||||
{
|
||||
if ((*m_Addresses)[eNTCP2V6Idx]->IsV4 ())
|
||||
(*m_Addresses)[eNTCP2V6Idx]->caps &= ~AddressCaps::eV6;
|
||||
(*m_Addresses)[eNTCP2V6Idx].reset ();
|
||||
}
|
||||
if ((*m_Addresses)[eSSU2V6Idx])
|
||||
{
|
||||
if ((*m_Addresses)[eSSU2V6Idx]->IsV4 ())
|
||||
(*m_Addresses)[eSSU2V6Idx]->caps &= ~AddressCaps::eV6;
|
||||
(*m_Addresses)[eSSU2V6Idx].reset ();
|
||||
auto addr = *it;
|
||||
if (addr->IsV6 ())
|
||||
{
|
||||
if (addr->IsV4 ())
|
||||
{
|
||||
addr->caps &= ~AddressCaps::eV6;
|
||||
++it;
|
||||
}
|
||||
else
|
||||
it = m_Addresses->erase (it);
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
UpdateSupportedTransports ();
|
||||
}
|
||||
@@ -762,17 +823,21 @@ namespace data
|
||||
{
|
||||
if (IsV4 ())
|
||||
{
|
||||
if ((*m_Addresses)[eNTCP2V4Idx])
|
||||
for (auto it = m_Addresses->begin (); it != m_Addresses->end ();)
|
||||
{
|
||||
if ((*m_Addresses)[eNTCP2V4Idx]->IsV6 ())
|
||||
(*m_Addresses)[eNTCP2V4Idx]->caps &= ~AddressCaps::eV4;
|
||||
(*m_Addresses)[eNTCP2V4Idx].reset ();
|
||||
}
|
||||
if ((*m_Addresses)[eSSU2V4Idx])
|
||||
{
|
||||
if ((*m_Addresses)[eSSU2V4Idx]->IsV6 ())
|
||||
(*m_Addresses)[eSSU2V4Idx]->caps &= ~AddressCaps::eV4;
|
||||
(*m_Addresses)[eSSU2V4Idx].reset ();
|
||||
auto addr = *it;
|
||||
if (addr->IsV4 ())
|
||||
{
|
||||
if (addr->IsV6 ())
|
||||
{
|
||||
addr->caps &= ~AddressCaps::eV4;
|
||||
++it;
|
||||
}
|
||||
else
|
||||
it = m_Addresses->erase (it);
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
UpdateSupportedTransports ();
|
||||
}
|
||||
@@ -793,42 +858,51 @@ namespace data
|
||||
{
|
||||
m_SupportedTransports &= ~eNTCP2V6Mesh;
|
||||
m_ReachableTransports &= ~eNTCP2V6Mesh;
|
||||
(*m_Addresses)[eNTCP2V6MeshIdx].reset ();
|
||||
for (auto it = m_Addresses->begin (); it != m_Addresses->end ();)
|
||||
{
|
||||
auto addr = *it;
|
||||
if (i2p::util::net::IsYggdrasilAddress (addr->host))
|
||||
it = m_Addresses->erase (it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetSSUAddress (bool v4only) const
|
||||
{
|
||||
return GetAddress (
|
||||
[v4only](std::shared_ptr<const RouterInfo::Address> address)->bool
|
||||
{
|
||||
return (address->transportStyle == eTransportSSU) && (!v4only || address->IsV4 ());
|
||||
});
|
||||
}
|
||||
|
||||
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetSSUV6Address () const
|
||||
{
|
||||
return GetAddress (
|
||||
[](std::shared_ptr<const RouterInfo::Address> address)->bool
|
||||
{
|
||||
return (address->transportStyle == eTransportSSU) && address->IsV6();
|
||||
});
|
||||
}
|
||||
|
||||
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetSSU2V4Address () const
|
||||
{
|
||||
return (*GetAddresses ())[eSSU2V4Idx];
|
||||
return GetAddress (
|
||||
[](std::shared_ptr<const RouterInfo::Address> address)->bool
|
||||
{
|
||||
return (address->transportStyle == eTransportSSU2) && address->IsV4();
|
||||
});
|
||||
}
|
||||
|
||||
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetSSU2V6Address () const
|
||||
{
|
||||
return (*GetAddresses ())[eSSU2V6Idx];
|
||||
}
|
||||
|
||||
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetSSU2Address (bool v4) const
|
||||
{
|
||||
if (v4)
|
||||
{
|
||||
if (m_SupportedTransports & eSSU2V4)
|
||||
return GetSSU2V4Address ();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_SupportedTransports & eSSU2V6)
|
||||
return GetSSU2V6Address ();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
boost::shared_ptr<RouterInfo::Addresses> RouterInfo::GetAddresses () const
|
||||
{
|
||||
#if (BOOST_VERSION >= 105300)
|
||||
return boost::atomic_load (&m_Addresses);
|
||||
#else
|
||||
return m_Addresses;
|
||||
#endif
|
||||
return GetAddress (
|
||||
[](std::shared_ptr<const RouterInfo::Address> address)->bool
|
||||
{
|
||||
return (address->transportStyle == eTransportSSU2) && address->IsV6();
|
||||
});
|
||||
}
|
||||
|
||||
template<typename Filter>
|
||||
@@ -841,7 +915,7 @@ namespace data
|
||||
auto addresses = m_Addresses;
|
||||
#endif
|
||||
for (const auto& address : *addresses)
|
||||
if (address && filter (address)) return address;
|
||||
if (filter (address)) return address;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
@@ -859,29 +933,39 @@ namespace data
|
||||
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetSSU2AddressWithStaticKey (const uint8_t * key, bool isV6) const
|
||||
{
|
||||
if (!key) return nullptr;
|
||||
auto addr = (*GetAddresses ())[isV6 ? eSSU2V6Idx : eSSU2V4Idx];
|
||||
if (addr && !memcmp (addr->s, key, 32))
|
||||
return addr;
|
||||
return nullptr;
|
||||
return GetAddress (
|
||||
[key, isV6](std::shared_ptr<const RouterInfo::Address> address)->bool
|
||||
{
|
||||
return address->IsSSU2 () && !memcmp (address->s, key, 32) && address->IsV6 () == isV6;
|
||||
});
|
||||
}
|
||||
|
||||
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetPublishedNTCP2V4Address () const
|
||||
{
|
||||
auto addr = (*GetAddresses ())[eNTCP2V4Idx];
|
||||
if (addr && addr->IsPublishedNTCP2 ()) return addr;
|
||||
return nullptr;
|
||||
return GetAddress (
|
||||
[](std::shared_ptr<const RouterInfo::Address> address)->bool
|
||||
{
|
||||
return address->IsPublishedNTCP2 () && address->host.is_v4 ();
|
||||
});
|
||||
}
|
||||
|
||||
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetPublishedNTCP2V6Address () const
|
||||
{
|
||||
auto addr = (*GetAddresses ())[eNTCP2V6Idx];
|
||||
if (addr && addr->IsPublishedNTCP2 ()) return addr;
|
||||
return nullptr;
|
||||
return GetAddress (
|
||||
[](std::shared_ptr<const RouterInfo::Address> address)->bool
|
||||
{
|
||||
return address->IsPublishedNTCP2 () && address->host.is_v6 () &&
|
||||
!i2p::util::net::IsYggdrasilAddress (address->host);
|
||||
});
|
||||
}
|
||||
|
||||
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetYggdrasilAddress () const
|
||||
{
|
||||
return (*GetAddresses ())[eNTCP2V6MeshIdx];
|
||||
return GetAddress (
|
||||
[](std::shared_ptr<const RouterInfo::Address> address)->bool
|
||||
{
|
||||
return address->IsPublishedNTCP2 () && i2p::util::net::IsYggdrasilAddress (address->host);
|
||||
});
|
||||
}
|
||||
|
||||
std::shared_ptr<RouterProfile> RouterInfo::GetProfile () const
|
||||
@@ -901,29 +985,49 @@ namespace data
|
||||
bool RouterInfo::IsEligibleFloodfill () const
|
||||
{
|
||||
// floodfill must be reachable by ipv4, >= 0.9.38 and not DSA
|
||||
return IsReachableBy (eNTCP2V4 | eSSU2V4) && m_Version >= NETDB_MIN_FLOODFILL_VERSION &&
|
||||
return IsReachableBy (eNTCP2V4 | eSSUV4) && m_Version >= NETDB_MIN_FLOODFILL_VERSION &&
|
||||
GetIdentity ()->GetSigningKeyType () != SIGNING_KEY_TYPE_DSA_SHA1;
|
||||
}
|
||||
|
||||
bool RouterInfo::IsPeerTesting (bool v4) const
|
||||
{
|
||||
if (!(m_SupportedTransports & (v4 ? eSSUV4 : eSSUV6))) return false;
|
||||
return (bool)GetAddress (
|
||||
[v4](std::shared_ptr<const RouterInfo::Address> address)->bool
|
||||
{
|
||||
return (address->transportStyle == eTransportSSU) && address->IsPeerTesting () &&
|
||||
((v4 && address->IsV4 ()) || (!v4 && address->IsV6 ())) && address->IsReachableSSU ();
|
||||
});
|
||||
}
|
||||
|
||||
bool RouterInfo::IsSSU2PeerTesting (bool v4) const
|
||||
{
|
||||
if (!(m_SupportedTransports & (v4 ? eSSU2V4 : eSSU2V6))) return false;
|
||||
auto addr = (*GetAddresses ())[v4 ? eSSU2V4Idx : eSSU2V6Idx];
|
||||
return addr && addr->IsPeerTesting () && addr->IsReachableSSU ();
|
||||
}
|
||||
|
||||
bool RouterInfo::IsSSU2Introducer (bool v4) const
|
||||
return (bool)GetAddress (
|
||||
[v4](std::shared_ptr<const RouterInfo::Address> address)->bool
|
||||
{
|
||||
return (address->IsSSU2 ()) && address->IsPeerTesting () &&
|
||||
((v4 && address->IsV4 ()) || (!v4 && address->IsV6 ())) && address->IsReachableSSU ();
|
||||
});
|
||||
}
|
||||
|
||||
bool RouterInfo::IsIntroducer (bool v4) const
|
||||
{
|
||||
if (!(m_SupportedTransports & (v4 ? eSSU2V4 : eSSU2V6))) return false;
|
||||
auto addr = (*GetAddresses ())[v4 ? eSSU2V4Idx : eSSU2V6Idx];
|
||||
return addr && addr->IsIntroducer () && !addr->host.is_unspecified () && addr->port;
|
||||
if (!(m_SupportedTransports & (v4 ? eSSUV4 : eSSUV6))) return false;
|
||||
return (bool)GetAddress (
|
||||
[v4](std::shared_ptr<const RouterInfo::Address> address)->bool
|
||||
{
|
||||
return (address->transportStyle == eTransportSSU) && address->IsIntroducer () &&
|
||||
((v4 && address->IsV4 ()) || (!v4 && address->IsV6 ())) && !address->host.is_unspecified ();
|
||||
});
|
||||
}
|
||||
|
||||
void RouterInfo::SetUnreachableAddressesTransportCaps (uint8_t transports)
|
||||
{
|
||||
for (auto& addr: *m_Addresses)
|
||||
{
|
||||
if (addr && !addr->published)
|
||||
// TODO: implement SSU
|
||||
if (!addr->published && (addr->transportStyle == eTransportNTCP || addr->transportStyle == eTransportSSU2))
|
||||
{
|
||||
addr->caps &= ~(eV4 | eV6);
|
||||
addr->caps |= transports;
|
||||
@@ -937,24 +1041,21 @@ namespace data
|
||||
m_ReachableTransports = 0;
|
||||
for (const auto& addr: *m_Addresses)
|
||||
{
|
||||
if (!addr) continue;
|
||||
uint8_t transports = 0;
|
||||
switch (addr->transportStyle)
|
||||
if (addr->transportStyle == eTransportNTCP)
|
||||
{
|
||||
case eTransportNTCP2:
|
||||
if (addr->IsV4 ()) transports |= eNTCP2V4;
|
||||
if (addr->IsV6 ())
|
||||
transports |= (i2p::util::net::IsYggdrasilAddress (addr->host) ? eNTCP2V6Mesh : eNTCP2V6);
|
||||
if (addr->IsPublishedNTCP2 ())
|
||||
m_ReachableTransports |= transports;
|
||||
break;
|
||||
case eTransportSSU2:
|
||||
if (addr->IsV4 ()) transports |= eSSU2V4;
|
||||
if (addr->IsV6 ()) transports |= eSSU2V6;
|
||||
if (addr->IsReachableSSU ())
|
||||
m_ReachableTransports |= transports;
|
||||
break;
|
||||
default: ;
|
||||
if (addr->IsV4 ()) transports |= eNTCP2V4;
|
||||
if (addr->IsV6 ())
|
||||
transports |= (i2p::util::net::IsYggdrasilAddress (addr->host) ? eNTCP2V6Mesh : eNTCP2V6);
|
||||
if (addr->IsPublishedNTCP2 ())
|
||||
m_ReachableTransports |= transports;
|
||||
}
|
||||
else if (addr->transportStyle == eTransportSSU)
|
||||
{
|
||||
if (addr->IsV4 ()) transports |= eSSUV4;
|
||||
if (addr->IsV6 ()) transports |= eSSUV6;
|
||||
if (addr->IsReachableSSU ())
|
||||
m_ReachableTransports |= transports;
|
||||
}
|
||||
m_SupportedTransports |= transports;
|
||||
}
|
||||
@@ -974,16 +1075,6 @@ namespace data
|
||||
return netdb.NewRouterInfoBuffer ();
|
||||
}
|
||||
|
||||
std::shared_ptr<RouterInfo::Address> RouterInfo::NewAddress () const
|
||||
{
|
||||
return netdb.NewRouterInfoAddress ();
|
||||
}
|
||||
|
||||
boost::shared_ptr<RouterInfo::Addresses> RouterInfo::NewAddresses () const
|
||||
{
|
||||
return netdb.NewRouterInfoAddresses ();
|
||||
}
|
||||
|
||||
void RouterInfo::RefreshTimestamp ()
|
||||
{
|
||||
m_Timestamp = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
@@ -1045,42 +1136,29 @@ namespace data
|
||||
|
||||
void LocalRouterInfo::WriteToStream (std::ostream& s) const
|
||||
{
|
||||
auto addresses = GetAddresses ();
|
||||
if (!addresses) return;
|
||||
|
||||
uint64_t ts = htobe64 (GetTimestamp ());
|
||||
s.write ((const char *)&ts, sizeof (ts));
|
||||
|
||||
// addresses
|
||||
uint8_t numAddresses = 0;
|
||||
for (size_t idx = 0; idx < addresses->size(); idx++)
|
||||
{
|
||||
auto addr_ptr = (*addresses)[idx];
|
||||
if (!addr_ptr) continue;
|
||||
if (idx == eNTCP2V6Idx && addr_ptr == (*addresses)[eNTCP2V4Idx]) continue;
|
||||
if (idx == eSSU2V6Idx && addr_ptr == (*addresses)[eSSU2V4Idx]) continue;
|
||||
numAddresses++;
|
||||
}
|
||||
const Addresses& addresses = GetAddresses ();
|
||||
uint8_t numAddresses = addresses.size ();
|
||||
s.write ((char *)&numAddresses, sizeof (numAddresses));
|
||||
for (size_t idx = 0; idx < addresses->size(); idx++)
|
||||
for (const auto& addr_ptr : addresses)
|
||||
{
|
||||
auto addr_ptr = (*addresses)[idx];
|
||||
if (!addr_ptr) continue;
|
||||
if (idx == eNTCP2V6Idx && addr_ptr == (*addresses)[eNTCP2V4Idx]) continue;
|
||||
if (idx == eSSU2V6Idx && addr_ptr == (*addresses)[eSSU2V4Idx]) continue;
|
||||
const Address& address = *addr_ptr;
|
||||
// calculate cost
|
||||
uint8_t cost = 0x7f;
|
||||
if (address.transportStyle == eTransportNTCP2)
|
||||
if (address.transportStyle == eTransportNTCP)
|
||||
cost = address.published ? COST_NTCP2_PUBLISHED : COST_NTCP2_NON_PUBLISHED;
|
||||
else if (address.transportStyle == eTransportSSU)
|
||||
cost = address.published ? COST_SSU_DIRECT : COST_SSU_THROUGH_INTRODUCERS;
|
||||
else if (address.transportStyle == eTransportSSU2)
|
||||
cost = address.published ? COST_SSU2_DIRECT : COST_SSU2_NON_PUBLISHED;
|
||||
else
|
||||
continue; // skip unknown address
|
||||
s.write ((const char *)&cost, sizeof (cost));
|
||||
s.write ((const char *)&address.date, sizeof (address.date));
|
||||
std::stringstream properties;
|
||||
bool isPublished = false;
|
||||
if (address.transportStyle == eTransportNTCP2)
|
||||
if (address.transportStyle == eTransportNTCP)
|
||||
{
|
||||
if (address.IsNTCP2 ())
|
||||
{
|
||||
@@ -1102,6 +1180,43 @@ namespace data
|
||||
else
|
||||
continue; // don't write NTCP address
|
||||
}
|
||||
else if (address.transportStyle == eTransportSSU)
|
||||
{
|
||||
WriteString ("SSU", s);
|
||||
// caps
|
||||
WriteString ("caps", properties);
|
||||
properties << '=';
|
||||
std::string caps;
|
||||
if (address.IsPeerTesting ()) caps += CAPS_FLAG_SSU_TESTING;
|
||||
if (address.host.is_v4 ())
|
||||
{
|
||||
if (address.published)
|
||||
{
|
||||
isPublished = true;
|
||||
if (address.IsIntroducer ()) caps += CAPS_FLAG_SSU_INTRODUCER;
|
||||
}
|
||||
else
|
||||
caps += CAPS_FLAG_V4;
|
||||
}
|
||||
else if (address.host.is_v6 ())
|
||||
{
|
||||
if (address.published)
|
||||
{
|
||||
isPublished = true;
|
||||
if (address.IsIntroducer ()) caps += CAPS_FLAG_SSU_INTRODUCER;
|
||||
}
|
||||
else
|
||||
caps += CAPS_FLAG_V6;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (address.IsV4 ()) caps += CAPS_FLAG_V4;
|
||||
if (address.IsV6 ()) caps += CAPS_FLAG_V6;
|
||||
if (caps.empty ()) caps += CAPS_FLAG_V4;
|
||||
}
|
||||
WriteString (caps, properties);
|
||||
properties << ';';
|
||||
}
|
||||
else if (address.transportStyle == eTransportSSU2)
|
||||
{
|
||||
WriteString ("SSU2", s);
|
||||
@@ -1110,8 +1225,7 @@ namespace data
|
||||
if (address.published)
|
||||
{
|
||||
isPublished = true;
|
||||
if (address.IsPeerTesting ()) caps += CAPS_FLAG_SSU2_TESTING;
|
||||
if (address.IsIntroducer ()) caps += CAPS_FLAG_SSU2_INTRODUCER;
|
||||
if (address.IsIntroducer ()) caps += CAPS_FLAG_SSU_INTRODUCER;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1130,7 +1244,7 @@ namespace data
|
||||
else
|
||||
WriteString ("", s);
|
||||
|
||||
if (isPublished && !address.host.is_unspecified ())
|
||||
if (isPublished)
|
||||
{
|
||||
WriteString ("host", properties);
|
||||
properties << '=';
|
||||
@@ -1144,7 +1258,7 @@ namespace data
|
||||
size_t len = address.IsSSU2 () ? 32 : 16;
|
||||
WriteString (address.i.ToBase64 (len), properties); properties << ';';
|
||||
}
|
||||
if (address.transportStyle == eTransportSSU2)
|
||||
if (address.transportStyle == eTransportSSU || address.IsSSU2 ())
|
||||
{
|
||||
// write introducers if any
|
||||
if (address.ssu && !address.ssu->introducers.empty())
|
||||
@@ -1161,18 +1275,45 @@ namespace data
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (address.transportStyle == eTransportSSU)
|
||||
{
|
||||
i = 0;
|
||||
for (const auto& introducer: address.ssu->introducers)
|
||||
{
|
||||
WriteString ("ihost" + boost::lexical_cast<std::string>(i), properties);
|
||||
properties << '=';
|
||||
WriteString (introducer.iHost.to_string (), properties);
|
||||
properties << ';';
|
||||
i++;
|
||||
}
|
||||
}
|
||||
i = 0;
|
||||
for (const auto& introducer: address.ssu->introducers)
|
||||
{
|
||||
WriteString ("ih" + boost::lexical_cast<std::string>(i), properties);
|
||||
if (address.IsSSU2 ())
|
||||
WriteString ("ih" + boost::lexical_cast<std::string>(i), properties);
|
||||
else
|
||||
WriteString ("ikey" + boost::lexical_cast<std::string>(i), properties);
|
||||
properties << '=';
|
||||
char value[64];
|
||||
size_t l = ByteStreamToBase64 (introducer.iH, 32, value, 64);
|
||||
size_t l = ByteStreamToBase64 (introducer.iKey, 32, value, 64);
|
||||
value[l] = 0;
|
||||
WriteString (value, properties);
|
||||
properties << ';';
|
||||
i++;
|
||||
}
|
||||
if (address.transportStyle == eTransportSSU)
|
||||
{
|
||||
i = 0;
|
||||
for (const auto& introducer: address.ssu->introducers)
|
||||
{
|
||||
WriteString ("iport" + boost::lexical_cast<std::string>(i), properties);
|
||||
properties << '=';
|
||||
WriteString (boost::lexical_cast<std::string>(introducer.iPort), properties);
|
||||
properties << ';';
|
||||
i++;
|
||||
}
|
||||
}
|
||||
i = 0;
|
||||
for (const auto& introducer: address.ssu->introducers)
|
||||
{
|
||||
@@ -1184,8 +1325,18 @@ namespace data
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (address.transportStyle == eTransportSSU2)
|
||||
if (address.transportStyle == eTransportSSU)
|
||||
{
|
||||
// write intro key
|
||||
WriteString ("key", properties);
|
||||
properties << '=';
|
||||
char value[64];
|
||||
size_t l = ByteStreamToBase64 (address.i, 32, value, 64);
|
||||
value[l] = 0;
|
||||
WriteString (value, properties);
|
||||
properties << ';';
|
||||
}
|
||||
if (address.transportStyle == eTransportSSU || address.IsSSU2 ())
|
||||
{
|
||||
// write mtu
|
||||
if (address.ssu && address.ssu->mtu)
|
||||
@@ -1196,7 +1347,7 @@ namespace data
|
||||
properties << ';';
|
||||
}
|
||||
}
|
||||
if (isPublished && address.port)
|
||||
if (isPublished || (address.ssu && !address.IsSSU2 ()))
|
||||
{
|
||||
WriteString ("port", properties);
|
||||
properties << '=';
|
||||
@@ -1264,50 +1415,5 @@ namespace data
|
||||
{
|
||||
return std::make_shared<Buffer> ();
|
||||
}
|
||||
|
||||
std::shared_ptr<RouterInfo::Address> LocalRouterInfo::NewAddress () const
|
||||
{
|
||||
return std::make_shared<Address> ();
|
||||
}
|
||||
|
||||
boost::shared_ptr<RouterInfo::Addresses> LocalRouterInfo::NewAddresses () const
|
||||
{
|
||||
return boost::make_shared<Addresses> ();
|
||||
}
|
||||
|
||||
bool LocalRouterInfo::AddSSU2Introducer (const Introducer& introducer, bool v4)
|
||||
{
|
||||
auto addresses = GetAddresses ();
|
||||
if (!addresses) return false;
|
||||
auto addr = (*addresses)[v4 ? eSSU2V4Idx : eSSU2V6Idx];
|
||||
if (addr)
|
||||
{
|
||||
for (auto& intro: addr->ssu->introducers)
|
||||
if (intro.iTag == introducer.iTag) return false; // already presented
|
||||
addr->ssu->introducers.push_back (introducer);
|
||||
SetReachableTransports (GetReachableTransports () | ((addr->IsV4 () ? eSSU2V4 : eSSU2V6)));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LocalRouterInfo::RemoveSSU2Introducer (const IdentHash& h, bool v4)
|
||||
{
|
||||
auto addresses = GetAddresses ();
|
||||
if (!addresses) return false;
|
||||
auto addr = (*addresses)[v4 ? eSSU2V4Idx : eSSU2V6Idx];
|
||||
if (addr)
|
||||
{
|
||||
for (auto it = addr->ssu->introducers.begin (); it != addr->ssu->introducers.end (); ++it)
|
||||
if (h == it->iH)
|
||||
{
|
||||
addr->ssu->introducers.erase (it);
|
||||
if (addr->ssu->introducers.empty ())
|
||||
SetReachableTransports (GetReachableTransports () & ~(addr->IsV4 () ? eSSU2V4 : eSSU2V6));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2023, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -47,12 +47,14 @@ namespace data
|
||||
|
||||
const char CAPS_FLAG_V4 = '4';
|
||||
const char CAPS_FLAG_V6 = '6';
|
||||
const char CAPS_FLAG_SSU2_TESTING = 'B';
|
||||
const char CAPS_FLAG_SSU2_INTRODUCER = 'C';
|
||||
const char CAPS_FLAG_SSU_TESTING = 'B';
|
||||
const char CAPS_FLAG_SSU_INTRODUCER = 'C';
|
||||
|
||||
const uint8_t COST_NTCP2_PUBLISHED = 3;
|
||||
const uint8_t COST_NTCP2_NON_PUBLISHED = 14;
|
||||
const uint8_t COST_SSU2_DIRECT = 8;
|
||||
const uint8_t COST_SSU_DIRECT = 9;
|
||||
const uint8_t COST_SSU_THROUGH_INTRODUCERS = 11;
|
||||
const uint8_t COST_SSU2_NON_PUBLISHED = 15;
|
||||
|
||||
const size_t MAX_RI_BUFFER_SIZE = 3072; // if RouterInfo exceeds 3K we consider it as malformed, might extend later
|
||||
@@ -60,25 +62,15 @@ namespace data
|
||||
{
|
||||
public:
|
||||
|
||||
enum SupportedTransportsIdx
|
||||
{
|
||||
eNTCP2V4Idx = 0,
|
||||
eNTCP2V6Idx,
|
||||
eSSU2V4Idx,
|
||||
eSSU2V6Idx,
|
||||
eNTCP2V6MeshIdx,
|
||||
eNumTransports
|
||||
};
|
||||
|
||||
#define TransportBit(tr) e##tr = (1 << e##tr##Idx)
|
||||
|
||||
enum SupportedTransports
|
||||
{
|
||||
TransportBit(NTCP2V4), // 0x01
|
||||
TransportBit(NTCP2V6), // 0x02
|
||||
TransportBit(SSU2V4), // 0x04
|
||||
TransportBit(SSU2V6), // 0x08
|
||||
TransportBit(NTCP2V6Mesh), // 0x10
|
||||
eNTCP2V4 = 0x01,
|
||||
eNTCP2V6 = 0x02,
|
||||
eSSUV4 = 0x04,
|
||||
eSSUV6 = 0x08,
|
||||
eNTCP2V6Mesh = 0x10,
|
||||
eSSU2V4 = 0x20,
|
||||
eSSU2V6 = 0x40,
|
||||
eAllTransports = 0xFF
|
||||
};
|
||||
typedef uint8_t CompatibleTransports;
|
||||
@@ -104,17 +96,20 @@ namespace data
|
||||
enum TransportStyle
|
||||
{
|
||||
eTransportUnknown = 0,
|
||||
eTransportNTCP2,
|
||||
eTransportNTCP,
|
||||
eTransportSSU,
|
||||
eTransportSSU2
|
||||
};
|
||||
|
||||
typedef Tag<32> IntroKey; // should be castable to MacKey and AESKey
|
||||
struct Introducer
|
||||
{
|
||||
Introducer (): iTag (0), iExp (0), isH (false) {};
|
||||
IdentHash iH;
|
||||
Introducer (): iPort (0), iExp (0) {};
|
||||
boost::asio::ip::address iHost;
|
||||
int iPort;
|
||||
IntroKey iKey; // or ih for SSU2
|
||||
uint32_t iTag;
|
||||
uint32_t iExp;
|
||||
bool isH; // TODO: remove later
|
||||
};
|
||||
|
||||
struct SSUExt
|
||||
@@ -151,7 +146,7 @@ namespace data
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
bool IsNTCP2 () const { return transportStyle == eTransportNTCP2; };
|
||||
bool IsNTCP2 () const { return transportStyle == eTransportNTCP; };
|
||||
bool IsSSU2 () const { return transportStyle == eTransportSSU2; };
|
||||
bool IsPublishedNTCP2 () const { return IsNTCP2 () && published; };
|
||||
bool IsReachableSSU () const { return (bool)ssu && (published || UsesIntroducer ()); };
|
||||
@@ -172,7 +167,7 @@ namespace data
|
||||
Buffer (const uint8_t * buf, size_t len);
|
||||
};
|
||||
|
||||
typedef std::array<std::shared_ptr<Address>, eNumTransports> Addresses;
|
||||
typedef std::vector<std::shared_ptr<Address> > Addresses;
|
||||
|
||||
RouterInfo (const std::string& fullPath);
|
||||
RouterInfo (const RouterInfo& ) = default;
|
||||
@@ -188,32 +183,38 @@ namespace data
|
||||
int GetVersion () const { return m_Version; };
|
||||
virtual void SetProperty (const std::string& key, const std::string& value) {};
|
||||
virtual void ClearProperties () {};
|
||||
boost::shared_ptr<Addresses> GetAddresses () const; // should be called for local RI only, otherwise must return shared_ptr
|
||||
Addresses& GetAddresses () { return *m_Addresses; }; // should be called for local RI only, otherwise must return shared_ptr
|
||||
std::shared_ptr<const Address> GetNTCP2AddressWithStaticKey (const uint8_t * key) const;
|
||||
std::shared_ptr<const Address> GetSSU2AddressWithStaticKey (const uint8_t * key, bool isV6) const;
|
||||
std::shared_ptr<const Address> GetPublishedNTCP2V4Address () const;
|
||||
std::shared_ptr<const Address> GetPublishedNTCP2V6Address () const;
|
||||
std::shared_ptr<const Address> GetSSUAddress (bool v4only = true) const;
|
||||
std::shared_ptr<const Address> GetSSUV6Address () const;
|
||||
std::shared_ptr<const Address> GetYggdrasilAddress () const;
|
||||
std::shared_ptr<const Address> GetSSU2V4Address () const;
|
||||
std::shared_ptr<const Address> GetSSU2V6Address () const;
|
||||
std::shared_ptr<const Address> GetSSU2Address (bool v4) const;
|
||||
|
||||
void AddSSUAddress (const char * host, int port, const uint8_t * key, int mtu = 0);
|
||||
void AddNTCP2Address (const uint8_t * staticKey, const uint8_t * iv,
|
||||
const boost::asio::ip::address& host = boost::asio::ip::address(), int port = 0, uint8_t caps = 0);
|
||||
void AddSSU2Address (const uint8_t * staticKey, const uint8_t * introKey, uint8_t caps = 0); // non published
|
||||
void AddSSU2Address (const uint8_t * staticKey, const uint8_t * introKey,
|
||||
const boost::asio::ip::address& host, int port); // published
|
||||
bool AddIntroducer (const Introducer& introducer);
|
||||
bool RemoveIntroducer (const boost::asio::ip::udp::endpoint& e);
|
||||
void SetUnreachableAddressesTransportCaps (uint8_t transports); // bitmask of AddressCaps
|
||||
void UpdateSupportedTransports ();
|
||||
bool IsFloodfill () const { return m_Caps & Caps::eFloodfill; };
|
||||
bool IsReachable () const { return m_Caps & Caps::eReachable; };
|
||||
bool IsECIES () const { return m_RouterIdentity->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD; };
|
||||
bool IsSSU (bool v4only = true) const;
|
||||
bool IsSSUV6 () const { return m_SupportedTransports & eSSUV6; };
|
||||
bool IsNTCP2 (bool v4only = true) const;
|
||||
bool IsNTCP2V6 () const { return m_SupportedTransports & eNTCP2V6; };
|
||||
bool IsSSU2V4 () const { return m_SupportedTransports & eSSU2V4; };
|
||||
bool IsSSU2V6 () const { return m_SupportedTransports & eSSU2V6; };
|
||||
bool IsV6 () const { return m_SupportedTransports & (eNTCP2V6 | eSSU2V6); };
|
||||
bool IsV4 () const { return m_SupportedTransports & (eNTCP2V4 | eSSU2V4); };
|
||||
bool IsV6 () const { return m_SupportedTransports & (eSSUV6 | eNTCP2V6 | eSSU2V6); };
|
||||
bool IsV4 () const { return m_SupportedTransports & (eSSUV4 | eNTCP2V4 | eSSU2V4); };
|
||||
bool IsMesh () const { return m_SupportedTransports & eNTCP2V6Mesh; };
|
||||
void EnableV6 ();
|
||||
void DisableV6 ();
|
||||
@@ -230,8 +231,9 @@ namespace data
|
||||
bool IsHighBandwidth () const { return m_Caps & RouterInfo::eHighBandwidth; };
|
||||
bool IsExtraBandwidth () const { return m_Caps & RouterInfo::eExtraBandwidth; };
|
||||
bool IsEligibleFloodfill () const;
|
||||
bool IsPeerTesting (bool v4) const;
|
||||
bool IsSSU2PeerTesting (bool v4) const;
|
||||
bool IsSSU2Introducer (bool v4) const;
|
||||
bool IsIntroducer (bool v4) const;
|
||||
|
||||
uint8_t GetCaps () const { return m_Caps; };
|
||||
void SetCaps (uint8_t caps) { m_Caps = caps; };
|
||||
@@ -270,8 +272,7 @@ namespace data
|
||||
void UpdateBuffer (const uint8_t * buf, size_t len);
|
||||
void SetBufferLen (size_t len) { m_BufferLen = len; };
|
||||
void RefreshTimestamp ();
|
||||
CompatibleTransports GetReachableTransports () const { return m_ReachableTransports; };
|
||||
void SetReachableTransports (CompatibleTransports transports) { m_ReachableTransports = transports; };
|
||||
const Addresses& GetAddresses () const { return *m_Addresses; };
|
||||
|
||||
private:
|
||||
|
||||
@@ -285,8 +286,6 @@ namespace data
|
||||
template<typename Filter>
|
||||
std::shared_ptr<const Address> GetAddress (Filter filter) const;
|
||||
virtual std::shared_ptr<Buffer> NewBuffer () const;
|
||||
virtual std::shared_ptr<Address> NewAddress () const;
|
||||
virtual boost::shared_ptr<Addresses> NewAddresses () const;
|
||||
|
||||
private:
|
||||
|
||||
@@ -308,7 +307,6 @@ namespace data
|
||||
public:
|
||||
|
||||
LocalRouterInfo () = default;
|
||||
LocalRouterInfo (const std::string& fullPath): RouterInfo (fullPath) {};
|
||||
void CreateBuffer (const PrivateKeys& privateKeys);
|
||||
void UpdateCaps (uint8_t caps);
|
||||
|
||||
@@ -317,17 +315,12 @@ 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;
|
||||
void UpdateCapsProperty ();
|
||||
void WriteString (const std::string& str, std::ostream& s) const;
|
||||
std::shared_ptr<Buffer> NewBuffer () const override;
|
||||
std::shared_ptr<Address> NewAddress () const override;
|
||||
boost::shared_ptr<Addresses> NewAddresses () const override;
|
||||
|
||||
private:
|
||||
|
||||
|
||||
996
libi2pd/SSU.cpp
Normal file
996
libi2pd/SSU.cpp
Normal file
@@ -0,0 +1,996 @@
|
||||
/*
|
||||
* 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 <string.h>
|
||||
#include "Log.h"
|
||||
#include "Timestamp.h"
|
||||
#include "RouterContext.h"
|
||||
#include "NetDb.hpp"
|
||||
#include "Config.h"
|
||||
#include "util.h"
|
||||
#include "SSU.h"
|
||||
|
||||
#if defined(__linux__) && !defined(_NETINET_IN_H)
|
||||
#include <linux/in6.h>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <boost/winapi/error_codes.hpp>
|
||||
#endif
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace transport
|
||||
{
|
||||
SSUServer::SSUServer (int port):
|
||||
m_IsRunning(false), m_Thread (nullptr),
|
||||
m_ReceiversThread (nullptr), m_ReceiversThreadV6 (nullptr), m_Work (m_Service),
|
||||
m_ReceiversWork (m_ReceiversService), m_ReceiversWorkV6 (m_ReceiversServiceV6),
|
||||
m_Endpoint (boost::asio::ip::udp::v4 (), port), m_EndpointV6 (boost::asio::ip::udp::v6 (), port),
|
||||
m_Socket (m_ReceiversService), m_SocketV6 (m_ReceiversServiceV6),
|
||||
m_IntroducersUpdateTimer (m_Service), m_IntroducersUpdateTimerV6 (m_Service),
|
||||
m_PeerTestsCleanupTimer (m_Service), m_TerminationTimer (m_Service), m_TerminationTimerV6 (m_Service),
|
||||
m_IsSyncClockFromPeers (true)
|
||||
{
|
||||
}
|
||||
|
||||
SSUServer::~SSUServer ()
|
||||
{
|
||||
}
|
||||
|
||||
void SSUServer::OpenSocket ()
|
||||
{
|
||||
try
|
||||
{
|
||||
m_Socket.open (boost::asio::ip::udp::v4());
|
||||
m_Socket.set_option (boost::asio::socket_base::receive_buffer_size (SSU_SOCKET_RECEIVE_BUFFER_SIZE));
|
||||
m_Socket.set_option (boost::asio::socket_base::send_buffer_size (SSU_SOCKET_SEND_BUFFER_SIZE));
|
||||
m_Socket.bind (m_Endpoint);
|
||||
LogPrint (eLogInfo, "SSU: Start listening v4 port ", m_Endpoint.port());
|
||||
}
|
||||
catch ( std::exception & ex )
|
||||
{
|
||||
LogPrint (eLogError, "SSU: Failed to bind to v4 port ", m_Endpoint.port(), ": ", ex.what());
|
||||
ThrowFatal ("Unable to start IPv4 SSU transport at port ", m_Endpoint.port(), ": ", ex.what ());
|
||||
}
|
||||
}
|
||||
|
||||
void SSUServer::OpenSocketV6 ()
|
||||
{
|
||||
try
|
||||
{
|
||||
m_SocketV6.open (boost::asio::ip::udp::v6());
|
||||
m_SocketV6.set_option (boost::asio::ip::v6_only (true));
|
||||
m_SocketV6.set_option (boost::asio::socket_base::receive_buffer_size (SSU_SOCKET_RECEIVE_BUFFER_SIZE));
|
||||
m_SocketV6.set_option (boost::asio::socket_base::send_buffer_size (SSU_SOCKET_SEND_BUFFER_SIZE));
|
||||
#if defined(__linux__) && !defined(_NETINET_IN_H)
|
||||
if (m_EndpointV6.address() == boost::asio::ip::address().from_string("::")) // only if not binded to address
|
||||
{
|
||||
// Set preference to use public IPv6 address -- tested on linux, not works on windows, and not tested on others
|
||||
#if (BOOST_VERSION >= 105500)
|
||||
typedef boost::asio::detail::socket_option::integer<BOOST_ASIO_OS_DEF(IPPROTO_IPV6), IPV6_ADDR_PREFERENCES> ipv6PreferAddr;
|
||||
#else
|
||||
typedef boost::asio::detail::socket_option::integer<IPPROTO_IPV6, IPV6_ADDR_PREFERENCES> ipv6PreferAddr;
|
||||
#endif
|
||||
m_SocketV6.set_option (ipv6PreferAddr(IPV6_PREFER_SRC_PUBLIC | IPV6_PREFER_SRC_HOME | IPV6_PREFER_SRC_NONCGA));
|
||||
}
|
||||
#endif
|
||||
m_SocketV6.bind (m_EndpointV6);
|
||||
LogPrint (eLogInfo, "SSU: Start listening v6 port ", m_EndpointV6.port());
|
||||
}
|
||||
catch ( std::exception & ex )
|
||||
{
|
||||
LogPrint (eLogError, "SSU: Failed to bind to v6 port ", m_EndpointV6.port(), ": ", ex.what());
|
||||
ThrowFatal ("Unable to start IPv6 SSU transport at port ", m_Endpoint.port(), ": ", ex.what ());
|
||||
}
|
||||
}
|
||||
|
||||
void SSUServer::Start ()
|
||||
{
|
||||
i2p::config::GetOption("nettime.frompeers", m_IsSyncClockFromPeers);
|
||||
m_IsRunning = true;
|
||||
m_Thread = new std::thread (std::bind (&SSUServer::Run, this));
|
||||
if (context.SupportsV4 ())
|
||||
{
|
||||
OpenSocket ();
|
||||
m_ReceiversThread = new std::thread (std::bind (&SSUServer::RunReceivers, this));
|
||||
m_ReceiversService.post (std::bind (&SSUServer::Receive, this));
|
||||
ScheduleTermination ();
|
||||
ScheduleIntroducersUpdateTimer (); // wait for 30 seconds and decide if we need introducers
|
||||
}
|
||||
if (context.SupportsV6 ())
|
||||
{
|
||||
OpenSocketV6 ();
|
||||
m_ReceiversThreadV6 = new std::thread (std::bind (&SSUServer::RunReceiversV6, this));
|
||||
m_ReceiversServiceV6.post (std::bind (&SSUServer::ReceiveV6, this));
|
||||
ScheduleTerminationV6 ();
|
||||
ScheduleIntroducersUpdateTimerV6 (); // wait for 30 seconds and decide if we need introducers
|
||||
}
|
||||
SchedulePeerTestsCleanupTimer ();
|
||||
}
|
||||
|
||||
void SSUServer::Stop ()
|
||||
{
|
||||
DeleteAllSessions ();
|
||||
m_IsRunning = false;
|
||||
m_TerminationTimer.cancel ();
|
||||
m_TerminationTimerV6.cancel ();
|
||||
m_IntroducersUpdateTimer.cancel ();
|
||||
m_IntroducersUpdateTimerV6.cancel ();
|
||||
m_Service.stop ();
|
||||
m_Socket.close ();
|
||||
m_SocketV6.close ();
|
||||
m_ReceiversService.stop ();
|
||||
m_ReceiversServiceV6.stop ();
|
||||
if (m_ReceiversThread)
|
||||
{
|
||||
m_ReceiversThread->join ();
|
||||
delete m_ReceiversThread;
|
||||
m_ReceiversThread = nullptr;
|
||||
}
|
||||
if (m_ReceiversThreadV6)
|
||||
{
|
||||
m_ReceiversThreadV6->join ();
|
||||
delete m_ReceiversThreadV6;
|
||||
m_ReceiversThreadV6 = nullptr;
|
||||
}
|
||||
if (m_Thread)
|
||||
{
|
||||
m_Thread->join ();
|
||||
delete m_Thread;
|
||||
m_Thread = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void SSUServer::Run ()
|
||||
{
|
||||
i2p::util::SetThreadName("SSU");
|
||||
|
||||
while (m_IsRunning)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_Service.run ();
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint (eLogError, "SSU: Server runtime exception: ", ex.what ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SSUServer::RunReceivers ()
|
||||
{
|
||||
i2p::util::SetThreadName("SSUv4");
|
||||
|
||||
while (m_IsRunning)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_ReceiversService.run ();
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint (eLogError, "SSU: Receivers runtime exception: ", ex.what ());
|
||||
if (m_IsRunning)
|
||||
{
|
||||
// restart socket
|
||||
m_Socket.close ();
|
||||
OpenSocket ();
|
||||
Receive ();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SSUServer::RunReceiversV6 ()
|
||||
{
|
||||
i2p::util::SetThreadName("SSUv6");
|
||||
|
||||
while (m_IsRunning)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_ReceiversServiceV6.run ();
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint (eLogError, "SSU: v6 receivers runtime exception: ", ex.what ());
|
||||
if (m_IsRunning)
|
||||
{
|
||||
m_SocketV6.close ();
|
||||
OpenSocketV6 ();
|
||||
ReceiveV6 ();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SSUServer::SetLocalAddress (const boost::asio::ip::address& localAddress)
|
||||
{
|
||||
if (localAddress.is_v6 ())
|
||||
m_EndpointV6.address (localAddress);
|
||||
else if (localAddress.is_v4 ())
|
||||
m_Endpoint.address (localAddress);
|
||||
}
|
||||
|
||||
void SSUServer::AddRelay (uint32_t tag, std::shared_ptr<SSUSession> relay)
|
||||
{
|
||||
m_Relays.emplace (tag, relay);
|
||||
}
|
||||
|
||||
void SSUServer::RemoveRelay (uint32_t tag)
|
||||
{
|
||||
m_Relays.erase (tag);
|
||||
}
|
||||
|
||||
std::shared_ptr<SSUSession> SSUServer::FindRelaySession (uint32_t tag)
|
||||
{
|
||||
auto it = m_Relays.find (tag);
|
||||
if (it != m_Relays.end ())
|
||||
{
|
||||
if (it->second->GetState () == eSessionStateEstablished)
|
||||
return it->second;
|
||||
else
|
||||
m_Relays.erase (it);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void SSUServer::Send (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& to)
|
||||
{
|
||||
boost::system::error_code ec;
|
||||
if (to.protocol () == boost::asio::ip::udp::v4())
|
||||
m_Socket.send_to (boost::asio::buffer (buf, len), to, 0, ec);
|
||||
else
|
||||
m_SocketV6.send_to (boost::asio::buffer (buf, len), to, 0, ec);
|
||||
|
||||
if (ec)
|
||||
{
|
||||
LogPrint (eLogError, "SSU: Send exception: ", ec.message (), " while trying to send data to ", to.address (), ":", to.port (), " (length: ", len, ")");
|
||||
}
|
||||
}
|
||||
|
||||
void SSUServer::Receive ()
|
||||
{
|
||||
SSUPacket * packet = m_PacketsPool.AcquireMt ();
|
||||
m_Socket.async_receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V4), packet->from,
|
||||
std::bind (&SSUServer::HandleReceivedFrom, this, std::placeholders::_1, std::placeholders::_2, packet));
|
||||
}
|
||||
|
||||
void SSUServer::ReceiveV6 ()
|
||||
{
|
||||
SSUPacket * packet = m_PacketsPool.AcquireMt ();
|
||||
m_SocketV6.async_receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V6), packet->from,
|
||||
std::bind (&SSUServer::HandleReceivedFromV6, this, std::placeholders::_1, std::placeholders::_2, packet));
|
||||
}
|
||||
|
||||
void SSUServer::HandleReceivedFrom (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet)
|
||||
{
|
||||
if (!ecode
|
||||
|| ecode == boost::asio::error::connection_refused
|
||||
|| ecode == boost::asio::error::connection_reset
|
||||
|| ecode == boost::asio::error::network_unreachable
|
||||
|| ecode == boost::asio::error::host_unreachable
|
||||
#ifdef _WIN32 // windows can throw WinAPI error, which is not handled by ASIO
|
||||
|| ecode.value() == boost::winapi::ERROR_CONNECTION_REFUSED_
|
||||
|| ecode.value() == boost::winapi::ERROR_NETWORK_UNREACHABLE_
|
||||
|| ecode.value() == boost::winapi::ERROR_HOST_UNREACHABLE_
|
||||
#endif
|
||||
)
|
||||
// just try continue reading when received ICMP response otherwise socket can crash,
|
||||
// but better to find out which host were sent it and mark that router as unreachable
|
||||
{
|
||||
packet->len = bytes_transferred;
|
||||
std::vector<SSUPacket *> packets;
|
||||
packets.push_back (packet);
|
||||
|
||||
boost::system::error_code ec;
|
||||
size_t moreBytes = m_Socket.available(ec);
|
||||
if (!ec)
|
||||
{
|
||||
while (moreBytes && packets.size () < 25)
|
||||
{
|
||||
packet = m_PacketsPool.AcquireMt ();
|
||||
packet->len = m_Socket.receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V4), packet->from, 0, ec);
|
||||
if (!ec)
|
||||
{
|
||||
packets.push_back (packet);
|
||||
moreBytes = m_Socket.available(ec);
|
||||
if (ec) break;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "SSU: receive_from error: code ", ec.value(), ": ", ec.message ());
|
||||
m_PacketsPool.ReleaseMt (packet);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_Service.post (std::bind (&SSUServer::HandleReceivedPackets, this, packets, &m_Sessions));
|
||||
Receive ();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_PacketsPool.ReleaseMt (packet);
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
LogPrint (eLogError, "SSU: Receive error: code ", ecode.value(), ": ", ecode.message ());
|
||||
m_Socket.close ();
|
||||
OpenSocket ();
|
||||
Receive ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SSUServer::HandleReceivedFromV6 (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet)
|
||||
{
|
||||
if (!ecode
|
||||
|| ecode == boost::asio::error::connection_refused
|
||||
|| ecode == boost::asio::error::connection_reset
|
||||
|| ecode == boost::asio::error::network_unreachable
|
||||
|| ecode == boost::asio::error::host_unreachable
|
||||
#ifdef _WIN32 // windows can throw WinAPI error, which is not handled by ASIO
|
||||
|| ecode.value() == boost::winapi::ERROR_CONNECTION_REFUSED_
|
||||
|| ecode.value() == boost::winapi::ERROR_NETWORK_UNREACHABLE_
|
||||
|| ecode.value() == boost::winapi::ERROR_HOST_UNREACHABLE_
|
||||
#endif
|
||||
)
|
||||
// just try continue reading when received ICMP response otherwise socket can crash,
|
||||
// but better to find out which host were sent it and mark that router as unreachable
|
||||
{
|
||||
packet->len = bytes_transferred;
|
||||
std::vector<SSUPacket *> packets;
|
||||
packets.push_back (packet);
|
||||
|
||||
boost::system::error_code ec;
|
||||
size_t moreBytes = m_SocketV6.available (ec);
|
||||
if (!ec)
|
||||
{
|
||||
while (moreBytes && packets.size () < 25)
|
||||
{
|
||||
packet = m_PacketsPool.AcquireMt ();
|
||||
packet->len = m_SocketV6.receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V6), packet->from, 0, ec);
|
||||
if (!ec)
|
||||
{
|
||||
packets.push_back (packet);
|
||||
moreBytes = m_SocketV6.available(ec);
|
||||
if (ec) break;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "SSU: v6 receive_from error: code ", ec.value(), ": ", ec.message ());
|
||||
m_PacketsPool.ReleaseMt (packet);;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_Service.post (std::bind (&SSUServer::HandleReceivedPackets, this, packets, &m_SessionsV6));
|
||||
ReceiveV6 ();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_PacketsPool.ReleaseMt (packet);
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
LogPrint (eLogError, "SSU: v6 receive error: code ", ecode.value(), ": ", ecode.message ());
|
||||
m_SocketV6.close ();
|
||||
OpenSocketV6 ();
|
||||
ReceiveV6 ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SSUServer::HandleReceivedPackets (std::vector<SSUPacket *> packets,
|
||||
std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<SSUSession> > * sessions)
|
||||
{
|
||||
if (!m_IsRunning) return;
|
||||
std::shared_ptr<SSUSession> session;
|
||||
for (auto& packet: packets)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!session || session->GetRemoteEndpoint () != packet->from) // we received packet for other session than previous
|
||||
{
|
||||
if (session)
|
||||
{
|
||||
session->FlushData ();
|
||||
session = nullptr;
|
||||
}
|
||||
auto it = sessions->find (packet->from);
|
||||
if (it != sessions->end ())
|
||||
session = it->second;
|
||||
if (!session && packet->len > 0)
|
||||
{
|
||||
session = std::make_shared<SSUSession> (*this, packet->from);
|
||||
session->WaitForConnect ();
|
||||
(*sessions)[packet->from] = session;
|
||||
LogPrint (eLogDebug, "SSU: New session from ", packet->from.address ().to_string (), ":", packet->from.port (), " created");
|
||||
}
|
||||
}
|
||||
if (session)
|
||||
session->ProcessNextMessage (packet->buf, packet->len, packet->from);
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint (eLogError, "SSU: HandleReceivedPackets ", ex.what ());
|
||||
if (session) session->FlushData ();
|
||||
session = nullptr;
|
||||
}
|
||||
}
|
||||
m_PacketsPool.ReleaseMt (packets);
|
||||
if (session) session->FlushData ();
|
||||
}
|
||||
|
||||
std::shared_ptr<SSUSession> SSUServer::FindSession (const boost::asio::ip::udp::endpoint& e) const
|
||||
{
|
||||
auto& sessions = e.address ().is_v6 () ? m_SessionsV6 : m_Sessions;
|
||||
auto it = sessions.find (e);
|
||||
if (it != sessions.end ())
|
||||
return it->second;
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool SSUServer::CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest, bool v4only)
|
||||
{
|
||||
auto address = router->GetSSUAddress (v4only || !context.SupportsV6 ());
|
||||
if (address)
|
||||
return CreateSession (router, address, peerTest);
|
||||
else
|
||||
LogPrint (eLogWarning, "SSU: Router ", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()), " doesn't have SSU address");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SSUServer::CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router,
|
||||
std::shared_ptr<const i2p::data::RouterInfo::Address> address, bool peerTest)
|
||||
{
|
||||
if (router && address)
|
||||
{
|
||||
if (address->UsesIntroducer ())
|
||||
m_Service.post (std::bind (&SSUServer::CreateSessionThroughIntroducer, this, router, address, peerTest)); // always V4 thread
|
||||
else
|
||||
{
|
||||
if (address->host.is_unspecified () || !address->port) return false;
|
||||
boost::asio::ip::udp::endpoint remoteEndpoint (address->host, address->port);
|
||||
m_Service.post (std::bind (&SSUServer::CreateDirectSession, this, router, remoteEndpoint, peerTest));
|
||||
}
|
||||
}
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void SSUServer::CreateDirectSession (std::shared_ptr<const i2p::data::RouterInfo> router, boost::asio::ip::udp::endpoint remoteEndpoint, bool peerTest)
|
||||
{
|
||||
auto& sessions = remoteEndpoint.address ().is_v6 () ? m_SessionsV6 : m_Sessions;
|
||||
auto it = sessions.find (remoteEndpoint);
|
||||
if (it != sessions.end ())
|
||||
{
|
||||
auto session = it->second;
|
||||
if (peerTest && session->GetState () == eSessionStateEstablished)
|
||||
session->SendPeerTest ();
|
||||
}
|
||||
else
|
||||
{
|
||||
// otherwise create new session
|
||||
auto session = std::make_shared<SSUSession> (*this, remoteEndpoint, router, peerTest);
|
||||
sessions[remoteEndpoint] = session;
|
||||
|
||||
// connect
|
||||
LogPrint (eLogDebug, "SSU: Creating new session to [", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()), "] ",
|
||||
remoteEndpoint.address ().to_string (), ":", remoteEndpoint.port ());
|
||||
session->Connect ();
|
||||
}
|
||||
}
|
||||
|
||||
void SSUServer::CreateSessionThroughIntroducer (std::shared_ptr<const i2p::data::RouterInfo> router,
|
||||
std::shared_ptr<const i2p::data::RouterInfo::Address> address, bool peerTest)
|
||||
{
|
||||
if (router && address && address->UsesIntroducer ())
|
||||
{
|
||||
if (address->IsV4 () && !i2p::context.SupportsV4 ()) return;
|
||||
if (address->IsV6 () && !i2p::context.SupportsV6 ()) return;
|
||||
if (!address->host.is_unspecified () && address->port)
|
||||
{
|
||||
// we rarely come here
|
||||
auto& sessions = address->host.is_v6 () ? m_SessionsV6 : m_Sessions;
|
||||
boost::asio::ip::udp::endpoint remoteEndpoint (address->host, address->port);
|
||||
auto it = sessions.find (remoteEndpoint);
|
||||
// check if session is presented already
|
||||
if (it != sessions.end ())
|
||||
{
|
||||
auto session = it->second;
|
||||
if (peerTest && session->GetState () == eSessionStateEstablished)
|
||||
session->SendPeerTest ();
|
||||
return;
|
||||
}
|
||||
}
|
||||
// create new session
|
||||
int numIntroducers = address->ssu->introducers.size ();
|
||||
if (numIntroducers > 0)
|
||||
{
|
||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
std::shared_ptr<SSUSession> introducerSession;
|
||||
const i2p::data::RouterInfo::Introducer * introducer = nullptr;
|
||||
// we might have a session to introducer already
|
||||
auto offset = rand ();
|
||||
for (int i = 0; i < numIntroducers; i++)
|
||||
{
|
||||
auto intr = &(address->ssu->introducers[(offset + i)%numIntroducers]);
|
||||
if (!intr->iPort) continue; // skip invalid introducer
|
||||
if (intr->iExp > 0 && ts > intr->iExp) continue; // skip expired introducer
|
||||
boost::asio::ip::udp::endpoint ep (intr->iHost, intr->iPort);
|
||||
if (ep.address ().is_v4 () && address->IsV4 ()) // ipv4
|
||||
{
|
||||
if (!introducer) introducer = intr;
|
||||
auto it = m_Sessions.find (ep);
|
||||
if (it != m_Sessions.end ())
|
||||
{
|
||||
introducerSession = it->second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ep.address ().is_v6 () && address->IsV6 ()) // ipv6
|
||||
{
|
||||
if (!introducer) introducer = intr;
|
||||
auto it = m_SessionsV6.find (ep);
|
||||
if (it != m_SessionsV6.end ())
|
||||
{
|
||||
introducerSession = it->second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!introducer)
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU: Can't connect to unreachable router and no compatibe non-expired introducers presented");
|
||||
return;
|
||||
}
|
||||
|
||||
if (introducerSession) // session found
|
||||
LogPrint (eLogWarning, "SSU: Session to introducer already exists");
|
||||
else // create new
|
||||
{
|
||||
LogPrint (eLogDebug, "SSU: Creating new session to introducer ", introducer->iHost);
|
||||
boost::asio::ip::udp::endpoint introducerEndpoint (introducer->iHost, introducer->iPort);
|
||||
introducerSession = std::make_shared<SSUSession> (*this, introducerEndpoint, router);
|
||||
if (introducerEndpoint.address ().is_v4 ())
|
||||
m_Sessions[introducerEndpoint] = introducerSession;
|
||||
else if (introducerEndpoint.address ().is_v6 ())
|
||||
m_SessionsV6[introducerEndpoint] = introducerSession;
|
||||
}
|
||||
if (!address->host.is_unspecified () && address->port)
|
||||
{
|
||||
// create session
|
||||
boost::asio::ip::udp::endpoint remoteEndpoint (address->host, address->port);
|
||||
auto session = std::make_shared<SSUSession> (*this, remoteEndpoint, router, peerTest);
|
||||
if (address->host.is_v4 ())
|
||||
m_Sessions[remoteEndpoint] = session;
|
||||
else if (address->host.is_v6 ())
|
||||
m_SessionsV6[remoteEndpoint] = session;
|
||||
|
||||
// introduce
|
||||
LogPrint (eLogInfo, "SSU: Introduce new session to [", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()),
|
||||
"] through introducer ", introducer->iHost, ":", introducer->iPort);
|
||||
session->WaitForIntroduction ();
|
||||
if ((address->host.is_v4 () && i2p::context.GetStatus () == eRouterStatusFirewalled) ||
|
||||
(address->host.is_v6 () && i2p::context.GetStatusV6 () == eRouterStatusFirewalled))
|
||||
{
|
||||
uint8_t buf[1];
|
||||
Send (buf, 0, remoteEndpoint); // send HolePunch
|
||||
}
|
||||
}
|
||||
introducerSession->Introduce (*introducer, router);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "SSU: Can't connect to unreachable router and no introducers present");
|
||||
}
|
||||
}
|
||||
|
||||
void SSUServer::DeleteSession (std::shared_ptr<SSUSession> session)
|
||||
{
|
||||
if (session)
|
||||
{
|
||||
session->Close ();
|
||||
auto& ep = session->GetRemoteEndpoint ();
|
||||
if (ep.address ().is_v6 ())
|
||||
m_SessionsV6.erase (ep);
|
||||
else
|
||||
m_Sessions.erase (ep);
|
||||
}
|
||||
}
|
||||
|
||||
void SSUServer::DeleteAllSessions ()
|
||||
{
|
||||
for (auto& it: m_Sessions)
|
||||
it.second->Close ();
|
||||
m_Sessions.clear ();
|
||||
|
||||
for (auto& it: m_SessionsV6)
|
||||
it.second->Close ();
|
||||
m_SessionsV6.clear ();
|
||||
}
|
||||
|
||||
template<typename Filter>
|
||||
std::shared_ptr<SSUSession> SSUServer::GetRandomV4Session (Filter filter) // v4 only
|
||||
{
|
||||
std::vector<std::shared_ptr<SSUSession> > filteredSessions;
|
||||
for (const auto& s :m_Sessions)
|
||||
if (filter (s.second)) filteredSessions.push_back (s.second);
|
||||
if (filteredSessions.size () > 0)
|
||||
{
|
||||
auto ind = rand () % filteredSessions.size ();
|
||||
return filteredSessions[ind];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<SSUSession> SSUServer::GetRandomEstablishedV4Session (std::shared_ptr<const SSUSession> excluded) // v4 only
|
||||
{
|
||||
return GetRandomV4Session (
|
||||
[excluded](std::shared_ptr<SSUSession> session)->bool
|
||||
{
|
||||
return session->GetState () == eSessionStateEstablished && session != excluded;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
template<typename Filter>
|
||||
std::shared_ptr<SSUSession> SSUServer::GetRandomV6Session (Filter filter) // v6 only
|
||||
{
|
||||
std::vector<std::shared_ptr<SSUSession> > filteredSessions;
|
||||
for (const auto& s :m_SessionsV6)
|
||||
if (filter (s.second)) filteredSessions.push_back (s.second);
|
||||
if (filteredSessions.size () > 0)
|
||||
{
|
||||
auto ind = rand () % filteredSessions.size ();
|
||||
return filteredSessions[ind];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<SSUSession> SSUServer::GetRandomEstablishedV6Session (std::shared_ptr<const SSUSession> excluded) // v6 only
|
||||
{
|
||||
return GetRandomV6Session (
|
||||
[excluded](std::shared_ptr<SSUSession> session)->bool
|
||||
{
|
||||
return session->GetState () == eSessionStateEstablished && session != excluded;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
std::list<std::shared_ptr<SSUSession> > SSUServer::FindIntroducers (int maxNumIntroducers,
|
||||
bool v4, std::set<i2p::data::IdentHash>& excluded)
|
||||
{
|
||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
std::list<std::shared_ptr<SSUSession> > ret;
|
||||
const auto& sessions = v4 ? m_Sessions : m_SessionsV6;
|
||||
for (const auto& s : sessions)
|
||||
{
|
||||
if (s.second->GetRelayTag () && s.second->GetState () == eSessionStateEstablished &&
|
||||
ts < s.second->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_EXPIRATION)
|
||||
ret.push_back (s.second);
|
||||
else if (s.second->GetRemoteIdentity ())
|
||||
excluded.insert (s.second->GetRemoteIdentity ()->GetIdentHash ());
|
||||
}
|
||||
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 SSUServer::RescheduleIntroducersUpdateTimer ()
|
||||
{
|
||||
m_IntroducersUpdateTimer.cancel ();
|
||||
m_IntroducersUpdateTimer.expires_from_now (boost::posix_time::seconds(SSU_KEEP_ALIVE_INTERVAL/2));
|
||||
m_IntroducersUpdateTimer.async_wait (std::bind (&SSUServer::HandleIntroducersUpdateTimer,
|
||||
this, std::placeholders::_1, true));
|
||||
}
|
||||
|
||||
void SSUServer::ScheduleIntroducersUpdateTimer ()
|
||||
{
|
||||
m_IntroducersUpdateTimer.expires_from_now (boost::posix_time::seconds(SSU_KEEP_ALIVE_INTERVAL));
|
||||
m_IntroducersUpdateTimer.async_wait (std::bind (&SSUServer::HandleIntroducersUpdateTimer,
|
||||
this, std::placeholders::_1, true));
|
||||
}
|
||||
|
||||
void SSUServer::RescheduleIntroducersUpdateTimerV6 ()
|
||||
{
|
||||
m_IntroducersUpdateTimerV6.cancel ();
|
||||
m_IntroducersUpdateTimerV6.expires_from_now (boost::posix_time::seconds(SSU_KEEP_ALIVE_INTERVAL/2));
|
||||
m_IntroducersUpdateTimerV6.async_wait (std::bind (&SSUServer::HandleIntroducersUpdateTimer,
|
||||
this, std::placeholders::_1, false));
|
||||
}
|
||||
|
||||
void SSUServer::ScheduleIntroducersUpdateTimerV6 ()
|
||||
{
|
||||
m_IntroducersUpdateTimerV6.expires_from_now (boost::posix_time::seconds(SSU_KEEP_ALIVE_INTERVAL));
|
||||
m_IntroducersUpdateTimerV6.async_wait (std::bind (&SSUServer::HandleIntroducersUpdateTimer,
|
||||
this, std::placeholders::_1, false));
|
||||
}
|
||||
|
||||
void SSUServer::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
|
||||
m_Introducers.clear ();
|
||||
return;
|
||||
}
|
||||
// we are firewalled
|
||||
if (!i2p::context.IsUnreachable ()) i2p::context.SetUnreachable (true, false); // v4
|
||||
}
|
||||
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
|
||||
m_IntroducersV6.clear ();
|
||||
return;
|
||||
}
|
||||
// we are firewalled
|
||||
auto addr = i2p::context.GetRouterInfo ().GetSSUV6Address ();
|
||||
if (addr && addr->ssu && addr->ssu->introducers.empty ())
|
||||
i2p::context.SetUnreachable (false, true); // v6
|
||||
}
|
||||
|
||||
std::list<boost::asio::ip::udp::endpoint> newList;
|
||||
size_t numIntroducers = 0;
|
||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
std::set<i2p::data::IdentHash> excluded;
|
||||
auto& introducers = v4 ? m_Introducers : m_IntroducersV6;
|
||||
for (const auto& it : introducers)
|
||||
{
|
||||
auto session = FindSession (it);
|
||||
if (session)
|
||||
{
|
||||
if (ts < session->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_EXPIRATION)
|
||||
session->SendKeepAlive ();
|
||||
if (ts < session->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_DURATION)
|
||||
{
|
||||
newList.push_back (it);
|
||||
numIntroducers++;
|
||||
if (session->GetRemoteIdentity ())
|
||||
excluded.insert (session->GetRemoteIdentity ()->GetIdentHash ());
|
||||
}
|
||||
else
|
||||
session = nullptr;
|
||||
}
|
||||
if (!session)
|
||||
i2p::context.RemoveIntroducer (it);
|
||||
}
|
||||
if (numIntroducers < SSU_MAX_NUM_INTRODUCERS)
|
||||
{
|
||||
// create new
|
||||
auto sessions = FindIntroducers (SSU_MAX_NUM_INTRODUCERS, v4, excluded); // try to find if duplicates
|
||||
if (sessions.empty () && !introducers.empty ())
|
||||
{
|
||||
// bump creation time for previous introducers if no new sessions found
|
||||
LogPrint (eLogDebug, "SSU: No new introducers found. Trying to reuse existing");
|
||||
for (const auto& it : introducers)
|
||||
{
|
||||
auto session = FindSession (it);
|
||||
if (session)
|
||||
session->SetCreationTime (session->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_DURATION);
|
||||
}
|
||||
// try again
|
||||
excluded.clear ();
|
||||
sessions = FindIntroducers (SSU_MAX_NUM_INTRODUCERS, v4, excluded);
|
||||
}
|
||||
for (const auto& it1: sessions)
|
||||
{
|
||||
const auto& ep = it1->GetRemoteEndpoint ();
|
||||
i2p::data::RouterInfo::Introducer introducer;
|
||||
introducer.iHost = ep.address ();
|
||||
introducer.iPort = ep.port ();
|
||||
introducer.iTag = it1->GetRelayTag ();
|
||||
introducer.iKey = it1->GetIntroKey ();
|
||||
introducer.iExp = it1->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_EXPIRATION;
|
||||
if (i2p::context.AddIntroducer (introducer))
|
||||
{
|
||||
newList.push_back (ep);
|
||||
if (newList.size () >= SSU_MAX_NUM_INTRODUCERS) break;
|
||||
}
|
||||
if (it1->GetRemoteIdentity ())
|
||||
excluded.insert (it1->GetRemoteIdentity ()->GetIdentHash ());
|
||||
}
|
||||
}
|
||||
introducers = newList;
|
||||
if (introducers.size () < SSU_MAX_NUM_INTRODUCERS)
|
||||
{
|
||||
for (auto i = introducers.size (); i < SSU_MAX_NUM_INTRODUCERS; i++)
|
||||
{
|
||||
auto introducer = i2p::data::netdb.GetRandomIntroducer (v4, excluded);
|
||||
if (introducer)
|
||||
{
|
||||
auto address = v4 ? introducer->GetSSUAddress (true) : introducer->GetSSUV6Address ();
|
||||
if (address && !address->host.is_unspecified () && address->port)
|
||||
{
|
||||
boost::asio::ip::udp::endpoint ep (address->host, address->port);
|
||||
if (std::find (introducers.begin (), introducers.end (), ep) == introducers.end ()) // not connected yet
|
||||
{
|
||||
CreateDirectSession (introducer, ep, false);
|
||||
excluded.insert (introducer->GetIdentHash ());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogDebug, "SSU: Can't find more introducers");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (v4)
|
||||
ScheduleIntroducersUpdateTimer ();
|
||||
else
|
||||
ScheduleIntroducersUpdateTimerV6 ();
|
||||
}
|
||||
}
|
||||
|
||||
void SSUServer::NewPeerTest (uint32_t nonce, PeerTestParticipant role, std::shared_ptr<SSUSession> session)
|
||||
{
|
||||
m_PeerTests[nonce] = { i2p::util::GetMillisecondsSinceEpoch (), role, session };
|
||||
}
|
||||
|
||||
PeerTestParticipant SSUServer::GetPeerTestParticipant (uint32_t nonce)
|
||||
{
|
||||
auto it = m_PeerTests.find (nonce);
|
||||
if (it != m_PeerTests.end ())
|
||||
return it->second.role;
|
||||
else
|
||||
return ePeerTestParticipantUnknown;
|
||||
}
|
||||
|
||||
std::shared_ptr<SSUSession> SSUServer::GetPeerTestSession (uint32_t nonce)
|
||||
{
|
||||
auto it = m_PeerTests.find (nonce);
|
||||
if (it != m_PeerTests.end ())
|
||||
return it->second.session;
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void SSUServer::UpdatePeerTest (uint32_t nonce, PeerTestParticipant role)
|
||||
{
|
||||
auto it = m_PeerTests.find (nonce);
|
||||
if (it != m_PeerTests.end ())
|
||||
it->second.role = role;
|
||||
}
|
||||
|
||||
void SSUServer::RemovePeerTest (uint32_t nonce)
|
||||
{
|
||||
m_PeerTests.erase (nonce);
|
||||
}
|
||||
|
||||
void SSUServer::SchedulePeerTestsCleanupTimer ()
|
||||
{
|
||||
m_PeerTestsCleanupTimer.expires_from_now (boost::posix_time::seconds(SSU_PEER_TEST_TIMEOUT));
|
||||
m_PeerTestsCleanupTimer.async_wait (std::bind (&SSUServer::HandlePeerTestsCleanupTimer,
|
||||
this, std::placeholders::_1));
|
||||
}
|
||||
|
||||
void SSUServer::HandlePeerTestsCleanupTimer (const boost::system::error_code& ecode)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
int numDeleted = 0;
|
||||
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
for (auto it = m_PeerTests.begin (); it != m_PeerTests.end ();)
|
||||
{
|
||||
if (ts > it->second.creationTime + SSU_PEER_TEST_TIMEOUT*1000LL)
|
||||
{
|
||||
numDeleted++;
|
||||
it = m_PeerTests.erase (it);
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
if (numDeleted > 0)
|
||||
LogPrint (eLogDebug, "SSU: ", numDeleted, " peer tests have been expired");
|
||||
// some cleaups. TODO: use separate timer
|
||||
m_FragmentsPool.CleanUp ();
|
||||
m_IncompleteMessagesPool.CleanUp ();
|
||||
m_SentMessagesPool.CleanUp ();
|
||||
|
||||
SchedulePeerTestsCleanupTimer ();
|
||||
}
|
||||
}
|
||||
|
||||
void SSUServer::ScheduleTermination ()
|
||||
{
|
||||
uint64_t timeout = SSU_TERMINATION_CHECK_TIMEOUT + (rand () % SSU_TERMINATION_CHECK_TIMEOUT)/5;
|
||||
m_TerminationTimer.expires_from_now (boost::posix_time::seconds(timeout));
|
||||
m_TerminationTimer.async_wait (std::bind (&SSUServer::HandleTerminationTimer,
|
||||
this, std::placeholders::_1));
|
||||
}
|
||||
|
||||
void SSUServer::HandleTerminationTimer (const boost::system::error_code& ecode)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
for (auto& it: m_Sessions)
|
||||
if (it.second->IsTerminationTimeoutExpired (ts))
|
||||
{
|
||||
auto session = it.second;
|
||||
if (it.first != session->GetRemoteEndpoint ())
|
||||
LogPrint (eLogWarning, "SSU: Remote endpoint ", session->GetRemoteEndpoint (), " doesn't match key ", it.first, " adjusted");
|
||||
m_Service.post ([session]
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU: No activity with ", session->GetRemoteEndpoint (), " for ", session->GetTerminationTimeout (), " seconds");
|
||||
session->Failed ();
|
||||
});
|
||||
}
|
||||
else
|
||||
it.second->CleanUp (ts);
|
||||
ScheduleTermination ();
|
||||
}
|
||||
}
|
||||
|
||||
void SSUServer::ScheduleTerminationV6 ()
|
||||
{
|
||||
uint64_t timeout = SSU_TERMINATION_CHECK_TIMEOUT + (rand () % SSU_TERMINATION_CHECK_TIMEOUT)/5;
|
||||
m_TerminationTimerV6.expires_from_now (boost::posix_time::seconds(timeout));
|
||||
m_TerminationTimerV6.async_wait (std::bind (&SSUServer::HandleTerminationTimerV6,
|
||||
this, std::placeholders::_1));
|
||||
}
|
||||
|
||||
void SSUServer::HandleTerminationTimerV6 (const boost::system::error_code& ecode)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
for (auto& it: m_SessionsV6)
|
||||
if (it.second->IsTerminationTimeoutExpired (ts))
|
||||
{
|
||||
auto session = it.second;
|
||||
if (it.first != session->GetRemoteEndpoint ())
|
||||
LogPrint (eLogWarning, "SSU: Remote endpoint ", session->GetRemoteEndpoint (), " doesn't match key ", it.first);
|
||||
m_Service.post ([session]
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU: No activity with ", session->GetRemoteEndpoint (), " for ", session->GetTerminationTimeout (), " seconds");
|
||||
session->Failed ();
|
||||
});
|
||||
}
|
||||
else
|
||||
it.second->CleanUp (ts);
|
||||
ScheduleTerminationV6 ();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
159
libi2pd/SSU.h
Normal file
159
libi2pd/SSU.h
Normal file
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* 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 SSU_H__
|
||||
#define SSU_H__
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <set>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <boost/asio.hpp>
|
||||
#include "Crypto.h"
|
||||
#include "util.h"
|
||||
#include "I2PEndian.h"
|
||||
#include "Identity.h"
|
||||
#include "RouterInfo.h"
|
||||
#include "I2NPProtocol.h"
|
||||
#include "SSUSession.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace transport
|
||||
{
|
||||
const int SSU_KEEP_ALIVE_INTERVAL = 30; // 30 seconds
|
||||
const int SSU_PEER_TEST_TIMEOUT = 60; // 60 seconds
|
||||
const int SSU_TO_INTRODUCER_SESSION_DURATION = 3600; // 1 hour
|
||||
const int SSU_TO_INTRODUCER_SESSION_EXPIRATION = 4800; // 80 minutes
|
||||
const int SSU_TERMINATION_CHECK_TIMEOUT = 30; // 30 seconds
|
||||
const size_t SSU_MAX_NUM_INTRODUCERS = 3;
|
||||
const size_t SSU_SOCKET_RECEIVE_BUFFER_SIZE = 0x1FFFF; // 128K
|
||||
const size_t SSU_SOCKET_SEND_BUFFER_SIZE = 0x1FFFF; // 128K
|
||||
|
||||
struct SSUPacket
|
||||
{
|
||||
i2p::crypto::AESAlignedBuffer<SSU_MTU_V6 + 18> buf; // max MTU + iv + size
|
||||
boost::asio::ip::udp::endpoint from;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
class SSUServer
|
||||
{
|
||||
public:
|
||||
|
||||
SSUServer (int port);
|
||||
~SSUServer ();
|
||||
void Start ();
|
||||
void Stop ();
|
||||
bool CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest = false, bool v4only = false);
|
||||
bool CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router,
|
||||
std::shared_ptr<const i2p::data::RouterInfo::Address> address, bool peerTest = false);
|
||||
void CreateDirectSession (std::shared_ptr<const i2p::data::RouterInfo> router, boost::asio::ip::udp::endpoint remoteEndpoint, bool peerTest);
|
||||
std::shared_ptr<SSUSession> FindSession (const boost::asio::ip::udp::endpoint& e) const;
|
||||
std::shared_ptr<SSUSession> GetRandomEstablishedV4Session (std::shared_ptr<const SSUSession> excluded);
|
||||
std::shared_ptr<SSUSession> GetRandomEstablishedV6Session (std::shared_ptr<const SSUSession> excluded);
|
||||
void DeleteSession (std::shared_ptr<SSUSession> session);
|
||||
void DeleteAllSessions ();
|
||||
|
||||
boost::asio::io_service& GetService () { return m_Service; };
|
||||
i2p::util::MemoryPool<Fragment>& GetFragmentsPool () { return m_FragmentsPool; };
|
||||
i2p::util::MemoryPool<IncompleteMessage>& GetIncompleteMessagesPool () { return m_IncompleteMessagesPool; };
|
||||
i2p::util::MemoryPool<SentMessage>& GetSentMessagesPool () { return m_SentMessagesPool; };
|
||||
|
||||
uint16_t GetPort () const { return m_Endpoint.port (); };
|
||||
bool IsSyncClockFromPeers () const { return m_IsSyncClockFromPeers; };
|
||||
void SetLocalAddress (const boost::asio::ip::address& localAddress);
|
||||
|
||||
void Send (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& to);
|
||||
void AddRelay (uint32_t tag, std::shared_ptr<SSUSession> relay);
|
||||
void RemoveRelay (uint32_t tag);
|
||||
std::shared_ptr<SSUSession> FindRelaySession (uint32_t tag);
|
||||
void RescheduleIntroducersUpdateTimer ();
|
||||
void RescheduleIntroducersUpdateTimerV6 ();
|
||||
|
||||
void NewPeerTest (uint32_t nonce, PeerTestParticipant role, std::shared_ptr<SSUSession> session = nullptr);
|
||||
PeerTestParticipant GetPeerTestParticipant (uint32_t nonce);
|
||||
std::shared_ptr<SSUSession> GetPeerTestSession (uint32_t nonce);
|
||||
void UpdatePeerTest (uint32_t nonce, PeerTestParticipant role);
|
||||
void RemovePeerTest (uint32_t nonce);
|
||||
|
||||
private:
|
||||
|
||||
void OpenSocket ();
|
||||
void OpenSocketV6 ();
|
||||
void Run ();
|
||||
void RunReceivers ();
|
||||
void RunReceiversV6 ();
|
||||
void Receive ();
|
||||
void ReceiveV6 ();
|
||||
void HandleReceivedFrom (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet);
|
||||
void HandleReceivedFromV6 (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet);
|
||||
void HandleReceivedPackets (std::vector<SSUPacket *> packets,
|
||||
std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<SSUSession> >* sessions);
|
||||
|
||||
void CreateSessionThroughIntroducer (std::shared_ptr<const i2p::data::RouterInfo> router,
|
||||
std::shared_ptr<const i2p::data::RouterInfo::Address> address, bool peerTest = false);
|
||||
template<typename Filter>
|
||||
std::shared_ptr<SSUSession> GetRandomV4Session (Filter filter);
|
||||
template<typename Filter>
|
||||
std::shared_ptr<SSUSession> GetRandomV6Session (Filter filter);
|
||||
|
||||
std::list<std::shared_ptr<SSUSession> > FindIntroducers (int maxNumIntroducers, bool v4, std::set<i2p::data::IdentHash>& excluded);
|
||||
void ScheduleIntroducersUpdateTimer ();
|
||||
void ScheduleIntroducersUpdateTimerV6 ();
|
||||
void HandleIntroducersUpdateTimer (const boost::system::error_code& ecode, bool v4);
|
||||
|
||||
void SchedulePeerTestsCleanupTimer ();
|
||||
void HandlePeerTestsCleanupTimer (const boost::system::error_code& ecode);
|
||||
|
||||
// timer
|
||||
void ScheduleTermination ();
|
||||
void HandleTerminationTimer (const boost::system::error_code& ecode);
|
||||
void ScheduleTerminationV6 ();
|
||||
void HandleTerminationTimerV6 (const boost::system::error_code& ecode);
|
||||
|
||||
private:
|
||||
|
||||
struct PeerTest
|
||||
{
|
||||
uint64_t creationTime;
|
||||
PeerTestParticipant role;
|
||||
std::shared_ptr<SSUSession> session; // for Bob to Alice
|
||||
};
|
||||
|
||||
volatile bool m_IsRunning;
|
||||
std::thread * m_Thread, * m_ReceiversThread, * m_ReceiversThreadV6;
|
||||
boost::asio::io_service m_Service, m_ReceiversService, m_ReceiversServiceV6;
|
||||
boost::asio::io_service::work m_Work, m_ReceiversWork, m_ReceiversWorkV6;
|
||||
boost::asio::ip::udp::endpoint m_Endpoint, m_EndpointV6;
|
||||
boost::asio::ip::udp::socket m_Socket, m_SocketV6;
|
||||
boost::asio::deadline_timer m_IntroducersUpdateTimer, m_IntroducersUpdateTimerV6,
|
||||
m_PeerTestsCleanupTimer, m_TerminationTimer, m_TerminationTimerV6;
|
||||
bool m_IsSyncClockFromPeers;
|
||||
std::list<boost::asio::ip::udp::endpoint> m_Introducers, m_IntroducersV6; // introducers we are connected to
|
||||
std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<SSUSession> > m_Sessions, m_SessionsV6;
|
||||
std::map<uint32_t, std::shared_ptr<SSUSession> > m_Relays; // we are introducer
|
||||
std::map<uint32_t, PeerTest> m_PeerTests; // nonce -> creation time in milliseconds
|
||||
|
||||
i2p::util::MemoryPool<Fragment> m_FragmentsPool;
|
||||
i2p::util::MemoryPool<IncompleteMessage> m_IncompleteMessagesPool;
|
||||
i2p::util::MemoryPool<SentMessage> m_SentMessagesPool;
|
||||
i2p::util::MemoryPoolMt<SSUPacket> m_PacketsPool;
|
||||
|
||||
public:
|
||||
// for HTTP only
|
||||
const decltype(m_Sessions)& GetSessions () const { return m_Sessions; };
|
||||
const decltype(m_SessionsV6)& GetSessionsV6 () const { return m_SessionsV6; };
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
1037
libi2pd/SSU2.cpp
1037
libi2pd/SSU2.cpp
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user