mirror of
https://github.com/PurpleI2P/i2pd.git
synced 2025-03-07 06:09:42 +00:00
Compare commits
329 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ebee6c5f13 | ||
|
|
3adff82d4a | ||
|
|
200ad5524d | ||
|
|
82ac0aa492 | ||
|
|
b8a99878b6 | ||
|
|
eaca435a5b | ||
|
|
ce05cce331 | ||
|
|
e190dab7d6 | ||
|
|
289c308600 | ||
|
|
bff12b06f4 | ||
|
|
b8590075e6 | ||
|
|
1da9e2e1c0 | ||
|
|
a1fb97cfad | ||
|
|
e53fe2d988 | ||
|
|
182a721674 | ||
|
|
00c75a7afa | ||
|
|
385e592045 | ||
|
|
359781c698 | ||
|
|
c6c7191682 | ||
|
|
fa286a6fb3 | ||
|
|
02a36a9fa8 | ||
|
|
77142e59ee | ||
|
|
4156900fb5 | ||
|
|
76d39cc7c9 | ||
|
|
9e0c5d67c7 | ||
|
|
03111ad0df | ||
|
|
abb81c353d | ||
|
|
1ad38c0b95 | ||
|
|
532a29b0c4 | ||
|
|
4f3a416e37 | ||
|
|
4d0ad47491 | ||
|
|
7b632bd03d | ||
|
|
c2faa5c614 | ||
|
|
d15581d95e | ||
|
|
7bde4de1f5 | ||
|
|
88f19f551d | ||
|
|
d79bdc9f66 | ||
|
|
48a3c767e5 | ||
|
|
86fc12e395 | ||
|
|
3c65012a63 | ||
|
|
97b7ea0da5 | ||
|
|
1c4cc02c23 | ||
|
|
ac94341203 | ||
|
|
234540f9e7 | ||
|
|
cb44a35fde | ||
|
|
6ac849fe37 | ||
|
|
0829b186b7 | ||
|
|
993900aa77 | ||
|
|
aac9a8d18f | ||
|
|
2baaa8bb8f | ||
|
|
f29ef2c057 | ||
|
|
d1c98c58b9 | ||
|
|
4930157e93 | ||
|
|
d30d1e8a7d | ||
|
|
80a55c5b75 | ||
|
|
3c02cade58 | ||
|
|
21123e2afa | ||
|
|
f1415c2234 | ||
|
|
212a1156a1 | ||
|
|
54fb234424 | ||
|
|
5a6b50ae51 | ||
|
|
21542e8150 | ||
|
|
544fcd147f | ||
|
|
9bc3b11b96 | ||
|
|
02e7f6b0c3 | ||
|
|
f7101cc260 | ||
|
|
e9f0ed6473 | ||
|
|
4960587f46 | ||
|
|
bf8eecf407 | ||
|
|
09a1a78bd6 | ||
|
|
7df2ed6114 | ||
|
|
22b1066b0a | ||
|
|
89695a7040 | ||
|
|
2ed281472f | ||
|
|
fafdb0c590 | ||
|
|
2a6883e305 | ||
|
|
3cee8bfcb2 | ||
|
|
86dbfdb536 | ||
|
|
70fec2bc99 | ||
|
|
8a106eb09e | ||
|
|
f9331897b8 | ||
|
|
126ca0209b | ||
|
|
ad5540c9f3 | ||
|
|
e91d0bbec8 | ||
|
|
8ee461f60a | ||
|
|
4de6201b84 | ||
|
|
273aa31b1e | ||
|
|
b31f52e332 | ||
|
|
4fae7b8d65 | ||
|
|
5479ddd03c | ||
|
|
1f23584c24 | ||
|
|
067fb45a25 | ||
|
|
ac287a896c | ||
|
|
8baf62eb2c | ||
|
|
e1ec79daf2 | ||
|
|
d4426118c5 | ||
|
|
64fe56aa07 | ||
|
|
47eb49c34e | ||
|
|
cd6d86c8c3 | ||
|
|
84d4e074ce | ||
|
|
a0e71c4173 | ||
|
|
533c8a8a55 | ||
|
|
a57ae4dc56 | ||
|
|
88dfe3ca4e | ||
|
|
d68c7f8ea7 | ||
|
|
e8ace998ba | ||
|
|
e8be39af17 | ||
|
|
7196db09d6 | ||
|
|
b290ee1aa0 | ||
|
|
d105ab11af | ||
|
|
bc888167a7 | ||
|
|
6ca6591c43 | ||
|
|
36cb707e47 | ||
|
|
013d5ff74f | ||
|
|
9af5a90757 | ||
|
|
d8b6f5438c | ||
|
|
10030a4e0d | ||
|
|
993dc72ce6 | ||
|
|
324ace103b | ||
|
|
d530269e4f | ||
|
|
7146a4dbae | ||
|
|
f79900653b | ||
|
|
f172f44f32 | ||
|
|
f34abe60fa | ||
|
|
a3c305032a | ||
|
|
2921eaa055 | ||
|
|
1cc68ea402 | ||
|
|
c18e8f6c78 | ||
|
|
e59ca8420e | ||
|
|
a6f9a56e40 | ||
|
|
4011502f5f | ||
|
|
acd6af709e | ||
|
|
55704ece3a | ||
|
|
0d3ede56cb | ||
|
|
06fae976b3 | ||
|
|
e95035c143 | ||
|
|
55be5c74f0 | ||
|
|
a1c16e129d | ||
|
|
321c7cb7cf | ||
|
|
5f8d45154d | ||
|
|
503f522cc3 | ||
|
|
22179400c7 | ||
|
|
66f82cb43f | ||
|
|
1df67bd43c | ||
|
|
d5b03f214b | ||
|
|
cfb773351d | ||
|
|
6942c20879 | ||
|
|
7b341d5d30 | ||
|
|
e93718456f | ||
|
|
af838196be | ||
|
|
f8ba5b8c63 | ||
|
|
446bbf6b93 | ||
|
|
2c19da9aa7 | ||
|
|
01fc21ffb9 | ||
|
|
3e3e2c41bd | ||
|
|
cb139226df | ||
|
|
84d6028454 | ||
|
|
126dc0ebe0 | ||
|
|
099d9d977f | ||
|
|
5a167316cb | ||
|
|
85e31f84ec | ||
|
|
edb7a0e23c | ||
|
|
f401ccf5dd | ||
|
|
9f9e8bfa14 | ||
|
|
61168d5c9d | ||
|
|
8500aaa26f | ||
|
|
445cff0025 | ||
|
|
99356fd24d | ||
|
|
5ef5f5a170 | ||
|
|
54b7d4ef32 | ||
|
|
6376328c98 | ||
|
|
b6f83dfe9f | ||
|
|
3f728149ab | ||
|
|
36501fe31e | ||
|
|
e4ddc883d2 | ||
|
|
5ac01ddce8 | ||
|
|
d3656fcb3f | ||
|
|
d6c101d261 | ||
|
|
eeea02d834 | ||
|
|
0e0cd555eb | ||
|
|
5dfe483152 | ||
|
|
c210553a39 | ||
|
|
a315e4ce62 | ||
|
|
96cfd9acc2 | ||
|
|
d869bb25ed | ||
|
|
476e6aae35 | ||
|
|
d30ee99cf1 | ||
|
|
84d9c8f1b8 | ||
|
|
df737a65b2 | ||
|
|
c5230ca44b | ||
|
|
3471e6fe16 | ||
|
|
f1437feede | ||
|
|
0d523bd2a6 | ||
|
|
8943200ffa | ||
|
|
a902d68669 | ||
|
|
f6ca7c19af | ||
|
|
3458665df8 | ||
|
|
8320987124 | ||
|
|
648b09d45f | ||
|
|
857df5c734 | ||
|
|
737603e81b | ||
|
|
53ca5dc67a | ||
|
|
8ad5696e50 | ||
|
|
ef9d27e424 | ||
|
|
2bb5ff7184 | ||
|
|
753c7efde8 | ||
|
|
d0d0cd8445 | ||
|
|
410d2c2fa9 | ||
|
|
6a743f66e8 | ||
|
|
709c451400 | ||
|
|
cb73c7c72e | ||
|
|
50abeea82a | ||
|
|
8db352b4d0 | ||
|
|
6589bdf6b5 | ||
|
|
1ac171152a | ||
|
|
629c718527 | ||
|
|
519c0fe81d | ||
|
|
eb0ef80a17 | ||
|
|
9763499dbe | ||
|
|
949c38f5f0 | ||
|
|
2a6f906177 | ||
|
|
b4c226f4b3 | ||
|
|
64c3282aae | ||
|
|
f5d511ae0f | ||
|
|
aa9a9ef18d | ||
|
|
aa5e6400e4 | ||
|
|
61bcfebcc8 | ||
|
|
73b9c0302b | ||
|
|
aead9db971 | ||
|
|
d8230644b2 | ||
|
|
97ef908b0c | ||
|
|
fb8be32c28 | ||
|
|
a298588943 | ||
|
|
ccfeca728e | ||
|
|
7705423c42 | ||
|
|
379075c594 | ||
|
|
6a23153c0b | ||
|
|
9e02c99db5 | ||
|
|
ba3cee1cf1 | ||
|
|
9f59ff2df4 | ||
|
|
8df4082d6f | ||
|
|
cf005821d7 | ||
|
|
39b3996596 | ||
|
|
78357baca4 | ||
|
|
85b78dfb9b | ||
|
|
9fd60b52f1 | ||
|
|
851be41d0d | ||
|
|
c6a6a4e0e8 | ||
|
|
28aeebd4c7 | ||
|
|
c88638afe4 | ||
|
|
7f98a8b972 | ||
|
|
e1e4924592 | ||
|
|
8299f80ea5 | ||
|
|
b8ce0b0838 | ||
|
|
e13f151474 | ||
|
|
4ed4e8708e | ||
|
|
1738d118f7 | ||
|
|
f1f66d7b8f | ||
|
|
4ed5e44de7 | ||
|
|
3e3f92c616 | ||
|
|
5fb1247b87 | ||
|
|
016222463d | ||
|
|
0e477bf938 | ||
|
|
eb75eb0e55 | ||
|
|
2a703e0844 | ||
|
|
2b6d9eaa8b | ||
|
|
f9b0bb0383 | ||
|
|
c6e8873d57 | ||
|
|
b2767304e9 | ||
|
|
3d4d3ce80d | ||
|
|
01ea1854bc | ||
|
|
f3aada9e1a | ||
|
|
08fd32b3bf | ||
|
|
39a86ce5c9 | ||
|
|
fe25260ee2 | ||
|
|
63fd05c7d3 | ||
|
|
6c2aec8854 | ||
|
|
e5553f7528 | ||
|
|
6e3cec653d | ||
|
|
55976fd9dc | ||
|
|
bcbd5201e9 | ||
|
|
c2f91ea63b | ||
|
|
1d9d89b115 | ||
|
|
798dd8b27b | ||
|
|
3544f77e90 | ||
|
|
96c4463d39 | ||
|
|
650b7abef6 | ||
|
|
714b3856a2 | ||
|
|
6b939eba59 | ||
|
|
e82662b389 | ||
|
|
8f9dae8556 | ||
|
|
69ca3bc75d | ||
|
|
3945f34e96 | ||
|
|
549dcbee32 | ||
|
|
0a0c2350f2 | ||
|
|
cef2263a7f | ||
|
|
e338ce7da9 | ||
|
|
638c376e5b | ||
|
|
8eade86624 | ||
|
|
24ae8d5443 | ||
|
|
030af11d86 | ||
|
|
857a2bc399 | ||
|
|
09e6e2940f | ||
|
|
23e18a34d4 | ||
|
|
3bdef5f58d | ||
|
|
cf27581c76 | ||
|
|
cf41df82e2 | ||
|
|
4634bff9f0 | ||
|
|
1a9c658836 | ||
|
|
1a32c55ca3 | ||
|
|
f4e230f1ad | ||
|
|
9abc4cf359 | ||
|
|
c54fc7ee44 | ||
|
|
9df757a3fd | ||
|
|
9b5a885b3b | ||
|
|
f32b288785 | ||
|
|
f378119889 | ||
|
|
8fd466c5a9 | ||
|
|
36eddd48c3 | ||
|
|
2470ba76f0 | ||
|
|
d32475440a | ||
|
|
b4d73683d1 | ||
|
|
95f19a5fb2 | ||
|
|
f98780b1d7 | ||
|
|
150c89e48a | ||
|
|
c85bf82749 | ||
|
|
63227ab2f1 | ||
|
|
5b19237a85 | ||
|
|
150b8f8cbd |
32
.editorconfig
Normal file
32
.editorconfig
Normal file
@@ -0,0 +1,32 @@
|
||||
# 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
Normal file
32
.github/workflows/build-deb.yml
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
name: Build Debian packages
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: ${{ matrix.dist }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
dist: ['buster', 'bullseye', 'bookworm']
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: change debian changelog
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install devscripts
|
||||
debchange -v "`git describe --tags`-${{ matrix.dist }}" -b -M --distribution ${{ matrix.dist }} "trunk build"
|
||||
- uses: jtdor/build-deb-action@v1
|
||||
with:
|
||||
docker-image: debian:${{ matrix.dist }}-slim
|
||||
buildpackage-opts: --build=binary --no-sign
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: i2pd_${{ matrix.dist }}
|
||||
path: debian/artifacts/i2pd_*.deb
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: i2pd-dbgsym_${{ matrix.dist }}
|
||||
path: debian/artifacts/i2pd-dbgsym_*.deb
|
||||
11
.github/workflows/build-freebsd.yml
vendored
11
.github/workflows/build-freebsd.yml
vendored
@@ -10,12 +10,19 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Test in FreeBSD
|
||||
id: test
|
||||
uses: vmactions/freebsd-vm@v0.2.0
|
||||
uses: vmactions/freebsd-vm@v0.3.0
|
||||
with:
|
||||
usesh: true
|
||||
mem: 2048
|
||||
prepare: pkg install -y devel/cmake devel/gmake devel/boost-libs security/openssl net/miniupnpc
|
||||
sync: rsync
|
||||
copyback: true
|
||||
prepare: pkg install -y devel/cmake devel/gmake devel/boost-libs security/openssl net/miniupnpc
|
||||
run: |
|
||||
cd build
|
||||
cmake -DWITH_UPNP=ON -DCMAKE_BUILD_TYPE=Release .
|
||||
gmake -j2
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: i2pd-freebsd
|
||||
path: build/i2pd
|
||||
1
.github/workflows/build-osx.yml
vendored
1
.github/workflows/build-osx.yml
vendored
@@ -14,6 +14,7 @@ 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
|
||||
|
||||
48
.github/workflows/build.yml
vendored
48
.github/workflows/build.yml
vendored
@@ -38,51 +38,3 @@ jobs:
|
||||
cd build
|
||||
cmake -DWITH_UPNP=${{ matrix.with_upnp }} .
|
||||
make -j3
|
||||
build-deb-stretch:
|
||||
name: Build package for stretch
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: change debian changelog
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install devscripts
|
||||
debchange -v "`git describe --tags`-stretch" -b -M --distribution stretch "trunk build"
|
||||
- uses: singingwolfboy/build-dpkg-stretch@v1
|
||||
id: build
|
||||
with:
|
||||
args: --unsigned-source --unsigned-changes -b
|
||||
- uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: ${{ steps.build.outputs.filename }}
|
||||
path: ${{ steps.build.outputs.filename }}
|
||||
- uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: ${{ steps.build.outputs.filename-dbgsym }}
|
||||
path: ${{ steps.build.outputs.filename-dbgsym }}
|
||||
build-deb-buster:
|
||||
name: Build package for buster
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: change debian changelog
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install devscripts
|
||||
debchange -v "`git describe --tags`-buster" -b -M --distribution buster "trunk build"
|
||||
- uses: singingwolfboy/build-dpkg-buster@v1
|
||||
id: build
|
||||
with:
|
||||
args: --unsigned-source --unsigned-changes -b
|
||||
- uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: ${{ steps.build.outputs.filename }}
|
||||
path: ${{ steps.build.outputs.filename }}
|
||||
- uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: ${{ steps.build.outputs.filename-dbgsym }}
|
||||
path: ${{ steps.build.outputs.filename-dbgsym }}
|
||||
|
||||
105
.github/workflows/docker.yml
vendored
105
.github/workflows/docker.yml
vendored
@@ -4,67 +4,124 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- openssl
|
||||
- docker
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
jobs:
|
||||
docker:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
include: [
|
||||
{ platform: 'linux/amd64', archname: 'amd64' },
|
||||
{ platform: 'linux/386', archname: 'i386' },
|
||||
{ platform: 'linux/arm64', archname: 'arm64' },
|
||||
{ platform: 'linux/arm/v7', archname: 'armv7' },
|
||||
]
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
uses: docker/setup-qemu-action@v2
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v1
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Login to GitHub Container registry
|
||||
uses: docker/login-action@v1
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push trunk container
|
||||
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
|
||||
uses: docker/build-push-action@v2
|
||||
- name: Build container for ${{ matrix.archname }}
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: ./contrib/docker
|
||||
file: ./contrib/docker/Dockerfile
|
||||
platforms: linux/amd64,linux/386,linux/arm64,linux/arm/v7
|
||||
platforms: ${{ matrix.platform }}
|
||||
push: true
|
||||
tags: |
|
||||
purplei2p/i2pd:latest
|
||||
ghcr.io/purplei2p/i2pd:latest
|
||||
purplei2p/i2pd:latest-${{ matrix.archname }}
|
||||
ghcr.io/purplei2p/i2pd:latest-${{ matrix.archname }}
|
||||
provenance: false
|
||||
|
||||
- name: Set env
|
||||
push:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
|
||||
needs: build
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Login to GitHub Container registry
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Create and push latest manifest image to Docker Hub
|
||||
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
|
||||
uses: Noelware/docker-manifest-action@master
|
||||
with:
|
||||
inputs: purplei2p/i2pd:latest
|
||||
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
|
||||
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
|
||||
uses: Noelware/docker-manifest-action@master
|
||||
with:
|
||||
inputs: ghcr.io/purplei2p/i2pd:latest
|
||||
images: ghcr.io/purplei2p/i2pd:latest-amd64,ghcr.io/purplei2p/i2pd:latest-i386,ghcr.io/purplei2p/i2pd:latest-arm64,ghcr.io/purplei2p/i2pd:latest-armv7
|
||||
push: true
|
||||
|
||||
- name: Store release version to env
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||
run: echo "RELEASE_VERSION=${GITHUB_REF:10}" >> $GITHUB_ENV
|
||||
|
||||
- name: Build and push release container
|
||||
- name: Create and push release manifest to Docker Hub
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||
uses: docker/build-push-action@v2
|
||||
uses: Noelware/docker-manifest-action@master
|
||||
with:
|
||||
context: ./contrib/docker
|
||||
file: ./contrib/docker/Dockerfile
|
||||
platforms: linux/amd64,linux/386,linux/arm64,linux/arm/v7
|
||||
inputs: purplei2p/i2pd:latest,purplei2p/i2pd:latest-release,purplei2p/i2pd:release-${{ env.RELEASE_VERSION }}
|
||||
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 to GHCR
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||
uses: Noelware/docker-manifest-action@master
|
||||
with:
|
||||
inputs: ghcr.io/purplei2p/i2pd:latest,ghcr.io/purplei2p/i2pd:latest-release,ghcr.io/purplei2p/i2pd:release-${{ env.RELEASE_VERSION }}
|
||||
images: ghcr.io/purplei2p/i2pd:latest-amd64,ghcr.io/purplei2p/i2pd:latest-i386,ghcr.io/purplei2p/i2pd:latest-arm64,ghcr.io/purplei2p/i2pd:latest-armv7
|
||||
push: true
|
||||
tags: |
|
||||
purplei2p/i2pd:latest
|
||||
purplei2p/i2pd:latest-release
|
||||
purplei2p/i2pd:release-${{ env.RELEASE_VERSION }}
|
||||
ghcr.io/purplei2p/i2pd:latest
|
||||
ghcr.io/purplei2p/i2pd:latest-release
|
||||
ghcr.io/purplei2p/i2pd:release-${{ env.RELEASE_VERSION }}
|
||||
|
||||
116
ChangeLog
116
ChangeLog
@@ -1,6 +1,118 @@
|
||||
# for this file format description,
|
||||
# see https://github.com/olivierlacan/keep-a-changelog
|
||||
|
||||
## [2.46.1] - 2023-02-20
|
||||
### Fixed
|
||||
- Race condition while getting router's peer profile
|
||||
- Creation of new router.info
|
||||
- Displaying LeaseSets in the webconsole
|
||||
- Crash when processing ACK request
|
||||
|
||||
## [2.46.0] - 2023-02-15
|
||||
### Added
|
||||
- Limit number of acked SSU2 packets to 511
|
||||
- Localization to Swedish, Portuguese, Turkish, Polish
|
||||
- Periodically send Datetime block in NTCP2 and SSU2
|
||||
- Don't select random port from reserved
|
||||
- In memory table for peer profiles
|
||||
- Store if router was unreachable in it's peer profile
|
||||
- Show IPv6 addresses in square brackets in webconsole
|
||||
- Check referer when processing Addresshelper
|
||||
### Changed
|
||||
- Algorithm for tunnel creation success rate calculation
|
||||
- Drop incoming NTCP2 and SSU2 connection if published IP doesn't match actual endpoint
|
||||
- Exclude actually unreachable router from netdb for 2 hours
|
||||
- Select first hop from high bandwidth peers for client tunnels
|
||||
- Drop too long or too short LeaseSet
|
||||
- Delete router from netdb if became invalid after update
|
||||
- Terminate existing session if clock skew detected
|
||||
- Close previous UDP socket if open before reopening
|
||||
- Minimal version for floodfill is 0.9.51
|
||||
- Sort transports by endpoints in webconsole
|
||||
### Fixed
|
||||
- Deadlock during processing I2NP block with Garlic in ECIES encrypted message to router
|
||||
- Race condition with encrypted LeaseSets
|
||||
- HTTP query detection
|
||||
- Connection attempts to IPs from invalid ranges
|
||||
- Publish "0.0.0.0" in RouterInfo
|
||||
- Crash upon receiving PeerTest 7
|
||||
- Tunnels for closed SAM session socket
|
||||
- Missing NTCP2 address in RouterInfo if enabled back
|
||||
|
||||
## [2.45.1] - 2023-01-11
|
||||
### Added
|
||||
- Full Cone NAT status error
|
||||
### Changed
|
||||
- Drop duplicated I2NP messages in SSU2
|
||||
- Set rejection code 30 if tunnel with id already exists
|
||||
- Network status is always OK if peer test msg 5 received
|
||||
### Fixed
|
||||
- UPnP crash if SSU2 or NTCP2 is disabled
|
||||
- Crash on termination for some platforms
|
||||
|
||||
## [2.45.0] - 2023-01-03
|
||||
### Added
|
||||
- Test for Symmetric NAT with peer test msgs 6 and 7
|
||||
- Webconsole "No Descriptors" router error state
|
||||
- 1 and 15 seconds bandwidth calculation for i2pcontrol
|
||||
- Show non-zero send queue size for transports in web console
|
||||
- Compressible padding for I2P addresses
|
||||
- Localization to Czech
|
||||
- Don't accept incoming session from invalid/reserved addresses for NTCP2 and SSU2
|
||||
- Limit simultaneous tunnel build requests by 4 per pool
|
||||
### Changed
|
||||
- Removed SSU support
|
||||
- Reduced bandwidth calculation interval from 60 to 15 seconds
|
||||
- Increased default max transit tunnels number from 2500 to 5000 or 10000 for floodfill
|
||||
- Transit tunnels limit is doubled if floodfill mode is enabled
|
||||
- NTCP2 and SSU2 timestamps are rounded to seconds
|
||||
- Drop RouterInfos and LeaseSets with timestamp from future
|
||||
- Don't delete unreachable routers if tunnel creation success rate is too low
|
||||
- Refuse duplicated incoming pending NTCP2 session from same IP
|
||||
- Don't send SSU2 termination again if termination received block received
|
||||
- Handle standard network error for SSU2 without throwing an exception
|
||||
- Don't select overloaded peer for next tunnel
|
||||
- Remove "X-Requested-With" in HTTP Proxy for non-AJAX requests
|
||||
### Fixed
|
||||
- File descriptors leak
|
||||
- Random crash on AddressBook update
|
||||
- Crash if incorrect LeaseSet size
|
||||
- Spamming to log if no descriptors
|
||||
- ::1 address in RouterInfo
|
||||
- SSU2 network error handling (especially for Windows)
|
||||
- Race condition with pending outgoing SSU2 sessions
|
||||
- RTT self-reduction for long-live streams
|
||||
|
||||
## [2.44.0] - 2022-11-20
|
||||
### Added
|
||||
- SSL connection for server I2P tunnels
|
||||
- Localization to Italian and Spanish
|
||||
- SSU2 through SOCKS5 UDP proxy
|
||||
- Reload tunnels through web console
|
||||
- SSU2 send immediate ack request flag
|
||||
- SSU2 send and verify path challenge
|
||||
- Configurable ssu2.mtu4 and ssu2.mtu6
|
||||
### Changed
|
||||
- SSU2 is enabled and SSU is disabled by default
|
||||
- Separate network status and error
|
||||
- Random selection between NTCP2 and SSU2 priority
|
||||
- Added notbob.i2p to jump services
|
||||
- Remove DoNotTrack flag from HTTP Request header
|
||||
- Skip addresshelper page if destination was not changed
|
||||
- SSU2 allow different ports from RelayReponse and HolePunch
|
||||
- SSU2 resend PeerTest msg 1 and msg 2
|
||||
- SSU2 Send Retry instead SessionCreated if clock skew detected
|
||||
### Fixed
|
||||
- Long HTTP headers for HTTP proxy and HTTP server tunnel
|
||||
- SSU2 resends and resend limits
|
||||
- Crash at startup if addressbook is disabled
|
||||
- NTCP2 ipv6 connection through SOCKS5 proxy
|
||||
- SSU2 SessionRequest with zero token
|
||||
- SSU2 MTU less than 1280
|
||||
- SSU2 port=1
|
||||
- Incorrect addresses from network interfaces
|
||||
- Definitions for Darwin PPC; do not use pthread_setname_np
|
||||
|
||||
## [2.43.0] - 2022-08-22
|
||||
### Added
|
||||
- Complete SSU2 implementation
|
||||
@@ -411,7 +523,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
|
||||
@@ -715,7 +827,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
|
||||
|
||||
|
||||
14
Makefile
14
Makefile
@@ -47,6 +47,10 @@ else
|
||||
LD_DEBUG = -s
|
||||
endif
|
||||
|
||||
ifneq (, $(DESTDIR))
|
||||
PREFIX = $(DESTDIR)
|
||||
endif
|
||||
|
||||
ifneq (, $(findstring darwin, $(SYS)))
|
||||
DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp
|
||||
ifeq ($(HOMEBREW),1)
|
||||
@@ -54,15 +58,15 @@ ifneq (, $(findstring darwin, $(SYS)))
|
||||
else
|
||||
include Makefile.osx
|
||||
endif
|
||||
else ifneq (, $(findstring mingw, $(SYS))$(findstring windows-gnu, $(SYS))$(findstring cygwin, $(SYS)))
|
||||
DAEMON_SRC += Win32/DaemonWin32.cpp Win32/Win32App.cpp Win32/Win32Service.cpp Win32/Win32NetState.cpp
|
||||
include Makefile.mingw
|
||||
else ifneq (, $(findstring linux, $(SYS))$(findstring gnu, $(SYS)))
|
||||
DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp
|
||||
include Makefile.linux
|
||||
else ifneq (, $(findstring freebsd, $(SYS))$(findstring openbsd, $(SYS)))
|
||||
DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp
|
||||
include Makefile.bsd
|
||||
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 # not supported
|
||||
$(error Not supported platform)
|
||||
endif
|
||||
@@ -114,9 +118,9 @@ obj/%.o: %.cpp | mk_obj_dir
|
||||
$(I2PD): $(DAEMON_OBJS) $(ARLIB) $(ARLIB_CLIENT) $(ARLIB_LANG)
|
||||
$(CXX) -o $@ $(LDFLAGS) $^ $(LDLIBS)
|
||||
|
||||
$(SHLIB): $(LIB_OBJS) $(SHLIB_LANG)
|
||||
$(SHLIB): $(LIB_OBJS)
|
||||
ifneq ($(USE_STATIC),yes)
|
||||
$(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS) $(SHLIB_LANG)
|
||||
$(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS)
|
||||
endif
|
||||
|
||||
$(SHLIB_CLIENT): $(LIB_CLIENT_OBJS) $(SHLIB) $(SHLIB_LANG)
|
||||
|
||||
@@ -20,7 +20,11 @@ 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} "[1,7-9]"),1) # gcc >= 7
|
||||
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
|
||||
NEEDED_CXXFLAGS += -std=c++17
|
||||
LDLIBS = -latomic
|
||||
else # not supported
|
||||
|
||||
10
README.md
10
README.md
@@ -69,12 +69,12 @@ Build instructions:
|
||||
|
||||
**Supported systems:**
|
||||
|
||||
* 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.
|
||||
* 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.
|
||||
* Windows - [](https://github.com/PurpleI2P/i2pd/actions/workflows/build-windows.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)
|
||||
* Mac OS - [](https://github.com/PurpleI2P/i2pd/actions/workflows/build-osx.yml)
|
||||
* Docker image - [](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)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2023, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -29,7 +29,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)
|
||||
@@ -61,7 +61,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;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2020, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -154,25 +154,23 @@ namespace win32
|
||||
case eRouterStatusUnknown: s << "Unk"; break;
|
||||
case eRouterStatusProxy: s << "Proxy"; break;
|
||||
case eRouterStatusMesh: s << "Mesh"; break;
|
||||
case eRouterStatusError:
|
||||
{
|
||||
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;
|
||||
}
|
||||
default: s << "Unk";
|
||||
};
|
||||
if (i2p::context.GetError () != eRouterErrorNone)
|
||||
{
|
||||
switch (i2p::context.GetError ())
|
||||
{
|
||||
case eRouterErrorClockSkew:
|
||||
s << " - Clock skew";
|
||||
break;
|
||||
case eRouterErrorOffline:
|
||||
s << " - Offline";
|
||||
break;
|
||||
case eRouterErrorSymmetricNAT:
|
||||
s << " - Symmetric NAT";
|
||||
break;
|
||||
default: ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
3
build/.gitignore
vendored
3
build/.gitignore
vendored
@@ -1,5 +1,7 @@
|
||||
# Various generated files
|
||||
/CMakeFiles/
|
||||
/Testing/
|
||||
/tests/
|
||||
/i2pd
|
||||
/libi2pd.a
|
||||
/libi2pdclient.a
|
||||
@@ -8,6 +10,7 @@
|
||||
/CMakeCache.txt
|
||||
/CPackConfig.cmake
|
||||
/CPackSourceConfig.cmake
|
||||
/CTestTestfile.cmake
|
||||
/install_manifest.txt
|
||||
/arch.c
|
||||
# windows build script
|
||||
|
||||
@@ -20,12 +20,17 @@ 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}/..")
|
||||
|
||||
#Handle paths nicely
|
||||
# Handle paths nicely
|
||||
include(GNUInstallDirs)
|
||||
|
||||
# architecture
|
||||
@@ -85,6 +90,7 @@ set(DAEMON_SRC
|
||||
"${DAEMON_SRC_DIR}/Daemon.cpp"
|
||||
"${DAEMON_SRC_DIR}/HTTPServer.cpp"
|
||||
"${DAEMON_SRC_DIR}/I2PControl.cpp"
|
||||
"${DAEMON_SRC_DIR}/I2PControlHandlers.cpp"
|
||||
"${DAEMON_SRC_DIR}/i2pd.cpp"
|
||||
"${DAEMON_SRC_DIR}/UPnP.cpp"
|
||||
)
|
||||
@@ -170,6 +176,12 @@ 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
|
||||
# This has been fixed in Boost 1.81, nevertheless we retain the setting for the sake of compatibility.
|
||||
if(APPLE AND CMAKE_OSX_ARCHITECTURES MATCHES "ppc")
|
||||
add_definitions(-DBOOST_SP_USE_STD_ATOMIC)
|
||||
endif()
|
||||
|
||||
# libraries
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
@@ -267,9 +279,13 @@ if(WITH_BINARY)
|
||||
set(DL_LIB ${CMAKE_DL_LIBS})
|
||||
endif()
|
||||
|
||||
target_link_libraries("${PROJECT_NAME}" libi2pd libi2pdclient libi2pdlang ${DL_LIB} ${Boost_LIBRARIES} OpenSSL::SSL OpenSSL::Crypto ${MINIUPNPC_LIBRARY} ZLIB::ZLIB Threads::Threads ${DL_LIB} ${CMAKE_REQUIRED_LIBRARIES})
|
||||
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})
|
||||
|
||||
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,18 +1,23 @@
|
||||
# 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 "-std=c++11")
|
||||
set(CMAKE_REQUIRED_FLAGS "${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() {
|
||||
return x;
|
||||
++z;
|
||||
++y;
|
||||
return ++x;
|
||||
}
|
||||
" ${varname})
|
||||
set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQUIRED_FLAGS})
|
||||
@@ -27,6 +32,7 @@ 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})
|
||||
@@ -34,15 +40,16 @@ int main() {
|
||||
endfunction(check_working_cxx_atomics64)
|
||||
|
||||
|
||||
# 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)
|
||||
# Check for (non-64-bit) atomic operations.
|
||||
if(MSVC)
|
||||
set(HAVE_CXX_ATOMICS_WITHOUT_LIB True)
|
||||
else()
|
||||
# First check if atomics work without the library.
|
||||
check_working_cxx_atomics(HAVE_CXX_ATOMICS_WITHOUT_LIB)
|
||||
# If not, check if the library exists, and atomics work with it.
|
||||
if(NOT HAVE_CXX_ATOMICS_WITHOUT_LIB)
|
||||
check_library_exists(atomic __atomic_fetch_add_4 "" HAVE_LIBATOMIC)
|
||||
if( HAVE_LIBATOMIC )
|
||||
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)
|
||||
@@ -58,20 +65,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)
|
||||
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!")
|
||||
# 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()
|
||||
else()
|
||||
message(FATAL_ERROR "Host compiler appears to require libatomic, but cannot find it.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -80,7 +87,6 @@ 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() {
|
||||
|
||||
55
build/cmake_modules/FindCheck.cmake
Normal file
55
build/cmake_modules/FindCheck.cmake
Normal file
@@ -0,0 +1,55 @@
|
||||
# - 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,4 +1,4 @@
|
||||
# Copyright (c) 2017-2022, The PurpleI2P Project
|
||||
# Copyright (c) 2017-2023, The PurpleI2P Project
|
||||
# This file is part of Purple i2pd project and licensed under BSD3
|
||||
# See full license text in LICENSE file at top of project tree
|
||||
|
||||
@@ -61,7 +61,7 @@ set(archdetect_c_code "
|
||||
#else
|
||||
#error cmake_ARCH mips
|
||||
#endif
|
||||
#elif defined(__ppc__) || defined(__ppc) || defined(__powerpc__) \\
|
||||
#elif defined(__ppc__) || defined(__ppc) || defined(__powerpc__) || defined(__POWERPC__) \\
|
||||
|| defined(_ARCH_COM) || defined(_ARCH_PWR) || defined(_ARCH_PPC) \\
|
||||
|| defined(_M_MPPC) || defined(_M_PPC)
|
||||
#if defined(__ppc64__) || defined(__powerpc64__) || defined(__64BIT__)
|
||||
@@ -83,13 +83,13 @@ function(target_architecture output_var)
|
||||
# First let's normalize the order of the values
|
||||
|
||||
# Note that it's not possible to compile PowerPC applications if you are using
|
||||
# the OS X SDK version 10.6 or later - you'll need 10.4/10.5 for that, so we
|
||||
# disable it by default
|
||||
# the OS X SDK version 10.7 or later - you'll need 10.4/10.5/10.6 for that, so we
|
||||
# disable it by default. Also, ppc64 is not supported in 10.6.
|
||||
# See this page for more information:
|
||||
# http://stackoverflow.com/questions/5333490/how-can-we-restore-ppc-ppc64-as-well-as-full-10-4-10-5-sdk-support-to-xcode-4
|
||||
|
||||
# Architecture defaults to i386 or ppc on OS X 10.5 and earlier, depending on the CPU type detected at runtime.
|
||||
# On OS X 10.6+ the default is x86_64 if the CPU supports it, i386 otherwise.
|
||||
# On OS X 10.6+ the default is x86_64 if the CPU supports it, i386 otherwise; 10.6 also supports ppc.
|
||||
|
||||
foreach(osx_arch ${CMAKE_OSX_ARCHITECTURES})
|
||||
if("${osx_arch}" STREQUAL "ppc" AND ppc_support)
|
||||
@@ -133,11 +133,11 @@ function(target_architecture output_var)
|
||||
enable_language(C)
|
||||
|
||||
# Detect the architecture in a rather creative way...
|
||||
# This compiles a small C program which is a series of ifdefs that selects a
|
||||
# particular #error preprocessor directive whose message string contains the
|
||||
# target architecture. The program will always fail to compile (both because
|
||||
# file is not a valid C program, and obviously because of the presence of the
|
||||
# #error preprocessor directives... but by exploiting the preprocessor in this
|
||||
# This compiles a small C program which is a series of ifdefs that selects
|
||||
# a particular #error preprocessor directive whose message string contains
|
||||
# the target architecture. The program will always fail to compile (both because
|
||||
# file is not a valid C program, and obviously because of the presence of
|
||||
# the #error preprocessor directives... but by exploiting the preprocessor in this
|
||||
# way, we can detect the correct target architecture even when cross-compiling,
|
||||
# since the program itself never needs to be run (only the compiler/preprocessor)
|
||||
try_run(
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
#
|
||||
# Copyright (c) 2017-2022, The PurpleI2P Project
|
||||
#
|
||||
# This file is part of Purple i2pd project and licensed under BSD3
|
||||
#
|
||||
# See full license text in LICENSE file at top of project tree
|
||||
#
|
||||
|
||||
FROM alpine:latest
|
||||
LABEL authors "Mikal Villa <mikal@sigterm.no>, Darknet Villain <supervillain@riseup.net>"
|
||||
LABEL authors="Mikal Villa <mikal@sigterm.no>, Darknet Villain <supervillain@riseup.net>"
|
||||
LABEL maintainer="R4SAS <r4sas@i2pmail.org>"
|
||||
|
||||
LABEL org.opencontainers.image.source=https://github.com/PurpleI2P/i2pd
|
||||
LABEL org.opencontainers.image.documentation=https://i2pd.readthedocs.io/en/latest/
|
||||
LABEL org.opencontainers.image.licenses=BSD3
|
||||
|
||||
# Expose git branch, tag and URL variables as arguments
|
||||
ARG GIT_BRANCH="openssl"
|
||||
@@ -11,27 +24,28 @@ ENV REPO_URL=${REPO_URL}
|
||||
|
||||
ENV I2PD_HOME="/home/i2pd"
|
||||
ENV DATA_DIR="${I2PD_HOME}/data"
|
||||
ENV DEFAULT_ARGS=" --datadir=$DATA_DIR --reseed.verify=true --upnp.enabled=false --http.enabled=true --http.address=0.0.0.0 --httpproxy.enabled=true --httpproxy.address=0.0.0.0 --socksproxy.enabled=true --socksproxy.address=0.0.0.0 --sam.enabled=true --sam.address=0.0.0.0"
|
||||
ENV DEFAULT_ARGS=" --datadir=$DATA_DIR"
|
||||
|
||||
RUN mkdir -p "$I2PD_HOME" "$DATA_DIR" \
|
||||
&& adduser -S -h "$I2PD_HOME" i2pd \
|
||||
&& chown -R i2pd:nobody "$I2PD_HOME"
|
||||
|
||||
#
|
||||
# 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.
|
||||
# 1. Building binary
|
||||
# Each RUN is a layer, adding the dependencies and building i2pd in one layer takes around 8-900Mb, so to keep the
|
||||
# image under 20mb we need to remove all the build dependencies in the same "RUN" / layer.
|
||||
#
|
||||
# 1. install deps, clone and build.
|
||||
# 2. strip binaries.
|
||||
# 3. Purge all dependencies and other unrelated packages, including build directory.
|
||||
|
||||
RUN apk update \
|
||||
&& apk --no-cache --virtual build-dependendencies add make gcc g++ libtool zlib-dev boost-dev build-base openssl-dev openssl miniupnpc-dev git \
|
||||
&& mkdir -p /tmp/build \
|
||||
&& cd /tmp/build && git clone -b ${GIT_BRANCH} ${REPO_URL} \
|
||||
&& cd i2pd \
|
||||
&& if [ -n "${GIT_TAG}" ]; then git checkout tags/${GIT_TAG}; fi \
|
||||
&& make USE_UPNP=yes \
|
||||
&& make -j$(nproc) USE_UPNP=yes \
|
||||
&& cp -R contrib/certificates /i2pd_certificates \
|
||||
&& mkdir -p /usr/local/bin \
|
||||
&& mv i2pd /usr/local/bin \
|
||||
@@ -45,6 +59,9 @@ RUN apk update \
|
||||
# 2. Adding required libraries to run i2pd to ensure it will run.
|
||||
RUN apk --no-cache add boost-filesystem boost-system boost-program_options boost-date_time boost-thread boost-iostreams openssl miniupnpc musl-utils libstdc++
|
||||
|
||||
# 3. Copy preconfigured config file and entrypoint
|
||||
COPY i2pd-docker.conf "$DATA_DIR/i2pd.conf"
|
||||
RUN chown i2pd:nobody "$DATA_DIR/i2pd.conf"
|
||||
COPY entrypoint.sh /entrypoint.sh
|
||||
RUN chmod a+x /entrypoint.sh
|
||||
|
||||
|
||||
52
contrib/docker/i2pd-docker.conf
Normal file
52
contrib/docker/i2pd-docker.conf
Normal file
@@ -0,0 +1,52 @@
|
||||
## Preconfigured i2pd configuration file for a Docker container
|
||||
## See https://i2pd.readthedocs.io/en/latest/user-guide/configuration/
|
||||
## for more options you can use in this file.
|
||||
|
||||
## Note that for exposing ports outside of container you need to bind all services to 0.0.0.0
|
||||
|
||||
log = file
|
||||
loglevel = none
|
||||
|
||||
ipv4 = true
|
||||
ipv6 = false
|
||||
|
||||
# bandwidth = L
|
||||
# notransit = false
|
||||
# floodfill = false
|
||||
|
||||
[ntcp2]
|
||||
enabled = true
|
||||
published = true
|
||||
|
||||
[ssu2]
|
||||
enabled = true
|
||||
published = true
|
||||
|
||||
[http]
|
||||
enabled = true
|
||||
address = 0.0.0.0
|
||||
port = 7070
|
||||
|
||||
[httpproxy]
|
||||
enabled = true
|
||||
address = 0.0.0.0
|
||||
port = 4444
|
||||
|
||||
[socksproxy]
|
||||
enabled = true
|
||||
address = 0.0.0.0
|
||||
port = 4447
|
||||
|
||||
[sam]
|
||||
enabled = true
|
||||
address = 0.0.0.0
|
||||
port = 7656
|
||||
|
||||
[upnp]
|
||||
enabled = false
|
||||
|
||||
[reseed]
|
||||
verify = true
|
||||
|
||||
[limits]
|
||||
# transittunnels = 2500
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,29 +1,29 @@
|
||||
`xgettext` command for extracting translation
|
||||
---
|
||||
|
||||
```
|
||||
xgettext --omit-header -ctr: -ktr -ktr:1,2 daemon/HTTPServer.cpp libi2pd_client/HTTPProxy.cpp
|
||||
```
|
||||
|
||||
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", "$4", "$6", "$8", "$10"}},\n
|
||||
```
|
||||
|
||||
```
|
||||
in: msgid\ \"(.*)\"\nmsgstr\ \"(.*)\"\n
|
||||
out: {"$1", "$2"},\n
|
||||
```
|
||||
|
||||
```
|
||||
in: ^#[:.](.*)$\n
|
||||
out: <to empty line>
|
||||
```
|
||||
|
||||
```
|
||||
in: \n\n
|
||||
out: \n
|
||||
```
|
||||
`xgettext` command for extracting translation
|
||||
---
|
||||
|
||||
```
|
||||
xgettext --omit-header -ctr: -ktr -ktr:1,2 daemon/HTTPServer.cpp libi2pd_client/HTTPProxy.cpp
|
||||
```
|
||||
|
||||
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\ \"(.*)\"\nmsgstr\ \"(.*)\"\n
|
||||
out: {"$1", "$2"},\n
|
||||
```
|
||||
|
||||
```
|
||||
in: ^#[:.,](.*)$\n
|
||||
out: <to empty line>
|
||||
```
|
||||
|
||||
```
|
||||
in: \n\n
|
||||
out: \n
|
||||
```
|
||||
|
||||
@@ -75,7 +75,7 @@ ipv4 = true
|
||||
## Enable communication through ipv6
|
||||
ipv6 = false
|
||||
|
||||
## Enable SSU transport (default = true)
|
||||
## Enable SSU transport
|
||||
ssu = false
|
||||
|
||||
## Bandwidth configuration
|
||||
@@ -105,10 +105,10 @@ ssu = false
|
||||
# port = 4567
|
||||
|
||||
[ssu2]
|
||||
## Enable SSU2 transport (default = false for 2.43.0)
|
||||
enabled = true
|
||||
## Publish address in RouterInfo (default = false for 2.43.0)
|
||||
published = true
|
||||
## 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
|
||||
|
||||
@@ -122,12 +122,15 @@ 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, french,
|
||||
## german, russian, turkmen, ukrainian and uzbek languages
|
||||
## Currently supported english (default), afrikaans, armenian, chinese, czech, french,
|
||||
## german, italian, polish, portuguese, russian, spanish, turkish, turkmen, ukrainian
|
||||
## and uzbek languages
|
||||
# lang = english
|
||||
|
||||
[httpproxy]
|
||||
@@ -139,6 +142,8 @@ 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
|
||||
@@ -237,8 +242,9 @@ 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:2500)
|
||||
# transittunnels = 2500
|
||||
## Maximum active transit sessions (default: 5000)
|
||||
## This value is doubled if floodfill mode is enabled!
|
||||
# transittunnels = 5000
|
||||
## Limit number of open file descriptors (0 - use system limit)
|
||||
# openfiles = 0
|
||||
## Maximum size of corefile in Kb (0 - use system limit)
|
||||
|
||||
@@ -29,7 +29,7 @@ SendSIGKILL=yes
|
||||
#TimeoutStopSec=10m
|
||||
|
||||
# If you have problems with hanging i2pd, you can try increase this
|
||||
LimitNOFILE=4096
|
||||
LimitNOFILE=8192
|
||||
# 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.43.0
|
||||
Version: 2.46.1
|
||||
Release: git%{git_hash}%{?dist}
|
||||
Summary: I2P router written in C++
|
||||
Conflicts: i2pd
|
||||
@@ -158,6 +158,21 @@ getent passwd i2pd >/dev/null || \
|
||||
|
||||
|
||||
%changelog
|
||||
* Mon Feb 20 2023 r4sas <r4sas@i2pmail.org> - 2.46.1
|
||||
- update to 2.46.1
|
||||
|
||||
* Wed Feb 15 2023 orignal <orignal@i2pmail.org> - 2.46.0
|
||||
- update to 2.46.0
|
||||
|
||||
* 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
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Name: i2pd
|
||||
Version: 2.43.0
|
||||
Release: 1%{?dist}
|
||||
Version: 2.46.1
|
||||
Release: 2%{?dist}
|
||||
Summary: I2P router written in C++
|
||||
Conflicts: i2pd-git
|
||||
|
||||
@@ -155,6 +155,21 @@ getent passwd i2pd >/dev/null || \
|
||||
|
||||
|
||||
%changelog
|
||||
* Mon Feb 20 2023 r4sas <r4sas@i2pmail.org> - 2.46.1
|
||||
- update to 2.46.1
|
||||
|
||||
* Wed Feb 15 2023 orignal <orignal@i2pmail.org> - 2.46.0
|
||||
- update to 2.46.0
|
||||
|
||||
* 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
|
||||
|
||||
|
||||
@@ -1,293 +1,293 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2023, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -31,7 +31,6 @@
|
||||
#include "Crypto.h"
|
||||
#include "UPnP.h"
|
||||
#include "Timestamp.h"
|
||||
#include "util.h"
|
||||
#include "I18N.h"
|
||||
|
||||
namespace i2p
|
||||
@@ -158,128 +157,29 @@ namespace util
|
||||
precomputation = false; // we don't elgamal table if no ssu, unless it's specified explicitly
|
||||
i2p::crypto::InitCrypto (precomputation, aesni, avx, forceCpuExt);
|
||||
|
||||
i2p::transport::InitAddressFromIface (); // get address4/6 from interfaces
|
||||
|
||||
int netID; i2p::config::GetOption("netid", netID);
|
||||
i2p::context.SetNetID (netID);
|
||||
i2p::context.Init ();
|
||||
|
||||
bool ipv6; i2p::config::GetOption("ipv6", ipv6);
|
||||
bool ipv4; i2p::config::GetOption("ipv4", ipv4);
|
||||
|
||||
// ifname -> address
|
||||
std::string ifname; i2p::config::GetOption("ifname", ifname);
|
||||
if (ipv4 && i2p::config::IsDefault ("address4"))
|
||||
{
|
||||
std::string ifname4; i2p::config::GetOption("ifname4", ifname4);
|
||||
if (!ifname4.empty ())
|
||||
i2p::config::SetOption ("address4", i2p::util::net::GetInterfaceAddress(ifname4, false).to_string ()); // v4
|
||||
else if (!ifname.empty ())
|
||||
i2p::config::SetOption ("address4", i2p::util::net::GetInterfaceAddress(ifname, false).to_string ()); // v4
|
||||
}
|
||||
if (ipv6 && i2p::config::IsDefault ("address6"))
|
||||
{
|
||||
std::string ifname6; i2p::config::GetOption("ifname6", ifname6);
|
||||
if (!ifname6.empty ())
|
||||
i2p::config::SetOption ("address6", i2p::util::net::GetInterfaceAddress(ifname6, true).to_string ()); // v6
|
||||
else if (!ifname.empty ())
|
||||
i2p::config::SetOption ("address6", i2p::util::net::GetInterfaceAddress(ifname, true).to_string ()); // v6
|
||||
}
|
||||
|
||||
bool ygg; i2p::config::GetOption("meshnets.yggdrasil", ygg);
|
||||
boost::asio::ip::address_v6 yggaddr;
|
||||
if (ygg)
|
||||
{
|
||||
std::string yggaddress; i2p::config::GetOption ("meshnets.yggaddress", yggaddress);
|
||||
if (!yggaddress.empty ())
|
||||
{
|
||||
yggaddr = boost::asio::ip::address_v6::from_string (yggaddress);
|
||||
if (yggaddr.is_unspecified () || !i2p::util::net::IsYggdrasilAddress (yggaddr) ||
|
||||
!i2p::util::net::IsLocalAddress (yggaddr))
|
||||
{
|
||||
LogPrint(eLogWarning, "Daemon: Can't find Yggdrasil address ", yggaddress);
|
||||
ygg = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
yggaddr = i2p::util::net::GetYggdrasilAddress ();
|
||||
if (yggaddr.is_unspecified ())
|
||||
{
|
||||
LogPrint(eLogWarning, "Daemon: Yggdrasil is not running. Disabled");
|
||||
ygg = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t port; i2p::config::GetOption("port", port);
|
||||
if (!i2p::config::IsDefault("port"))
|
||||
{
|
||||
LogPrint(eLogInfo, "Daemon: Accepting incoming connections at port ", port);
|
||||
i2p::context.UpdatePort (port);
|
||||
}
|
||||
i2p::context.SetSupportsV6 (ipv6);
|
||||
i2p::context.SetSupportsV4 (ipv4);
|
||||
i2p::context.SetSupportsMesh (ygg, yggaddr);
|
||||
|
||||
i2p::context.RemoveNTCPAddress (!ipv6); // TODO: remove later
|
||||
bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2);
|
||||
if (ntcp2)
|
||||
{
|
||||
bool published; i2p::config::GetOption("ntcp2.published", published);
|
||||
if (published)
|
||||
{
|
||||
std::string ntcp2proxy; i2p::config::GetOption("ntcp2.proxy", ntcp2proxy);
|
||||
if (!ntcp2proxy.empty ()) published = false;
|
||||
}
|
||||
if (published)
|
||||
{
|
||||
uint16_t ntcp2port; i2p::config::GetOption("ntcp2.port", ntcp2port);
|
||||
if (!ntcp2port) ntcp2port = port; // use standard port
|
||||
i2p::context.PublishNTCP2Address (ntcp2port, true, ipv4, ipv6, false); // publish
|
||||
if (ipv6)
|
||||
{
|
||||
std::string ipv6Addr; i2p::config::GetOption("ntcp2.addressv6", ipv6Addr);
|
||||
auto addr = boost::asio::ip::address_v6::from_string (ipv6Addr);
|
||||
if (!addr.is_unspecified () && addr != boost::asio::ip::address_v6::any ())
|
||||
i2p::context.UpdateNTCP2V6Address (addr); // set ipv6 address if configured
|
||||
}
|
||||
}
|
||||
else
|
||||
i2p::context.PublishNTCP2Address (port, false, ipv4, ipv6, false); // unpublish
|
||||
}
|
||||
if (ygg)
|
||||
{
|
||||
i2p::context.PublishNTCP2Address (port, true, false, false, true);
|
||||
i2p::context.UpdateNTCP2V6Address (yggaddr);
|
||||
if (!ipv4 && !ipv6)
|
||||
i2p::context.SetStatus (eRouterStatusMesh);
|
||||
}
|
||||
if (!ssu) i2p::context.RemoveSSUAddress (); // TODO: remove later
|
||||
bool ssu2; i2p::config::GetOption("ssu2.enabled", ssu2);
|
||||
if (ssu2)
|
||||
{
|
||||
uint16_t ssu2port; i2p::config::GetOption("ssu2.port", ssu2port);
|
||||
if (!ssu2port) ssu2port = ssu ? (port + 1) : port;
|
||||
bool published; i2p::config::GetOption("ssu2.published", published);
|
||||
if (published)
|
||||
i2p::context.PublishSSU2Address (ssu2port, true, ipv4, ipv6); // publish
|
||||
else
|
||||
i2p::context.PublishSSU2Address (ssu2port, false, ipv4, ipv6); // unpublish
|
||||
}
|
||||
|
||||
bool transit; i2p::config::GetOption("notransit", transit);
|
||||
i2p::context.SetAcceptsTunnels (!transit);
|
||||
uint16_t transitTunnels; i2p::config::GetOption("limits.transittunnels", transitTunnels);
|
||||
SetMaxNumTransitTunnels (transitTunnels);
|
||||
i2p::transport::InitTransports ();
|
||||
|
||||
bool isFloodfill; i2p::config::GetOption("floodfill", isFloodfill);
|
||||
if (isFloodfill) {
|
||||
if (isFloodfill)
|
||||
{
|
||||
LogPrint(eLogInfo, "Daemon: Router configured as floodfill");
|
||||
i2p::context.SetFloodfill (true);
|
||||
}
|
||||
else
|
||||
{
|
||||
i2p::context.SetFloodfill (false);
|
||||
}
|
||||
|
||||
bool transit; i2p::config::GetOption("notransit", transit);
|
||||
i2p::context.SetAcceptsTunnels (!transit);
|
||||
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);
|
||||
|
||||
/* this section also honors 'floodfill' flag, if set above */
|
||||
std::string bandwidth; i2p::config::GetOption("bandwidth", bandwidth);
|
||||
@@ -399,15 +299,14 @@ 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(!ssu) LogPrint(eLogInfo, "Daemon: SSU disabled");
|
||||
if(!ssu2) LogPrint(eLogInfo, "Daemon: SSU2 disabled");
|
||||
if(!ntcp2) LogPrint(eLogInfo, "Daemon: NTCP2 disabled");
|
||||
|
||||
i2p::transport::transports.SetCheckReserved(checkInReserved);
|
||||
i2p::transport::transports.Start(ntcp2, ssu, ssu2);
|
||||
if (i2p::transport::transports.IsBoundSSU() || i2p::transport::transports.IsBoundSSU2() || i2p::transport::transports.IsBoundNTCP2())
|
||||
i2p::transport::transports.Start(ntcp2, ssu2);
|
||||
if (i2p::transport::transports.IsBoundSSU2() || i2p::transport::transports.IsBoundNTCP2())
|
||||
LogPrint(eLogInfo, "Daemon: Transports started");
|
||||
else
|
||||
{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2023, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -80,15 +80,13 @@ namespace http {
|
||||
const char HTTP_COMMAND_SHUTDOWN_CANCEL[] = "shutdown_cancel";
|
||||
const char HTTP_COMMAND_SHUTDOWN_NOW[] = "terminate";
|
||||
const char HTTP_COMMAND_RUN_PEER_TEST[] = "run_peer_test";
|
||||
const char HTTP_COMMAND_RELOAD_CONFIG[] = "reload_config";
|
||||
const char HTTP_COMMAND_RELOAD_TUNNELS_CONFIG[] = "reload_tunnels_config";
|
||||
const char HTTP_COMMAND_LOGLEVEL[] = "set_loglevel";
|
||||
const char HTTP_COMMAND_KILLSTREAM[] = "closestream";
|
||||
const char HTTP_COMMAND_LIMITTRANSIT[] = "limittransit";
|
||||
const char HTTP_COMMAND_GET_REG_STRING[] = "get_reg_string";
|
||||
const char HTTP_COMMAND_SETLANGUAGE[] = "setlanguage";
|
||||
const char HTTP_COMMAND_RELOAD_CSS[] = "reload_css";
|
||||
const char HTTP_PARAM_SAM_SESSION_ID[] = "id";
|
||||
const char HTTP_PARAM_ADDRESS[] = "address";
|
||||
|
||||
static std::string ConvertTime (uint64_t time)
|
||||
{
|
||||
@@ -105,18 +103,18 @@ namespace http {
|
||||
int num;
|
||||
|
||||
if ((num = seconds / 86400) > 0) {
|
||||
s << num << " " << tr("day", "days", num) << ", ";
|
||||
s << ntr("%d day", "%d days", num, num) << ", ";
|
||||
seconds -= num * 86400;
|
||||
}
|
||||
if ((num = seconds / 3600) > 0) {
|
||||
s << num << " " << tr("hour", "hours", num) << ", ";
|
||||
s << ntr("%d hour", "%d hours", num, num) << ", ";
|
||||
seconds -= num * 3600;
|
||||
}
|
||||
if ((num = seconds / 60) > 0) {
|
||||
s << num << " " << tr("minute", "minutes", num) << ", ";
|
||||
s << ntr("%d minute", "%d minutes", num, num) << ", ";
|
||||
seconds -= num * 60;
|
||||
}
|
||||
s << seconds << " " << tr("second", "seconds", seconds);
|
||||
s << ntr("%d second", "%d seconds", seconds, seconds);
|
||||
}
|
||||
|
||||
static void ShowTraffic (std::stringstream& s, uint64_t bytes)
|
||||
@@ -124,11 +122,11 @@ namespace http {
|
||||
s << std::fixed << std::setprecision(2);
|
||||
auto numKBytes = (double) bytes / 1024;
|
||||
if (numKBytes < 1024)
|
||||
s << numKBytes << " " << tr(/* tr: Kibibit */ "KiB");
|
||||
s << tr(/* tr: Kibibyte */ "%.2f KiB", numKBytes);
|
||||
else if (numKBytes < 1024 * 1024)
|
||||
s << numKBytes / 1024 << " " << tr(/* tr: Mebibit */ "MiB");
|
||||
s << tr(/* tr: Mebibyte */ "%.2f MiB", numKBytes / 1024);
|
||||
else
|
||||
s << numKBytes / 1024 / 1024 << " " << tr(/* tr: Gibibit */ "GiB");
|
||||
s << tr(/* tr: Gibibyte */ "%.2f GiB", numKBytes / 1024 / 1024);
|
||||
}
|
||||
|
||||
static void ShowTunnelDetails (std::stringstream& s, enum i2p::tunnel::TunnelState eState, bool explr, int bytes)
|
||||
@@ -152,7 +150,8 @@ namespace http {
|
||||
else stateText = tr("unknown");
|
||||
|
||||
s << "<span class=\"tunnel " << state << "\"> " << stateText << ((explr) ? " (" + tr("exploratory") + ")" : "") << "</span>, ";
|
||||
s << " " << (int) (bytes / 1024) << " " << tr(/* tr: Kibibit */ "KiB") << "\r\n";
|
||||
ShowTraffic(s, bytes);
|
||||
s << "\r\n";
|
||||
}
|
||||
|
||||
static void SetLogLevel (const std::string& level)
|
||||
@@ -200,7 +199,7 @@ namespace http {
|
||||
if (i2p::context.AcceptsTunnels () || i2p::tunnel::tunnels.CountTransitTunnels())
|
||||
s << " <a href=\"" << webroot << "?page=" << HTTP_PAGE_TRANSIT_TUNNELS << "\">" << tr("Transit Tunnels") << "</a><br>\r\n";
|
||||
s <<
|
||||
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_TRANSPORTS << "\">" << tr ("Transports") << "</a><br>\r\n"
|
||||
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_TRANSPORTS << "\">" << tr("Transports") << "</a><br>\r\n"
|
||||
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_I2P_TUNNELS << "\">" << tr("I2P tunnels") << "</a><br>\r\n";
|
||||
if (i2p::client::context.GetSAMBridge ())
|
||||
s << " <a href=\"" << webroot << "?page=" << HTTP_PAGE_SAM_SESSIONS << "\">" << tr("SAM sessions") << "</a><br>\r\n";
|
||||
@@ -232,26 +231,30 @@ namespace http {
|
||||
case eRouterStatusUnknown: s << tr("Unknown"); break;
|
||||
case eRouterStatusProxy: s << tr("Proxy"); break;
|
||||
case eRouterStatusMesh: s << tr("Mesh"); break;
|
||||
case eRouterStatusError:
|
||||
{
|
||||
s << tr("Error");
|
||||
switch (error)
|
||||
{
|
||||
case eRouterErrorClockSkew:
|
||||
s << " - " << tr("Clock skew");
|
||||
break;
|
||||
case eRouterErrorOffline:
|
||||
s << " - " << tr("Offline");
|
||||
break;
|
||||
case eRouterErrorSymmetricNAT:
|
||||
s << " - " << tr("Symmetric NAT");
|
||||
break;
|
||||
default: ;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: s << tr("Unknown");
|
||||
}
|
||||
if (error != eRouterErrorNone)
|
||||
{
|
||||
switch (error)
|
||||
{
|
||||
case eRouterErrorClockSkew:
|
||||
s << " - " << tr("Clock skew");
|
||||
break;
|
||||
case eRouterErrorOffline:
|
||||
s << " - " << tr("Offline");
|
||||
break;
|
||||
case eRouterErrorSymmetricNAT:
|
||||
s << " - " << tr("Symmetric NAT");
|
||||
break;
|
||||
case eRouterErrorFullConeNAT:
|
||||
s << " - " << tr("Full cone NAT");
|
||||
break;
|
||||
case eRouterErrorNoDescriptors:
|
||||
s << " - " << tr("No Descriptors");
|
||||
break;
|
||||
default: ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ShowStatus (std::stringstream& s, bool includeHiddenContent, i2p::http::OutputFormatEnum outputFormat)
|
||||
@@ -286,60 +289,69 @@ namespace http {
|
||||
if (family.length () > 0)
|
||||
s << "<b>"<< tr("Family") << ":</b> " << family << "<br>\r\n";
|
||||
s << "<b>" << tr("Tunnel creation success rate") << ":</b> " << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate () << "%<br>\r\n";
|
||||
bool isTotalTCSR;
|
||||
i2p::config::GetOption("http.showTotalTCSR", isTotalTCSR);
|
||||
if (isTotalTCSR) {
|
||||
s << "<b>" << tr("Total tunnel creation success rate") << ":</b> " << i2p::tunnel::tunnels.GetTotalTunnelCreationSuccessRate() << "%<br/>\r\n";
|
||||
}
|
||||
s << "<b>" << tr("Received") << ":</b> ";
|
||||
ShowTraffic (s, i2p::transport::transports.GetTotalReceivedBytes ());
|
||||
s << " (" << (double) i2p::transport::transports.GetInBandwidth () / 1024 << " " << tr(/* tr: Kibibit/s */ "KiB/s") << ")<br>\r\n";
|
||||
s << " (" << tr(/* tr: Kibibyte/s */ "%.2f KiB/s", (double) i2p::transport::transports.GetInBandwidth15s () / 1024) << ")<br>\r\n";
|
||||
s << "<b>" << tr("Sent") << ":</b> ";
|
||||
ShowTraffic (s, i2p::transport::transports.GetTotalSentBytes ());
|
||||
s << " (" << (double) i2p::transport::transports.GetOutBandwidth () / 1024 << " " << tr(/* tr: Kibibit/s */ "KiB/s") << ")<br>\r\n";
|
||||
s << " (" << tr(/* tr: Kibibyte/s */ "%.2f KiB/s", (double) i2p::transport::transports.GetOutBandwidth15s () / 1024) << ")<br>\r\n";
|
||||
s << "<b>" << tr("Transit") << ":</b> ";
|
||||
ShowTraffic (s, i2p::transport::transports.GetTotalTransitTransmittedBytes ());
|
||||
s << " (" << (double) i2p::transport::transports.GetTransitBandwidth () / 1024 << " " << tr(/* tr: Kibibit/s */ "KiB/s") << ")<br>\r\n";
|
||||
s << " (" << tr(/* tr: Kibibyte/s */ "%.2f KiB/s", (double) i2p::transport::transports.GetTransitBandwidth15s () / 1024) << ")<br>\r\n";
|
||||
s << "<b>" << tr("Data path") << ":</b> " << i2p::fs::GetUTF8DataDir() << "<br>\r\n";
|
||||
s << "<div class='slide'>";
|
||||
if ((outputFormat == OutputFormatEnum::forWebConsole) || !includeHiddenContent) {
|
||||
s << "<label for=\"slide-info\">" << tr("Hidden content. Press on text to see.") << "</label>\r\n<input type=\"checkbox\" id=\"slide-info\" />\r\n<div class=\"slidecontent\">\r\n";
|
||||
}
|
||||
if (includeHiddenContent) {
|
||||
if (includeHiddenContent)
|
||||
{
|
||||
s << "<b>" << tr("Router Ident") << ":</b> " << i2p::context.GetRouterInfo().GetIdentHashBase64() << "<br>\r\n";
|
||||
if (!i2p::context.GetRouterInfo().GetProperty("family").empty())
|
||||
s << "<b>" << tr("Router Family") << ":</b> " << i2p::context.GetRouterInfo().GetProperty("family") << "<br>\r\n";
|
||||
s << "<b>" << tr("Router Caps") << ":</b> " << i2p::context.GetRouterInfo().GetProperty("caps") << "<br>\r\n";
|
||||
s << "<b>" << tr("Version") << ":</b> " VERSION "<br>\r\n";
|
||||
s << "<b>"<< tr("Our external address") << ":</b>" << "<br>\r\n<table class=\"extaddr\"><tbody>\r\n";
|
||||
for (const auto& address : i2p::context.GetRouterInfo().GetAddresses())
|
||||
auto addresses = i2p::context.GetRouterInfo().GetAddresses ();
|
||||
if (addresses)
|
||||
{
|
||||
s << "<tr>\r\n<td>";
|
||||
switch (address->transportStyle)
|
||||
for (const auto& address : *addresses)
|
||||
{
|
||||
case i2p::data::RouterInfo::eTransportNTCP:
|
||||
s << "NTCP2";
|
||||
break;
|
||||
case i2p::data::RouterInfo::eTransportSSU:
|
||||
s << "SSU";
|
||||
break;
|
||||
case i2p::data::RouterInfo::eTransportSSU2:
|
||||
s << "SSU2";
|
||||
break;
|
||||
default:
|
||||
s << tr("Unknown");
|
||||
}
|
||||
if (address->IsV6 ())
|
||||
{
|
||||
if (address->IsV4 ()) s << "v4";
|
||||
s << "v6";
|
||||
}
|
||||
s << "</td>\r\n";
|
||||
if (address->published)
|
||||
s << "<td>" << address->host.to_string() << ":" << address->port << "</td>\r\n";
|
||||
else
|
||||
{
|
||||
s << "<td>" << tr("supported");
|
||||
if (address->port)
|
||||
s << " :" << address->port;
|
||||
if (!address) continue;
|
||||
s << "<tr>\r\n<td>";
|
||||
switch (address->transportStyle)
|
||||
{
|
||||
case i2p::data::RouterInfo::eTransportNTCP2:
|
||||
s << "NTCP2";
|
||||
break;
|
||||
case i2p::data::RouterInfo::eTransportSSU2:
|
||||
s << "SSU2";
|
||||
break;
|
||||
default:
|
||||
s << tr("Unknown");
|
||||
}
|
||||
bool v6 = address->IsV6 ();
|
||||
if (v6)
|
||||
{
|
||||
if (address->IsV4 ()) s << "v4";
|
||||
s << "v6";
|
||||
}
|
||||
s << "</td>\r\n";
|
||||
if (address->published)
|
||||
s << "<td>" << (v6 ? "[" : "") << address->host.to_string() << (v6 ? "]:" : ":") << address->port << "</td>\r\n";
|
||||
else
|
||||
{
|
||||
s << "<td>" << tr(/* tr: Shown when router doesn't publish itself and have "Firewalled" state */ "supported");
|
||||
if (address->port)
|
||||
s << " :" << address->port;
|
||||
s << "</td>\r\n";
|
||||
}
|
||||
s << "</tr>\r\n";
|
||||
}
|
||||
s << "</tr>\r\n";
|
||||
}
|
||||
s << "</tbody></table>\r\n";
|
||||
}
|
||||
@@ -461,7 +473,7 @@ namespace http {
|
||||
}
|
||||
s << "⇒ " << it->GetTunnelID () << ":me";
|
||||
if (it->LatencyIsKnown())
|
||||
s << " ( " << it->GetMeanLatency() << tr(/* tr: Milliseconds */ "ms") << " )";
|
||||
s << " ( " << tr(/* tr: Milliseconds */ "%dms", it->GetMeanLatency()) << " )";
|
||||
ShowTunnelDetails(s, it->GetState (), false, it->GetNumReceivedBytes ());
|
||||
s << "</div>\r\n";
|
||||
}
|
||||
@@ -481,22 +493,26 @@ namespace http {
|
||||
);
|
||||
}
|
||||
if (it->LatencyIsKnown())
|
||||
s << " ( " << it->GetMeanLatency() << tr("ms") << " )";
|
||||
s << " ( " << tr("%dms", it->GetMeanLatency()) << " )";
|
||||
ShowTunnelDetails(s, it->GetState (), false, it->GetNumSentBytes ());
|
||||
s << "</div>\r\n";
|
||||
}
|
||||
}
|
||||
s << "<br>\r\n";
|
||||
|
||||
s << "<b>" << tr("Tags") << "</b><br>\r\n" << tr("Incoming") << ": <i>" << dest->GetNumIncomingTags () << "</i><br>\r\n";
|
||||
s << "<b>" << tr("Tags") << "</b><br>\r\n"
|
||||
<< tr("Incoming") << ": <i>" << dest->GetNumIncomingTags () << "</i><br>\r\n";
|
||||
if (!dest->GetSessions ().empty ()) {
|
||||
std::stringstream tmp_s; uint32_t out_tags = 0;
|
||||
for (const auto& it: dest->GetSessions ()) {
|
||||
tmp_s << "<tr><td>" << i2p::client::context.GetAddressBook ().ToAddress(it.first) << "</td><td>" << it.second->GetNumOutgoingTags () << "</td></tr>\r\n";
|
||||
out_tags += it.second->GetNumOutgoingTags ();
|
||||
}
|
||||
s << "<div class='slide'><label for='slide-tags'>" << tr("Outgoing") << ": <i>" << out_tags << "</i></label>\r\n<input type=\"checkbox\" id=\"slide-tags\" />\r\n"
|
||||
<< "<div class=\"slidecontent\">\r\n<table>\r\n<thead><th>" << tr("Destination") << "</th><th>" << tr("Amount") << "</th></thead>\r\n<tbody class=\"tableitem\">\r\n" << tmp_s.str () << "</tbody></table>\r\n</div>\r\n</div>\r\n";
|
||||
s << "<div class='slide'><label for='slide-tags'>" << tr("Outgoing") << ": <i>" << out_tags << "</i></label>\r\n"
|
||||
<< "<input type=\"checkbox\" id=\"slide-tags\" />\r\n"
|
||||
<< "<div class=\"slidecontent\">\r\n"
|
||||
<< "<table>\r\n<thead><th>" << tr("Destination") << "</th><th>" << tr("Amount") << "</th></thead>\r\n"
|
||||
<< "<tbody class=\"tableitem\">\r\n" << tmp_s.str () << "</tbody></table>\r\n</div>\r\n</div>\r\n";
|
||||
} else
|
||||
s << tr("Outgoing") << ": <i>0</i><br>\r\n";
|
||||
s << "<br>\r\n";
|
||||
@@ -511,8 +527,11 @@ namespace http {
|
||||
tmp_s << "<tr><td>" << i2p::client::context.GetAddressBook ().ToAddress(it.second->GetDestination ()) << "</td><td>" << it.second->GetState () << "</td></tr>\r\n";
|
||||
ecies_sessions++;
|
||||
}
|
||||
s << "<div class='slide'><label for='slide-ecies-sessions'>" << tr("Tags sessions") << ": <i>" << ecies_sessions << "</i></label>\r\n<input type=\"checkbox\" id=\"slide-ecies-sessions\" />\r\n"
|
||||
<< "<div class=\"slidecontent\">\r\n<table>\r\n<thead><th>" << tr("Destination") << "</th><th>" << tr("Status") << "</th></thead>\r\n<tbody class=\"tableitem\">\r\n" << tmp_s.str () << "</tbody></table>\r\n</div>\r\n</div>\r\n";
|
||||
s << "<div class='slide'><label for='slide-ecies-sessions'>" << tr("Tags sessions") << ": <i>" << ecies_sessions << "</i></label>\r\n"
|
||||
<< "<input type=\"checkbox\" id=\"slide-ecies-sessions\" />\r\n"
|
||||
<< "<div class=\"slidecontent\">\r\n<table>\r\n"
|
||||
<< "<thead><th>" << tr("Destination") << "</th><th>" << tr("Status") << "</th></thead>\r\n"
|
||||
<< "<tbody class=\"tableitem\">\r\n" << tmp_s.str () << "</tbody></table>\r\n</div>\r\n</div>\r\n";
|
||||
} else
|
||||
s << tr("Tags sessions") << ": <i>0</i><br>\r\n";
|
||||
s << "<br>\r\n";
|
||||
@@ -535,7 +554,7 @@ namespace http {
|
||||
<< tr("Streams")
|
||||
<< "</caption>\r\n<thead>\r\n<tr>"
|
||||
<< "<th style=\"width:25px;\">StreamID</th>"
|
||||
<< "<th style=\"width:5px;\" \\>" // Stream closing button column
|
||||
<< "<th style=\"width:5px;\"> </th>" // Stream closing button column
|
||||
<< "<th class=\"streamdest\">Destination</th>"
|
||||
<< "<th>Sent</th>"
|
||||
<< "<th>Received</th>"
|
||||
@@ -606,7 +625,10 @@ namespace http {
|
||||
if (storeType == i2p::data::NETDB_STORE_TYPE_LEASESET)
|
||||
ls.reset (new i2p::data::LeaseSet (leaseSet->GetBuffer(), leaseSet->GetBufferLen()));
|
||||
else
|
||||
ls.reset (new i2p::data::LeaseSet2 (storeType, leaseSet->GetBuffer(), leaseSet->GetBufferLen()));
|
||||
{
|
||||
ls.reset (new i2p::data::LeaseSet2 (storeType));
|
||||
ls->Update (leaseSet->GetBuffer(), leaseSet->GetBufferLen(), false);
|
||||
}
|
||||
if (!ls) return;
|
||||
s << "<div class=\"leaseset listitem";
|
||||
if (ls->IsExpired())
|
||||
@@ -637,7 +659,7 @@ namespace http {
|
||||
}
|
||||
else if (!i2p::context.IsFloodfill ())
|
||||
{
|
||||
s << "<b>" << tr("LeaseSets") << ":</b> " << tr("not floodfill") << ".<br>\r\n";
|
||||
s << "<b>" << tr("LeaseSets") << ":</b> " << tr(/* Message on LeaseSets page */ "floodfill mode is disabled") << ".<br>\r\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -666,7 +688,7 @@ namespace http {
|
||||
}
|
||||
s << "⇒ " << it->GetTunnelID () << ":me";
|
||||
if (it->LatencyIsKnown())
|
||||
s << " ( " << it->GetMeanLatency() << tr("ms") << " )";
|
||||
s << " ( " << tr("%dms", it->GetMeanLatency()) << " )";
|
||||
ShowTunnelDetails(s, it->GetState (), (it->GetTunnelPool () == ExplPool), it->GetNumReceivedBytes ());
|
||||
s << "</div>\r\n";
|
||||
}
|
||||
@@ -686,7 +708,7 @@ namespace http {
|
||||
);
|
||||
}
|
||||
if (it->LatencyIsKnown())
|
||||
s << " ( " << it->GetMeanLatency() << tr("ms") << " )";
|
||||
s << " ( " << tr("%dms", it->GetMeanLatency()) << " )";
|
||||
ShowTunnelDetails(s, it->GetState (), (it->GetTunnelPool () == ExplPool), it->GetNumSentBytes ());
|
||||
s << "</div>\r\n";
|
||||
}
|
||||
@@ -699,8 +721,7 @@ namespace http {
|
||||
|
||||
s << "<b>" << tr("Router commands") << "</b><br>\r\n<br>\r\n<div class=\"commands\">\r\n";
|
||||
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_RUN_PEER_TEST << "&token=" << token << "\">" << tr("Run peer test") << "</a><br>\r\n";
|
||||
|
||||
// s << " <a href=\"/?cmd=" << HTTP_COMMAND_RELOAD_CONFIG << "\">Reload config</a><br>\r\n";
|
||||
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_RELOAD_TUNNELS_CONFIG << "&token=" << token << "\">" << tr("Reload tunnels configuration") << "</a><br>\r\n";
|
||||
|
||||
if (i2p::context.AcceptsTunnels ())
|
||||
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_DISABLE_TRANSIT << "&token=" << token << "\">" << tr("Decline transit tunnels") << "</a><br>\r\n";
|
||||
@@ -725,19 +746,20 @@ namespace http {
|
||||
|
||||
s << "<br>\r\n<small>" << tr("<b>Note:</b> any action done here are not persistent and not changes your config files.") << "</small>\r\n<br>\r\n";
|
||||
|
||||
auto loglevel = i2p::log::Logger().GetLogLevel();
|
||||
s << "<b>" << tr("Logging level") << "</b><br>\r\n";
|
||||
s << " <a class=\"button\" href=\"" << webroot << "?cmd=" << HTTP_COMMAND_LOGLEVEL << "&level=none&token=" << token << "\"> none </a> \r\n";
|
||||
s << " <a class=\"button\" href=\"" << webroot << "?cmd=" << HTTP_COMMAND_LOGLEVEL << "&level=error&token=" << token << "\"> error </a> \r\n";
|
||||
s << " <a class=\"button\" href=\"" << webroot << "?cmd=" << HTTP_COMMAND_LOGLEVEL << "&level=warn&token=" << token << "\"> warn </a> \r\n";
|
||||
s << " <a class=\"button\" href=\"" << webroot << "?cmd=" << HTTP_COMMAND_LOGLEVEL << "&level=info&token=" << token << "\"> info </a> \r\n";
|
||||
s << " <a class=\"button\" href=\"" << webroot << "?cmd=" << HTTP_COMMAND_LOGLEVEL << "&level=debug&token=" << token << "\"> debug </a><br>\r\n<br>\r\n";
|
||||
s << " <a class=\"button" << (loglevel == eLogNone ? " selected" : "") << "\" href=\"" << webroot << "?cmd=" << HTTP_COMMAND_LOGLEVEL << "&level=none&token=" << token << "\"> none </a> \r\n";
|
||||
s << " <a class=\"button" << (loglevel == eLogError ? " selected" : "") << "\" href=\"" << webroot << "?cmd=" << HTTP_COMMAND_LOGLEVEL << "&level=error&token=" << token << "\"> error </a> \r\n";
|
||||
s << " <a class=\"button" << (loglevel == eLogWarning ? " selected" : "") << "\" href=\"" << webroot << "?cmd=" << HTTP_COMMAND_LOGLEVEL << "&level=warn&token=" << token << "\"> warn </a> \r\n";
|
||||
s << " <a class=\"button" << (loglevel == eLogInfo ? " selected" : "") << "\" href=\"" << webroot << "?cmd=" << HTTP_COMMAND_LOGLEVEL << "&level=info&token=" << token << "\"> info </a> \r\n";
|
||||
s << " <a class=\"button" << (loglevel == eLogDebug ? " selected" : "") << "\" href=\"" << webroot << "?cmd=" << HTTP_COMMAND_LOGLEVEL << "&level=debug&token=" << token << "\"> debug </a><br>\r\n<br>\r\n";
|
||||
|
||||
uint16_t maxTunnels = GetMaxNumTransitTunnels ();
|
||||
s << "<b>" << tr("Transit tunnels limit") << "</b><br>\r\n";
|
||||
s << "<form method=\"get\" action=\"" << webroot << "\">\r\n";
|
||||
s << " <input type=\"hidden\" name=\"cmd\" value=\"" << HTTP_COMMAND_LIMITTRANSIT << "\">\r\n";
|
||||
s << " <input type=\"hidden\" name=\"token\" value=\"" << token << "\">\r\n";
|
||||
s << " <input type=\"number\" min=\"0\" max=\"65535\" name=\"limit\" value=\"" << maxTunnels << "\">\r\n";
|
||||
s << " <input type=\"number\" min=\"0\" max=\"" << TRANSIT_TUNNELS_LIMIT <<"\" name=\"limit\" value=\"" << maxTunnels << "\">\r\n";
|
||||
s << " <button type=\"submit\">" << tr("Change") << "</button>\r\n";
|
||||
s << "</form>\r\n<br>\r\n";
|
||||
|
||||
@@ -767,56 +789,70 @@ namespace http {
|
||||
{
|
||||
if (i2p::tunnel::tunnels.CountTransitTunnels())
|
||||
{
|
||||
s << "<b>" << tr("Transit Tunnels") << ":</b><br>\r\n<div class=\"list\">\r\n";
|
||||
s << "<b>" << tr("Transit Tunnels") << ":</b><br>\r\n";
|
||||
s << "<table><thead><th>⇒</th><th>ID</th><th>⇒</th><th>" << tr("Amount") << "</th></thead><tbody class=\"tableitem\">";
|
||||
for (const auto& it: i2p::tunnel::tunnels.GetTransitTunnels ())
|
||||
{
|
||||
s << "<div class=\"listitem\">\r\n";
|
||||
if (std::dynamic_pointer_cast<i2p::tunnel::TransitTunnelGateway>(it))
|
||||
s << it->GetTunnelID () << " ⇒ ";
|
||||
s << "<tr><td></td><td>" << it->GetTunnelID () << "</td><td>⇒</td><td>";
|
||||
else if (std::dynamic_pointer_cast<i2p::tunnel::TransitTunnelEndpoint>(it))
|
||||
s << " ⇒ " << it->GetTunnelID ();
|
||||
s << "<tr><td>⇒</td><td>" << it->GetTunnelID () << "</td><td></td><td>";
|
||||
else
|
||||
s << " ⇒ " << it->GetTunnelID () << " ⇒ ";
|
||||
s << " " << it->GetNumTransmittedBytes () << "</div>\r\n";
|
||||
s << "<tr><td>⇒</td><td>" << it->GetTunnelID () << "</td><td>⇒</td><td>";
|
||||
ShowTraffic(s, it->GetNumTransmittedBytes ());
|
||||
s << "</td></tr>\r\n";
|
||||
}
|
||||
s << "</div>\r\n";
|
||||
s << "</tbody></table>\r\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
s << "<b>" << tr("Transit Tunnels") << ":</b> " << tr("no transit tunnels currently built") << ".<br>\r\n";
|
||||
s << "<b>" << tr("Transit Tunnels") << ":</b> " << tr(/* Message on transit tunnels page */ "no transit tunnels currently built") << ".<br>\r\n";
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Sessions>
|
||||
static void ShowTransportSessions (std::stringstream& s, const Sessions& sessions, const std::string name)
|
||||
{
|
||||
std::stringstream tmp_s, tmp_s6; uint16_t cnt = 0, cnt6 = 0;
|
||||
for (const auto& it: sessions )
|
||||
auto comp = [](typename Sessions::mapped_type a, typename Sessions::mapped_type b)
|
||||
{ return a->GetRemoteEndpoint() < b->GetRemoteEndpoint(); };
|
||||
std::set<typename Sessions::mapped_type, decltype(comp)> sortedSessions(comp);
|
||||
for (const auto& it : sessions)
|
||||
{
|
||||
auto endpoint = it.second->GetRemoteEndpoint ();
|
||||
if (it.second && it.second->IsEstablished () && endpoint.address ().is_v4 ())
|
||||
auto ret = sortedSessions.insert(it.second);
|
||||
if (!ret.second)
|
||||
LogPrint(eLogError, "HTTPServer: Duplicate remote endpoint detected: ", (*ret.first)->GetRemoteEndpoint());
|
||||
}
|
||||
std::stringstream tmp_s, tmp_s6; uint16_t cnt = 0, cnt6 = 0;
|
||||
for (const auto& it: sortedSessions)
|
||||
{
|
||||
auto endpoint = it->GetRemoteEndpoint ();
|
||||
if (it && it->IsEstablished () && endpoint.address ().is_v4 ())
|
||||
{
|
||||
tmp_s << "<div class=\"listitem\">\r\n";
|
||||
if (it.second->IsOutgoing ()) tmp_s << " ⇒ ";
|
||||
tmp_s << i2p::data::GetIdentHashAbbreviation (it.second->GetRemoteIdentity ()->GetIdentHash ()) << ": "
|
||||
if (it->IsOutgoing ()) tmp_s << " ⇒ ";
|
||||
tmp_s << i2p::data::GetIdentHashAbbreviation (it->GetRemoteIdentity ()->GetIdentHash ()) << ": "
|
||||
<< endpoint.address ().to_string () << ":" << endpoint.port ();
|
||||
if (!it.second->IsOutgoing ()) tmp_s << " ⇒ ";
|
||||
tmp_s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
|
||||
if (it.second->GetRelayTag ())
|
||||
tmp_s << " [itag:" << it.second->GetRelayTag () << "]";
|
||||
if (!it->IsOutgoing ()) tmp_s << " ⇒ ";
|
||||
tmp_s << " [" << it->GetNumSentBytes () << ":" << it->GetNumReceivedBytes () << "]";
|
||||
if (it->GetRelayTag ())
|
||||
tmp_s << " [itag:" << it->GetRelayTag () << "]";
|
||||
if (it->GetSendQueueSize () > 0)
|
||||
tmp_s << " [queue:" << it->GetSendQueueSize () << "]";
|
||||
tmp_s << "</div>\r\n" << std::endl;
|
||||
cnt++;
|
||||
}
|
||||
if (it.second && it.second->IsEstablished () && endpoint.address ().is_v6 ())
|
||||
if (it && it->IsEstablished () && endpoint.address ().is_v6 ())
|
||||
{
|
||||
tmp_s6 << "<div class=\"listitem\">\r\n";
|
||||
if (it.second->IsOutgoing ()) tmp_s6 << " ⇒ ";
|
||||
tmp_s6 << i2p::data::GetIdentHashAbbreviation (it.second->GetRemoteIdentity ()->GetIdentHash ()) << ": "
|
||||
if (it->IsOutgoing ()) tmp_s6 << " ⇒ ";
|
||||
tmp_s6 << i2p::data::GetIdentHashAbbreviation (it->GetRemoteIdentity ()->GetIdentHash ()) << ": "
|
||||
<< "[" << endpoint.address ().to_string () << "]:" << endpoint.port ();
|
||||
if (!it.second->IsOutgoing ()) tmp_s6 << " ⇒ ";
|
||||
tmp_s6 << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
|
||||
if (it.second->GetRelayTag ())
|
||||
tmp_s6 << " [itag:" << it.second->GetRelayTag () << "]";
|
||||
if (!it->IsOutgoing ()) tmp_s6 << " ⇒ ";
|
||||
tmp_s6 << " [" << it->GetNumSentBytes () << ":" << it->GetNumReceivedBytes () << "]";
|
||||
if (it->GetRelayTag ())
|
||||
tmp_s6 << " [itag:" << it->GetRelayTag () << "]";
|
||||
if (it->GetSendQueueSize () > 0)
|
||||
tmp_s6 << " [queue:" << it->GetSendQueueSize () << "]";
|
||||
tmp_s6 << "</div>\r\n" << std::endl;
|
||||
cnt6++;
|
||||
}
|
||||
@@ -845,46 +881,6 @@ namespace http {
|
||||
if (!sessions.empty ())
|
||||
ShowTransportSessions (s, sessions, "NTCP2");
|
||||
}
|
||||
auto ssuServer = i2p::transport::transports.GetSSUServer ();
|
||||
if (ssuServer)
|
||||
{
|
||||
auto sessions = ssuServer->GetSessions ();
|
||||
if (!sessions.empty ())
|
||||
{
|
||||
s << "<div class='slide'><label for='slide_ssu'><b>SSU</b> ( " << (int) sessions.size() << " )</label>\r\n<input type=\"checkbox\" id=\"slide_ssu\" />\r\n<div class=\"slidecontent list\">";
|
||||
for (const auto& it: sessions)
|
||||
{
|
||||
s << "<div class=\"listitem\">\r\n";
|
||||
auto endpoint = it.second->GetRemoteEndpoint ();
|
||||
if (it.second->IsOutgoing ()) s << " ⇒ ";
|
||||
s << endpoint.address ().to_string () << ":" << endpoint.port ();
|
||||
if (!it.second->IsOutgoing ()) s << " ⇒ ";
|
||||
s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
|
||||
if (it.second->GetRelayTag ())
|
||||
s << " [itag:" << it.second->GetRelayTag () << "]";
|
||||
s << "</div>\r\n" << std::endl;
|
||||
}
|
||||
s << "</div>\r\n</div>\r\n";
|
||||
}
|
||||
auto sessions6 = ssuServer->GetSessionsV6 ();
|
||||
if (!sessions6.empty ())
|
||||
{
|
||||
s << "<div class='slide'><label for='slide_ssuv6'><b>SSUv6</b> ( " << (int) sessions6.size() << " )</label>\r\n<input type=\"checkbox\" id=\"slide_ssuv6\" />\r\n<div class=\"slidecontent list\">";
|
||||
for (const auto& it: sessions6)
|
||||
{
|
||||
s << "<div class=\"listitem\">\r\n";
|
||||
auto endpoint = it.second->GetRemoteEndpoint ();
|
||||
if (it.second->IsOutgoing ()) s << " ⇒ ";
|
||||
s << "[" << endpoint.address ().to_string () << "]:" << endpoint.port ();
|
||||
if (!it.second->IsOutgoing ()) s << " ⇒ ";
|
||||
s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
|
||||
if (it.second->GetRelayTag ())
|
||||
s << " [itag:" << it.second->GetRelayTag () << "]";
|
||||
s << "</div>\r\n" << std::endl;
|
||||
}
|
||||
s << "</div>\r\n</div>\r\n";
|
||||
}
|
||||
}
|
||||
auto ssu2Server = i2p::transport::transports.GetSSU2Server ();
|
||||
if (ssu2Server)
|
||||
{
|
||||
@@ -916,7 +912,7 @@ namespace http {
|
||||
s << "</div>\r\n";
|
||||
}
|
||||
else
|
||||
s << "<b>" << tr("SAM sessions") << ":</b> " << tr("no sessions currently running") << ".<br>\r\n";
|
||||
s << "<b>" << tr("SAM sessions") << ":</b> " << tr(/* Message on SAM sessions page */ "no sessions currently running") << ".<br>\r\n";
|
||||
}
|
||||
|
||||
void ShowSAMSession (std::stringstream& s, const std::string& id)
|
||||
@@ -1025,7 +1021,7 @@ namespace http {
|
||||
for (auto& it: serverForwards)
|
||||
{
|
||||
auto& ident = it.second->GetLocalDestination ()->GetIdentHash();
|
||||
s << "<a href=\"" << webroot << "?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
|
||||
s << "<div class=\"listitem\"><a href=\"" << webroot << "?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
|
||||
s << it.second->GetName () << "</a> ⇐ ";
|
||||
s << i2p::client::context.GetAddressBook ().ToAddress(ident);
|
||||
s << "</div>\r\n"<< std::endl;
|
||||
@@ -1235,7 +1231,7 @@ namespace http {
|
||||
url.parse_query(params);
|
||||
|
||||
std::string webroot; i2p::config::GetOption("http.webroot", webroot);
|
||||
std::string redirect = "5; url=" + webroot + "?page=commands";
|
||||
std::string redirect = std::to_string(COMMAND_REDIRECT_TIMEOUT) + "; url=" + webroot + "?page=commands";
|
||||
std::string token = params["token"];
|
||||
|
||||
if (token.empty () || m_Tokens.find (std::stoi (token)) == m_Tokens.end ())
|
||||
@@ -1247,7 +1243,7 @@ namespace http {
|
||||
std::string cmd = params["cmd"];
|
||||
if (cmd == HTTP_COMMAND_RUN_PEER_TEST)
|
||||
i2p::transport::transports.PeerTest ();
|
||||
else if (cmd == HTTP_COMMAND_RELOAD_CONFIG)
|
||||
else if (cmd == HTTP_COMMAND_RELOAD_TUNNELS_CONFIG)
|
||||
i2p::client::context.ReloadConfig ();
|
||||
else if (cmd == HTTP_COMMAND_ENABLE_TRANSIT)
|
||||
i2p::context.SetAcceptsTunnels (true);
|
||||
@@ -1309,20 +1305,20 @@ namespace http {
|
||||
s << "<b>" << tr("ERROR") << "</b>: " << tr("StreamID can't be null") << "<br>\r\n<br>\r\n";
|
||||
|
||||
s << "<a href=\"" << webroot << "?page=local_destination&b32=" << b32 << "\">" << tr("Return to destination page") << "</a><br>\r\n";
|
||||
s << "<p>" << tr("You will be redirected in 5 seconds") << "</b>";
|
||||
redirect = "5; url=" + webroot + "?page=local_destination&b32=" + b32;
|
||||
s << "<p>" << tr("You will be redirected in %d seconds", COMMAND_REDIRECT_TIMEOUT) << "</b>";
|
||||
redirect = std::to_string(COMMAND_REDIRECT_TIMEOUT) + "; url=" + webroot + "?page=local_destination&b32=" + b32;
|
||||
res.add_header("Refresh", redirect.c_str());
|
||||
return;
|
||||
}
|
||||
else if (cmd == HTTP_COMMAND_LIMITTRANSIT)
|
||||
{
|
||||
uint32_t limit = std::stoul(params["limit"], nullptr);
|
||||
if (limit > 0 && limit <= 65535)
|
||||
if (limit > 0 && limit <= TRANSIT_TUNNELS_LIMIT)
|
||||
SetMaxNumTransitTunnels (limit);
|
||||
else {
|
||||
s << "<b>" << tr("ERROR") << "</b>: " << tr("Transit tunnels count must not exceed 65535") << "\r\n<br>\r\n<br>\r\n";
|
||||
s << "<b>" << tr("ERROR") << "</b>: " << tr("Transit tunnels count must not exceed %d", TRANSIT_TUNNELS_LIMIT) << "\r\n<br>\r\n<br>\r\n";
|
||||
s << "<a href=\"" << webroot << "?page=commands\">" << tr("Back to commands list") << "</a>\r\n<br>\r\n";
|
||||
s << "<p>" << tr("You will be redirected in 5 seconds") << "</b>";
|
||||
s << "<p>" << tr("You will be redirected in %d seconds", COMMAND_REDIRECT_TIMEOUT) << "</b>";
|
||||
res.add_header("Refresh", redirect.c_str());
|
||||
return;
|
||||
}
|
||||
@@ -1397,7 +1393,7 @@ namespace http {
|
||||
|
||||
s << "<b>" << tr("SUCCESS") << "</b>: " << tr("Command accepted") << "<br><br>\r\n";
|
||||
s << "<a href=\"" << webroot << "?page=commands\">" << tr("Back to commands list") << "</a><br>\r\n";
|
||||
s << "<p>" << tr("You will be redirected in 5 seconds") << "</b>";
|
||||
s << "<p>" << tr("You will be redirected in %d seconds", COMMAND_REDIRECT_TIMEOUT) << "</b>";
|
||||
res.add_header("Refresh", redirect.c_str());
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2020, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2023, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -24,6 +24,8 @@ namespace http
|
||||
{
|
||||
const size_t HTTP_CONNECTION_BUFFER_SIZE = 8192;
|
||||
const int TOKEN_EXPIRATION_TIMEOUT = 30; // in seconds
|
||||
const int COMMAND_REDIRECT_TIMEOUT = 5; // in seconds
|
||||
const int TRANSIT_TUNNELS_LIMIT = 65535;
|
||||
|
||||
class HTTPConnection: public std::enable_shared_from_this<HTTPConnection>
|
||||
{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2023, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -38,7 +38,7 @@ namespace http
|
||||
"@media (prefers-color-scheme: dark) { :root { --main-bg-color: #242424; --main-text-color: #17ab5c; --main-link-color: #bf64b7; --main-link-hover-color: #000000; } }\r\n"
|
||||
"body { font: 100%/1.5em sans-serif; margin: 0; padding: 1.5em; background: var(--main-bg-color); color: var(--main-text-color); }\r\n"
|
||||
"a, .slide label { text-decoration: none; color: var(--main-link-color); }\r\n"
|
||||
"a:hover, .slide label:hover, button[type=submit]:hover { color: var(--main-link-hover-color); background: var(--main-link-color); }\r\n"
|
||||
"a:hover, a.button.selected, .slide label:hover, button[type=submit]:hover { color: var(--main-link-hover-color); background: var(--main-link-color); }\r\n"
|
||||
"a.button { appearance: button; text-decoration: none; padding: 0 5px; border: 1px solid var(--main-link-color); }\r\n"
|
||||
".header { font-size: 2.5em; text-align: center; margin: 1em 0; color: var(--main-link-color); }\r\n"
|
||||
".wrapper { margin: 0 auto; padding: 1em; max-width: 64em; }\r\n"
|
||||
|
||||
@@ -14,25 +14,17 @@
|
||||
// Use global placeholders from boost introduced when local_time.hpp is loaded
|
||||
#define BOOST_BIND_GLOBAL_PLACEHOLDERS
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/date_time/local_time/local_time.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||
#include <boost/property_tree/ini_parser.hpp>
|
||||
#include <boost/property_tree/json_parser.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include "Crypto.h"
|
||||
#include "FS.h"
|
||||
#include "Log.h"
|
||||
#include "Config.h"
|
||||
#include "NetDb.hpp"
|
||||
#include "RouterContext.h"
|
||||
#include "Daemon.h"
|
||||
#include "Tunnel.h"
|
||||
#include "Timestamp.h"
|
||||
#include "Transports.h"
|
||||
#include "version.h"
|
||||
#include "util.h"
|
||||
#include "ClientContext.h"
|
||||
#include "Daemon.h"
|
||||
#include "I2PControl.h"
|
||||
|
||||
namespace i2p
|
||||
@@ -69,44 +61,18 @@ namespace client
|
||||
m_MethodHandlers["Authenticate"] = &I2PControlService::AuthenticateHandler;
|
||||
m_MethodHandlers["Echo"] = &I2PControlService::EchoHandler;
|
||||
m_MethodHandlers["I2PControl"] = &I2PControlService::I2PControlHandler;
|
||||
m_MethodHandlers["RouterInfo"] = &I2PControlService::RouterInfoHandler;
|
||||
m_MethodHandlers["RouterInfo"] = &I2PControlHandlers::RouterInfoHandler;
|
||||
m_MethodHandlers["RouterManager"] = &I2PControlService::RouterManagerHandler;
|
||||
m_MethodHandlers["NetworkSetting"] = &I2PControlService::NetworkSettingHandler;
|
||||
m_MethodHandlers["ClientServicesInfo"] = &I2PControlService::ClientServicesInfoHandler;
|
||||
m_MethodHandlers["NetworkSetting"] = &I2PControlHandlers::NetworkSettingHandler;
|
||||
m_MethodHandlers["ClientServicesInfo"] = &I2PControlHandlers::ClientServicesInfoHandler;
|
||||
|
||||
// I2PControl
|
||||
m_I2PControlHandlers["i2pcontrol.password"] = &I2PControlService::PasswordHandler;
|
||||
|
||||
// RouterInfo
|
||||
m_RouterInfoHandlers["i2p.router.uptime"] = &I2PControlService::UptimeHandler;
|
||||
m_RouterInfoHandlers["i2p.router.version"] = &I2PControlService::VersionHandler;
|
||||
m_RouterInfoHandlers["i2p.router.status"] = &I2PControlService::StatusHandler;
|
||||
m_RouterInfoHandlers["i2p.router.netdb.knownpeers"] = &I2PControlService::NetDbKnownPeersHandler;
|
||||
m_RouterInfoHandlers["i2p.router.netdb.activepeers"] = &I2PControlService::NetDbActivePeersHandler;
|
||||
m_RouterInfoHandlers["i2p.router.net.bw.inbound.1s"] = &I2PControlService::InboundBandwidth1S;
|
||||
m_RouterInfoHandlers["i2p.router.net.bw.outbound.1s"] = &I2PControlService::OutboundBandwidth1S;
|
||||
m_RouterInfoHandlers["i2p.router.net.status"] = &I2PControlService::NetStatusHandler;
|
||||
m_RouterInfoHandlers["i2p.router.net.tunnels.participating"] = &I2PControlService::TunnelsParticipatingHandler;
|
||||
m_RouterInfoHandlers["i2p.router.net.tunnels.successrate"] = &I2PControlService::TunnelsSuccessRateHandler;
|
||||
m_RouterInfoHandlers["i2p.router.net.total.received.bytes"] = &I2PControlService::NetTotalReceivedBytes;
|
||||
m_RouterInfoHandlers["i2p.router.net.total.sent.bytes"] = &I2PControlService::NetTotalSentBytes;
|
||||
|
||||
// RouterManager
|
||||
m_RouterManagerHandlers["Reseed"] = &I2PControlService::ReseedHandler;
|
||||
m_RouterManagerHandlers["Shutdown"] = &I2PControlService::ShutdownHandler;
|
||||
m_RouterManagerHandlers["ShutdownGraceful"] = &I2PControlService::ShutdownGracefulHandler;
|
||||
|
||||
// NetworkSetting
|
||||
m_NetworkSettingHandlers["i2p.router.net.bw.in"] = &I2PControlService::InboundBandwidthLimit;
|
||||
m_NetworkSettingHandlers["i2p.router.net.bw.out"] = &I2PControlService::OutboundBandwidthLimit;
|
||||
|
||||
// ClientServicesInfo
|
||||
m_ClientServicesInfoHandlers["I2PTunnel"] = &I2PControlService::I2PTunnelInfoHandler;
|
||||
m_ClientServicesInfoHandlers["HTTPProxy"] = &I2PControlService::HTTPProxyInfoHandler;
|
||||
m_ClientServicesInfoHandlers["SOCKS"] = &I2PControlService::SOCKSInfoHandler;
|
||||
m_ClientServicesInfoHandlers["SAM"] = &I2PControlService::SAMInfoHandler;
|
||||
m_ClientServicesInfoHandlers["BOB"] = &I2PControlService::BOBInfoHandler;
|
||||
m_ClientServicesInfoHandlers["I2CP"] = &I2PControlService::I2CPInfoHandler;
|
||||
}
|
||||
|
||||
I2PControlService::~I2PControlService ()
|
||||
@@ -280,37 +246,6 @@ namespace client
|
||||
}
|
||||
}
|
||||
|
||||
void I2PControlService::InsertParam (std::ostringstream& ss, const std::string& name, int value) const
|
||||
{
|
||||
ss << "\"" << name << "\":" << value;
|
||||
}
|
||||
|
||||
void I2PControlService::InsertParam (std::ostringstream& ss, const std::string& name, const std::string& value, bool quotes) const
|
||||
{
|
||||
ss << "\"" << name << "\":";
|
||||
if (value.length () > 0)
|
||||
{
|
||||
if (quotes)
|
||||
ss << "\"" << value << "\"";
|
||||
else
|
||||
ss << value;
|
||||
}
|
||||
else
|
||||
ss << "null";
|
||||
}
|
||||
|
||||
void I2PControlService::InsertParam (std::ostringstream& ss, const std::string& name, double value) const
|
||||
{
|
||||
ss << "\"" << name << "\":" << std::fixed << std::setprecision(2) << value;
|
||||
}
|
||||
|
||||
void I2PControlService::InsertParam (std::ostringstream& ss, const std::string& name, const boost::property_tree::ptree& value) const
|
||||
{
|
||||
std::ostringstream buf;
|
||||
boost::property_tree::write_json (buf, value, false);
|
||||
ss << "\"" << name << "\":" << buf.str();
|
||||
}
|
||||
|
||||
void I2PControlService::SendResponse (std::shared_ptr<ssl_socket> socket,
|
||||
std::shared_ptr<I2PControlBuffer> buf, std::ostringstream& response, bool isHtml)
|
||||
{
|
||||
@@ -396,91 +331,6 @@ namespace client
|
||||
m_Tokens.clear ();
|
||||
}
|
||||
|
||||
// RouterInfo
|
||||
|
||||
void I2PControlService::RouterInfoHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
|
||||
{
|
||||
bool first = true;
|
||||
for (auto it = params.begin (); it != params.end (); it++)
|
||||
{
|
||||
LogPrint (eLogDebug, "I2PControl: RouterInfo request: ", it->first);
|
||||
auto it1 = m_RouterInfoHandlers.find (it->first);
|
||||
if (it1 != m_RouterInfoHandlers.end ())
|
||||
{
|
||||
if (!first) results << ",";
|
||||
else first = false;
|
||||
(this->*(it1->second))(results);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "I2PControl: RouterInfo unknown request ", it->first);
|
||||
}
|
||||
}
|
||||
|
||||
void I2PControlService::UptimeHandler (std::ostringstream& results)
|
||||
{
|
||||
InsertParam (results, "i2p.router.uptime", std::to_string (i2p::context.GetUptime ()*1000LL), false);
|
||||
}
|
||||
|
||||
void I2PControlService::VersionHandler (std::ostringstream& results)
|
||||
{
|
||||
InsertParam (results, "i2p.router.version", VERSION);
|
||||
}
|
||||
|
||||
void I2PControlService::StatusHandler (std::ostringstream& results)
|
||||
{
|
||||
auto dest = i2p::client::context.GetSharedLocalDestination ();
|
||||
InsertParam (results, "i2p.router.status", (dest && dest->IsReady ()) ? "1" : "0");
|
||||
}
|
||||
|
||||
void I2PControlService::NetDbKnownPeersHandler (std::ostringstream& results)
|
||||
{
|
||||
InsertParam (results, "i2p.router.netdb.knownpeers", i2p::data::netdb.GetNumRouters ());
|
||||
}
|
||||
|
||||
void I2PControlService::NetDbActivePeersHandler (std::ostringstream& results)
|
||||
{
|
||||
InsertParam (results, "i2p.router.netdb.activepeers", (int)i2p::transport::transports.GetPeers ().size ());
|
||||
}
|
||||
|
||||
void I2PControlService::NetStatusHandler (std::ostringstream& results)
|
||||
{
|
||||
InsertParam (results, "i2p.router.net.status", (int)i2p::context.GetStatus ());
|
||||
}
|
||||
|
||||
void I2PControlService::TunnelsParticipatingHandler (std::ostringstream& results)
|
||||
{
|
||||
int transit = i2p::tunnel::tunnels.GetTransitTunnels ().size ();
|
||||
InsertParam (results, "i2p.router.net.tunnels.participating", transit);
|
||||
}
|
||||
|
||||
void I2PControlService::TunnelsSuccessRateHandler (std::ostringstream& results)
|
||||
{
|
||||
int rate = i2p::tunnel::tunnels.GetTunnelCreationSuccessRate ();
|
||||
InsertParam (results, "i2p.router.net.tunnels.successrate", rate);
|
||||
}
|
||||
|
||||
void I2PControlService::InboundBandwidth1S (std::ostringstream& results)
|
||||
{
|
||||
double bw = i2p::transport::transports.GetInBandwidth ();
|
||||
InsertParam (results, "i2p.router.net.bw.inbound.1s", bw);
|
||||
}
|
||||
|
||||
void I2PControlService::OutboundBandwidth1S (std::ostringstream& results)
|
||||
{
|
||||
double bw = i2p::transport::transports.GetOutBandwidth ();
|
||||
InsertParam (results, "i2p.router.net.bw.outbound.1s", bw);
|
||||
}
|
||||
|
||||
void I2PControlService::NetTotalReceivedBytes (std::ostringstream& results)
|
||||
{
|
||||
InsertParam (results, "i2p.router.net.total.received.bytes", (double)i2p::transport::transports.GetTotalReceivedBytes ());
|
||||
}
|
||||
|
||||
void I2PControlService::NetTotalSentBytes (std::ostringstream& results)
|
||||
{
|
||||
InsertParam (results, "i2p.router.net.total.sent.bytes", (double)i2p::transport::transports.GetTotalSentBytes ());
|
||||
}
|
||||
|
||||
|
||||
// RouterManager
|
||||
|
||||
@@ -532,37 +382,6 @@ namespace client
|
||||
i2p::data::netdb.Reseed ();
|
||||
}
|
||||
|
||||
// network setting
|
||||
void I2PControlService::NetworkSettingHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
|
||||
{
|
||||
for (auto it = params.begin (); it != params.end (); it++)
|
||||
{
|
||||
LogPrint (eLogDebug, "I2PControl: NetworkSetting request: ", it->first);
|
||||
auto it1 = m_NetworkSettingHandlers.find (it->first);
|
||||
if (it1 != m_NetworkSettingHandlers.end ()) {
|
||||
if (it != params.begin ()) results << ",";
|
||||
(this->*(it1->second))(it->second.data (), results);
|
||||
} else
|
||||
LogPrint (eLogError, "I2PControl: NetworkSetting unknown request: ", it->first);
|
||||
}
|
||||
}
|
||||
|
||||
void I2PControlService::InboundBandwidthLimit (const std::string& value, std::ostringstream& results)
|
||||
{
|
||||
if (value != "null")
|
||||
i2p::context.SetBandwidth (std::atoi(value.c_str()));
|
||||
int bw = i2p::context.GetBandwidthLimit();
|
||||
InsertParam (results, "i2p.router.net.bw.in", bw);
|
||||
}
|
||||
|
||||
void I2PControlService::OutboundBandwidthLimit (const std::string& value, std::ostringstream& results)
|
||||
{
|
||||
if (value != "null")
|
||||
i2p::context.SetBandwidth (std::atoi(value.c_str()));
|
||||
int bw = i2p::context.GetBandwidthLimit();
|
||||
InsertParam (results, "i2p.router.net.bw.out", bw);
|
||||
}
|
||||
|
||||
// certificate
|
||||
void I2PControlService::CreateCertificate (const char *crt_path, const char *key_path)
|
||||
{
|
||||
@@ -611,178 +430,5 @@ namespace client
|
||||
}
|
||||
EVP_PKEY_free (pkey);
|
||||
}
|
||||
|
||||
// ClientServicesInfo
|
||||
|
||||
void I2PControlService::ClientServicesInfoHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
|
||||
{
|
||||
for (auto it = params.begin (); it != params.end (); it++)
|
||||
{
|
||||
LogPrint (eLogDebug, "I2PControl: ClientServicesInfo request: ", it->first);
|
||||
auto it1 = m_ClientServicesInfoHandlers.find (it->first);
|
||||
if (it1 != m_ClientServicesInfoHandlers.end ())
|
||||
{
|
||||
if (it != params.begin ()) results << ",";
|
||||
(this->*(it1->second))(results);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "I2PControl: ClientServicesInfo unknown request ", it->first);
|
||||
}
|
||||
}
|
||||
|
||||
void I2PControlService::I2PTunnelInfoHandler (std::ostringstream& results)
|
||||
{
|
||||
boost::property_tree::ptree pt;
|
||||
boost::property_tree::ptree client_tunnels, server_tunnels;
|
||||
|
||||
for (auto& it: i2p::client::context.GetClientTunnels ())
|
||||
{
|
||||
auto& ident = it.second->GetLocalDestination ()->GetIdentHash();
|
||||
boost::property_tree::ptree ct;
|
||||
ct.put("address", i2p::client::context.GetAddressBook ().ToAddress(ident));
|
||||
client_tunnels.add_child(it.second->GetName (), ct);
|
||||
}
|
||||
|
||||
auto& serverTunnels = i2p::client::context.GetServerTunnels ();
|
||||
if (!serverTunnels.empty ()) {
|
||||
for (auto& it: serverTunnels)
|
||||
{
|
||||
auto& ident = it.second->GetLocalDestination ()->GetIdentHash();
|
||||
boost::property_tree::ptree st;
|
||||
st.put("address", i2p::client::context.GetAddressBook ().ToAddress(ident));
|
||||
st.put("port", it.second->GetLocalPort ());
|
||||
server_tunnels.add_child(it.second->GetName (), st);
|
||||
}
|
||||
}
|
||||
|
||||
auto& clientForwards = i2p::client::context.GetClientForwards ();
|
||||
if (!clientForwards.empty ())
|
||||
{
|
||||
for (auto& it: clientForwards)
|
||||
{
|
||||
auto& ident = it.second->GetLocalDestination ()->GetIdentHash();
|
||||
boost::property_tree::ptree ct;
|
||||
ct.put("address", i2p::client::context.GetAddressBook ().ToAddress(ident));
|
||||
client_tunnels.add_child(it.second->GetName (), ct);
|
||||
}
|
||||
}
|
||||
|
||||
auto& serverForwards = i2p::client::context.GetServerForwards ();
|
||||
if (!serverForwards.empty ())
|
||||
{
|
||||
for (auto& it: serverForwards)
|
||||
{
|
||||
auto& ident = it.second->GetLocalDestination ()->GetIdentHash();
|
||||
boost::property_tree::ptree st;
|
||||
st.put("address", i2p::client::context.GetAddressBook ().ToAddress(ident));
|
||||
server_tunnels.add_child(it.second->GetName (), st);
|
||||
}
|
||||
}
|
||||
|
||||
pt.add_child("client", client_tunnels);
|
||||
pt.add_child("server", server_tunnels);
|
||||
|
||||
InsertParam (results, "I2PTunnel", pt);
|
||||
}
|
||||
|
||||
void I2PControlService::HTTPProxyInfoHandler (std::ostringstream& results)
|
||||
{
|
||||
boost::property_tree::ptree pt;
|
||||
|
||||
auto httpProxy = i2p::client::context.GetHttpProxy ();
|
||||
if (httpProxy)
|
||||
{
|
||||
auto& ident = httpProxy->GetLocalDestination ()->GetIdentHash();
|
||||
pt.put("enabled", true);
|
||||
pt.put("address", i2p::client::context.GetAddressBook ().ToAddress(ident));
|
||||
}
|
||||
else
|
||||
pt.put("enabled", false);
|
||||
|
||||
InsertParam (results, "HTTPProxy", pt);
|
||||
}
|
||||
|
||||
void I2PControlService::SOCKSInfoHandler (std::ostringstream& results)
|
||||
{
|
||||
boost::property_tree::ptree pt;
|
||||
|
||||
auto socksProxy = i2p::client::context.GetSocksProxy ();
|
||||
if (socksProxy)
|
||||
{
|
||||
auto& ident = socksProxy->GetLocalDestination ()->GetIdentHash();
|
||||
pt.put("enabled", true);
|
||||
pt.put("address", i2p::client::context.GetAddressBook ().ToAddress(ident));
|
||||
}
|
||||
else
|
||||
pt.put("enabled", false);
|
||||
|
||||
InsertParam (results, "SOCKS", pt);
|
||||
}
|
||||
|
||||
void I2PControlService::SAMInfoHandler (std::ostringstream& results)
|
||||
{
|
||||
boost::property_tree::ptree pt;
|
||||
auto sam = i2p::client::context.GetSAMBridge ();
|
||||
if (sam)
|
||||
{
|
||||
pt.put("enabled", true);
|
||||
boost::property_tree::ptree sam_sessions;
|
||||
for (auto& it: sam->GetSessions ())
|
||||
{
|
||||
boost::property_tree::ptree sam_session, sam_session_sockets;
|
||||
auto& name = it.second->GetLocalDestination ()->GetNickname ();
|
||||
auto& ident = it.second->GetLocalDestination ()->GetIdentHash();
|
||||
sam_session.put("name", name);
|
||||
sam_session.put("address", i2p::client::context.GetAddressBook ().ToAddress(ident));
|
||||
|
||||
for (const auto& socket: sam->ListSockets(it.first))
|
||||
{
|
||||
boost::property_tree::ptree stream;
|
||||
stream.put("type", socket->GetSocketType ());
|
||||
stream.put("peer", socket->GetSocket ().remote_endpoint());
|
||||
|
||||
sam_session_sockets.push_back(std::make_pair("", stream));
|
||||
}
|
||||
sam_session.add_child("sockets", sam_session_sockets);
|
||||
sam_sessions.add_child(it.first, sam_session);
|
||||
}
|
||||
|
||||
pt.add_child("sessions", sam_sessions);
|
||||
}
|
||||
else
|
||||
pt.put("enabled", false);
|
||||
|
||||
InsertParam (results, "SAM", pt);
|
||||
}
|
||||
|
||||
void I2PControlService::BOBInfoHandler (std::ostringstream& results)
|
||||
{
|
||||
boost::property_tree::ptree pt;
|
||||
auto bob = i2p::client::context.GetBOBCommandChannel ();
|
||||
if (bob)
|
||||
{
|
||||
/* TODO more info */
|
||||
pt.put("enabled", true);
|
||||
}
|
||||
else
|
||||
pt.put("enabled", false);
|
||||
|
||||
InsertParam (results, "BOB", pt);
|
||||
}
|
||||
|
||||
void I2PControlService::I2CPInfoHandler (std::ostringstream& results)
|
||||
{
|
||||
boost::property_tree::ptree pt;
|
||||
auto i2cp = i2p::client::context.GetI2CPServer ();
|
||||
if (i2cp)
|
||||
{
|
||||
/* TODO more info */
|
||||
pt.put("enabled", true);
|
||||
}
|
||||
else
|
||||
pt.put("enabled", false);
|
||||
|
||||
InsertParam (results, "I2CP", pt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/asio/ssl.hpp>
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
#include "I2PControlHandlers.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
@@ -32,7 +33,7 @@ namespace client
|
||||
const char I2P_CONTROL_CERTIFICATE_COMMON_NAME[] = "i2pd.i2pcontrol";
|
||||
const char I2P_CONTROL_CERTIFICATE_ORGANIZATION[] = "Purple I2P";
|
||||
|
||||
class I2PControlService
|
||||
class I2PControlService: public I2PControlHandlers
|
||||
{
|
||||
typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> ssl_socket;
|
||||
|
||||
@@ -63,61 +64,24 @@ namespace client
|
||||
|
||||
private:
|
||||
|
||||
void InsertParam (std::ostringstream& ss, const std::string& name, int value) const;
|
||||
void InsertParam (std::ostringstream& ss, const std::string& name, double value) const;
|
||||
void InsertParam (std::ostringstream& ss, const std::string& name, const std::string& value, bool quotes = true) const;
|
||||
void InsertParam (std::ostringstream& ss, const std::string& name, const boost::property_tree::ptree& value) const;
|
||||
|
||||
// methods
|
||||
typedef void (I2PControlService::*MethodHandler)(const boost::property_tree::ptree& params, std::ostringstream& results);
|
||||
|
||||
void AuthenticateHandler (const boost::property_tree::ptree& params, std::ostringstream& results);
|
||||
void EchoHandler (const boost::property_tree::ptree& params, std::ostringstream& results);
|
||||
void I2PControlHandler (const boost::property_tree::ptree& params, std::ostringstream& results);
|
||||
void RouterInfoHandler (const boost::property_tree::ptree& params, std::ostringstream& results);
|
||||
void RouterManagerHandler (const boost::property_tree::ptree& params, std::ostringstream& results);
|
||||
void NetworkSettingHandler (const boost::property_tree::ptree& params, std::ostringstream& results);
|
||||
void ClientServicesInfoHandler (const boost::property_tree::ptree& params, std::ostringstream& results);
|
||||
|
||||
// I2PControl
|
||||
typedef void (I2PControlService::*I2PControlRequestHandler)(const std::string& value);
|
||||
void PasswordHandler (const std::string& value);
|
||||
|
||||
// RouterInfo
|
||||
typedef void (I2PControlService::*RouterInfoRequestHandler)(std::ostringstream& results);
|
||||
void UptimeHandler (std::ostringstream& results);
|
||||
void VersionHandler (std::ostringstream& results);
|
||||
void StatusHandler (std::ostringstream& results);
|
||||
void NetDbKnownPeersHandler (std::ostringstream& results);
|
||||
void NetDbActivePeersHandler (std::ostringstream& results);
|
||||
void NetStatusHandler (std::ostringstream& results);
|
||||
void TunnelsParticipatingHandler (std::ostringstream& results);
|
||||
void TunnelsSuccessRateHandler (std::ostringstream& results);
|
||||
void InboundBandwidth1S (std::ostringstream& results);
|
||||
void OutboundBandwidth1S (std::ostringstream& results);
|
||||
void NetTotalReceivedBytes (std::ostringstream& results);
|
||||
void NetTotalSentBytes (std::ostringstream& results);
|
||||
|
||||
// RouterManager
|
||||
typedef void (I2PControlService::*RouterManagerRequestHandler)(std::ostringstream& results);
|
||||
void ShutdownHandler (std::ostringstream& results);
|
||||
void ShutdownGracefulHandler (std::ostringstream& results);
|
||||
void ReseedHandler (std::ostringstream& results);
|
||||
|
||||
// NetworkSetting
|
||||
typedef void (I2PControlService::*NetworkSettingRequestHandler)(const std::string& value, std::ostringstream& results);
|
||||
void InboundBandwidthLimit (const std::string& value, std::ostringstream& results);
|
||||
void OutboundBandwidthLimit (const std::string& value, std::ostringstream& results);
|
||||
|
||||
// ClientServicesInfo
|
||||
typedef void (I2PControlService::*ClientServicesInfoRequestHandler)(std::ostringstream& results);
|
||||
void I2PTunnelInfoHandler (std::ostringstream& results);
|
||||
void HTTPProxyInfoHandler (std::ostringstream& results);
|
||||
void SOCKSInfoHandler (std::ostringstream& results);
|
||||
void SAMInfoHandler (std::ostringstream& results);
|
||||
void BOBInfoHandler (std::ostringstream& results);
|
||||
void I2CPInfoHandler (std::ostringstream& results);
|
||||
|
||||
private:
|
||||
|
||||
std::string m_Password;
|
||||
@@ -132,10 +96,7 @@ namespace client
|
||||
|
||||
std::map<std::string, MethodHandler> m_MethodHandlers;
|
||||
std::map<std::string, I2PControlRequestHandler> m_I2PControlHandlers;
|
||||
std::map<std::string, RouterInfoRequestHandler> m_RouterInfoHandlers;
|
||||
std::map<std::string, RouterManagerRequestHandler> m_RouterManagerHandlers;
|
||||
std::map<std::string, NetworkSettingRequestHandler> m_NetworkSettingHandlers;
|
||||
std::map<std::string, ClientServicesInfoRequestHandler> m_ClientServicesInfoHandlers;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
390
daemon/I2PControlHandlers.cpp
Normal file
390
daemon/I2PControlHandlers.cpp
Normal file
@@ -0,0 +1,390 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
82
daemon/I2PControlHandlers.h
Normal file
82
daemon/I2PControlHandlers.h
Normal file
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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,10 +159,11 @@ namespace transport
|
||||
|
||||
void UPnP::PortMapping ()
|
||||
{
|
||||
const auto& a = context.GetRouterInfo().GetAddresses();
|
||||
for (const auto& address : a)
|
||||
auto a = context.GetRouterInfo().GetAddresses();
|
||||
if (!a) return;
|
||||
for (const auto& address : *a)
|
||||
{
|
||||
if (!address->host.is_v6 () && address->port)
|
||||
if (address && !address->host.is_v6 () && address->port)
|
||||
TryPortMapping (address);
|
||||
}
|
||||
m_Timer.expires_from_now (boost::posix_time::minutes(20)); // every 20 minutes
|
||||
@@ -210,10 +211,11 @@ namespace transport
|
||||
|
||||
void UPnP::CloseMapping ()
|
||||
{
|
||||
const auto& a = context.GetRouterInfo().GetAddresses();
|
||||
for (const auto& address : a)
|
||||
auto a = context.GetRouterInfo().GetAddresses();
|
||||
if (!a) return;
|
||||
for (const auto& address : *a)
|
||||
{
|
||||
if (!address->host.is_v6 () && address->port)
|
||||
if (address && !address->host.is_v6 () && address->port)
|
||||
CloseMapping (address);
|
||||
}
|
||||
}
|
||||
@@ -248,10 +250,10 @@ namespace transport
|
||||
{
|
||||
switch (address->transportStyle)
|
||||
{
|
||||
case i2p::data::RouterInfo::eTransportNTCP:
|
||||
case i2p::data::RouterInfo::eTransportNTCP2:
|
||||
return "TCP";
|
||||
break;
|
||||
case i2p::data::RouterInfo::eTransportSSU:
|
||||
case i2p::data::RouterInfo::eTransportSSU2:
|
||||
default:
|
||||
return "UDP";
|
||||
}
|
||||
|
||||
41
debian/changelog
vendored
41
debian/changelog
vendored
@@ -1,3 +1,44 @@
|
||||
i2pd (2.46.1-2) unstable; urgency=critical
|
||||
|
||||
* re-pushed release due to new critical bug
|
||||
|
||||
-- r4sas <r4sas@i2pmail.org> Mon, 20 Feb 2023 23:40:00 +0000
|
||||
|
||||
i2pd (2.46.1-1) unstable; urgency=high
|
||||
|
||||
* updated to version 2.46.1/0.9.57
|
||||
|
||||
-- r4sas <r4sas@i2pmail.org> Mon, 20 Feb 2023 02:45:00 +0000
|
||||
|
||||
i2pd (2.46.0-1) unstable; urgency=high
|
||||
|
||||
* updated to version 2.46.0/0.9.57
|
||||
|
||||
-- orignal <orignal@i2pmail.org> Wed, 15 Feb 2023 19:00:00 +0000
|
||||
|
||||
i2pd (2.45.1-1) unstable; urgency=medium
|
||||
|
||||
* updated to version 2.45.1/0.9.57
|
||||
|
||||
-- orignal <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
|
||||
|
||||
2
debian/compat
vendored
2
debian/compat
vendored
@@ -1 +1 @@
|
||||
9
|
||||
12
|
||||
|
||||
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 (>= 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
|
||||
Build-Depends: debhelper (>= 12~), libboost-system-dev (>= 1.46), libboost-date-time-dev (>= 1.46), libboost-filesystem-dev (>= 1.46), libboost-program-options-dev (>= 1.46), libminiupnpc-dev, libssl-dev, zlib1g-dev
|
||||
Standards-Version: 4.3.0
|
||||
Homepage: http://i2pd.website/
|
||||
Vcs-Git: git://github.com/PurpleI2P/i2pd.git
|
||||
Vcs-Browser: https://github.com/PurpleI2P/i2pd
|
||||
|
||||
6
debian/copyright
vendored
6
debian/copyright
vendored
@@ -3,18 +3,18 @@ Upstream-Name: i2pd
|
||||
Source: https://github.com/PurpleI2P
|
||||
|
||||
Files: *
|
||||
Copyright: 2013-2020 PurpleI2P
|
||||
Copyright: 2013-2023 PurpleI2P
|
||||
License: BSD-3-clause
|
||||
|
||||
Files: debian/*
|
||||
Copyright: 2013-2015 Kill Your TV <killyourtv@i2pmail.org>
|
||||
2014-2016 hagen <hagen@i2pmail.org>
|
||||
2016-2020 R4SAS <r4sas@i2pmail.org>
|
||||
2016-2023 R4SAS <r4sas@i2pmail.org>
|
||||
2017-2020 Yangfl <mmyangfl@gmail.com>
|
||||
License: GPL-2+
|
||||
|
||||
License: BSD-3-clause
|
||||
Copyright (c) 2013-2017, The PurpleI2P Project
|
||||
Copyright (c) 2013-2023, 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 4096
|
||||
ulimit -n 8192
|
||||
|
||||
1
debian/i2pd.install
vendored
1
debian/i2pd.install
vendored
@@ -1,7 +1,6 @@
|
||||
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,5 +1,4 @@
|
||||
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
27
debian/patches/01-fix-1210.patch
vendored
@@ -1,27 +0,0 @@
|
||||
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,2 +1 @@
|
||||
01-fix-1210.patch
|
||||
02-upnp.patch
|
||||
01-upnp.patch
|
||||
|
||||
2
debian/rules
vendored
2
debian/rules
vendored
@@ -8,6 +8,6 @@ export DEB_CXXFLAGS_MAINT_APPEND = -Wall -pedantic
|
||||
export DEB_LDFLAGS_MAINT_APPEND =
|
||||
|
||||
%:
|
||||
dh $@ --parallel
|
||||
dh $@
|
||||
|
||||
override_dh_auto_install:
|
||||
|
||||
3
debian/watch
vendored
3
debian/watch
vendored
@@ -1,3 +1,4 @@
|
||||
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
|
||||
|
||||
@@ -64,10 +64,10 @@ namespace afrikaans // language namespace
|
||||
|
||||
static std::map<std::string, std::vector<std::string>> plurals
|
||||
{
|
||||
{"days", {"dag", "dae"}},
|
||||
{"hours", {"uur", "ure"}},
|
||||
{"minutes", {"minuut", "minute"}},
|
||||
{"seconds", {"seconde", "sekondes"}},
|
||||
{"%d days", {"%d dag", "%d dae"}},
|
||||
{"%d hours", {"%d uur", "%d ure"}},
|
||||
{"%d minutes", {"%d minuut", "%d minute"}},
|
||||
{"%d seconds", {"%d seconde", "%d sekondes"}},
|
||||
{"", {"", ""}},
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2021, The PurpleI2P Project
|
||||
* Copyright (c) 2021-2023, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -31,15 +31,16 @@ namespace armenian // language namespace
|
||||
|
||||
static std::map<std::string, std::string> strings
|
||||
{
|
||||
{"KiB", "ԿիԲ"},
|
||||
{"MiB", "ՄիԲ"},
|
||||
{"GiB", "ԳիԲ"},
|
||||
{"%.2f KiB", "%.2f ԿիԲ"},
|
||||
{"%.2f MiB", "%.2f ՄիԲ"},
|
||||
{"%.2f GiB", "%.2f ԳիԲ"},
|
||||
{"building", "կառուցվում է"},
|
||||
{"failed", "Անհաջող"},
|
||||
{"expiring", "Լրանում է"},
|
||||
{"established", "կարգավոյված է"},
|
||||
{"unknown", "անհայտ"},
|
||||
{"exploratory", "հետազոտոկան"},
|
||||
{"Purple I2P Webconsole", "Վեբ-կոնսոլ Purple I2P"},
|
||||
{"<b>i2pd</b> webconsole", "Վեբ-կոնսոլ <b>i2pd</b>"},
|
||||
{"Main page", "Գլխավոր էջ"},
|
||||
{"Router commands", "Երթուղիչի հրահանգներ"},
|
||||
@@ -57,10 +58,10 @@ namespace armenian // language namespace
|
||||
{"Unknown", "Անհայտ"},
|
||||
{"Proxy", "Պրոկսի"},
|
||||
{"Mesh", "MESH-ցանց"},
|
||||
{"Error", "Սխալ"},
|
||||
{"Clock skew", "Ոչ ճշգրիտ ժամանակ"},
|
||||
{"Offline", "Օֆլայն"},
|
||||
{"Symmetric NAT", "Սիմետրիկ NAT"},
|
||||
{"Full cone NAT", "Full cone NAT"},
|
||||
{"Uptime", "Առկայություն"},
|
||||
{"Network status", "Ցանցի կարգավիճակ"},
|
||||
{"Network status v6", "Ցանցի կարգավիճակ v6"},
|
||||
@@ -68,7 +69,7 @@ namespace armenian // language namespace
|
||||
{"Family", "Խմբատեսակ"},
|
||||
{"Tunnel creation success rate", "Հաջողությամբ կառուցված թունելներ"},
|
||||
{"Received", "Ստացվել է"},
|
||||
{"KiB/s", "ԿիԲ/վ"},
|
||||
{"%.2f KiB/s", "%.2f ԿիԲ/վ"},
|
||||
{"Sent", "Ուղարկվել է"},
|
||||
{"Transit", "Տարանցում"},
|
||||
{"Data path", "Տվյալների ուղին"},
|
||||
@@ -89,12 +90,12 @@ namespace armenian // language namespace
|
||||
{"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> արդյունքի տողը կարող է օգտագործվել միայն 2LD տիրույթներ գրանցելու համար (example.i2p): Ենթատիրույթներ գրանցելու համար խնդրում ենք օգտագործել i2pd-tools գործիքակազմը"},
|
||||
{"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "<b> Նշում. </b> արդյունքի տողը կարող է օգտագործվել միայն 2LD տիրույթներ գրանցելու համար (example.i2p): Ենթատիրույթներ գրանցելու համար խնդրում ենք օգտագործել i2pd-tools գործիքակազմը:"},
|
||||
{"Address", "Հասցե"},
|
||||
{"Type", "Տեսակը"},
|
||||
{"EncType", "Գաղտնագրի տեսակը"},
|
||||
{"Inbound tunnels", "Մուտքային թունելներ"},
|
||||
{"ms", "մլվ"},
|
||||
{"%dms", "%dմլվ"},
|
||||
{"Outbound tunnels", "Ելքային թունելներ"},
|
||||
{"Tags", "Թեգեր"},
|
||||
{"Incoming", "Մուտքային"},
|
||||
@@ -112,11 +113,10 @@ namespace armenian // language namespace
|
||||
{"Invalid", "Անվավեր"},
|
||||
{"Store type", "Պահեստավորման տեսակը"},
|
||||
{"Expires", "Սպառվում է"},
|
||||
{"Non Expired Leases", "Չսպառված Lease-եր"},
|
||||
{"Non Expired Leases", "Չսպառված Lease-եր"},
|
||||
{"Gateway", "Դարպաս"},
|
||||
{"TunnelID", "Թունելի ID"},
|
||||
{"EndDate", "Ավարտ"},
|
||||
{"not floodfill", "ոչ floodfill-ներ"},
|
||||
{"Queue size", "Հերթի չափսը"},
|
||||
{"Run peer test", "Գործարկել փորձարկումը"},
|
||||
{"Decline transit tunnels", "Մերժել տարանցիկ թունելներ"},
|
||||
@@ -146,10 +146,7 @@ namespace armenian // language namespace
|
||||
{"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", "Ուղարկվել"},
|
||||
@@ -165,43 +162,35 @@ namespace armenian // language namespace
|
||||
{"You may try to find this host on jump services below", "Ստորև Դուք կարող եք գտնել այս հոսթը jump ծառայությունների միջոցով"},
|
||||
{"Invalid request", "Սխալ հարցում"},
|
||||
{"Proxy unable to parse your request", "Պրոկսին չի կարող հասկանալ Ձեր հարցումը"},
|
||||
{"addresshelper is not supported", "addresshelper-ը համատեղելի չէ"},
|
||||
{"Host", "Հոսթ"},
|
||||
{"added to router's addressbook from helper", "Ավելացված է երթուղիչի հասցեագրքում helper-ի միջոցով"},
|
||||
{"Click here to proceed:", "Շարունակելու համար սեղմեք այստեղ"},
|
||||
{"Continue", "Շարունակել"},
|
||||
{"Addresshelper found", "addresshelper-ը գնտված է"},
|
||||
{"already in router's addressbook", "արդեն առկա է երթուղիչի հասցեագրքում"},
|
||||
{"Click here to update record:", "Սեղմեկ այստեղ որպեսզի թարվացնեք գրառումը"},
|
||||
{"invalid request uri", "Սխալ ձևավորված URI հարցում"},
|
||||
{"Invalid request URI", "Սխալ ձևավորված URI հարցում"},
|
||||
{"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", "արտաքին պրոքսիի անհայտ URL"},
|
||||
{"cannot resolve upstream proxy", "Չհաջողվեց որոշել վերադաս պրոկսին"},
|
||||
{"hostname too long", "Հոսթի անունը չափազանց երկար է"},
|
||||
{"cannot connect to upstream socks proxy", "չհաջողվեց միանալ վերադաս socks պրոկսիին"},
|
||||
{"Cannot negotiate with socks proxy", "Չհաջողվեց պայմանավորվել վերադաս socks պրոկսիի հետ"},
|
||||
{"Bad outproxy settings", "Սխալ արտաքին պրոկսի կարգավորումներ"},
|
||||
{"Host %s is not inside I2P network, but outproxy is not enabled", "Հոսթ %s Հարցումը I2P ցանցից դուրս է, բայց արտաքին պրոքսին միացված չէ"},
|
||||
{"Unknown outproxy URL", "Արտաքին պրոքսիի անհայտ URL"},
|
||||
{"Cannot resolve upstream proxy", "Չհաջողվեց որոշել վերադաս պրոկսին"},
|
||||
{"Hostname is too long", "Հոսթի անունը չափազանց երկար է"},
|
||||
{"Cannot connect to upstream SOCKS proxy", "Չհաջողվեց միանալ վերադաս SOCKS պրոկսի սերվերին"},
|
||||
{"Cannot negotiate with SOCKS proxy", "Չհաջողվեց պայմանավորվել վերադաս SOCKS պրոկսիի հետ"},
|
||||
{"CONNECT error", "Սխալ CONNECT հարցում"},
|
||||
{"Failed to Connect", "Միանալ չhաջողվեց"},
|
||||
{"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 պրոկսի սերվերին"},
|
||||
{"Failed to connect", "Միանալ չhաջողվեց"},
|
||||
{"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.", "Հոսթի հետ կապը հաստատել չհաջողվեց, հնարավոր է այն անջատված է, փորձեք միանալ քիչ ուշ"},
|
||||
{"Can't create connection to requested host, it may be down. Please try again later.", "Հոսթի հետ կապը հաստատել չհաջողվեց, հնարավոր է այն անջատված է, փորձեք միանալ քիչ ուշ:"},
|
||||
{"", ""},
|
||||
};
|
||||
|
||||
static std::map<std::string, std::vector<std::string>> plurals
|
||||
{
|
||||
{"days", {"օր", "օր"}},
|
||||
{"hours", {"ժամ", "ժամ"}},
|
||||
{"minutes", {"րոպե", "րոպե"}},
|
||||
{"seconds", {"վարկյան", "վարկյան"}},
|
||||
{"%d days", {"%d օր", "%d օր"}},
|
||||
{"%d hours", {"%d ժամ", "%d ժամ"}},
|
||||
{"%d minutes", {"%d րոպե", "%d րոպե"}},
|
||||
{"%d seconds", {"%d վարկյան", "%d վարկյան"}},
|
||||
{"", {"", ""}},
|
||||
};
|
||||
|
||||
|
||||
108
i18n/Chinese.cpp
108
i18n/Chinese.cpp
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2022, The PurpleI2P Project
|
||||
* Copyright (c) 2022-2023, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -13,7 +13,6 @@
|
||||
#include "I18N.h"
|
||||
|
||||
// Simplified Chinese localization file
|
||||
// This is an example translation file without strings in it.
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
@@ -32,21 +31,21 @@ namespace chinese // language namespace
|
||||
|
||||
static std::map<std::string, std::string> strings
|
||||
{
|
||||
{"KiB", "KiB"},
|
||||
{"MiB", "MiB"},
|
||||
{"GiB", "GiB"},
|
||||
{"%.2f KiB", "%.2f KiB"},
|
||||
{"%.2f MiB", "%.2f MiB"},
|
||||
{"%.2f GiB", "%.2f GiB"},
|
||||
{"building", "正在构建"},
|
||||
{"failed", "连接失败"},
|
||||
{"expiring", "即将过期"},
|
||||
{"established", "连接已建立"},
|
||||
{"established", "连接成功"},
|
||||
{"unknown", "未知"},
|
||||
{"exploratory", "探测"},
|
||||
{"exploratory", "探索"},
|
||||
{"Purple I2P Webconsole", "Purple I2P 网页控制台"},
|
||||
{"<b>i2pd</b> webconsole", "<b>i2pd</b> 网页控制台"},
|
||||
{"Main page", "主页"},
|
||||
{"Router commands", "路由命令"},
|
||||
{"Local Destinations", "本地目标"},
|
||||
{"LeaseSets", "租契集"},
|
||||
{"LeaseSets", "租约集"},
|
||||
{"Tunnels", "隧道"},
|
||||
{"Transit Tunnels", "中转隧道"},
|
||||
{"Transports", "传输"},
|
||||
@@ -58,11 +57,12 @@ namespace chinese // language namespace
|
||||
{"Firewalled", "受到防火墙限制"},
|
||||
{"Unknown", "未知"},
|
||||
{"Proxy", "代理"},
|
||||
{"Mesh", "Mesh组网"},
|
||||
{"Error", "错误"},
|
||||
{"Mesh", "自组网"},
|
||||
{"Clock skew", "时钟偏移"},
|
||||
{"Offline", "离线"},
|
||||
{"Symmetric NAT", "对称 NAT"},
|
||||
{"Full cone NAT", "全锥型NAT"},
|
||||
{"No Descriptors", "无描述符"},
|
||||
{"Uptime", "运行时间"},
|
||||
{"Network status", "IPv4 网络状态"},
|
||||
{"Network status v6", "IPv6 网络状态"},
|
||||
@@ -70,7 +70,7 @@ namespace chinese // language namespace
|
||||
{"Family", "家族"},
|
||||
{"Tunnel creation success rate", "隧道创建成功率"},
|
||||
{"Received", "已接收"},
|
||||
{"KiB/s", "KiB/s"},
|
||||
{"%.2f KiB/s", "%.2f KiB/s"},
|
||||
{"Sent", "已发送"},
|
||||
{"Transit", "中转"},
|
||||
{"Data path", "数据文件路径"},
|
||||
@@ -91,12 +91,12 @@ namespace chinese // language namespace
|
||||
{"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。"},
|
||||
{"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "<b>注意:</b> 结果字符串只能用于注册二级域名(例如:example.i2p)。若需注册三级域名,请使用 i2pd-tools。"},
|
||||
{"Address", "地址"},
|
||||
{"Type", "类型"},
|
||||
{"EncType", "加密类型"},
|
||||
{"Inbound tunnels", "入站隧道"},
|
||||
{"ms", "毫秒"},
|
||||
{"%dms", "%dms"},
|
||||
{"Outbound tunnels", "出站隧道"},
|
||||
{"Tags", "标签"},
|
||||
{"Incoming", "传入"},
|
||||
@@ -118,20 +118,21 @@ namespace chinese // language namespace
|
||||
{"Gateway", "网关"},
|
||||
{"TunnelID", "隧道 ID"},
|
||||
{"EndDate", "结束日期"},
|
||||
{"not floodfill", "非洪泛"},
|
||||
{"floodfill mode is disabled", "洪泛已禁用"},
|
||||
{"Queue size", "队列大小"},
|
||||
{"Run peer test", "运行群体测试"},
|
||||
{"Run peer test", "运行节点测试"},
|
||||
{"Reload tunnels configuration", "重新载入隧道配置"},
|
||||
{"Decline transit tunnels", "拒绝中转隧道"},
|
||||
{"Accept transit tunnels", "允许中转隧道"},
|
||||
{"Cancel graceful shutdown", "取消离线"},
|
||||
{"Start graceful shutdown", "优雅地离线"},
|
||||
{"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", "更换语言"},
|
||||
{"Change", "修改"},
|
||||
{"Change language", "更改语言"},
|
||||
{"no transit tunnels currently built", "目前未构建中转隧道"},
|
||||
{"SAM disabled", "SAM 已禁用"},
|
||||
{"no sessions currently running", "没有正在运行的会话"},
|
||||
@@ -141,15 +142,15 @@ namespace chinese // language namespace
|
||||
{"Client Forwards", "客户端转发"},
|
||||
{"Server Forwards", "服务器转发"},
|
||||
{"Unknown page", "未知页面"},
|
||||
{"Invalid token", "无效凭证"},
|
||||
{"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"},
|
||||
{"You will be redirected in %d seconds", "您将在%d秒内被重定向"},
|
||||
{"Transit tunnels count must not exceed %d", "中转隧道数量限制为 %d"},
|
||||
{"Back to commands list", "返回命令列表"},
|
||||
{"Register at reg.i2p", "在 reg.i2p 注册域名"},
|
||||
{"Description", "描述"},
|
||||
@@ -162,37 +163,38 @@ namespace chinese // language namespace
|
||||
{"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", "您可以尝试在下方的跳转服务上找到这个主机"},
|
||||
{"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 请求"},
|
||||
{"Addresshelper is not supported", "不支持地址助手"},
|
||||
{"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>.", "主机 %s <font color=red>已在路由地址簿中</font>。<b>请注意:此地址的来源可能是有害的!</b>点击此处更新记录:<a href=\"%s%s%s&update=true\">继续</a>"},
|
||||
{"Addresshelper forced update rejected", "地址助手强制更新被拒绝"},
|
||||
{"To add host <b>%s</b> in router's addressbook, click here: <a href=\"%s%s%s\">Continue</a>.", "若要在路由器地址簿中添加主机 <b>%s</b> 请点击这里: <a href=\"%s%s%s\">继续</a>"},
|
||||
{"Addresshelper request", "请求地址助手"},
|
||||
{"Host %s added to router's addressbook from helper. Click here to proceed: <a href=\"%s\">Continue</a>.", "主机 %s 已通过地址助手添加到路由地址簿中。点击此处继续:<a href=\"%s\">继续</a>"},
|
||||
{"Addresshelper adding", "正在添加地址助手"},
|
||||
{"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>.", "主机 %s <font color=red>已在路由地址簿中</font>。点击此处更新记录:<a href=\"%s%s%s&update=true\">继续</a>"},
|
||||
{"Addresshelper update", "更新地址助手"},
|
||||
{"Invalid request URI", "无效的 URI 请求"},
|
||||
{"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 代理协商"},
|
||||
{"Outproxy failure", "出口代理故障"},
|
||||
{"Bad outproxy settings", "错误的出口代理设置"},
|
||||
{"Host %s is not inside I2P network, but outproxy is not enabled", "主机 %s 不在 I2P 网络内,但出口代理未启用"},
|
||||
{"Unknown outproxy URL", "未知的出口代理地址"},
|
||||
{"Cannot resolve upstream proxy", "无法解析上游代理"},
|
||||
{"Hostname is 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 代理"},
|
||||
{"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.", "无法创建到目标主机的连接。主机可能已下线,请稍后再试。"},
|
||||
{"", ""},
|
||||
@@ -200,10 +202,10 @@ namespace chinese // language namespace
|
||||
|
||||
static std::map<std::string, std::vector<std::string>> plurals
|
||||
{
|
||||
{"days", {"天"}},
|
||||
{"hours", {"时"}},
|
||||
{"minutes", {"分"}},
|
||||
{"seconds", {"秒"}},
|
||||
{"%d days", {"%d 天"}},
|
||||
{"%d hours", {"%d 小时"}},
|
||||
{"%d minutes", {"%d 分钟"}},
|
||||
{"%d seconds", {"%d 秒"}},
|
||||
{"", {""}},
|
||||
};
|
||||
|
||||
|
||||
204
i18n/Czech.cpp
Normal file
204
i18n/Czech.cpp
Normal file
@@ -0,0 +1,204 @@
|
||||
/*
|
||||
* Copyright (c) 2022-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"
|
||||
|
||||
// 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íť"},
|
||||
{"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í"},
|
||||
{"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í"},
|
||||
{"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"},
|
||||
{"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í"},
|
||||
{"Host %s is not inside I2P network, but outproxy is not enabled", "Hostitel %s 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 is 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 HTTP 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
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2022, The PurpleI2P Project
|
||||
* Copyright (c) 2022-2023, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -31,9 +31,9 @@ namespace french // language namespace
|
||||
|
||||
static std::map<std::string, std::string> strings
|
||||
{
|
||||
{"KiB", "Kio"},
|
||||
{"MiB", "Mio"},
|
||||
{"GiB", "Gio"},
|
||||
{"%.2f KiB", "%.2f Kio"},
|
||||
{"%.2f MiB", "%.2f Mio"},
|
||||
{"%.2f GiB", "%.2f Gio"},
|
||||
{"building", "En construction"},
|
||||
{"failed", "échoué"},
|
||||
{"expiring", "expiré"},
|
||||
@@ -58,10 +58,11 @@ namespace french // language namespace
|
||||
{"Unknown", "Inconnu"},
|
||||
{"Proxy", "Proxy"},
|
||||
{"Mesh", "Maillé"},
|
||||
{"Error", "Erreur"},
|
||||
{"Clock skew", "Horloge décalée"},
|
||||
{"Offline", "Hors ligne"},
|
||||
{"Symmetric NAT", "NAT symétrique"},
|
||||
{"Full cone NAT", "NAT à cône complet"},
|
||||
{"No Descriptors", "Aucuns Descripteurs"},
|
||||
{"Uptime", "Temps de fonctionnement"},
|
||||
{"Network status", "État du réseau"},
|
||||
{"Network status v6", "État du réseau v6"},
|
||||
@@ -69,7 +70,7 @@ namespace french // language namespace
|
||||
{"Family", "Famille"},
|
||||
{"Tunnel creation success rate", "Taux de succès de création de tunnels"},
|
||||
{"Received", "Reçu"},
|
||||
{"KiB/s", "kio/s"},
|
||||
{"%.2f KiB/s", "%.2f Kio/s"},
|
||||
{"Sent", "Envoyé"},
|
||||
{"Transit", "Transité"},
|
||||
{"Data path", "Emplacement des données"},
|
||||
@@ -81,6 +82,7 @@ namespace french // language namespace
|
||||
{"Our external address", "Notre adresse externe"},
|
||||
{"supported", "supporté"},
|
||||
{"Routers", "Routeurs"},
|
||||
{"Floodfills", "Remplisseurs"},
|
||||
{"Client Tunnels", "Tunnels clients"},
|
||||
{"Services", "Services"},
|
||||
{"Enabled", "Activé"},
|
||||
@@ -92,8 +94,9 @@ namespace french // language namespace
|
||||
{"<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"},
|
||||
{"EncType", "EncType"},
|
||||
{"Inbound tunnels", "Tunnels entrants"},
|
||||
{"ms", "ms"},
|
||||
{"%dms", "%dms"},
|
||||
{"Outbound tunnels", "Tunnels sortants"},
|
||||
{"Tags", "Balises"},
|
||||
{"Incoming", "Entrant"},
|
||||
@@ -115,8 +118,10 @@ namespace french // language namespace
|
||||
{"Gateway", "Passerelle"},
|
||||
{"TunnelID", "ID du tunnel"},
|
||||
{"EndDate", "Date de fin"},
|
||||
{"floodfill mode is disabled", "le mode de remplissage est désactivé"},
|
||||
{"Queue size", "Longueur de la file"},
|
||||
{"Run peer test", "Lancer test des pairs"},
|
||||
{"Reload tunnels configuration", "Recharger la configuration des tunnels"},
|
||||
{"Decline transit tunnels", "Refuser les tunnels transitoires"},
|
||||
{"Accept transit tunnels", "Accepter les tunnels transitoires"},
|
||||
{"Cancel graceful shutdown", "Annuler l'arrêt gracieux"},
|
||||
@@ -134,6 +139,8 @@ namespace french // language namespace
|
||||
{"SAM session not found", "session SAM introuvable"},
|
||||
{"SAM Session", "Session SAM"},
|
||||
{"Server Tunnels", "Tunnels serveurs"},
|
||||
{"Client Forwards", "Transmission du client"},
|
||||
{"Server Forwards", "Transmission du serveur"},
|
||||
{"Unknown page", "Page inconnue"},
|
||||
{"Invalid token", "Jeton invalide"},
|
||||
{"SUCCESS", "SUCCÈS"},
|
||||
@@ -142,8 +149,8 @@ namespace french // language namespace
|
||||
{"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"},
|
||||
{"You will be redirected in %d seconds", "Vous serez redirigé dans %d secondes"},
|
||||
{"Transit tunnels count must not exceed %d", "Le nombre de tunnels de transit ne doit pas excéder %d"},
|
||||
{"Back to commands list", "Retour à la liste des commandes"},
|
||||
{"Register at reg.i2p", "Inscription à reg.i2p"},
|
||||
{"Description", "Description"},
|
||||
@@ -161,32 +168,33 @@ namespace french // language namespace
|
||||
{"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"},
|
||||
{"Addresshelper is not supported", "Assistant d'adresse non supporté"},
|
||||
{"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>.", "L'hôte %s est <font color=red>déjà dans le carnet d'adresses du routeur</font>. <b>Attention : la source de cette URL peut être nuisible !</b> Cliquez ici pour mettre à jour l'enregistrement : <a href=\"%s%s%s&update=true\">Continuer</a>."},
|
||||
{"Addresshelper forced update rejected", "Mise à jour forcée des assistants d'adresses rejetée"},
|
||||
{"To add host <b>%s</b> in router's addressbook, click here: <a href=\"%s%s%s\">Continue</a>.", "Pour ajouter l'hôte <b>%s</b> au carnet d'adresses du routeur, cliquez ici : <a href=\"%s%s%s\">Continuer</a>."},
|
||||
{"Addresshelper request", "Demande à l'assistant d'adresse"},
|
||||
{"Host %s added to router's addressbook from helper. Click here to proceed: <a href=\"%s\">Continue</a>.", "L'hôte %s a été ajouté au carnet d'adresses du routeur depuis l'assistant. Cliquez ici pour continuer : <a href=\"%s\">Continuer</a>."},
|
||||
{"Addresshelper adding", "Ajout de l'assistant d'adresse"},
|
||||
{"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>.", "L'hôte %s est <font color=red>déjà dans le carnet d'adresses du routeur</font>. Cliquez ici pour mettre à jour le dossier : <a href=\"%s%s%s&update=true\">Continuer</a>."},
|
||||
{"Addresshelper update", "Mise à jour de l'assistant 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"},
|
||||
{"Bad outproxy settings", "Mauvaise configuration du proxy de sortie"},
|
||||
{"Host %s is not inside I2P network, but outproxy is not enabled", "Hôte %s 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 is 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"},
|
||||
{"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."},
|
||||
{"", ""},
|
||||
@@ -194,10 +202,10 @@ namespace french // language namespace
|
||||
|
||||
static std::map<std::string, std::vector<std::string>> plurals
|
||||
{
|
||||
{"days", {"jour", "jours"}},
|
||||
{"hours", {"heure", "heures"}},
|
||||
{"minutes", {"minute", "minutes"}},
|
||||
{"seconds", {"seconde", "secondes"}},
|
||||
{"%d days", {"%d jour", "%d jours"}},
|
||||
{"%d hours", {"%d heure", "%d heures"}},
|
||||
{"%d minutes", {"%d minute", "%d minutes"}},
|
||||
{"%d seconds", {"%d seconde", "%d secondes"}},
|
||||
{"", {"", ""}},
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2022, The PurpleI2P Project
|
||||
* Copyright (c) 2022-2023, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -31,16 +31,16 @@ namespace german // language namespace
|
||||
|
||||
static std::map<std::string, std::string> strings
|
||||
{
|
||||
{"Purple I2P Webconsole", "Purple-I2P-Webkonsole"},
|
||||
{"KiB", "KiB"},
|
||||
{"MiB", "MiB"},
|
||||
{"GiB", "GiB"},
|
||||
{"%.2f KiB", "%.2f KiB"},
|
||||
{"%.2f MiB", "%.2f MiB"},
|
||||
{"%.2f GiB", "%.2f GiB"},
|
||||
{"building", "In Bau"},
|
||||
{"failed", "fehlgeschlagen"},
|
||||
{"expiring", "läuft ab"},
|
||||
{"established", "hergestellt"},
|
||||
{"unknown", "Unbekannt"},
|
||||
{"exploratory", "erforschend"},
|
||||
{"Purple I2P Webconsole", "Purple I2P-Webkonsole"},
|
||||
{"<b>i2pd</b> webconsole", "<b>i2pd</b>-Webkonsole"},
|
||||
{"Main page", "Startseite"},
|
||||
{"Router commands", "Routerbefehle"},
|
||||
@@ -49,8 +49,8 @@ namespace german // language namespace
|
||||
{"Tunnels", "Tunnel"},
|
||||
{"Transit Tunnels", "Transittunnel"},
|
||||
{"Transports", "Transporte"},
|
||||
{"I2P tunnels", "I2P-Tunnel"},
|
||||
{"SAM sessions", "SAM-Sitzungen"},
|
||||
{"I2P tunnels", "I2P Tunnel"},
|
||||
{"SAM sessions", "SAM Sitzungen"},
|
||||
{"ERROR", "FEHLER"},
|
||||
{"OK", "OK"},
|
||||
{"Testing", "Testen"},
|
||||
@@ -58,7 +58,6 @@ namespace german // language namespace
|
||||
{"Unknown", "Unbekannt"},
|
||||
{"Proxy", "Proxy"},
|
||||
{"Mesh", "Mesh"},
|
||||
{"Error", "Fehler"},
|
||||
{"Clock skew", "Zeitabweichung"},
|
||||
{"Offline", "Offline"},
|
||||
{"Symmetric NAT", "Symmetrisches NAT"},
|
||||
@@ -69,7 +68,7 @@ namespace german // language namespace
|
||||
{"Family", "Familie"},
|
||||
{"Tunnel creation success rate", "Erfolgsrate der Tunnelerstellung"},
|
||||
{"Received", "Eingegangen"},
|
||||
{"KiB/s", "KiB/s"},
|
||||
{"%.2f KiB/s", "%.2f KiB/s"},
|
||||
{"Sent", "Gesendet"},
|
||||
{"Transit", "Transit"},
|
||||
{"Data path", "Datenpfad"},
|
||||
@@ -95,7 +94,7 @@ namespace german // language namespace
|
||||
{"Type", "Typ"},
|
||||
{"EncType", "Verschlüsselungstyp"},
|
||||
{"Inbound tunnels", "Eingehende Tunnel"},
|
||||
{"ms", "ms"},
|
||||
{"%dms", "%dms"},
|
||||
{"Outbound tunnels", "Ausgehende Tunnel"},
|
||||
{"Tags", "Tags"},
|
||||
{"Incoming", "Eingehend"},
|
||||
@@ -117,7 +116,6 @@ namespace german // language namespace
|
||||
{"Gateway", "Gateway"},
|
||||
{"TunnelID", "TunnelID"},
|
||||
{"EndDate", "Enddatum"},
|
||||
{"not floodfill", "kein Floodfill"},
|
||||
{"Queue size", "Größe der Warteschlange"},
|
||||
{"Run peer test", "Peer-Test durchführen"},
|
||||
{"Decline transit tunnels", "Transittunnel ablehnen"},
|
||||
@@ -147,8 +145,6 @@ namespace german // language namespace
|
||||
{"Destination not found", "Ziel nicht gefunden"},
|
||||
{"StreamID can't be null", "StreamID kann nicht null sein"},
|
||||
{"Return to destination page", "Zurück zur Ziel-Seite"},
|
||||
{"You will be redirected in 5 seconds", "Du wirst in 5 Sekunden weitergeleitet"},
|
||||
{"Transit tunnels count must not exceed 65535", "Es darf maximal 65535 Transittunnel geben"},
|
||||
{"Back to commands list", "Zurück zur Befehlsliste"},
|
||||
{"Register at reg.i2p", "Auf reg.i2p registrieren"},
|
||||
{"Description", "Beschreibung"},
|
||||
@@ -166,32 +162,24 @@ namespace german // language namespace
|
||||
{"You may try to find this host on jump services below", "Vielleicht kannst du diesen Host auf einem der nachfolgenden Jump-Services finden"},
|
||||
{"Invalid request", "Ungültige Anfrage"},
|
||||
{"Proxy unable to parse your request", "Proxy konnte die Anfrage nicht verarbeiten"},
|
||||
{"addresshelper is not supported", "Addresshelfer wird nicht unterstützt"},
|
||||
{"Host", "Host"},
|
||||
{"added to router's addressbook from helper", "vom Helfer zum Router-Adressbuch hinzugefügt"},
|
||||
{"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"},
|
||||
{"Invalid request URI", "Ungültige Anfrage-URI"},
|
||||
{"Can't detect destination host from request", "Kann den Ziel-Host von der Anfrage nicht erkennen"},
|
||||
{"Outproxy failure", "Outproxy-Fehler"},
|
||||
{"bad outproxy settings", "ungültige Outproxy-Einstellungen"},
|
||||
{"not inside I2P network, but outproxy is not enabled", "außerhalb des I2P-Netzwerks, aber Outproxy ist nicht aktiviert"},
|
||||
{"unknown outproxy url", "unbekannte Outproxy-URL"},
|
||||
{"cannot resolve upstream proxy", "kann den Upstream-Proxy nicht auflösen"},
|
||||
{"hostname too long", "Hostname zu lang"},
|
||||
{"cannot connect to upstream socks proxy", "Kann keine Verbindung zum Upstream-Socks-Proxy herstellen"},
|
||||
{"Cannot negotiate with socks proxy", "Kann nicht mit Socks-Proxy verhandeln"},
|
||||
{"Bad outproxy settings", "Ungültige Outproxy-Einstellungen"},
|
||||
{"Host %s is not inside I2P network, but outproxy is not enabled", "Host %s außerhalb des I2P-Netzwerks, aber Outproxy ist nicht aktiviert"},
|
||||
{"Unknown outproxy URL", "Unbekannte Outproxy-URL"},
|
||||
{"Cannot resolve upstream proxy", "Kann den Upstream-Proxy nicht auflösen"},
|
||||
{"Hostname is too long", "Hostname zu lang"},
|
||||
{"Cannot connect to upstream SOCKS proxy", "Kann keine Verbindung zum Upstream-SOCKS-Proxy herstellen"},
|
||||
{"Cannot negotiate with SOCKS proxy", "Kann nicht mit SOCKS-Proxy verhandeln"},
|
||||
{"CONNECT error", "CONNECT-Fehler"},
|
||||
{"Failed to Connect", "Verbindung konnte nicht hergestellt werden"},
|
||||
{"socks proxy error", "Socks-Proxy-Fehler"},
|
||||
{"failed to send request to upstream", "Anfrage an den Upstream zu senden ist gescheitert"},
|
||||
{"No Reply From socks proxy", "Keine Antwort vom Socks-Proxy"},
|
||||
{"cannot connect", "kann nicht verbinden"},
|
||||
{"http out proxy not implemented", "HTTP-Outproxy nicht implementiert"},
|
||||
{"cannot connect to upstream http proxy", "Kann nicht zu Upstream-HTTP-Proxy verbinden"},
|
||||
{"Failed to connect", "Verbindung konnte nicht hergestellt werden"},
|
||||
{"SOCKS proxy error", "SOCKS-Proxy-Fehler"},
|
||||
{"Failed to send request to upstream", "Anfrage an den Upstream zu senden ist gescheitert"},
|
||||
{"No reply from SOCKS proxy", "Keine Antwort vom SOCKS-Proxy"},
|
||||
{"Cannot connect", "Kann nicht verbinden"},
|
||||
{"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."},
|
||||
{"", ""},
|
||||
@@ -199,10 +187,10 @@ namespace german // language namespace
|
||||
|
||||
static std::map<std::string, std::vector<std::string>> plurals
|
||||
{
|
||||
{"days", {"Tag", "Tage"}},
|
||||
{"hours", {"Stunde", "Stunden"}},
|
||||
{"minutes", {"Minute", "Minuten"}},
|
||||
{"seconds", {"Sekunde", "Sekunden"}},
|
||||
{"%d days", {"%d Tag", "%d Tage"}},
|
||||
{"%d hours", {"%d Stunde", "%d Stunden"}},
|
||||
{"%d minutes", {"%d Minute", "%d Minuten"}},
|
||||
{"%d seconds", {"%d Sekunde", "%d Sekunden"}},
|
||||
{"", {"", ""}},
|
||||
};
|
||||
|
||||
|
||||
43
i18n/I18N.cpp
Normal file
43
i18n/I18N.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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-2022, The PurpleI2P Project
|
||||
* Copyright (c) 2021-2023, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -9,37 +9,129 @@
|
||||
#ifndef __I18N_H__
|
||||
#define __I18N_H__
|
||||
|
||||
#include "ClientContext.h"
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <utility>
|
||||
#include <functional>
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace i18n
|
||||
{
|
||||
inline void SetLanguage(const std::string &lang)
|
||||
class Locale
|
||||
{
|
||||
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());
|
||||
}
|
||||
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) { };
|
||||
|
||||
inline std::string translate (const std::string& arg)
|
||||
{
|
||||
return i2p::client::context.GetLanguage ()->GetString (arg);
|
||||
}
|
||||
// Get activated language name for webconsole
|
||||
std::string GetLanguage() const
|
||||
{
|
||||
return m_Language;
|
||||
}
|
||||
|
||||
inline std::string translate (const std::string& arg, const std::string& arg2, const int& n)
|
||||
{
|
||||
return i2p::client::context.GetLanguage ()->GetPlural (arg, arg2, n);
|
||||
}
|
||||
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);
|
||||
} // i18n
|
||||
} // i2p
|
||||
|
||||
template<typename... TArgs>
|
||||
std::string tr (TArgs&&... args)
|
||||
/**
|
||||
* @brief Get translation of string
|
||||
* @param arg String with message
|
||||
*/
|
||||
template<typename TValue>
|
||||
std::string tr (TValue&& arg)
|
||||
{
|
||||
return i2p::i18n::translate(std::forward<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;
|
||||
}
|
||||
|
||||
#endif // __I18N_H__
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2021-2022, The PurpleI2P Project
|
||||
* Copyright (c) 2021-2023, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -9,60 +9,12 @@
|
||||
#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
|
||||
@@ -71,16 +23,23 @@ namespace i18n
|
||||
};
|
||||
|
||||
// Add localization here with language name as namespace
|
||||
namespace afrikaans { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
|
||||
namespace armenian { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
|
||||
namespace chinese { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
|
||||
namespace english { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
|
||||
namespace french { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
|
||||
namespace german { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
|
||||
namespace russian { 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 (); }
|
||||
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 polish { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
|
||||
namespace portuguese { 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 turkish { 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 (); }
|
||||
|
||||
/**
|
||||
* That map contains international language name lower-case, name in it's language and it's code
|
||||
@@ -88,14 +47,21 @@ namespace i18n
|
||||
static std::map<std::string, langData> languages
|
||||
{
|
||||
{ "afrikaans", {"Afrikaans", "af", i2p::i18n::afrikaans::GetLocale} },
|
||||
{ "armenian", {"հայերէն", "hy", i2p::i18n::armenian::GetLocale} },
|
||||
{ "armenian", {"hայերէն", "hy", i2p::i18n::armenian::GetLocale} },
|
||||
{ "chinese", {"简体字", "zh-CN", i2p::i18n::chinese::GetLocale} },
|
||||
{ "czech", {"čeština", "cs", i2p::i18n::czech::GetLocale} },
|
||||
{ "english", {"English", "en", i2p::i18n::english::GetLocale} },
|
||||
{ "french", {"Français", "fr", i2p::i18n::french::GetLocale} },
|
||||
{ "german", {"Deutsch", "de", i2p::i18n::german::GetLocale} },
|
||||
{ "russian", {"русский язык", "ru", i2p::i18n::russian::GetLocale} },
|
||||
{ "turkmen", {"türkmen dili", "tk", i2p::i18n::turkmen::GetLocale} },
|
||||
{ "ukrainian", {"украї́нська мо́ва", "uk", i2p::i18n::ukrainian::GetLocale} },
|
||||
{ "italian", {"Italiano", "it", i2p::i18n::italian::GetLocale} },
|
||||
{ "polish", {"Polski", "pl", i2p::i18n::polish::GetLocale} },
|
||||
{ "portuguese", {"Português", "pt", i2p::i18n::portuguese::GetLocale} },
|
||||
{ "russian", {"Русский язык", "ru", i2p::i18n::russian::GetLocale} },
|
||||
{ "spanish", {"Español", "es", i2p::i18n::spanish::GetLocale} },
|
||||
{ "swedish", {"Svenska", "sv", i2p::i18n::swedish::GetLocale} },
|
||||
{ "turkish", {"Türk dili", "tr", i2p::i18n::turkish::GetLocale} },
|
||||
{ "turkmen", {"Türkmen dili", "tk", i2p::i18n::turkmen::GetLocale} },
|
||||
{ "ukrainian", {"Украї́нська мо́ва", "uk", i2p::i18n::ukrainian::GetLocale} },
|
||||
{ "uzbek", {"Oʻzbek", "uz", i2p::i18n::uzbek::GetLocale} },
|
||||
};
|
||||
|
||||
|
||||
209
i18n/Italian.cpp
Normal file
209
i18n/Italian.cpp
Normal file
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* Copyright (c) 2022-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"
|
||||
|
||||
// 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"},
|
||||
{"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"},
|
||||
{"Queue size", "Dimensione della coda"},
|
||||
{"Run peer test", "Esegui il test dei peer"},
|
||||
{"Reload tunnels configuration", "Ricarica la configurazione dei tunnel"},
|
||||
{"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 %d seconds", "Sarai reindirizzato tra %d secondi"},
|
||||
{"Transit tunnels count must not exceed %d", "Il conteggio dei tunnel di transito non deve superare %d"},
|
||||
{"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"},
|
||||
{"To add host <b>%s</b> in router's addressbook, click here: <a href=\"%s%s%s\">Continue</a>.", "Per aggiungere host <b>%s</b> nella rubrica del router, clicca qui: <a href=\"%s%s%s\">Continua</a>."},
|
||||
{"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>.", "L'host %s è <font color=red>già nella rubrica del router</font>. Clicca qui per aggiornare il record: <a href=\"%s%s%s&update=true\">Continua</a>."},
|
||||
{"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"},
|
||||
{"Host %s is not inside I2P network, but outproxy is not enabled", "Host %s 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 is 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 flusso a monte del proxy HTTP"},
|
||||
{"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
|
||||
59
i18n/Polish.cpp
Normal file
59
i18n/Polish.cpp
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
// Polish localization file
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace i18n
|
||||
{
|
||||
namespace polish // language namespace
|
||||
{
|
||||
// language name in lowercase
|
||||
static std::string language = "polish";
|
||||
|
||||
// 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 % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2);
|
||||
}
|
||||
|
||||
static std::map<std::string, std::string> strings
|
||||
{
|
||||
{"building", "Kompilowanie"},
|
||||
{"failed", "nieudane"},
|
||||
{"expiring", "wygasający"},
|
||||
{"established", "ustanowiony"},
|
||||
{"Main page", "Strona główna"},
|
||||
{"Router commands", "Komendy routera"},
|
||||
{"Tunnels", "Tunele"},
|
||||
{"OK", "Ok"},
|
||||
{"Uptime", "Czas pracy"},
|
||||
{"Sent", "Wysłane"},
|
||||
{"", ""},
|
||||
};
|
||||
|
||||
static std::map<std::string, std::vector<std::string>> plurals
|
||||
{
|
||||
{"", {"", "", ""}},
|
||||
};
|
||||
|
||||
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
|
||||
219
i18n/Portuguese.cpp
Normal file
219
i18n/Portuguese.cpp
Normal file
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
// Portuguese localization file
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace i18n
|
||||
{
|
||||
namespace portuguese // language namespace
|
||||
{
|
||||
// language name in lowercase
|
||||
static std::string language = "portuguese";
|
||||
|
||||
// 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", "construindo"},
|
||||
{"failed", "falhou"},
|
||||
{"expiring", "expirando"},
|
||||
{"established", "estabelecido"},
|
||||
{"unknown", "desconhecido"},
|
||||
{"exploratory", "exploratório"},
|
||||
{"Purple I2P Webconsole", "Webconsole Purple I2P"},
|
||||
{"<b>i2pd</b> webconsole", "webconsole <b>i2pd</b>"},
|
||||
{"Main page", "Página Principal"},
|
||||
{"Router commands", "Comandos do Roteador"},
|
||||
{"Local Destinations", "Destinos Locais"},
|
||||
{"LeaseSets", "LeaseSets"},
|
||||
{"Tunnels", "Túneis"},
|
||||
{"Transit Tunnels", "Túneis de Trânsito"},
|
||||
{"Transports", "Transportes"},
|
||||
{"I2P tunnels", "Túneis I2P"},
|
||||
{"SAM sessions", "Sessões do SAM"},
|
||||
{"ERROR", "ERRO"},
|
||||
{"OK", "OK"},
|
||||
{"Testing", "Testando"},
|
||||
{"Firewalled", "Sob Firewall"},
|
||||
{"Unknown", "Desconhecido"},
|
||||
{"Proxy", "Proxy"},
|
||||
{"Mesh", "Malha"},
|
||||
{"Clock skew", "Defasagem do Relógio"},
|
||||
{"Offline", "Desligado"},
|
||||
{"Symmetric NAT", "NAT Simétrico"},
|
||||
{"Full cone NAT", "Full cone NAT"},
|
||||
{"No Descriptors", "Sem Descritores"},
|
||||
{"Uptime", "Tempo Ativo"},
|
||||
{"Network status", "Estado da rede"},
|
||||
{"Network status v6", "Estado da rede v6"},
|
||||
{"Stopping in", "Parando em"},
|
||||
{"Family", "Família"},
|
||||
{"Tunnel creation success rate", "Taxa de sucesso na criação de túneis"},
|
||||
{"Received", "Recebido"},
|
||||
{"%.2f KiB/s", "%.2f KiB/s"},
|
||||
{"Sent", "Enviado"},
|
||||
{"Transit", "Trânsito"},
|
||||
{"Data path", "Caminho dos dados"},
|
||||
{"Hidden content. Press on text to see.", "Conteúdo oculto. Clique no texto para revelar."},
|
||||
{"Router Ident", "Identidade do Roteador"},
|
||||
{"Router Family", "Família do Roteador"},
|
||||
{"Router Caps", "Limites do Roteador"},
|
||||
{"Version", "Versão"},
|
||||
{"Our external address", "Nosso endereço externo"},
|
||||
{"supported", "suportado"},
|
||||
{"Routers", "Roteadores"},
|
||||
{"Floodfills", "Modo Inundação"},
|
||||
{"Client Tunnels", "Túneis de Clientes"},
|
||||
{"Services", "Serviços"},
|
||||
{"Enabled", "Ativado"},
|
||||
{"Disabled", "Desativado"},
|
||||
{"Encrypted B33 address", "Endereço B33 criptografado"},
|
||||
{"Address registration line", "Linha de cadastro de endereço"},
|
||||
{"Domain", "Domínio"},
|
||||
{"Generate", "Gerar"},
|
||||
{"<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>A string resultante só pode ser usada para registrar domínios 2LD (exemplo.i2p). Para registrar subdomínios por favor utilize o i2pd-tools."},
|
||||
{"Address", "Endereço"},
|
||||
{"Type", "Tipo"},
|
||||
{"EncType", "Tipo de Criptografia"},
|
||||
{"Inbound tunnels", "Túneis de Entrada"},
|
||||
{"%dms", "%dms"},
|
||||
{"Outbound tunnels", "Túneis de Saída"},
|
||||
{"Tags", "Etiquetas"},
|
||||
{"Incoming", "Entradas"},
|
||||
{"Outgoing", "Saídas"},
|
||||
{"Destination", "Destinos"},
|
||||
{"Amount", "Quantidade"},
|
||||
{"Incoming Tags", "Etiquetas de Entrada"},
|
||||
{"Tags sessions", "Sessões de etiquetas"},
|
||||
{"Status", "Estado"},
|
||||
{"Local Destination", "Destinos Locais"},
|
||||
{"Streams", "Fluxos"},
|
||||
{"Close stream", "Fechar fluxo"},
|
||||
{"I2CP session not found", "Sessão do I2CP não encontrada"},
|
||||
{"I2CP is not enabled", "I2CP não está ativado"},
|
||||
{"Invalid", "Inválido"},
|
||||
{"Store type", "Tipo de armazenamento"},
|
||||
{"Expires", "Expira em"},
|
||||
{"Non Expired Leases", "Sessões não expiradas"},
|
||||
{"Gateway", "Gateway"},
|
||||
{"TunnelID", "TunnelID"},
|
||||
{"EndDate", "Data final"},
|
||||
{"floodfill mode is disabled", "Mode de inundação está desativado"},
|
||||
{"Queue size", "Tamanho da fila"},
|
||||
{"Run peer test", "Executar teste de peers"},
|
||||
{"Reload tunnels configuration", "Recarregar a configuração dos túneis"},
|
||||
{"Decline transit tunnels", "Negar túnel de trânsito"},
|
||||
{"Accept transit tunnels", "Aceitar túnel de trânsito"},
|
||||
{"Cancel graceful shutdown", "Cancelar desligamento gracioso"},
|
||||
{"Start graceful shutdown", "Iniciar desligamento gracioso"},
|
||||
{"Force shutdown", "Forçar desligamento"},
|
||||
{"Reload external CSS styles", "Recarregar estilos CSS externos"},
|
||||
{"<b>Note:</b> any action done here are not persistent and not changes your config files.", "<b> Nota: </b> Qualquer ação feita aqui não será permanente e não altera os seus arquivos de configuração."},
|
||||
{"Logging level", "Nível de registro"},
|
||||
{"Transit tunnels limit", "Limite nos túneis de trânsito"},
|
||||
{"Change", "Mudar"},
|
||||
{"Change language", "Trocar idioma"},
|
||||
{"no transit tunnels currently built", "Nenhum túnel de trânsito construido no momento"},
|
||||
{"SAM disabled", "SAM desativado"},
|
||||
{"no sessions currently running", "Nenhuma sessão funcionando no momento"},
|
||||
{"SAM session not found", "Nenhuma sessão do SAM encontrada"},
|
||||
{"SAM Session", "Sessão do SAM"},
|
||||
{"Server Tunnels", "Túneis de Servidor"},
|
||||
{"Client Forwards", "Túneis de Cliente"},
|
||||
{"Server Forwards", "Encaminhamentos de Servidor"},
|
||||
{"Unknown page", "Página desconhecida"},
|
||||
{"Invalid token", "Token Inválido"},
|
||||
{"SUCCESS", "SUCESSO"},
|
||||
{"Stream closed", "Fluxo fechado"},
|
||||
{"Stream not found or already was closed", "Fluxo não encontrado ou já encerrado"},
|
||||
{"Destination not found", "Destino não encontrado"},
|
||||
{"StreamID can't be null", "StreamID não pode ser nulo"},
|
||||
{"Return to destination page", "Retornar para à página de destino"},
|
||||
{"You will be redirected in %d seconds", "Você será redirecionado em %d segundos"},
|
||||
{"Transit tunnels count must not exceed %d", "A contagem de túneis de trânsito não deve exceder %d"},
|
||||
{"Back to commands list", "Voltar para a lista de comandos"},
|
||||
{"Register at reg.i2p", "Registrar na reg.i2p"},
|
||||
{"Description", "Descrição"},
|
||||
{"A bit information about service on domain", "Algumas informações sobre o serviço no domínio"},
|
||||
{"Submit", "Enviar"},
|
||||
{"Domain can't end with .b32.i2p", "O domínio não pode terminar com .b32.i2p"},
|
||||
{"Domain must end with .i2p", "O domínio não pode terminar com .i2p"},
|
||||
{"Such destination is not found", "Tal destino não foi encontrado"},
|
||||
{"Unknown command", "Comando desconhecido"},
|
||||
{"Command accepted", "Comando aceito"},
|
||||
{"Proxy error", "Erro no proxy"},
|
||||
{"Proxy info", "Informações do proxy"},
|
||||
{"Proxy error: Host not found", "Erro no proxy: Host não encontrado"},
|
||||
{"Remote host not found in router's addressbook", "O host remoto não foi encontrado no livro de endereços do roteador"},
|
||||
{"You may try to find this host on jump services below", "Você pode tentar encontrar este host nos jump services abaixo"},
|
||||
{"Invalid request", "Requisição inválida"},
|
||||
{"Proxy unable to parse your request", "O proxy foi incapaz de processar a sua requisição"},
|
||||
{"Addresshelper is not supported", "O Auxiliar de Endereços não é suportado"},
|
||||
{"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>.", "O host %s já <font color=red>está no catálogo de endereços do roteador</font>. <b>Cuidado: a fonte desta URL pode ser perigosa!</b> Clique aqui para atualizar o registro: <a href=\"%s%s%s&update=true\">Continuar</a>."},
|
||||
{"Addresshelper forced update rejected", "A atualização forçada do Auxiliar de Endereços foi rejeitada"},
|
||||
{"To add host <b>%s</b> in router's addressbook, click here: <a href=\"%s%s%s\">Continue</a>.", "Para adicionar o host <b> %s </b> ao catálogo de endereços do roteador, clique aqui: <a href='%s%s%s'>Continuar </a>."},
|
||||
{"Addresshelper request", "Requisição do Auxiliar de Endereços"},
|
||||
{"Host %s added to router's addressbook from helper. Click here to proceed: <a href=\"%s\">Continue</a>.", "O host %s foi adicionado ao catálogo de endereços do roteador por um auxiliar. Clique aqui para proceder: <a href='%s'> Continuar </a>."},
|
||||
{"Addresshelper adding", "Auxiliar de Endereço adicionando"},
|
||||
{"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>.", "O host %s já <font color=red>está no catálogo de endereços do roteador </font>. Clique aqui para atualizar o registro: <a href=\"%s%s%s&update=true\">Continuar</a>."},
|
||||
{"Addresshelper update", "Atualização do Auxiliar de Endereços"},
|
||||
{"Invalid request URI", "A URI de requisição é inválida"},
|
||||
{"Can't detect destination host from request", "Incapaz de detectar o host de destino da requisição"},
|
||||
{"Outproxy failure", "Falha no outproxy"},
|
||||
{"Bad outproxy settings", "Configurações ruins de outproxy"},
|
||||
{"Host %s is not inside I2P network, but outproxy is not enabled", "O host %s não está dentro da rede I2P, mas o outproxy não está ativado"},
|
||||
{"Unknown outproxy URL", "URL de outproxy desconhecida"},
|
||||
{"Cannot resolve upstream proxy", "Não é possível resolver o proxy de entrada"},
|
||||
{"Hostname is too long", "O hostname é muito longo"},
|
||||
{"Cannot connect to upstream SOCKS proxy", "Não é possível se conectar ao proxy SOCKS de entrada"},
|
||||
{"Cannot negotiate with SOCKS proxy", "Não é possível negociar com o proxy SOCKS"},
|
||||
{"CONNECT error", "Erro de CONEXÃO"},
|
||||
{"Failed to connect", "Falha ao conectar"},
|
||||
{"SOCKS proxy error", "Erro no proxy SOCKS"},
|
||||
{"Failed to send request to upstream", "Falha ao enviar requisição para o fluxo de entrada"},
|
||||
{"No reply from SOCKS proxy", "Sem resposta do proxy SOCKS"},
|
||||
{"Cannot connect", "Impossível conectar"},
|
||||
{"HTTP out proxy not implemented", "proxy de saída HTTP não implementado"},
|
||||
{"Cannot connect to upstream HTTP proxy", "Não é possível conectar ao proxy HTTP de entrada"},
|
||||
{"Host is down", "Host está desligado"},
|
||||
{"Can't create connection to requested host, it may be down. Please try again later.", "Não é possível se conectar ao host requisitado, talvez ele esteja for do ar. Por favor, tente novamente mais tarde."},
|
||||
{"", ""},
|
||||
};
|
||||
|
||||
static std::map<std::string, std::vector<std::string>> plurals
|
||||
{
|
||||
{"%d days", {"%d Dia", "%d Dias"}},
|
||||
{"%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
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2021, The PurpleI2P Project
|
||||
* Copyright (c) 2021-2023, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -31,15 +31,16 @@ namespace russian // language namespace
|
||||
|
||||
static std::map<std::string, std::string> strings
|
||||
{
|
||||
{"KiB", "КиБ"},
|
||||
{"MiB", "МиБ"},
|
||||
{"GiB", "ГиБ"},
|
||||
{"%.2f KiB", "%.2f КиБ"},
|
||||
{"%.2f MiB", "%.2f МиБ"},
|
||||
{"%.2f GiB", "%.2f ГиБ"},
|
||||
{"building", "строится"},
|
||||
{"failed", "неудачный"},
|
||||
{"expiring", "истекает"},
|
||||
{"established", "работает"},
|
||||
{"unknown", "неизвестно"},
|
||||
{"exploratory", "исследовательский"},
|
||||
{"Purple I2P Webconsole", "Веб-консоль Purple I2P"},
|
||||
{"<b>i2pd</b> webconsole", "Веб-консоль <b>i2pd</b>"},
|
||||
{"Main page", "Главная"},
|
||||
{"Router commands", "Команды роутера"},
|
||||
@@ -57,10 +58,11 @@ namespace russian // language namespace
|
||||
{"Unknown", "Неизвестно"},
|
||||
{"Proxy", "Прокси"},
|
||||
{"Mesh", "MESH-сеть"},
|
||||
{"Error", "Ошибка"},
|
||||
{"Clock skew", "Не точное время"},
|
||||
{"Offline", "Оффлайн"},
|
||||
{"Symmetric NAT", "Симметричный NAT"},
|
||||
{"Full cone NAT", "Full cone NAT"},
|
||||
{"No Descriptors", "Нет дескрипторов"},
|
||||
{"Uptime", "В сети"},
|
||||
{"Network status", "Сетевой статус"},
|
||||
{"Network status v6", "Сетевой статус v6"},
|
||||
@@ -68,7 +70,7 @@ namespace russian // language namespace
|
||||
{"Family", "Семейство"},
|
||||
{"Tunnel creation success rate", "Успешно построенных туннелей"},
|
||||
{"Received", "Получено"},
|
||||
{"KiB/s", "КиБ/с"},
|
||||
{"%.2f KiB/s", "%.2f КиБ/с"},
|
||||
{"Sent", "Отправлено"},
|
||||
{"Transit", "Транзит"},
|
||||
{"Data path", "Путь к данным"},
|
||||
@@ -94,7 +96,7 @@ namespace russian // language namespace
|
||||
{"Type", "Тип"},
|
||||
{"EncType", "ТипШифр"},
|
||||
{"Inbound tunnels", "Входящие туннели"},
|
||||
{"ms", "мс"},
|
||||
{"%dms", "%dмс"},
|
||||
{"Outbound tunnels", "Исходящие туннели"},
|
||||
{"Tags", "Теги"},
|
||||
{"Incoming", "Входящие"},
|
||||
@@ -116,9 +118,10 @@ namespace russian // language namespace
|
||||
{"Gateway", "Шлюз"},
|
||||
{"TunnelID", "ID туннеля"},
|
||||
{"EndDate", "Заканчивается"},
|
||||
{"not floodfill", "не флудфил"},
|
||||
{"floodfill mode is disabled", "режим флудфила отключен"},
|
||||
{"Queue size", "Размер очереди"},
|
||||
{"Run peer test", "Запустить тестирование"},
|
||||
{"Reload tunnels configuration", "Перезагрузить конфигурацию туннелей"},
|
||||
{"Decline transit tunnels", "Отклонять транзитные туннели"},
|
||||
{"Accept transit tunnels", "Принимать транзитные туннели"},
|
||||
{"Cancel graceful shutdown", "Отменить плавную остановку"},
|
||||
@@ -146,8 +149,8 @@ namespace russian // language namespace
|
||||
{"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"},
|
||||
{"You will be redirected in %d seconds", "Вы будете переадресованы через %d секунд"},
|
||||
{"Transit tunnels count must not exceed %d", "Число транзитных туннелей не должно превышать %d"},
|
||||
{"Back to commands list", "Вернуться к списку команд"},
|
||||
{"Register at reg.i2p", "Зарегистрировать на reg.i2p"},
|
||||
{"Description", "Описание"},
|
||||
@@ -165,32 +168,33 @@ namespace russian // language namespace
|
||||
{"You may try to find this host on jump services below", "Вы можете попробовать найти узел через джамп сервисы ниже"},
|
||||
{"Invalid request", "Некорректный запрос"},
|
||||
{"Proxy unable to parse your request", "Прокси не может разобрать ваш запрос"},
|
||||
{"addresshelper is not supported", "addresshelper не поддерживается"},
|
||||
{"Host", "Узел"},
|
||||
{"added to router's addressbook from helper", "добавлен в адресную книгу роутера через хелпер"},
|
||||
{"Click here to proceed:", "Нажмите здесь, чтобы продолжить:"},
|
||||
{"Continue", "Продолжить"},
|
||||
{"Addresshelper found", "Найден addresshelper"},
|
||||
{"already in router's addressbook", "уже в адресной книге роутера"},
|
||||
{"Click here to update record:", "Нажмите здесь, чтобы обновить запись:"},
|
||||
{"invalid request uri", "некорректный URI запроса"},
|
||||
{"Addresshelper is not supported", "Addresshelper не поддерживается"},
|
||||
{"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>.", "Узел %s <font color=red>уже в адресной книге роутера</font>. <b>Будьте осторожны: источник данной ссылки может быть вредоносным!</b> Нажмите здесь, чтобы обновить запись: <a href=\"%s%s%s&update=true\">Продолжить</a>."},
|
||||
{"Addresshelper forced update rejected", "Принудительное обновление через Addresshelper отклонено"},
|
||||
{"To add host <b>%s</b> in router's addressbook, click here: <a href=\"%s%s%s\">Continue</a>.", "Чтобы добавить узел <b>%s</b> в адресную книгу роутера, нажмите здесь: <a href=\"%s%s%s\">Продолжить</a>."},
|
||||
{"Addresshelper request", "Запрос добавления Addresshelper"},
|
||||
{"Host %s added to router's addressbook from helper. Click here to proceed: <a href=\"%s\">Continue</a>.", "Узел %s добавлен в адресную книгу роутера через хелпер. Нажмите здесь, чтобы продолжить: <a href=\"%s\">Продолжить</a>."},
|
||||
{"Addresshelper adding", "Добавление Addresshelper"},
|
||||
{"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>.", "Узел %s <font color=red>уже в адресной книге роутера</font>. Нажмите здесь, чтобы обновить запись: <a href=\"%s%s%s&update=true\">Продолжить</a>."},
|
||||
{"Addresshelper update", "Обновление записи через Addresshelper"},
|
||||
{"Invalid request URI", "Некорректный URI запроса"},
|
||||
{"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", "неизвестный URL внешнего прокси"},
|
||||
{"cannot resolve upstream proxy", "не удается определить вышестоящий прокси"},
|
||||
{"hostname too long", "имя хоста слишком длинное"},
|
||||
{"cannot connect to upstream socks proxy", "не удается подключиться к вышестоящему SOCKS прокси"},
|
||||
{"Cannot negotiate with socks proxy", "Не удается договориться с вышестоящим SOCKS прокси"},
|
||||
{"Bad outproxy settings", "Некорректные настройки внешнего прокси"},
|
||||
{"Host %s is not inside I2P network, but outproxy is not enabled", "Узел %s не в I2P сети, но внешний прокси не включен"},
|
||||
{"Unknown outproxy URL", "Неизвестный URL внешнего прокси"},
|
||||
{"Cannot resolve upstream proxy", "Не удается определить вышестоящий прокси"},
|
||||
{"Hostname is too long", "Имя хоста слишком длинное"},
|
||||
{"Cannot connect to upstream SOCKS proxy", "Не удалось подключиться к вышестоящему SOCKS прокси серверу"},
|
||||
{"Cannot negotiate with SOCKS proxy", "Не удается договориться с вышестоящим SOCKS прокси"},
|
||||
{"CONNECT error", "Ошибка CONNECT запроса"},
|
||||
{"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 прокси серверу"},
|
||||
{"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.", "Не удалось установить соединение к запрошенному узлу, возможно он не в сети. Попробуйте повторить запрос позже."},
|
||||
{"", ""},
|
||||
@@ -198,10 +202,10 @@ namespace russian // language namespace
|
||||
|
||||
static std::map<std::string, std::vector<std::string>> plurals
|
||||
{
|
||||
{"days", {"день", "дня", "дней"}},
|
||||
{"hours", {"час", "часа", "часов"}},
|
||||
{"minutes", {"минуту", "минуты", "минут"}},
|
||||
{"seconds", {"секунду", "секунды", "секунд"}},
|
||||
{"%d days", {"%d день", "%d дня", "%d дней"}},
|
||||
{"%d hours", {"%d час", "%d часа", "%d часов"}},
|
||||
{"%d minutes", {"%d минуту", "%d минуты", "%d минут"}},
|
||||
{"%d seconds", {"%d секунду", "%d секунды", "%d секунд"}},
|
||||
{"", {"", "", ""}},
|
||||
};
|
||||
|
||||
|
||||
204
i18n/Spanish.cpp
Normal file
204
i18n/Spanish.cpp
Normal file
@@ -0,0 +1,204 @@
|
||||
/*
|
||||
* Copyright (c) 2022-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"
|
||||
|
||||
// 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"},
|
||||
{"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"},
|
||||
{"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"},
|
||||
{"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"},
|
||||
{"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"},
|
||||
{"Host %s is not inside I2P network, but outproxy is not enabled", "Dominio %s 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 is 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
|
||||
208
i18n/Swedish.cpp
Normal file
208
i18n/Swedish.cpp
Normal file
@@ -0,0 +1,208 @@
|
||||
/*
|
||||
* 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> webconsole", "<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"},
|
||||
{"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"},
|
||||
{"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 %d seconds", "Du omdirigeras inom %d sekunder"},
|
||||
{"Transit tunnels count must not exceed %d", "Förmedlande tunnlar får inte överstiga %d"},
|
||||
{"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"},
|
||||
{"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"},
|
||||
{"Host %s is not inside I2P network, but outproxy is not enabled", "Värd %s ä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 is too long", "Värdnamnet är för långt"},
|
||||
{"Cannot connect to upstream SOCKS proxy", "Kan inte ansluta till uppström SOCKS-proxy"},
|
||||
{"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
|
||||
|
||||
114
i18n/Turkish.cpp
Normal file
114
i18n/Turkish.cpp
Normal file
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
// Turkish localization file
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace i18n
|
||||
{
|
||||
namespace turkish // language namespace
|
||||
{
|
||||
// language name in lowercase
|
||||
static std::string language = "turkish";
|
||||
|
||||
// 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", "kuruluyor"},
|
||||
{"failed", "başarısız"},
|
||||
{"expiring", "süresi geçiyor"},
|
||||
{"established", "kurulmuş"},
|
||||
{"unknown", "bilinmeyen"},
|
||||
{"Purple I2P Webconsole", "Mor I2P Webkonsolu"},
|
||||
{"<b>i2pd</b> webconsole", "<b>i2pd</b> webkonsolu"},
|
||||
{"Main page", "Ana sayfa"},
|
||||
{"Router commands", "Router komutları"},
|
||||
{"Local Destinations", "Yerel Hedefler"},
|
||||
{"Tunnels", "Tüneller"},
|
||||
{"Transit Tunnels", "Transit Tünelleri"},
|
||||
{"Transports", "Taşıma"},
|
||||
{"I2P tunnels", "I2P tünelleri"},
|
||||
{"SAM sessions", "SAM oturumları"},
|
||||
{"ERROR", "HATA"},
|
||||
{"OK", "TAMAM"},
|
||||
{"Testing", "Test ediliyor"},
|
||||
{"Firewalled", "Güvenlik Duvarı Kısıtlaması"},
|
||||
{"Unknown", "Bilinmeyen"},
|
||||
{"Proxy", "Proxy"},
|
||||
{"Clock skew", "Saat sorunu"},
|
||||
{"Offline", "Çevrimdışı"},
|
||||
{"Symmetric NAT", "Simetrik NAT"},
|
||||
{"Full cone NAT", "Full cone NAT"},
|
||||
{"No Descriptors", "Tanımlayıcı Yok"},
|
||||
{"Uptime", "Bağlantı süresi"},
|
||||
{"Network status", "Ağ durumu"},
|
||||
{"Network status v6", "Ağ durumu v6"},
|
||||
{"Family", "Aile"},
|
||||
{"Tunnel creation success rate", "Tünel oluşturma başarı oranı"},
|
||||
{"Received", "Alındı"},
|
||||
{"%.2f KiB/s", "%.2f KiB/s"},
|
||||
{"Sent", "Gönderildi"},
|
||||
{"Transit", "Transit"},
|
||||
{"Data path", "Veri yolu"},
|
||||
{"Hidden content. Press on text to see.", "Gizlenmiş içerik. Görmek için yazıya tıklayınız."},
|
||||
{"Router Family", "Router Familyası"},
|
||||
{"Decline transit tunnels", "Transit tünellerini reddet"},
|
||||
{"Accept transit tunnels", "Transit tünellerini kabul et"},
|
||||
{"Cancel graceful shutdown", "Düzgün durdurmayı iptal Et"},
|
||||
{"Start graceful shutdown", "Düzgün durdurmayı başlat"},
|
||||
{"Force shutdown", "Durdurmaya zorla"},
|
||||
{"Reload external CSS styles", "Harici CSS stilini yeniden yükle"},
|
||||
{"<b>Note:</b> any action done here are not persistent and not changes your config files.", "<b>Not:</b> burada yapılan ayarların hiçbiri kalıcı değildir ve ayar dosyalarınızı değiştirmez."},
|
||||
{"Logging level", "Kayıt tutma seviyesi"},
|
||||
{"Transit tunnels limit", "Transit tünel limiti"},
|
||||
{"Change", "Değiştir"},
|
||||
{"Change language", "Dil değiştir"},
|
||||
{"no transit tunnels currently built", "kurulmuş bir transit tüneli bulunmamakta"},
|
||||
{"SAM disabled", "SAM devre dışı"},
|
||||
{"no sessions currently running", "hiçbir oturum şu anda çalışmıyor"},
|
||||
{"SAM session not found", "SAM oturumu bulunamadı"},
|
||||
{"SAM Session", "SAM oturumu"},
|
||||
{"Server Tunnels", "Sunucu Tünelleri"},
|
||||
{"Unknown page", "Bilinmeyen sayfa"},
|
||||
{"Invalid token", "Geçersiz token"},
|
||||
{"SUCCESS", "BAŞARILI"},
|
||||
{"", ""},
|
||||
};
|
||||
|
||||
static std::map<std::string, std::vector<std::string>> plurals
|
||||
{
|
||||
{"%d days", {"%d gün", "%d gün"}},
|
||||
{"%d hours", {"%d saat", "%d saat"}},
|
||||
{"%d minutes", {"%d dakika", "%d dakika"}},
|
||||
{"%d seconds", {"%d saniye", "%d saniye"}},
|
||||
{"", {"", ""}},
|
||||
};
|
||||
|
||||
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
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2021-2022, The PurpleI2P Project
|
||||
* Copyright (c) 2021-2023, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -31,15 +31,16 @@ namespace turkmen // language namespace
|
||||
|
||||
static std::map<std::string, std::string> strings
|
||||
{
|
||||
{"KiB", "KiB"},
|
||||
{"MiB", "MiB"},
|
||||
{"GiB", "GiB"},
|
||||
{"%.2f KiB", "%.2f KiB"},
|
||||
{"%.2f MiB", "%.2f MiB"},
|
||||
{"%.2f GiB", "%.2f GiB"},
|
||||
{"building", "bina"},
|
||||
{"failed", "şowsuz"},
|
||||
{"expiring", "möhleti gutarýar"},
|
||||
{"established", "işleýär"},
|
||||
{"unknown", "näbelli"},
|
||||
{"exploratory", "gözleg"},
|
||||
{"Purple I2P Webconsole", "Web konsoly Purple I2P"},
|
||||
{"<b>i2pd</b> webconsole", "Web konsoly <b>i2pd</b>"},
|
||||
{"Main page", "Esasy sahypa"},
|
||||
{"Router commands", "Marşrutizator buýruklary"},
|
||||
@@ -57,7 +58,6 @@ namespace turkmen // language namespace
|
||||
{"Unknown", "Näbelli"},
|
||||
{"Proxy", "Proksi"},
|
||||
{"Mesh", "MESH-tor"},
|
||||
{"Error", "Ýalňyşlyk"},
|
||||
{"Clock skew", "Takyk wagt däl"},
|
||||
{"Offline", "Awtonom"},
|
||||
{"Symmetric NAT", "Simmetriklik NAT"},
|
||||
@@ -68,7 +68,7 @@ namespace turkmen // language namespace
|
||||
{"Family", "Maşgala"},
|
||||
{"Tunnel creation success rate", "Gurlan teneller üstünlikli gurlan teneller"},
|
||||
{"Received", "Alnan"},
|
||||
{"KiB/s", "KiB/s"},
|
||||
{"%.2f KiB/s", "%.2f 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"},
|
||||
{"ms", "ms"},
|
||||
{"%dms", "%dms"},
|
||||
{"Outbound tunnels", "Çykýan tuneller"},
|
||||
{"Tags", "Bellikler"},
|
||||
{"Incoming", "Gelýän"},
|
||||
@@ -116,7 +116,6 @@ namespace turkmen // language namespace
|
||||
{"Gateway", "Derweze"},
|
||||
{"TunnelID", "Tuneliň ID"},
|
||||
{"EndDate", "Gutarýar"},
|
||||
{"not floodfill", "fludfil däl"},
|
||||
{"Queue size", "Nobatyň ululygy"},
|
||||
{"Run peer test", "Synag başlaň"},
|
||||
{"Decline transit tunnels", "Tranzit tunellerini ret ediň"},
|
||||
@@ -146,8 +145,6 @@ namespace turkmen // language namespace
|
||||
{"Destination not found", "Niýetlenen ýeri tapylmady"},
|
||||
{"StreamID can't be null", "StreamID boş bolup bilmez"},
|
||||
{"Return to destination page", "Barmaly nokadynyň nokadyna gaýdyp geliň"},
|
||||
{"You will be redirected in 5 seconds", "5 sekuntdan soň täzeden ugrukdyrylarsyňyz"},
|
||||
{"Transit tunnels count must not exceed 65535", "Tranzit tagtalaryň sany 65535-den geçmeli däldir"},
|
||||
{"Back to commands list", "Topar sanawyna dolan"},
|
||||
{"Register at reg.i2p", "Reg.i2P-de hasaba duruň"},
|
||||
{"Description", "Beýany"},
|
||||
@@ -165,32 +162,24 @@ namespace turkmen // language namespace
|
||||
{"You may try to find this host on jump services below", "Aşakdaky böküş hyzmatlarynda bu öý eýesini tapmaga synanyşyp bilersiňiz"},
|
||||
{"Invalid request", "Nädogry haýyş"},
|
||||
{"Proxy unable to parse your request", "Proksi haýyşyňyzy derňäp bilmeýär"},
|
||||
{"addresshelper is not supported", "Salgylandyryjy goldanok"},
|
||||
{"Host", "Adres"},
|
||||
{"added to router's addressbook from helper", "marşruteriň adresini kömekçiden goşdy"},
|
||||
{"Click here to proceed:", "Dowam etmek bu ýerde basyň:"},
|
||||
{"Continue", "Dowam et"},
|
||||
{"Addresshelper found", "Forgelper tapyldy"},
|
||||
{"already in router's addressbook", "marşruteriň adres kitaby"},
|
||||
{"Click here to update record:", "Recordazgyny täzelemek üçin bu ýerde basyň:"},
|
||||
{"invalid request uri", "nädogry haýyş URI"},
|
||||
{"Invalid request URI", "Nädogry haýyş URI"},
|
||||
{"Can't detect destination host from request", "Haýyşdan barmaly ýerini tapyp bilemok"},
|
||||
{"Outproxy failure", "Daşarky proksi ýalňyşlyk"},
|
||||
{"bad outproxy settings", "daşarky daşarky proksi sazlamalary nädogry"},
|
||||
{"not inside I2P network, but outproxy is not enabled", "I2P torunda däl, ýöne daşarky proksi goşulmaýar"},
|
||||
{"unknown outproxy url", "näbelli daşarky proksi URL"},
|
||||
{"cannot resolve upstream proxy", "has ýokary proksi kesgitläp bilmeýär"},
|
||||
{"hostname too long", "hoster eýesi ady gaty uzyn"},
|
||||
{"cannot connect to upstream socks proxy", "ýokary jorap SOCKS proksi bilen birigip bolmaýar"},
|
||||
{"Cannot negotiate with socks proxy", "Iň ýokary jorap SOCKS proksi bilen ylalaşyp bilmeýärler"},
|
||||
{"Bad outproxy settings", "Daşarky Daşarky proksi sazlamalary nädogry"},
|
||||
{"Host %s is not inside I2P network, but outproxy is not enabled", "Adres %s I2P torunda däl, ýöne daşarky proksi goşulmaýar"},
|
||||
{"Unknown outproxy URL", "Näbelli daşarky proksi URL"},
|
||||
{"Cannot resolve upstream proxy", "Has ýokary proksi kesgitläp bilmeýär"},
|
||||
{"Hostname is too long", "Hoster eýesi ady gaty uzyn"},
|
||||
{"Cannot connect to upstream SOCKS proxy", "Ýokary jorap SOCKS proksi bilen birigip bolmaýar"},
|
||||
{"Cannot negotiate with SOCKS proxy", "Iň ýokary jorap SOCKS proksi bilen ylalaşyp bilmeýärler"},
|
||||
{"CONNECT error", "Bagyr haýyşy säwligi"},
|
||||
{"Failed to Connect", "Birikdirip bilmedi"},
|
||||
{"socks proxy error", "socks proksi ýalňyşlygy"},
|
||||
{"failed to send request to upstream", "öý eýesi proksi üçin haýyş iberip bilmedi"},
|
||||
{"No Reply From socks proxy", "Jorap proksi serwerinden hiç hili jogap ýok"},
|
||||
{"cannot connect", "birikdirip bilmedi"},
|
||||
{"http out proxy not implemented", "daşarky HTTP proksi serwerini goldamak amala aşyrylmaýar"},
|
||||
{"cannot connect to upstream http proxy", "ýokary akym HTTP proksi serwerine birigip bilmedi"},
|
||||
{"Failed to connect", "Birikdirip bilmedi"},
|
||||
{"SOCKS proxy error", "SOCKS proksi ýalňyşlygy"},
|
||||
{"Failed to send request to upstream", "Öý eýesi proksi üçin haýyş iberip bilmedi"},
|
||||
{"No reply from SOCKS proxy", "Jorap SOCKS proksi serwerinden hiç hili jogap ýok"},
|
||||
{"Cannot connect", "Birikdirip bilmedi"},
|
||||
{"HTTP out proxy not implemented", "Daşarky HTTP proksi serwerini goldamak amala aşyrylmaýar"},
|
||||
{"Cannot connect to upstream HTTP proxy", "Ýokary jorap HTTP proksi bilen birigip bolmaýar"},
|
||||
{"Host is down", "Salgy elýeterli däl"},
|
||||
{"Can't create connection to requested host, it may be down. Please try again later.", "Talap edilýän salgyda birikmäni gurup bilmedim, onlaýn bolup bilmez. Soňra haýyşy soň gaýtalamaga synanyşyň."},
|
||||
{"", ""},
|
||||
@@ -198,10 +187,10 @@ namespace turkmen // language namespace
|
||||
|
||||
static std::map<std::string, std::vector<std::string>> plurals
|
||||
{
|
||||
{"days", {"gün", "gün"}},
|
||||
{"hours", {"sagat", "sagat"}},
|
||||
{"minutes", {"minut", "minut"}},
|
||||
{"seconds", {"sekunt", "sekunt"}},
|
||||
{"%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"}},
|
||||
{"", {"", ""}},
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2021, The PurpleI2P Project
|
||||
* Copyright (c) 2021-2023, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -31,15 +31,16 @@ namespace ukrainian // language namespace
|
||||
|
||||
static std::map<std::string, std::string> strings
|
||||
{
|
||||
{"KiB", "КіБ"},
|
||||
{"MiB", "МіБ"},
|
||||
{"GiB", "ГіБ"},
|
||||
{"%.2f KiB", "%.2f КіБ"},
|
||||
{"%.2f MiB", "%.2f МіБ"},
|
||||
{"%.2f GiB", "%.2f ГіБ"},
|
||||
{"building", "будується"},
|
||||
{"failed", "невдалий"},
|
||||
{"expiring", "завершується"},
|
||||
{"established", "працює"},
|
||||
{"unknown", "невідомо"},
|
||||
{"exploratory", "дослідницький"},
|
||||
{"Purple I2P Webconsole", "Веб-консоль Purple I2P"},
|
||||
{"<b>i2pd</b> webconsole", "Веб-консоль <b>i2pd</b>"},
|
||||
{"Main page", "Головна"},
|
||||
{"Router commands", "Команди маршрутизатора"},
|
||||
@@ -57,10 +58,11 @@ namespace ukrainian // language namespace
|
||||
{"Unknown", "Невідомо"},
|
||||
{"Proxy", "Проксі"},
|
||||
{"Mesh", "MESH-мережа"},
|
||||
{"Error", "Помилка"},
|
||||
{"Clock skew", "Неточний час"},
|
||||
{"Offline", "Офлайн"},
|
||||
{"Symmetric NAT", "Симетричний NAT"},
|
||||
{"Full cone NAT", "Повний NAT"},
|
||||
{"No Descriptors", "Немає Описів"},
|
||||
{"Uptime", "У мережі"},
|
||||
{"Network status", "Мережевий статус"},
|
||||
{"Network status v6", "Мережевий статус v6"},
|
||||
@@ -68,7 +70,7 @@ namespace ukrainian // language namespace
|
||||
{"Family", "Сімейство"},
|
||||
{"Tunnel creation success rate", "Успішно побудованих тунелів"},
|
||||
{"Received", "Отримано"},
|
||||
{"KiB/s", "КіБ/с"},
|
||||
{"%.2f KiB/s", "%.2f КіБ/с"},
|
||||
{"Sent", "Відправлено"},
|
||||
{"Transit", "Транзит"},
|
||||
{"Data path", "Шлях до даних"},
|
||||
@@ -94,7 +96,7 @@ namespace ukrainian // language namespace
|
||||
{"Type", "Тип"},
|
||||
{"EncType", "ТипШифр"},
|
||||
{"Inbound tunnels", "Вхідні тунелі"},
|
||||
{"ms", "мс"},
|
||||
{"%dms", "%dмс"},
|
||||
{"Outbound tunnels", "Вихідні тунелі"},
|
||||
{"Tags", "Теги"},
|
||||
{"Incoming", "Вхідні"},
|
||||
@@ -116,9 +118,10 @@ namespace ukrainian // language namespace
|
||||
{"Gateway", "Шлюз"},
|
||||
{"TunnelID", "ID тунеля"},
|
||||
{"EndDate", "Закінчується"},
|
||||
{"not floodfill", "не флудфіл"},
|
||||
{"floodfill mode is disabled", "режим floodfill вимкнено"},
|
||||
{"Queue size", "Розмір черги"},
|
||||
{"Run peer test", "Запустити тестування"},
|
||||
{"Reload tunnels configuration", "Перезавантажити налаштування тунелів"},
|
||||
{"Decline transit tunnels", "Відхиляти транзитні тунелі"},
|
||||
{"Accept transit tunnels", "Ухвалювати транзитні тунелі"},
|
||||
{"Cancel graceful shutdown", "Скасувати плавну зупинку"},
|
||||
@@ -146,8 +149,8 @@ namespace ukrainian // language namespace
|
||||
{"Destination not found", "Точка призначення не знайдена"},
|
||||
{"StreamID can't be null", "Ідентифікатор потоку не може бути порожнім"},
|
||||
{"Return to destination page", "Повернутися на сторінку точки призначення"},
|
||||
{"You will be redirected in 5 seconds", "Ви будете переадресовані через 5 секунд"},
|
||||
{"Transit tunnels count must not exceed 65535", "Кількість транзитних тунелів не повинна перевищувати 65535"},
|
||||
{"You will be redirected in %d seconds", "Ви будете переадресовані через %d секунд"},
|
||||
{"Transit tunnels count must not exceed %d", "Кількість транзитних тунелів не повинна перевищувати %d"},
|
||||
{"Back to commands list", "Повернутися до списку команд"},
|
||||
{"Register at reg.i2p", "Зареєструвати на reg.i2p"},
|
||||
{"Description", "Опис"},
|
||||
@@ -165,32 +168,33 @@ namespace ukrainian // language namespace
|
||||
{"You may try to find this host on jump services below", "Ви можете спробувати знайти дану адресу на джамп сервісах нижче"},
|
||||
{"Invalid request", "Некоректний запит"},
|
||||
{"Proxy unable to parse your request", "Проксі не може розібрати ваш запит"},
|
||||
{"addresshelper is not supported", "addresshelper не підтримується"},
|
||||
{"Host", "Адреса"},
|
||||
{"added to router's addressbook from helper", "доданий в адресну книгу маршрутизатора через хелпер"},
|
||||
{"Click here to proceed:", "Натисніть тут щоб продовжити:"},
|
||||
{"Continue", "Продовжити"},
|
||||
{"Addresshelper found", "Знайдено addresshelper"},
|
||||
{"already in router's addressbook", "вже в адресній книзі маршрутизатора"},
|
||||
{"Click here to update record:", "Натисніть тут щоб оновити запис:"},
|
||||
{"invalid request uri", "некоректний URI запиту"},
|
||||
{"Addresshelper is not supported", "Адресна книга не підтримується"},
|
||||
{"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>.", "Хост %s <font color=red>вже в адресній книзі маршрутизатора</font>. <b>Будьте обережні: джерело цієї адреси може зашкодити!</b> Натисніть тут, щоб оновити запис: <a href=\"%s%s%s&update=true\">Продовжити</a>."},
|
||||
{"Addresshelper forced update rejected", "Адресна книга відхилила примусове оновлення"},
|
||||
{"To add host <b>%s</b> in router's addressbook, click here: <a href=\"%s%s%s\">Continue</a>.", "Щоб додати хост <b>%s</b> в адресі маршрутизатора, натисніть тут: <a href=\"%s%s%s\">Продовжити</a>."},
|
||||
{"Addresshelper request", "Запит на адресну сторінку"},
|
||||
{"Host %s added to router's addressbook from helper. Click here to proceed: <a href=\"%s\">Continue</a>.", "Хост %s доданий в адресну книгу маршрутизатора від помічника. Натисніть тут, щоб продовжити: <a href=\"%s\">Продовжити</a>."},
|
||||
{"Addresshelper adding", "Адреса додана"},
|
||||
{"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>.", "Хост %s <font color=red>вже в адресній книзі маршрутизатора</font>. Натисніть тут, щоб оновити запис: <a href=\"%s%s%s&update=true\">Продовжити</a>."},
|
||||
{"Addresshelper update", "Оновлення адресної книги"},
|
||||
{"Invalid request URI", "Некоректний URI запиту"},
|
||||
{"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", "невідомий URL зовнішнього проксі"},
|
||||
{"cannot resolve upstream proxy", "не вдається визначити висхідний проксі"},
|
||||
{"hostname too long", "ім'я вузла надто довге"},
|
||||
{"cannot connect to upstream socks proxy", "не вдається підключитися до висхідного SOCKS проксі"},
|
||||
{"Cannot negotiate with socks proxy", "Не вдається домовитися з висхідним SOCKS проксі"},
|
||||
{"Bad outproxy settings", "Некоректні налаштування зовнішнього проксі"},
|
||||
{"Host %s is not inside I2P network, but outproxy is not enabled", "Адрес %s не в I2P мережі, але зовнішній проксі не включений"},
|
||||
{"Unknown outproxy URL", "Невідомий URL зовнішнього проксі"},
|
||||
{"Cannot resolve upstream proxy", "Не вдається визначити висхідний проксі"},
|
||||
{"Hostname is too long", "Ім'я вузла надто довге"},
|
||||
{"Cannot connect to upstream SOCKS proxy", "Не вдалося підключитися до висхідного SOCKS проксі сервера"},
|
||||
{"Cannot negotiate with SOCKS proxy", "Не вдається домовитися з висхідним SOCKS проксі"},
|
||||
{"CONNECT error", "Помилка CONNECT запиту"},
|
||||
{"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 проксі сервера"},
|
||||
{"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.", "Не вдалося встановити з'єднання до запитаного вузла, можливо він не в мережі. Спробуйте повторити запит пізніше."},
|
||||
{"", ""},
|
||||
@@ -198,10 +202,10 @@ namespace ukrainian // language namespace
|
||||
|
||||
static std::map<std::string, std::vector<std::string>> plurals
|
||||
{
|
||||
{"days", {"день", "дня", "днів"}},
|
||||
{"hours", {"годину", "години", "годин"}},
|
||||
{"minutes", {"хвилину", "хвилини", "хвилин"}},
|
||||
{"seconds", {"секунду", "секунди", "секунд"}},
|
||||
{"%d days", {"%d день", "%d дня", "%d днів"}},
|
||||
{"%d hours", {"%d годину", "%d години", "%d годин"}},
|
||||
{"%d minutes", {"%d хвилину", "%d хвилини", "%d хвилин"}},
|
||||
{"%d seconds", {"%d секунду", "%d секунди", "%d секунд"}},
|
||||
{"", {"", "", ""}},
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2021-2022, The PurpleI2P Project
|
||||
* Copyright (c) 2021-2023, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -31,15 +31,16 @@ namespace uzbek // language namespace
|
||||
|
||||
static std::map<std::string, std::string> strings
|
||||
{
|
||||
{"KiB", "KiB"},
|
||||
{"MiB", "MiB"},
|
||||
{"GiB", "GiB"},
|
||||
{"%.2f KiB", "%.2f KiB"},
|
||||
{"%.2f MiB", "%.2f MiB"},
|
||||
{"%.2f GiB", "%.2f GiB"},
|
||||
{"building", "yaratilmoqda"},
|
||||
{"failed", "muvaffaqiyatsiz"},
|
||||
{"expiring", "muddati tugaydi"},
|
||||
{"established", "aloqa o'rnatildi"},
|
||||
{"unknown", "noma'lum"},
|
||||
{"exploratory", "tadqiqiy"},
|
||||
{"Purple I2P Webconsole", "Veb-konsoli Purple I2P"},
|
||||
{"<b>i2pd</b> webconsole", "<b>i2pd</b> veb-konsoli"},
|
||||
{"Main page", "Asosiy sahifa"},
|
||||
{"Router commands", "Router buyruqlari"},
|
||||
@@ -57,10 +58,11 @@ namespace uzbek // language namespace
|
||||
{"Unknown", "Notanish"},
|
||||
{"Proxy", "Proksi"},
|
||||
{"Mesh", "Mesh To'r"},
|
||||
{"Error", "Xato"},
|
||||
{"Clock skew", "Aniq vaqt emas"},
|
||||
{"Offline", "Oflayn"},
|
||||
{"Symmetric NAT", "Simmetrik NAT"},
|
||||
{"Full cone NAT", "Full cone NAT"},
|
||||
{"No Descriptors", "Deskriptorlar yo'q"},
|
||||
{"Uptime", "Ish vaqti"},
|
||||
{"Network status", "Tarmoq holati"},
|
||||
{"Network status v6", "Tarmoq holati v6"},
|
||||
@@ -68,7 +70,7 @@ namespace uzbek // language namespace
|
||||
{"Family", "Oila"},
|
||||
{"Tunnel creation success rate", "Tunnel yaratish muvaffaqiyat darajasi"},
|
||||
{"Received", "Qabul qilindi"},
|
||||
{"KiB/s", "KiB/s"},
|
||||
{"%.2f KiB/s", "%.2f KiB/s"},
|
||||
{"Sent", "Yuborilgan"},
|
||||
{"Transit", "Tranzit"},
|
||||
{"Data path", "Ma'lumotlar joylanishi"},
|
||||
@@ -94,7 +96,7 @@ namespace uzbek // language namespace
|
||||
{"Type", "Turi"},
|
||||
{"EncType", "ShifrlashTuri"},
|
||||
{"Inbound tunnels", "Kirish tunnellari"},
|
||||
{"ms", "ms"},
|
||||
{"%dms", "%dms"},
|
||||
{"Outbound tunnels", "Chiquvchi tunnellar"},
|
||||
{"Tags", "Teglar"},
|
||||
{"Incoming", "Kiruvchi"},
|
||||
@@ -116,9 +118,10 @@ namespace uzbek // language namespace
|
||||
{"Gateway", "Kirish yo'li"},
|
||||
{"TunnelID", "TunnelID"},
|
||||
{"EndDate", "Tugash Sanasi"},
|
||||
{"not floodfill", "floodfill emas"},
|
||||
{"floodfill mode is disabled", "floodfill rejimi o'chirilgan"},
|
||||
{"Queue size", "Navbat hajmi"},
|
||||
{"Run peer test", "Sinovni boshlang"},
|
||||
{"Reload tunnels configuration", "Tunnel konfiguratsiyasini qayta yuklash"},
|
||||
{"Decline transit tunnels", "Tranzit tunnellarini rad etish"},
|
||||
{"Accept transit tunnels", "Tranzit tunnellarni qabul qilish"},
|
||||
{"Cancel graceful shutdown", "Yumshoq to'xtashni bekor qilish"},
|
||||
@@ -146,8 +149,8 @@ namespace uzbek // language namespace
|
||||
{"Destination not found", "Yo'nalish topilmadi"},
|
||||
{"StreamID can't be null", "StreamID bo'sh bo'lishi mumkin emas"},
|
||||
{"Return to destination page", "Manzilgoh sahifasiga qaytish"},
|
||||
{"You will be redirected in 5 seconds", "Siz 5 soniya ichida qayta yo'naltirilasiz"},
|
||||
{"Transit tunnels count must not exceed 65535", "Tranzit tunnellar soni 65535 dan oshmasligi kerak"},
|
||||
{"You will be redirected in %d seconds", "Siz %d soniyadan so‘ng boshqa yo‘nalishga yo‘naltirilasiz"},
|
||||
{"Transit tunnels count must not exceed %d", "Tranzit tunnellar soni %d dan oshmasligi kerak"},
|
||||
{"Back to commands list", "Buyruqlar ro'yxatiga qaytish"},
|
||||
{"Register at reg.i2p", "Reg.i2p-da ro'yxatdan o'ting"},
|
||||
{"Description", "Tavsif"},
|
||||
@@ -165,32 +168,33 @@ namespace uzbek // language namespace
|
||||
{"You may try to find this host on jump services below", "Siz xost quyida o'tish xizmatlari orqali topishga harakat qilishingiz mumkin"},
|
||||
{"Invalid request", "Noto‘g‘ri so‘rov"},
|
||||
{"Proxy unable to parse your request", "Proksi sizning so'rovingizni aniqlab ololmayapti"},
|
||||
{"addresshelper is not supported", "addresshelper qo'llab -quvvatlanmaydi"},
|
||||
{"Host", "Xost"},
|
||||
{"added to router's addressbook from helper", "'helper'dan routerning 'addressbook'ga qo'shildi"},
|
||||
{"Click here to proceed:", "Davom etish uchun shu yerni bosing:"},
|
||||
{"Continue", "Davom etish"},
|
||||
{"Addresshelper found", "Addresshelper topildi"},
|
||||
{"already in router's addressbook", "allaqachon 'addressbook'da yozilgan"},
|
||||
{"Click here to update record:", "Yozuvni yangilash uchun shu yerni bosing:"},
|
||||
{"invalid request uri", "noto'g'ri URI so'rovi"},
|
||||
{"Addresshelper is not supported", "Addresshelper qo'llab-quvvatlanmaydi"},
|
||||
{"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>.", "%s xosti <font color=red>allaqachon routerning manzillar kitobida</font>. <b>Ehtiyot bo'ling: bu URL manbasi zararli bo'lishi mumkin!</b> Yozuvni yangilash uchun bu yerni bosing: <a href=\"%s%s%s&update=true\">Davom etish</a>."},
|
||||
{"Addresshelper forced update rejected", "Addresshelperni majburiy yangilash rad etildi"},
|
||||
{"To add host <b>%s</b> in router's addressbook, click here: <a href=\"%s%s%s\">Continue</a>.", "Routerning manzillar kitobiga <b>%s</b> xostini qo'shish uchun bu yerni bosing: <a href=\"%s%s%s\">Davom etish</a>."},
|
||||
{"Addresshelper request", "Addresshelper so'rovi"},
|
||||
{"Host %s added to router's addressbook from helper. Click here to proceed: <a href=\"%s\">Continue</a>.", "Yordamchidan router manzillar kitobiga %s xost qo‘shildi. Davom etish uchun bu yerga bosing: <a href=\"%s\">Davom etish</a>."},
|
||||
{"Addresshelper adding", "Addresshelperni qo'shish"},
|
||||
{"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>.", "%s xosti <font color=red>allaqachon routerning manzillar kitobida</font>. Yozuvni yangilash uchun shu yerni bosing: <a href=\"%s%s%s&update=true\">Davom etish</a>."},
|
||||
{"Addresshelper update", "Addresshelperni yangilash"},
|
||||
{"Invalid request URI", "Noto'g'ri URI so'rovi"},
|
||||
{"Can't detect destination host from request", "So‘rov orqali manzil xostini aniqlab bo'lmayapti"},
|
||||
{"Outproxy failure", "Tashqi proksi muvaffaqiyatsizligi"},
|
||||
{"bad outproxy settings", "noto'g'ri tashqi proksi-server sozlamalari"},
|
||||
{"not inside I2P network, but outproxy is not enabled", "I2P tarmog'ida emas, lekin tashqi proksi yoqilmagan"},
|
||||
{"unknown outproxy url", "noma'lum outproxy url"},
|
||||
{"cannot resolve upstream proxy", "yuqoridagi 'proxy-server'ni aniqlab olib bolmayapti"},
|
||||
{"hostname too long", "xost nomi juda uzun"},
|
||||
{"cannot connect to upstream socks proxy", "yuqori 'socks proxy'ga ulanib bo'lmayapti"},
|
||||
{"Cannot negotiate with socks proxy", "'Socks proxy' bilan muzokara olib bo'lmaydi"},
|
||||
{"Bad outproxy settings", "Noto'g'ri tashqi proksi-server sozlamalari"},
|
||||
{"Host %s is not inside I2P network, but outproxy is not enabled", "Xost %s I2P tarmog'ida emas, lekin tashqi proksi yoqilmagan"},
|
||||
{"Unknown outproxy URL", "Noma'lum outproxy URL"},
|
||||
{"Cannot resolve upstream proxy", "Yuqoridagi 'proxy-server'ni aniqlab olib bolmayapti"},
|
||||
{"Hostname is too long", "Xost nomi juda uzun"},
|
||||
{"Cannot connect to upstream SOCKS proxy", "Yuqori 'SOCKS proxy'ga ulanib bo'lmayapti"},
|
||||
{"Cannot negotiate with SOCKS proxy", "'SOCKS proxy' bilan muzokara olib bo'lmaydi"},
|
||||
{"CONNECT error", "CONNECT xatosi"},
|
||||
{"Failed to Connect", "Ulanib bo'lmayapti"},
|
||||
{"socks proxy error", "'socks proxy' xatosi"},
|
||||
{"failed to send request to upstream", "yuqori http proksi-serveriga so'rovni uborib bo'lmadi"},
|
||||
{"No Reply From socks proxy", "'Socks proxy'dan javob yo'q"},
|
||||
{"cannot connect", "ulanib bo'lmaydi"},
|
||||
{"http out proxy not implemented", "tashqi HTTP proksi-serverni qo'llab-quvvatlash amalga oshirilmagan"},
|
||||
{"cannot connect to upstream http proxy", "yuqori http 'proxy-server'iga ulanib bo'lmayapti"},
|
||||
{"Failed to connect", "Ulanib bo'lmayapti"},
|
||||
{"SOCKS proxy error", "'SOCKS proxy' xatosi"},
|
||||
{"Failed to send request to upstream", "Yuqori proksi-serveriga so'rovni uborib bo'lmadi"},
|
||||
{"No reply from SOCKS proxy", "'SOCKS proxy'dan javob yo'q"},
|
||||
{"Cannot connect", "Ulanib bo'lmaydi"},
|
||||
{"HTTP out proxy not implemented", "Tashqi HTTP proksi-serverni qo'llab-quvvatlash amalga oshirilmagan"},
|
||||
{"Cannot connect to upstream HTTP proxy", "Yuqori 'HTTP proxy'ga ulanib bo'lmayapti"},
|
||||
{"Host is down", "Xost ishlamayapti"},
|
||||
{"Can't create connection to requested host, it may be down. Please try again later.", "Talab qilingan xost bilan aloqa o'rnatilmadi, u ishlamay qolishi mumkin. Iltimos keyinroq qayta urinib ko'ring."},
|
||||
{"", ""},
|
||||
@@ -198,10 +202,10 @@ namespace uzbek // language namespace
|
||||
|
||||
static std::map<std::string, std::vector<std::string>> plurals
|
||||
{
|
||||
{"days", {"kun", "kun"}},
|
||||
{"hours", {"soat", "soat"}},
|
||||
{"minutes", {"daqiqa", "daqiqa"}},
|
||||
{"seconds", {"soniya", "soniya"}},
|
||||
{"%d days", {"%d kun", "%d kun"}},
|
||||
{"%d hours", {"%d soat", "%d soat"}},
|
||||
{"%d minutes", {"%d daqiqa", "%d daqiqa"}},
|
||||
{"%d seconds", {"%d soniya", "%d 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):
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2020, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
* See full license text in LICENSE file at top of project tree
|
||||
*/
|
||||
|
||||
#include "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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2020, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
* See full license text in LICENSE file at top of project tree
|
||||
*/
|
||||
|
||||
#ifndef 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
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2023, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -45,7 +45,7 @@ namespace config {
|
||||
("logclftime", bool_switch()->default_value(false), "Write full CLF-formatted date and time to log (default: disabled, write only time)")
|
||||
("family", value<std::string>()->default_value(""), "Specify a family, router belongs to")
|
||||
("datadir", value<std::string>()->default_value(""), "Path to storage of i2pd data (RI, keys, peer profiles, ...)")
|
||||
("host", value<std::string>()->default_value("0.0.0.0"), "External IP")
|
||||
("host", value<std::string>()->default_value(""), "External IP")
|
||||
("ifname", value<std::string>()->default_value(""), "Network interface to bind to")
|
||||
("ifname4", value<std::string>()->default_value(""), "Network interface to bind to for ipv4")
|
||||
("ifname6", value<std::string>()->default_value(""), "Network interface to bind to for ipv6")
|
||||
@@ -64,7 +64,7 @@ namespace config {
|
||||
("bandwidth", value<std::string>()->default_value(""), "Transit traffic bandwidth limit: integer in KBps or letters: L (32), O (256), P (2048), X (>9000)")
|
||||
("share", value<int>()->default_value(100), "Limit of transit traffic from max bandwidth in percents. (default: 100)")
|
||||
("ntcp", bool_switch()->default_value(false), "Ignored. Always false")
|
||||
("ssu", bool_switch()->default_value(true), "Enable SSU transport (default: enabled)")
|
||||
("ssu", bool_switch()->default_value(false), "Ignored. Always false")
|
||||
("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(2500), "Maximum active transit sessions (default:2500)")
|
||||
("limits.transittunnels", value<uint16_t>()->default_value(5000), "Maximum active transit tunnels (default:5000)")
|
||||
("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")
|
||||
@@ -95,6 +95,7 @@ namespace config {
|
||||
("http.hostname", value<std::string>()->default_value("localhost"), "Expected hostname for WebUI")
|
||||
("http.webroot", value<std::string>()->default_value("/"), "WebUI root path (default: / )")
|
||||
("http.lang", value<std::string>()->default_value("english"), "WebUI language (default: english )")
|
||||
("http.showTotalTCSR", value<bool>()->default_value(false), "Show additional value with total TCSR since router's start (default: false)")
|
||||
;
|
||||
|
||||
options_description httpproxy("HTTP Proxy options");
|
||||
@@ -274,14 +275,17 @@ namespace config {
|
||||
|
||||
options_description ssu2("SSU2 Options");
|
||||
ssu2.add_options()
|
||||
("ssu2.enabled", value<bool>()->default_value(false), "Enable SSU2 (default: disabled)")
|
||||
("ssu2.published", value<bool>()->default_value(false), "Publish SSU2 (default: disabled)")
|
||||
("ssu2.enabled", value<bool>()->default_value(true), "Enable SSU2 (default: enabled)")
|
||||
("ssu2.published", value<bool>()->default_value(true), "Publish SSU2 (default: enabled)")
|
||||
("ssu2.port", value<uint16_t>()->default_value(0), "Port to listen for incoming SSU2 packets (default: auto)")
|
||||
("ssu2.mtu4", value<uint16_t>()->default_value(0), "MTU for ipv4 address (default: detect)")
|
||||
("ssu2.mtu6", value<uint16_t>()->default_value(0), "MTU for ipv6 address (default: detect)")
|
||||
("ssu2.proxy", value<std::string>()->default_value(""), "Socks5 proxy URL for SSU2 transport")
|
||||
;
|
||||
|
||||
options_description nettime("Time sync options");
|
||||
nettime.add_options()
|
||||
("nettime.enabled", value<bool>()->default_value(false), "Disable time sync (default: disabled)")
|
||||
("nettime.enabled", value<bool>()->default_value(false), "Enable NTP time sync (default: disabled)")
|
||||
("nettime.ntpservers", value<std::string>()->default_value(
|
||||
"0.pool.ntp.org,"
|
||||
"1.pool.ntp.org,"
|
||||
|
||||
@@ -159,8 +159,10 @@ 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;
|
||||
|
||||
@@ -239,55 +241,6 @@ 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 ()
|
||||
{
|
||||
@@ -601,77 +554,6 @@ 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,24 +62,6 @@ 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
|
||||
{
|
||||
@@ -121,10 +103,6 @@ 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-2021, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include "Base.h"
|
||||
#include "Gzip.h"
|
||||
#include "Identity.h"
|
||||
#include "LeaseSet.h"
|
||||
#include "I2NPProtocol.h"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2023, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -399,6 +399,11 @@ namespace client
|
||||
|
||||
void LeaseSetDestination::HandleDatabaseStoreMessage (const uint8_t * buf, size_t len)
|
||||
{
|
||||
if (len < DATABASE_STORE_HEADER_SIZE)
|
||||
{
|
||||
LogPrint (eLogError, "Destination: Database store msg is too short ", len);
|
||||
return;
|
||||
}
|
||||
uint32_t replyToken = bufbe32toh (buf + DATABASE_STORE_REPLY_TOKEN_OFFSET);
|
||||
size_t offset = DATABASE_STORE_HEADER_SIZE;
|
||||
if (replyToken)
|
||||
@@ -406,6 +411,11 @@ namespace client
|
||||
LogPrint (eLogInfo, "Destination: Reply token is ignored for DatabaseStore");
|
||||
offset += 36;
|
||||
}
|
||||
if (offset > len || len > i2p::data::MAX_LS_BUFFER_SIZE + offset)
|
||||
{
|
||||
LogPrint (eLogError, "Destination: Database store message is too long ", len);
|
||||
return;
|
||||
}
|
||||
i2p::data::IdentHash key (buf + DATABASE_STORE_KEY_OFFSET);
|
||||
std::shared_ptr<i2p::data::LeaseSet> leaseSet;
|
||||
switch (buf[DATABASE_STORE_TYPE_OFFSET])
|
||||
@@ -467,12 +477,15 @@ namespace client
|
||||
{
|
||||
auto ls2 = std::make_shared<i2p::data::LeaseSet2> (buf + offset, len - offset,
|
||||
it2->second->requestedBlindedKey, m_LeaseSetPrivKey ? ((const uint8_t *)*m_LeaseSetPrivKey) : nullptr , GetPreferredCryptoType ());
|
||||
if (ls2->IsValid ())
|
||||
if (ls2->IsValid () && !ls2->IsExpired ())
|
||||
{
|
||||
leaseSet = ls2;
|
||||
std::lock_guard<std::mutex> lock(m_RemoteLeaseSetsMutex);
|
||||
m_RemoteLeaseSets[ls2->GetIdentHash ()] = ls2; // ident is not key
|
||||
m_RemoteLeaseSets[key] = ls2; // also store as key for next lookup
|
||||
leaseSet = ls2;
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "Destination: New remote encrypted LeaseSet2 failed");
|
||||
}
|
||||
else
|
||||
LogPrint (eLogInfo, "Destination: Couldn't find request for encrypted LeaseSet2");
|
||||
@@ -763,9 +776,17 @@ namespace client
|
||||
request->requestTime = ts;
|
||||
if (!SendLeaseSetRequest (dest, floodfill, request))
|
||||
{
|
||||
// request failed
|
||||
m_LeaseSetRequests.erase (ret.first);
|
||||
if (requestComplete) requestComplete (nullptr);
|
||||
// try another
|
||||
LogPrint (eLogWarning, "Destination: Couldn't send LeaseSet request to ", floodfill->GetIdentHash ().ToBase64 (), ". Trying another");
|
||||
request->excluded.insert (floodfill->GetIdentHash ());
|
||||
floodfill = i2p::data::netdb.GetClosestFloodfill (dest, request->excluded);
|
||||
if (!SendLeaseSetRequest (dest, floodfill, request))
|
||||
{
|
||||
// request failed
|
||||
LogPrint (eLogWarning, "Destination: LeaseSet request for ", dest.ToBase32 (), " was not sent");
|
||||
m_LeaseSetRequests.erase (ret.first);
|
||||
if (requestComplete) requestComplete (nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
else // duplicate
|
||||
@@ -792,11 +813,11 @@ namespace client
|
||||
std::shared_ptr<const i2p::data::RouterInfo> nextFloodfill, std::shared_ptr<LeaseSetRequest> request)
|
||||
{
|
||||
if (!request->replyTunnel || !request->replyTunnel->IsEstablished ())
|
||||
request->replyTunnel = m_Pool->GetNextInboundTunnel (nullptr, nextFloodfill->GetCompatibleTransports (true));
|
||||
if (!request->replyTunnel) LogPrint (eLogError, "Destination: Can't send LeaseSet request, no inbound tunnels found");
|
||||
request->replyTunnel = m_Pool->GetNextInboundTunnel (nullptr, nextFloodfill->GetCompatibleTransports (false)); // outbound from floodfill
|
||||
if (!request->replyTunnel) LogPrint (eLogWarning, "Destination: Can't send LeaseSet request, no compatible inbound tunnels found");
|
||||
if (!request->outboundTunnel || !request->outboundTunnel->IsEstablished ())
|
||||
request->outboundTunnel = m_Pool->GetNextOutboundTunnel (nullptr, nextFloodfill->GetCompatibleTransports (false));
|
||||
if (!request->outboundTunnel) LogPrint (eLogError, "Destination: Can't send LeaseSet request, no outbound tunnels found");
|
||||
request->outboundTunnel = m_Pool->GetNextOutboundTunnel (nullptr, nextFloodfill->GetCompatibleTransports (true)); // inbound from floodfill
|
||||
if (!request->outboundTunnel) LogPrint (eLogWarning, "Destination: Can't send LeaseSet request, no compatible outbound tunnels found");
|
||||
|
||||
if (request->replyTunnel && request->outboundTunnel)
|
||||
{
|
||||
@@ -1096,7 +1117,13 @@ namespace client
|
||||
}
|
||||
auto leaseSet = FindLeaseSet (dest);
|
||||
if (leaseSet)
|
||||
streamRequestComplete(CreateStream (leaseSet, port));
|
||||
{
|
||||
auto stream = CreateStream (leaseSet, port);
|
||||
GetService ().post ([streamRequestComplete, stream]()
|
||||
{
|
||||
streamRequestComplete(stream);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
auto s = GetSharedFromThis ();
|
||||
@@ -1129,6 +1156,41 @@ 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,6 +247,8 @@ namespace client
|
||||
// following methods operate with default streaming destination
|
||||
void CreateStream (StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash& dest, int port = 0);
|
||||
void CreateStream (StreamRequestComplete streamRequestComplete, std::shared_ptr<const i2p::data::BlindedPublicKey> dest, int port = 0);
|
||||
std::shared_ptr<i2p::stream::Stream> CreateStream (const i2p::data::IdentHash& dest, int port = 0); // sync
|
||||
std::shared_ptr<i2p::stream::Stream> CreateStream (std::shared_ptr<const i2p::data::BlindedPublicKey> dest, int port = 0); // sync
|
||||
std::shared_ptr<i2p::stream::Stream> CreateStream (std::shared_ptr<const i2p::data::LeaseSet> remote, int port = 0);
|
||||
void SendPing (const i2p::data::IdentHash& to);
|
||||
void SendPing (std::shared_ptr<const i2p::data::BlindedPublicKey> to);
|
||||
@@ -282,6 +284,9 @@ namespace client
|
||||
void PersistTemporaryKeys (EncryptionKey * keys, bool isSingleKey);
|
||||
void ReadAuthKey (const std::string& group, const std::map<std::string, std::string> * params);
|
||||
|
||||
template<typename Dest>
|
||||
std::shared_ptr<i2p::stream::Stream> CreateStreamSync (const Dest& dest, int port);
|
||||
|
||||
private:
|
||||
|
||||
i2p::data::PrivateKeys m_Keys;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2021, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2023, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -335,7 +335,8 @@ namespace garlic
|
||||
case eECIESx25519BlkAckRequest:
|
||||
{
|
||||
LogPrint (eLogDebug, "Garlic: Ack request");
|
||||
m_AckRequests.push_back ({receiveTagset->GetTagSetID (), index});
|
||||
if (receiveTagset)
|
||||
m_AckRequests.push_back ({receiveTagset->GetTagSetID (), index});
|
||||
break;
|
||||
}
|
||||
case eECIESx25519BlkTermination:
|
||||
|
||||
@@ -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
|
||||
*
|
||||
@@ -57,7 +57,8 @@ namespace data
|
||||
if ((err = inflate (&m_Inflator, Z_NO_FLUSH)) == Z_STREAM_END)
|
||||
return outLen - m_Inflator.avail_out;
|
||||
// else
|
||||
LogPrint (eLogError, "Gzip: Inflate error ", err);
|
||||
if (err)
|
||||
LogPrint (eLogError, "Gzip: Inflate error ", err);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -128,7 +129,8 @@ namespace data
|
||||
return outLen - m_Deflator.avail_out;
|
||||
}
|
||||
// else
|
||||
LogPrint (eLogError, "Gzip: Deflate error ", err);
|
||||
if (err)
|
||||
LogPrint (eLogError, "Gzip: Deflate error ", err);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -158,7 +160,8 @@ namespace data
|
||||
offset = outLen - m_Deflator.avail_out;
|
||||
}
|
||||
// else
|
||||
LogPrint (eLogError, "Gzip: Deflate error ", err);
|
||||
if (err)
|
||||
LogPrint (eLogError, "Gzip: Deflate error ", err);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2021, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2023, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -135,6 +135,7 @@ namespace http
|
||||
? url.substr(pos_p, std::string::npos)
|
||||
: url.substr(pos_p, pos_c - pos_p);
|
||||
/* stoi throws exception on failure, we don't need it */
|
||||
port = 0;
|
||||
for (char c : port_str) {
|
||||
if (c < '0' || c > '9')
|
||||
return false;
|
||||
@@ -159,6 +160,7 @@ namespace http
|
||||
return true;
|
||||
} else if (url.at(pos_c) == '?') {
|
||||
/* found query part */
|
||||
hasquery = true;
|
||||
path = url.substr(pos_p, pos_c - pos_p);
|
||||
pos_p = pos_c + 1;
|
||||
pos_c = url.find('#', pos_p);
|
||||
@@ -217,8 +219,10 @@ namespace http
|
||||
}
|
||||
}
|
||||
out += path;
|
||||
if (hasquery) // add query even if it was empty
|
||||
out += "?";
|
||||
if (query != "")
|
||||
out += "?" + query;
|
||||
out += query;
|
||||
if (frag != "")
|
||||
out += "#" + frag;
|
||||
return out;
|
||||
@@ -346,6 +350,14 @@ namespace http
|
||||
return "";
|
||||
}
|
||||
|
||||
size_t HTTPReq::GetNumHeaders (const std::string& name) const
|
||||
{
|
||||
size_t num = 0;
|
||||
for (auto& it : headers)
|
||||
if (it.first == name) num++;
|
||||
return num;
|
||||
}
|
||||
|
||||
bool HTTPRes::is_chunked() const
|
||||
{
|
||||
auto it = headers.find("Transfer-Encoding");
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2021, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2023, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -33,10 +33,11 @@ namespace http
|
||||
std::string host;
|
||||
unsigned short int port;
|
||||
std::string path;
|
||||
bool hasquery;
|
||||
std::string query;
|
||||
std::string frag;
|
||||
|
||||
URL(): schema(""), user(""), pass(""), host(""), port(0), path(""), query(""), frag("") {};
|
||||
URL(): schema(""), user(""), pass(""), host(""), port(0), path(""), hasquery(false), query(""), frag("") {};
|
||||
|
||||
/**
|
||||
* @brief Tries to parse url from string
|
||||
@@ -101,6 +102,8 @@ namespace http
|
||||
void RemoveHeader (const std::string& name, const std::string& exempt); // remove all headers starting with name, but exempt
|
||||
void RemoveHeader (const std::string& name) { RemoveHeader (name, ""); };
|
||||
std::string GetHeader (const std::string& name) const;
|
||||
size_t GetNumHeaders (const std::string& name) const;
|
||||
size_t GetNumHeaders () const { return headers.size (); };
|
||||
};
|
||||
|
||||
struct HTTPRes : HTTPMsg {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2023, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -392,7 +392,8 @@ 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);
|
||||
i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel);
|
||||
if (!i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel))
|
||||
retCode = 30;
|
||||
}
|
||||
else
|
||||
retCode = 30; // always reject with bandwidth reason (30)
|
||||
@@ -558,7 +559,8 @@ namespace i2p
|
||||
return;
|
||||
}
|
||||
auto& noiseState = i2p::context.GetCurrentNoiseState ();
|
||||
uint8_t replyKey[32], layerKey[32], ivKey[32];
|
||||
uint8_t replyKey[32]; // AEAD/Chacha20/Poly1305
|
||||
i2p::crypto::AESKey layerKey, ivKey; // AES
|
||||
i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "SMTunnelReplyKey", noiseState.m_CK);
|
||||
memcpy (replyKey, noiseState.m_CK + 32, 32);
|
||||
i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "SMTunnelLayerKey", noiseState.m_CK);
|
||||
@@ -589,7 +591,8 @@ 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);
|
||||
i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel);
|
||||
if (!i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel))
|
||||
retCode = 30;
|
||||
}
|
||||
|
||||
// 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 = 2500;
|
||||
const uint16_t DEFAULT_MAX_NUM_TRANSIT_TUNNELS = 5000;
|
||||
void SetMaxNumTransitTunnels (uint16_t maxNumTransitTunnels);
|
||||
uint16_t GetMaxNumTransitTunnels ();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2020, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -49,13 +49,22 @@ 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, 32);
|
||||
RAND_bytes (m_StandardIdentity.publicKey + 32, 224);
|
||||
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);
|
||||
}
|
||||
else
|
||||
memcpy (m_StandardIdentity.publicKey, publicKey, 256);
|
||||
{
|
||||
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);
|
||||
}
|
||||
if (type != SIGNING_KEY_TYPE_DSA_SHA1)
|
||||
{
|
||||
size_t excessLen = 0;
|
||||
@@ -93,7 +102,8 @@ namespace data
|
||||
case SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519:
|
||||
{
|
||||
size_t padding = 128 - i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH; // 96 = 128 - 32
|
||||
RAND_bytes (m_StandardIdentity.signingKey, padding);
|
||||
for (int i = 0; i < 3; i++) // 96 bytes
|
||||
memcpy (m_StandardIdentity.signingKey + 32*i, randomPaddingBlock, 32);
|
||||
memcpy (m_StandardIdentity.signingKey + padding, signingKey, i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH);
|
||||
break;
|
||||
}
|
||||
@@ -695,7 +705,7 @@ namespace data
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PrivateKeys PrivateKeys::CreateRandomKeys (SigningKeyType type, CryptoKeyType cryptoType)
|
||||
PrivateKeys PrivateKeys::CreateRandomKeys (SigningKeyType type, CryptoKeyType cryptoType, bool isDestination)
|
||||
{
|
||||
if (type != SIGNING_KEY_TYPE_DSA_SHA1)
|
||||
{
|
||||
@@ -705,9 +715,12 @@ namespace data
|
||||
GenerateSigningKeyPair (type, keys.m_SigningPrivateKey, signingPublicKey);
|
||||
// encryption
|
||||
uint8_t publicKey[256];
|
||||
GenerateCryptoKeyPair (cryptoType, keys.m_PrivateKey, publicKey);
|
||||
if (isDestination)
|
||||
RAND_bytes (keys.m_PrivateKey, 256);
|
||||
else
|
||||
GenerateCryptoKeyPair (cryptoType, keys.m_PrivateKey, publicKey);
|
||||
// identity
|
||||
keys.m_Public = std::make_shared<IdentityEx> (publicKey, signingPublicKey, type, cryptoType);
|
||||
keys.m_Public = std::make_shared<IdentityEx> (isDestination ? nullptr : publicKey, signingPublicKey, type, cryptoType);
|
||||
|
||||
keys.CreateSigner ();
|
||||
return keys;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2021, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -171,7 +171,7 @@ namespace data
|
||||
std::shared_ptr<i2p::crypto::CryptoKeyDecryptor> CreateDecryptor (const uint8_t * key) const;
|
||||
|
||||
static std::shared_ptr<i2p::crypto::CryptoKeyDecryptor> CreateDecryptor (CryptoKeyType cryptoType, const uint8_t * key);
|
||||
static PrivateKeys CreateRandomKeys (SigningKeyType type = SIGNING_KEY_TYPE_DSA_SHA1, CryptoKeyType cryptoType = CRYPTO_KEY_TYPE_ELGAMAL);
|
||||
static PrivateKeys CreateRandomKeys (SigningKeyType type = SIGNING_KEY_TYPE_DSA_SHA1, CryptoKeyType cryptoType = CRYPTO_KEY_TYPE_ELGAMAL, bool isDestination = false);
|
||||
static void GenerateSigningKeyPair (SigningKeyType type, uint8_t * priv, uint8_t * pub);
|
||||
static void GenerateCryptoKeyPair (CryptoKeyType type, uint8_t * priv, uint8_t * pub); // priv and pub are 256 bytes long
|
||||
static i2p::crypto::Signer * CreateSigner (SigningKeyType keyType, const uint8_t * priv);
|
||||
|
||||
271
libi2pd/KadDHT.cpp
Normal file
271
libi2pd/KadDHT.cpp
Normal file
@@ -0,0 +1,271 @@
|
||||
/*
|
||||
* 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 "KadDHT.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace data
|
||||
{
|
||||
DHTNode::DHTNode ():
|
||||
zero (nullptr), one (nullptr), hash (nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
DHTNode::~DHTNode ()
|
||||
{
|
||||
if (zero) delete zero;
|
||||
if (one) delete one;
|
||||
if (hash) delete hash;
|
||||
}
|
||||
|
||||
void DHTNode::MoveHashUp (bool fromOne)
|
||||
{
|
||||
DHTNode *& side = fromOne ? one : zero;
|
||||
if (side)
|
||||
{
|
||||
if (hash) delete hash; // shouldn't happen
|
||||
hash = side->hash;
|
||||
side->hash = nullptr;
|
||||
delete side;
|
||||
side = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
DHTTable::DHTTable ():
|
||||
m_Size (0)
|
||||
{
|
||||
m_Root = new DHTNode;
|
||||
}
|
||||
|
||||
DHTTable::~DHTTable ()
|
||||
{
|
||||
delete m_Root;
|
||||
}
|
||||
|
||||
DHTNode * DHTTable::Insert (const IdentHash& h)
|
||||
{
|
||||
return Insert (new IdentHash (h), m_Root, 0);
|
||||
}
|
||||
|
||||
DHTNode * DHTTable::Insert (IdentHash * h, DHTNode * root, int level)
|
||||
{
|
||||
if (root->hash)
|
||||
{
|
||||
if (*(root->hash) == *h)
|
||||
{
|
||||
delete h;
|
||||
return root;
|
||||
}
|
||||
auto h2 = root->hash;
|
||||
root->hash = nullptr; m_Size--;
|
||||
int bit1, bit2;
|
||||
do
|
||||
{
|
||||
bit1 = h->GetBit (level);
|
||||
bit2 = h2->GetBit (level);
|
||||
if (bit1 == bit2)
|
||||
{
|
||||
if (bit1)
|
||||
{
|
||||
if (root->one) return nullptr; // someting wrong
|
||||
root->one = new DHTNode;
|
||||
root = root->one;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (root->zero) return nullptr; // someting wrong
|
||||
root->zero = new DHTNode;
|
||||
root = root->zero;
|
||||
}
|
||||
level++;
|
||||
}
|
||||
}
|
||||
while (bit1 == bit2);
|
||||
|
||||
if (!root->zero)
|
||||
root->zero = new DHTNode;
|
||||
if (!root->one)
|
||||
root->one = new DHTNode;
|
||||
if (bit1)
|
||||
{
|
||||
Insert (h2, root->zero, level + 1);
|
||||
return Insert (h, root->one, level + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
Insert (h2, root->one, level + 1);
|
||||
return Insert (h, root->zero, level + 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!root->zero && !root->one)
|
||||
{
|
||||
root->hash = h; m_Size++;
|
||||
return root;
|
||||
}
|
||||
int bit = h->GetBit (level);
|
||||
if (bit)
|
||||
{
|
||||
if (!root->one)
|
||||
root->one = new DHTNode;
|
||||
return Insert (h, root->one, level + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!root->zero)
|
||||
root->zero = new DHTNode;
|
||||
return Insert (h, root->zero, level + 1);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool DHTTable::Remove (const IdentHash& h)
|
||||
{
|
||||
return Remove (h, m_Root, 0);
|
||||
}
|
||||
|
||||
bool DHTTable::Remove (const IdentHash& h, DHTNode * root, int level)
|
||||
{
|
||||
if (root)
|
||||
{
|
||||
if (root->hash && *(root->hash) == h)
|
||||
{
|
||||
delete root->hash; root->hash = nullptr;
|
||||
m_Size--;
|
||||
return true;
|
||||
}
|
||||
int bit = h.GetBit (level);
|
||||
if (bit)
|
||||
{
|
||||
if (root->one && Remove (h, root->one, level + 1))
|
||||
{
|
||||
if (root->one->IsEmpty ())
|
||||
{
|
||||
delete root->one;
|
||||
root->one = nullptr;
|
||||
if (root->zero && root->zero->hash)
|
||||
root->MoveHashUp (false);
|
||||
}
|
||||
else if (root->one->hash && !root->zero)
|
||||
root->MoveHashUp (true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (root->zero && Remove (h, root->zero, level + 1))
|
||||
{
|
||||
if (root->zero->IsEmpty ())
|
||||
{
|
||||
delete root->zero;
|
||||
root->zero = nullptr;
|
||||
if (root->one && root->one->hash)
|
||||
root->MoveHashUp (true);
|
||||
}
|
||||
else if (root->zero->hash && !root->one)
|
||||
root->MoveHashUp (false);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
IdentHash * DHTTable::FindClosest (const IdentHash& h)
|
||||
{
|
||||
return FindClosest (h, m_Root, 0);
|
||||
}
|
||||
|
||||
IdentHash * DHTTable::FindClosest (const IdentHash& h, DHTNode * root, int level)
|
||||
{
|
||||
if (root->hash) return root->hash;
|
||||
int bit = h.GetBit (level);
|
||||
if (bit)
|
||||
{
|
||||
if (root->one)
|
||||
return FindClosest (h, root->one, level + 1);
|
||||
if (root->zero)
|
||||
return FindClosest (h, root->zero, level + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (root->zero)
|
||||
return FindClosest (h, root->zero, level + 1);
|
||||
if (root->one)
|
||||
return FindClosest (h, root->one, level + 1);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<IdentHash *> DHTTable::FindClosest (const IdentHash& h, size_t num)
|
||||
{
|
||||
std::vector<IdentHash *> vec;
|
||||
if (num > 0)
|
||||
FindClosest (h, num, m_Root, 0, vec);
|
||||
return vec;
|
||||
}
|
||||
|
||||
void DHTTable::FindClosest (const IdentHash& h, size_t num, DHTNode * root, int level, std::vector<IdentHash *>& hashes)
|
||||
{
|
||||
if (hashes.size () >= num) return;
|
||||
if (root->hash)
|
||||
{
|
||||
hashes.push_back (root->hash);
|
||||
return;
|
||||
}
|
||||
int bit = h.GetBit (level);
|
||||
if (bit)
|
||||
{
|
||||
if (root->one)
|
||||
FindClosest (h, num, root->one, level + 1, hashes);
|
||||
if (hashes.size () < num && root->zero)
|
||||
FindClosest (h, num, root->zero, level + 1, hashes);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (root->zero)
|
||||
FindClosest (h, num, root->zero, level + 1, hashes);
|
||||
if (hashes.size () < num && root->one)
|
||||
FindClosest (h, num, root->one, level + 1, hashes);
|
||||
}
|
||||
}
|
||||
|
||||
void DHTTable::Print (std::stringstream& s)
|
||||
{
|
||||
Print (s, m_Root, 0);
|
||||
}
|
||||
|
||||
void DHTTable::Print (std::stringstream& s, DHTNode * root, int level)
|
||||
{
|
||||
if (!root) return;
|
||||
s << std::string (level, '-');
|
||||
if (root->hash)
|
||||
{
|
||||
if (!root->zero && !root->one)
|
||||
s << '>' << GetIdentHashAbbreviation (*(root->hash));
|
||||
else
|
||||
s << "error";
|
||||
}
|
||||
s << std::endl;
|
||||
if (root->zero)
|
||||
{
|
||||
s << std::string (level, '-') << "0" << std::endl;
|
||||
Print (s, root->zero, level + 1);
|
||||
}
|
||||
if (root->one)
|
||||
{
|
||||
s << std::string (level, '-') << "1" << std::endl;
|
||||
Print (s, root->one, level + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
67
libi2pd/KadDHT.h
Normal file
67
libi2pd/KadDHT.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef KADDHT_H__
|
||||
#define KADDHT_H__
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
#include "Identity.h"
|
||||
|
||||
// Kademlia DHT (XOR distance)
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace data
|
||||
{
|
||||
struct DHTNode
|
||||
{
|
||||
DHTNode * zero, * one;
|
||||
IdentHash * hash;
|
||||
|
||||
DHTNode ();
|
||||
~DHTNode ();
|
||||
|
||||
bool IsEmpty () const { return !zero && !one && !hash; };
|
||||
void MoveHashUp (bool fromOne);
|
||||
};
|
||||
|
||||
class DHTTable
|
||||
{
|
||||
public:
|
||||
|
||||
DHTTable ();
|
||||
~DHTTable ();
|
||||
|
||||
DHTNode * Insert (const IdentHash& h);
|
||||
bool Remove (const IdentHash& h);
|
||||
IdentHash * FindClosest (const IdentHash& h);
|
||||
std::vector<IdentHash *> FindClosest (const IdentHash& h, size_t num);
|
||||
|
||||
void Print (std::stringstream& s);
|
||||
size_t GetSize () const { return m_Size; };
|
||||
|
||||
private:
|
||||
|
||||
DHTNode * Insert (IdentHash * h, DHTNode * root, int level); // recursive
|
||||
bool Remove (const IdentHash& h, DHTNode * root, int level);
|
||||
IdentHash * FindClosest (const IdentHash& h, DHTNode * root, int level);
|
||||
void FindClosest (const IdentHash& h, size_t num, DHTNode * root, int level, std::vector<IdentHash *>& hashes);
|
||||
void Print (std::stringstream& s, DHTNode * root, int level);
|
||||
|
||||
private:
|
||||
|
||||
DHTNode * m_Root;
|
||||
size_t m_Size;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2023, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -37,14 +37,7 @@ namespace data
|
||||
|
||||
void LeaseSet::Update (const uint8_t * buf, size_t len, bool verifySignature)
|
||||
{
|
||||
if (len > m_BufferLen)
|
||||
{
|
||||
auto oldBuffer = m_Buffer;
|
||||
m_Buffer = new uint8_t[len];
|
||||
delete[] oldBuffer;
|
||||
}
|
||||
memcpy (m_Buffer, buf, len);
|
||||
m_BufferLen = len;
|
||||
SetBuffer (buf, len);
|
||||
ReadFromBuffer (false, verifySignature);
|
||||
}
|
||||
|
||||
@@ -264,8 +257,18 @@ namespace data
|
||||
|
||||
void LeaseSet::SetBuffer (const uint8_t * buf, size_t len)
|
||||
{
|
||||
if (m_Buffer) delete[] m_Buffer;
|
||||
m_Buffer = new uint8_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];
|
||||
m_BufferLen = len;
|
||||
memcpy (m_Buffer, buf, len);
|
||||
}
|
||||
@@ -312,7 +315,7 @@ namespace data
|
||||
{
|
||||
// standard LS2 header
|
||||
std::shared_ptr<const IdentityEx> identity;
|
||||
if (readIdentity)
|
||||
if (readIdentity || !GetIdentity ())
|
||||
{
|
||||
identity = std::make_shared<IdentityEx>(buf, len);
|
||||
SetIdentity (identity);
|
||||
@@ -363,6 +366,8 @@ namespace data
|
||||
VerifySignature (identity, buf, len, offset);
|
||||
SetIsValid (verified);
|
||||
}
|
||||
else
|
||||
SetIsValid (true);
|
||||
offset += m_TransientVerifier ? m_TransientVerifier->GetSignatureLen () : identity->GetSignatureLen ();
|
||||
if (offset > len) {
|
||||
LogPrint (eLogWarning, "LeaseSet2: short buffer: wanted ", int(offset), "bytes, have ", int(len));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2021, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2023, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -145,6 +145,7 @@ namespace data
|
||||
{
|
||||
public:
|
||||
|
||||
LeaseSet2 (uint8_t storeType): LeaseSet (true), m_StoreType (storeType) {}; // for update
|
||||
LeaseSet2 (uint8_t storeType, const uint8_t * buf, size_t len, bool storeLeases = true, CryptoKeyType preferredCrypto = CRYPTO_KEY_TYPE_ELGAMAL);
|
||||
LeaseSet2 (const uint8_t * buf, size_t len, std::shared_ptr<const BlindedPublicKey> key, const uint8_t * secret = nullptr, CryptoKeyType preferredCrypto = CRYPTO_KEY_TYPE_ELGAMAL); // store type 5, called from local netdb only
|
||||
uint8_t GetStoreType () const { return m_StoreType; };
|
||||
|
||||
@@ -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
|
||||
*
|
||||
@@ -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", // eLogWarn
|
||||
"warn", // eLogWarning
|
||||
"info", // eLogInfo
|
||||
"debug" // eLogDebug
|
||||
};
|
||||
@@ -35,12 +35,12 @@ namespace log {
|
||||
static const char *LogMsgColors[] = { "", "", "", "", "", "" };
|
||||
#else /* UNIX */
|
||||
static const char *LogMsgColors[] = {
|
||||
[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 */
|
||||
"\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 */
|
||||
};
|
||||
#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-2022, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2023, 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::GetSecondsSinceEpoch ()); // tsA
|
||||
htobe32buf (options + 8, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000); // tsA, rounded to seconds
|
||||
// 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::GetSecondsSinceEpoch ()); // tsB
|
||||
htobe32buf (options + 8, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000); // tsB, rounded to seconds
|
||||
// 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
|
||||
// caclulate new h again for KDF data
|
||||
// calculate new h again for KDF data
|
||||
MixHash (m_SessionConfirmedBuffer + 48, m3p2Len); // h = SHA256(h || ciphertext)
|
||||
else
|
||||
{
|
||||
@@ -374,10 +374,16 @@ 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);
|
||||
@@ -687,10 +693,20 @@ namespace transport
|
||||
SendTerminationAndTerminate (eNTCP2Message3Error);
|
||||
return;
|
||||
}
|
||||
auto addr = ri.GetNTCP2AddressWithStaticKey (m_Establisher->m_RemoteStaticKey);
|
||||
if (!addr)
|
||||
auto addr = m_RemoteEndpoint.address ().is_v4 () ? ri.GetNTCP2V4Address () :
|
||||
(i2p::util::net::IsYggdrasilAddress (m_RemoteEndpoint.address ()) ? ri.GetYggdrasilAddress () : ri.GetNTCP2V6Address ());
|
||||
if (!addr || memcmp (m_Establisher->m_RemoteStaticKey, addr->s, 32))
|
||||
{
|
||||
LogPrint (eLogError, "NTCP2: No NTCP2 address with static key found in SessionConfirmed");
|
||||
LogPrint (eLogError, "NTCP2: Wrong static key in SessionConfirmed");
|
||||
Terminate ();
|
||||
return;
|
||||
}
|
||||
if (addr->IsPublishedNTCP2 () && m_RemoteEndpoint.address () != addr->host &&
|
||||
(!m_RemoteEndpoint.address ().is_v6 () || (i2p::util::net::IsYggdrasilAddress (m_RemoteEndpoint.address ()) ?
|
||||
memcmp (m_RemoteEndpoint.address ().to_v6 ().to_bytes ().data () + 1, addr->host.to_v6 ().to_bytes ().data () + 1, 7) : // from the same yggdrasil subnet
|
||||
memcmp (m_RemoteEndpoint.address ().to_v6 ().to_bytes ().data (), addr->host.to_v6 ().to_bytes ().data (), 8)))) // temporary address
|
||||
{
|
||||
LogPrint (eLogError, "NTCP2: Host mismatch between published address ", addr->host, " and actual endpoint ", m_RemoteEndpoint.address ());
|
||||
Terminate ();
|
||||
return;
|
||||
}
|
||||
@@ -746,6 +762,8 @@ 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 (),
|
||||
@@ -866,8 +884,20 @@ namespace transport
|
||||
switch (blk)
|
||||
{
|
||||
case eNTCP2BlkDateTime:
|
||||
{
|
||||
LogPrint (eLogDebug, "NTCP2: Datetime");
|
||||
break;
|
||||
if (m_IsEstablished)
|
||||
{
|
||||
uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
uint64_t tsA = bufbe32toh (frame + offset);
|
||||
if (tsA < ts - NTCP2_CLOCK_SKEW || tsA > ts + NTCP2_CLOCK_SKEW)
|
||||
{
|
||||
LogPrint (eLogWarning, "NTCP2: Established session time difference ", (int)(ts - tsA), " exceeds clock skew");
|
||||
SendTerminationAndTerminate (eNTCP2ClockSkew);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case eNTCP2BlkOptions:
|
||||
LogPrint (eLogDebug, "NTCP2: Options");
|
||||
break;
|
||||
@@ -1050,7 +1080,10 @@ namespace transport
|
||||
SendRouterInfo ();
|
||||
}
|
||||
else
|
||||
{
|
||||
SendQueue ();
|
||||
m_SendQueueSize = m_SendQueue.size ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1109,12 +1142,17 @@ namespace transport
|
||||
{
|
||||
if (!IsEstablished ()) return;
|
||||
auto riLen = i2p::context.GetRouterInfo ().GetBufferLen ();
|
||||
size_t payloadLen = riLen + 4; // 3 bytes block header + 1 byte RI flag
|
||||
size_t payloadLen = riLen + 3 + 1 + 7; // 3 bytes block header + 1 byte RI flag + 7 bytes DateTime
|
||||
m_NextSendBuffer = new uint8_t[payloadLen + 16 + 2 + 64]; // up to 64 bytes padding
|
||||
m_NextSendBuffer[2] = eNTCP2BlkRouterInfo;
|
||||
htobe16buf (m_NextSendBuffer + 3, riLen + 1); // size
|
||||
m_NextSendBuffer[5] = 0; // flag
|
||||
memcpy (m_NextSendBuffer + 6, i2p::context.GetRouterInfo ().GetBuffer (), riLen);
|
||||
// DateTime block
|
||||
m_NextSendBuffer[2] = eNTCP2BlkDateTime;
|
||||
htobe16buf (m_NextSendBuffer + 3, 4);
|
||||
htobe32buf (m_NextSendBuffer + 5, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000);
|
||||
// RouterInfo block
|
||||
m_NextSendBuffer[9] = eNTCP2BlkRouterInfo;
|
||||
htobe16buf (m_NextSendBuffer + 10, riLen + 1); // size
|
||||
m_NextSendBuffer[12] = 0; // flag
|
||||
memcpy (m_NextSendBuffer + 13, i2p::context.GetRouterInfo ().GetBuffer (), riLen);
|
||||
// padding block
|
||||
auto paddingSize = CreatePaddingBlock (payloadLen, m_NextSendBuffer + 2 + payloadLen, 64);
|
||||
payloadLen += paddingSize;
|
||||
@@ -1158,7 +1196,7 @@ namespace transport
|
||||
{
|
||||
if (m_IsTerminated) return;
|
||||
for (auto it: msgs)
|
||||
m_SendQueue.push_back (it);
|
||||
m_SendQueue.push_back (std::move (it));
|
||||
if (!m_IsSending)
|
||||
SendQueue ();
|
||||
else if (m_SendQueue.size () > NTCP2_MAX_OUTGOING_QUEUE_SIZE)
|
||||
@@ -1167,6 +1205,7 @@ namespace transport
|
||||
GetIdentHashBase64(), " exceeds ", NTCP2_MAX_OUTGOING_QUEUE_SIZE);
|
||||
Terminate ();
|
||||
}
|
||||
m_SendQueueSize = m_SendQueue.size ();
|
||||
}
|
||||
|
||||
void NTCP2Session::SendLocalRouterInfo (bool update)
|
||||
@@ -1210,8 +1249,9 @@ namespace transport
|
||||
else
|
||||
LogPrint(eLogInfo, "NTCP2: Proxy is not used");
|
||||
// start acceptors
|
||||
auto& addresses = context.GetRouterInfo ().GetAddresses ();
|
||||
for (const auto& address: addresses)
|
||||
auto addresses = context.GetRouterInfo ().GetAddresses ();
|
||||
if (!addresses) return;
|
||||
for (const auto& address: *addresses)
|
||||
{
|
||||
if (!address) continue;
|
||||
if (address->IsPublishedNTCP2 () && address->port)
|
||||
@@ -1288,7 +1328,7 @@ namespace transport
|
||||
for (auto& it: ntcpSessions)
|
||||
it.second->Terminate ();
|
||||
for (auto& it: m_PendingIncomingSessions)
|
||||
it->Terminate ();
|
||||
it.second->Terminate ();
|
||||
}
|
||||
m_NTCP2Sessions.clear ();
|
||||
|
||||
@@ -1304,20 +1344,32 @@ namespace transport
|
||||
{
|
||||
if (!session) return false;
|
||||
if (incoming)
|
||||
m_PendingIncomingSessions.remove (session);
|
||||
if (!session->GetRemoteIdentity ()) return false;
|
||||
m_PendingIncomingSessions.erase (session->GetRemoteEndpoint ().address ());
|
||||
if (!session->GetRemoteIdentity ())
|
||||
{
|
||||
LogPrint (eLogWarning, "NTCP2: Unknown identity for ", session->GetRemoteEndpoint ());
|
||||
session->Terminate ();
|
||||
return false;
|
||||
}
|
||||
auto& ident = session->GetRemoteIdentity ()->GetIdentHash ();
|
||||
auto it = m_NTCP2Sessions.find (ident);
|
||||
if (it != m_NTCP2Sessions.end ())
|
||||
{
|
||||
LogPrint (eLogWarning, "NTCP2: Session to ", ident.ToBase64 (), " already exists");
|
||||
LogPrint (eLogWarning, "NTCP2: Session with ", ident.ToBase64 (), " already exists. ", incoming ? "Replaced" : "Dropped");
|
||||
if (incoming)
|
||||
{
|
||||
// replace by new session
|
||||
it->second->Terminate ();
|
||||
auto s = it->second;
|
||||
m_NTCP2Sessions.erase (it);
|
||||
s->Terminate ();
|
||||
}
|
||||
else
|
||||
{
|
||||
session->Terminate ();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
m_NTCP2Sessions.insert (std::make_pair (ident, session));
|
||||
m_NTCP2Sessions.emplace (ident, session);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1412,19 +1464,35 @@ namespace transport
|
||||
if (!ec)
|
||||
{
|
||||
LogPrint (eLogDebug, "NTCP2: Connected from ", ep);
|
||||
if (conn)
|
||||
if (!i2p::util::net::IsInReservedRange(ep.address ()))
|
||||
{
|
||||
conn->SetRemoteEndpoint (ep);
|
||||
conn->ServerLogin ();
|
||||
m_PendingIncomingSessions.push_back (conn);
|
||||
conn = nullptr;
|
||||
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");
|
||||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
@@ -1446,20 +1514,43 @@ namespace transport
|
||||
if (!ec)
|
||||
{
|
||||
LogPrint (eLogDebug, "NTCP2: Connected from ", ep);
|
||||
if (conn)
|
||||
if (!i2p::util::net::IsInReservedRange(ep.address ()) ||
|
||||
i2p::util::net::IsYggdrasilAddress (ep.address ()))
|
||||
{
|
||||
conn->SetRemoteEndpoint (ep);
|
||||
conn->ServerLogin ();
|
||||
m_PendingIncomingSessions.push_back (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");
|
||||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
conn = std::make_shared<NTCP2Session> (*this);
|
||||
if (!conn) // connection is used, create new one
|
||||
conn = std::make_shared<NTCP2Session> (*this);
|
||||
else // reuse failed
|
||||
conn->Close ();
|
||||
m_NTCP2V6Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCP2Server::HandleAcceptV6, this,
|
||||
conn, std::placeholders::_1));
|
||||
}
|
||||
@@ -1490,18 +1581,34 @@ namespace transport
|
||||
// pending
|
||||
for (auto it = m_PendingIncomingSessions.begin (); it != m_PendingIncomingSessions.end ();)
|
||||
{
|
||||
if ((*it)->IsEstablished () || (*it)->IsTerminationTimeoutExpired (ts))
|
||||
if (it->second->IsEstablished () || it->second->IsTerminationTimeoutExpired (ts))
|
||||
{
|
||||
(*it)->Terminate ();
|
||||
it->second->Terminate ();
|
||||
it = m_PendingIncomingSessions.erase (it); // established of expired
|
||||
}
|
||||
else if ((*it)->IsTerminated ())
|
||||
else if (it->second->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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1558,7 +1665,7 @@ namespace transport
|
||||
case eSocksProxy:
|
||||
{
|
||||
// TODO: support username/password auth etc
|
||||
static const uint8_t buff[3] = {0x05, 0x01, 0x00};
|
||||
static const uint8_t buff[3] = {SOCKS5_VER, 0x01, 0x00};
|
||||
boost::asio::async_write(conn->GetSocket(), boost::asio::buffer(buff, 3), boost::asio::transfer_all(),
|
||||
[] (const boost::system::error_code & ec, std::size_t transferred)
|
||||
{
|
||||
@@ -1672,21 +1779,21 @@ namespace transport
|
||||
size_t sz = 6; // header + port
|
||||
auto buff = std::make_shared<std::vector<int8_t> >(256);
|
||||
auto readbuff = std::make_shared<std::vector<int8_t> >(256);
|
||||
(*buff)[0] = 0x05;
|
||||
(*buff)[1] = 0x01;
|
||||
(*buff)[0] = SOCKS5_VER;
|
||||
(*buff)[1] = SOCKS5_CMD_CONNECT;
|
||||
(*buff)[2] = 0x00;
|
||||
|
||||
auto& ep = conn->GetRemoteEndpoint ();
|
||||
if(ep.address ().is_v4 ())
|
||||
{
|
||||
(*buff)[3] = 0x01;
|
||||
(*buff)[3] = SOCKS5_ATYP_IPV4;
|
||||
auto addrbytes = ep.address ().to_v4().to_bytes();
|
||||
sz += 4;
|
||||
memcpy(buff->data () + 4, addrbytes.data(), 4);
|
||||
}
|
||||
else if (ep.address ().is_v6 ())
|
||||
{
|
||||
(*buff)[3] = 0x04;
|
||||
(*buff)[3] = SOCKS5_ATYP_IPV6;
|
||||
auto addrbytes = ep.address ().to_v6().to_bytes();
|
||||
sz += 16;
|
||||
memcpy(buff->data () + 4, addrbytes.data(), 16);
|
||||
@@ -1708,22 +1815,24 @@ namespace transport
|
||||
}
|
||||
});
|
||||
|
||||
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)
|
||||
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)
|
||||
{
|
||||
if(e)
|
||||
{
|
||||
if (e)
|
||||
LogPrint(eLogError, "NTCP2: SOCKS proxy read error ", e.message());
|
||||
}
|
||||
else if(transferred == sz)
|
||||
else if (!(*readbuff)[1]) // succeeded
|
||||
{
|
||||
if((*readbuff)[1] == 0x00)
|
||||
{
|
||||
timer->cancel();
|
||||
conn->ClientLogin();
|
||||
return;
|
||||
}
|
||||
boost::system::error_code ec;
|
||||
size_t moreBytes = conn->GetSocket ().available(ec);
|
||||
if (moreBytes) // read remaining portion of reply if ipv6 received
|
||||
boost::asio::read (conn->GetSocket (), boost::asio::buffer(readbuff->data (), moreBytes), boost::asio::transfer_all (), ec);
|
||||
timer->cancel();
|
||||
conn->ClientLogin();
|
||||
return;
|
||||
}
|
||||
else
|
||||
LogPrint(eLogError, "NTCP2: Proxy reply error ", (int)(*readbuff)[1]);
|
||||
timer->cancel();
|
||||
conn->Terminate();
|
||||
});
|
||||
|
||||
@@ -134,22 +134,22 @@ namespace transport
|
||||
~NTCP2Session ();
|
||||
void Terminate ();
|
||||
void TerminateByTimeout ();
|
||||
void Done ();
|
||||
void Close () { m_Socket.close (); }; // for accept
|
||||
void Done () override;
|
||||
void 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 { return m_IsEstablished; };
|
||||
bool IsEstablished () const override { return m_IsEstablished; };
|
||||
bool IsTerminated () const { return m_IsTerminated; };
|
||||
|
||||
void ClientLogin (); // Alice
|
||||
void ServerLogin (); // Bob
|
||||
|
||||
void SendLocalRouterInfo (bool update); // after handshake or by update
|
||||
void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs);
|
||||
void SendLocalRouterInfo (bool update) override; // after handshake or by update
|
||||
void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs) override;
|
||||
|
||||
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::list<std::shared_ptr<NTCP2Session> > m_PendingIncomingSessions;
|
||||
std::map<boost::asio::ip::address, std::shared_ptr<NTCP2Session> > m_PendingIncomingSessions;
|
||||
|
||||
ProxyType m_ProxyType;
|
||||
std::string m_ProxyAddress, m_ProxyAuthorization;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2023, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -85,8 +85,7 @@ namespace data
|
||||
if (m_IsRunning)
|
||||
{
|
||||
if (m_PersistProfiles)
|
||||
for (auto& it: m_RouterInfos)
|
||||
it.second->SaveProfile ();
|
||||
SaveProfiles ();
|
||||
DeleteObsoleteProfiles ();
|
||||
m_RouterInfos.clear ();
|
||||
m_Floodfills.clear ();
|
||||
@@ -153,13 +152,13 @@ namespace data
|
||||
if (!i2p::transport::transports.IsOnline ()) continue; // don't manage netdb when offline
|
||||
|
||||
uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
if (ts - lastManageRequest >= 15) // manage requests every 15 seconds
|
||||
if (ts - lastManageRequest >= 15 || ts + 15 < lastManageRequest) // manage requests every 15 seconds
|
||||
{
|
||||
m_Requests.ManageRequests ();
|
||||
lastManageRequest = ts;
|
||||
}
|
||||
|
||||
if (ts - lastSave >= 60) // save routers, manage leasesets and validate subscriptions every minute
|
||||
if (ts - lastSave >= 60 || ts + 60 < lastSave) // save routers, manage leasesets and validate subscriptions every minute
|
||||
{
|
||||
if (lastSave)
|
||||
{
|
||||
@@ -169,14 +168,17 @@ namespace data
|
||||
lastSave = ts;
|
||||
}
|
||||
|
||||
if (ts - lastDestinationCleanup >= i2p::garlic::INCOMING_TAGS_EXPIRATION_TIMEOUT)
|
||||
if (ts - lastDestinationCleanup >= i2p::garlic::INCOMING_TAGS_EXPIRATION_TIMEOUT ||
|
||||
ts + i2p::garlic::INCOMING_TAGS_EXPIRATION_TIMEOUT < lastDestinationCleanup)
|
||||
{
|
||||
i2p::context.CleanupDestination ();
|
||||
lastDestinationCleanup = ts;
|
||||
}
|
||||
|
||||
if (ts - lastProfilesCleanup >= (uint64_t)(i2p::data::PEER_PROFILE_AUTOCLEAN_TIMEOUT + profilesCleanupVariance))
|
||||
if (ts - lastProfilesCleanup >= (uint64_t)(i2p::data::PEER_PROFILE_AUTOCLEAN_TIMEOUT + profilesCleanupVariance) ||
|
||||
ts + i2p::data::PEER_PROFILE_AUTOCLEAN_TIMEOUT < lastProfilesCleanup)
|
||||
{
|
||||
if (m_PersistProfiles) PersistProfiles ();
|
||||
DeleteObsoleteProfiles ();
|
||||
lastProfilesCleanup = ts;
|
||||
profilesCleanupVariance = (rand () % (2 * i2p::data::PEER_PROFILE_AUTOCLEAN_VARIANCE) - i2p::data::PEER_PROFILE_AUTOCLEAN_VARIANCE);
|
||||
@@ -192,7 +194,8 @@ namespace data
|
||||
if (ts - lastPublish >= NETDB_PUBLISH_CONFIRMATION_TIMEOUT) publish = true;
|
||||
}
|
||||
else if (i2p::context.GetLastUpdateTime () > lastPublish ||
|
||||
ts - lastPublish >= NETDB_PUBLISH_INTERVAL)
|
||||
ts - lastPublish >= NETDB_PUBLISH_INTERVAL ||
|
||||
ts + NETDB_PUBLISH_INTERVAL < lastPublish)
|
||||
{
|
||||
// new publish
|
||||
m_PublishExcluded.clear ();
|
||||
@@ -208,7 +211,7 @@ namespace data
|
||||
}
|
||||
}
|
||||
|
||||
if (ts - lastExploratory >= 30) // exploratory every 30 seconds
|
||||
if (ts - lastExploratory >= 30 || ts + 30 < lastExploratory) // exploratory every 30 seconds
|
||||
{
|
||||
auto numRouters = m_RouterInfos.size ();
|
||||
if (!numRouters)
|
||||
@@ -273,8 +276,25 @@ namespace data
|
||||
bool wasFloodfill = r->IsFloodfill ();
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_RouterInfosMutex);
|
||||
r->Update (buf, len);
|
||||
}
|
||||
if (!r->Update (buf, len))
|
||||
{
|
||||
updated = false;
|
||||
m_Requests.RequestComplete (ident, r);
|
||||
return r;
|
||||
}
|
||||
if (r->IsUnreachable ())
|
||||
{
|
||||
// delete router as invalid after update
|
||||
m_RouterInfos.erase (ident);
|
||||
if (wasFloodfill)
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_FloodfillsMutex);
|
||||
m_Floodfills.remove (r);
|
||||
}
|
||||
m_Requests.RequestComplete (ident, nullptr);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
LogPrint (eLogInfo, "NetDb: RouterInfo updated: ", ident.ToBase64());
|
||||
if (wasFloodfill != r->IsFloodfill ()) // if floodfill status updated
|
||||
{
|
||||
@@ -295,7 +315,8 @@ namespace data
|
||||
else
|
||||
{
|
||||
r = std::make_shared<RouterInfo> (buf, len);
|
||||
if (!r->IsUnreachable () && r->HasValidAddresses ())
|
||||
if (!r->IsUnreachable () && r->HasValidAddresses () && (!r->IsFloodfill () || !r->GetProfile ()->IsUnreachable ()) &&
|
||||
i2p::util::GetMillisecondsSinceEpoch () + NETDB_EXPIRATION_TIMEOUT_THRESHOLD*1000LL > r->GetTimestamp ())
|
||||
{
|
||||
bool inserted = false;
|
||||
{
|
||||
@@ -365,15 +386,16 @@ 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 ())
|
||||
if (leaseSet->IsPublic () && !leaseSet->IsExpired () &&
|
||||
i2p::util::GetSecondsSinceEpoch () + NETDB_EXPIRATION_TIMEOUT_THRESHOLD > leaseSet->GetPublishedTimestamp ())
|
||||
{
|
||||
// TODO: implement actual update
|
||||
LogPrint (eLogInfo, "NetDb: LeaseSet2 updated: ", ident.ToBase32());
|
||||
@@ -382,7 +404,7 @@ namespace data
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "NetDb: Unpublished or expired LeaseSet2 received: ", ident.ToBase32());
|
||||
LogPrint (eLogWarning, "NetDb: Unpublished or expired or future LeaseSet2 received: ", ident.ToBase32());
|
||||
m_LeaseSets.erase (ident);
|
||||
}
|
||||
}
|
||||
@@ -425,7 +447,15 @@ namespace data
|
||||
{
|
||||
auto it = m_RouterInfos.find (ident);
|
||||
if (it != m_RouterInfos.end ())
|
||||
return it->second->SetUnreachable (unreachable);
|
||||
{
|
||||
it->second->SetUnreachable (unreachable);
|
||||
if (unreachable)
|
||||
{
|
||||
auto profile = it->second->GetProfile ();
|
||||
if (profile)
|
||||
profile->Unreachable ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NetDb::Reseed ()
|
||||
@@ -438,14 +468,14 @@ 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");
|
||||
@@ -494,7 +524,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)
|
||||
ts < r->GetTimestamp () + 24*60*60*NETDB_MAX_OFFLINE_EXPIRATION_TIMEOUT*1000LL) // too old
|
||||
{
|
||||
r->DeleteBuffer ();
|
||||
if (m_RouterInfos.emplace (r->GetIdentHash (), r).second)
|
||||
@@ -603,6 +633,7 @@ 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
|
||||
@@ -616,26 +647,38 @@ namespace data
|
||||
std::string ident = it.second->GetIdentHashBase64();
|
||||
if (it.second->IsUpdated ())
|
||||
{
|
||||
it.second->SaveToFile (m_Storage.Path(ident));
|
||||
if (it.second->GetBuffer ())
|
||||
{
|
||||
// we have something to save
|
||||
it.second->SaveToFile (m_Storage.Path(ident));
|
||||
it.second->SetUnreachable (false);
|
||||
it.second->DeleteBuffer ();
|
||||
}
|
||||
it.second->SetUpdated (false);
|
||||
it.second->SetUnreachable (false);
|
||||
it.second->DeleteBuffer ();
|
||||
updatedCount++;
|
||||
continue;
|
||||
}
|
||||
if (it.second->GetProfile ()->IsUnreachable ())
|
||||
it.second->SetUnreachable (true);
|
||||
// make router reachable back if too few routers or floodfills
|
||||
if (it.second->IsUnreachable () && (total - deletedCount < NETDB_MIN_ROUTERS ||
|
||||
if (it.second->IsUnreachable () && (total - deletedCount < NETDB_MIN_ROUTERS || isLowRate ||
|
||||
(it.second->IsFloodfill () && totalFloodfills - deletedFloodfillsCount < NETDB_MIN_FLOODFILLS)))
|
||||
it.second->SetUnreachable (false);
|
||||
// find & mark expired routers
|
||||
if (!it.second->IsReachable () && it.second->IsSSU (false))
|
||||
if (!it.second->IsReachable () && (it.second->GetCompatibleTransports (true) & RouterInfo::eSSU2V4))
|
||||
// non-reachable router, but reachable by ipv4 SSU2 means introducers
|
||||
{
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
if (it.second->IsUnreachable ())
|
||||
{
|
||||
@@ -648,6 +691,8 @@ 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");
|
||||
@@ -660,12 +705,12 @@ namespace data
|
||||
for (auto it = m_RouterInfos.begin (); it != m_RouterInfos.end ();)
|
||||
{
|
||||
if (it->second->IsUnreachable ())
|
||||
{
|
||||
if (m_PersistProfiles) it->second->SaveProfile ();
|
||||
it = m_RouterInfos.erase (it);
|
||||
continue;
|
||||
}
|
||||
++it;
|
||||
else
|
||||
{
|
||||
it->second->DropProfile ();
|
||||
it++;
|
||||
}
|
||||
}
|
||||
}
|
||||
// clean up expired floodfills or not floodfills anymore
|
||||
@@ -675,7 +720,7 @@ namespace data
|
||||
if ((*it)->IsUnreachable () || !(*it)->IsFloodfill ())
|
||||
it = m_Floodfills.erase (it);
|
||||
else
|
||||
++it;
|
||||
it++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -748,6 +793,11 @@ namespace data
|
||||
{
|
||||
const uint8_t * buf = m->GetPayload ();
|
||||
size_t len = m->GetSize ();
|
||||
if (len < DATABASE_STORE_HEADER_SIZE)
|
||||
{
|
||||
LogPrint (eLogError, "NetDb: Database store msg is too short ", len, ". Dropped");
|
||||
return;
|
||||
}
|
||||
IdentHash ident (buf + DATABASE_STORE_KEY_OFFSET);
|
||||
if (ident.IsZero ())
|
||||
{
|
||||
@@ -758,6 +808,11 @@ namespace data
|
||||
size_t offset = DATABASE_STORE_HEADER_SIZE;
|
||||
if (replyToken)
|
||||
{
|
||||
if (len < offset + 36) // 32 + 4
|
||||
{
|
||||
LogPrint (eLogError, "NetDb: Database store msg with reply token is too short ", len, ". Dropped");
|
||||
return;
|
||||
}
|
||||
auto deliveryStatus = CreateDeliveryStatusMsg (replyToken);
|
||||
uint32_t tunnelID = bufbe32toh (buf + offset);
|
||||
offset += 4;
|
||||
@@ -786,6 +841,11 @@ 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
|
||||
@@ -795,7 +855,7 @@ namespace data
|
||||
}
|
||||
else // all others are considered as LeaseSet2
|
||||
{
|
||||
LogPrint (eLogDebug, "NetDb: Store request: LeaseSet2 of type ", storeType, " for ", ident.ToBase32());
|
||||
LogPrint (eLogDebug, "NetDb: Store request: LeaseSet2 of type ", int(storeType), " for ", ident.ToBase32());
|
||||
updated = AddLeaseSet2 (ident, buf + offset, len - offset, storeType);
|
||||
}
|
||||
}
|
||||
@@ -980,11 +1040,10 @@ namespace data
|
||||
lookupType == DATABASE_LOOKUP_TYPE_NORMAL_LOOKUP)
|
||||
{
|
||||
auto router = FindRouter (ident);
|
||||
if (router)
|
||||
if (router && !router->IsUnreachable ())
|
||||
{
|
||||
LogPrint (eLogDebug, "NetDb: Requested RouterInfo ", key, " found");
|
||||
PopulateRouterInfoBuffer (router);
|
||||
if (router->GetBuffer ())
|
||||
if (PopulateRouterInfoBuffer (router))
|
||||
replyMsg = CreateDatabaseStoreMsg (router);
|
||||
}
|
||||
}
|
||||
@@ -1198,16 +1257,6 @@ 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 (
|
||||
@@ -1217,36 +1266,17 @@ 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::GetRandomIntroducer (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->IsFloodfill () && // floodfills don't send relay tag
|
||||
router->IsIntroducer (v4) && !excluded.count (router->GetIdentHash ());
|
||||
});
|
||||
}
|
||||
|
||||
std::shared_ptr<const RouterInfo> NetDb::GetRandomSSU2Introducer (bool v4, const std::set<IdentHash>& excluded) const
|
||||
{
|
||||
return GetRandomRouter (
|
||||
[v4, &excluded](std::shared_ptr<const RouterInfo> router)->bool
|
||||
{
|
||||
return !router->IsHidden () && router->IsSSU2Introducer (v4) &&
|
||||
return !router->IsHidden () && router->IsSSU2Introducer (v4) &&
|
||||
!excluded.count (router->GetIdentHash ());
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
std::shared_ptr<const RouterInfo> NetDb::GetHighBandwidthRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith, bool reverse) const
|
||||
{
|
||||
return GetRandomRouter (
|
||||
@@ -1337,7 +1367,7 @@ namespace data
|
||||
std::unique_lock<std::mutex> l(m_FloodfillsMutex);
|
||||
for (const auto& it: m_Floodfills)
|
||||
{
|
||||
if (!it->IsUnreachable ())
|
||||
if (!it->IsUnreachable () && !it->GetProfile ()->IsUnreachable ())
|
||||
{
|
||||
XORMetric m = destKey ^ it->GetIdentHash ();
|
||||
if (m < minMetric && !excluded.count (it->GetIdentHash ()))
|
||||
@@ -1368,7 +1398,7 @@ namespace data
|
||||
std::unique_lock<std::mutex> l(m_FloodfillsMutex);
|
||||
for (const auto& it: m_Floodfills)
|
||||
{
|
||||
if (!it->IsUnreachable ())
|
||||
if (!it->IsUnreachable () && !it->GetProfile ()->IsUnreachable ())
|
||||
{
|
||||
XORMetric m = destKey ^ it->GetIdentHash ();
|
||||
if (closeThanUsOnly && ourMetric < m) continue;
|
||||
@@ -1450,10 +1480,11 @@ namespace data
|
||||
m_LeasesPool.CleanUpMt ();
|
||||
}
|
||||
|
||||
void NetDb::PopulateRouterInfoBuffer (std::shared_ptr<RouterInfo> r)
|
||||
bool NetDb::PopulateRouterInfoBuffer (std::shared_ptr<RouterInfo> r)
|
||||
{
|
||||
if (!r || r->GetBuffer ()) return;
|
||||
r->LoadBuffer (m_Storage.Path (r->GetIdentHashBase64 ()));
|
||||
}
|
||||
if (!r) return false;
|
||||
if (r->GetBuffer ()) return true;
|
||||
return r->LoadBuffer (m_Storage.Path (r->GetIdentHashBase64 ()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2023, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -38,16 +38,18 @@ 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;
|
||||
const int NETDB_MIN_HIGHBANDWIDTH_VERSION = MAKE_VERSION_NUMBER(0, 9, 36); // 0.9.36
|
||||
const int NETDB_MIN_FLOODFILL_VERSION = MAKE_VERSION_NUMBER(0, 9, 38); // 0.9.38
|
||||
const int NETDB_MIN_HIGHBANDWIDTH_VERSION = MAKE_VERSION_NUMBER(0, 9, 51); // 0.9.51
|
||||
const int NETDB_MIN_FLOODFILL_VERSION = MAKE_VERSION_NUMBER(0, 9, 51); // 0.9.51
|
||||
const int NETDB_MIN_SHORT_TUNNEL_BUILD_VERSION = MAKE_VERSION_NUMBER(0, 9, 51); // 0.9.51
|
||||
|
||||
/** function for visiting a leaseset stored in a floodfill */
|
||||
@@ -89,10 +91,7 @@ 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> GetRandomSSUV6Router () const; // TODO: change to v6 peer test later
|
||||
std::shared_ptr<const RouterInfo> GetRandomIntroducer (bool v4, const std::set<IdentHash>& excluded) const;
|
||||
std::shared_ptr<const RouterInfo> GetRandomSSU2Introducer (bool v4, const std::set<IdentHash>& excluded) const;
|
||||
std::shared_ptr<const RouterInfo> GetClosestFloodfill (const IdentHash& destination, const std::set<IdentHash>& excluded, bool closeThanUsOnly = false) const;
|
||||
std::vector<IdentHash> GetClosestFloodfills (const IdentHash& destination, size_t num,
|
||||
@@ -125,8 +124,16 @@ namespace data
|
||||
|
||||
void ClearRouterInfos () { m_RouterInfos.clear (); };
|
||||
std::shared_ptr<RouterInfo::Buffer> NewRouterInfoBuffer () { return m_RouterInfoBuffersPool.AcquireSharedMt (); };
|
||||
void PopulateRouterInfoBuffer (std::shared_ptr<RouterInfo> r);
|
||||
std::shared_ptr<Lease> NewLease (const Lease& lease) { return m_LeasesPool.AcquireSharedMt (lease); };
|
||||
bool 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; };
|
||||
|
||||
@@ -183,6 +190,8 @@ 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;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2020, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2023, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -7,34 +7,42 @@
|
||||
*/
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <unordered_map>
|
||||
#include <list>
|
||||
#include <thread>
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
#include <boost/property_tree/ini_parser.hpp>
|
||||
#include "Base.h"
|
||||
#include "FS.h"
|
||||
#include "Log.h"
|
||||
#include "Timestamp.h"
|
||||
#include "Profiling.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace data
|
||||
{
|
||||
i2p::fs::HashedStorage m_ProfilesStorage("peerProfiles", "p", "profile-", "txt");
|
||||
static i2p::fs::HashedStorage g_ProfilesStorage("peerProfiles", "p", "profile-", "txt");
|
||||
static std::unordered_map<i2p::data::IdentHash, std::shared_ptr<RouterProfile> > g_Profiles;
|
||||
static std::mutex g_ProfilesMutex;
|
||||
|
||||
static boost::posix_time::ptime GetTime ()
|
||||
{
|
||||
return boost::posix_time::second_clock::local_time();
|
||||
}
|
||||
|
||||
RouterProfile::RouterProfile ():
|
||||
m_LastUpdateTime (boost::posix_time::second_clock::local_time()),
|
||||
m_LastUpdateTime (GetTime ()), m_IsUpdated (false),
|
||||
m_LastDeclineTime (0), m_LastUnreachableTime (0),
|
||||
m_NumTunnelsAgreed (0), m_NumTunnelsDeclined (0), m_NumTunnelsNonReplied (0),
|
||||
m_NumTimesTaken (0), m_NumTimesRejected (0)
|
||||
{
|
||||
}
|
||||
|
||||
boost::posix_time::ptime RouterProfile::GetTime () const
|
||||
{
|
||||
return boost::posix_time::second_clock::local_time();
|
||||
}
|
||||
|
||||
void RouterProfile::UpdateTime ()
|
||||
{
|
||||
m_LastUpdateTime = GetTime ();
|
||||
m_IsUpdated = true;
|
||||
}
|
||||
|
||||
void RouterProfile::Save (const IdentHash& identHash)
|
||||
@@ -50,12 +58,14 @@ namespace data
|
||||
// fill property tree
|
||||
boost::property_tree::ptree pt;
|
||||
pt.put (PEER_PROFILE_LAST_UPDATE_TIME, boost::posix_time::to_simple_string (m_LastUpdateTime));
|
||||
if (m_LastUnreachableTime)
|
||||
pt.put (PEER_PROFILE_LAST_UNREACHABLE_TIME, m_LastUnreachableTime);
|
||||
pt.put_child (PEER_PROFILE_SECTION_PARTICIPATION, participation);
|
||||
pt.put_child (PEER_PROFILE_SECTION_USAGE, usage);
|
||||
|
||||
// save to file
|
||||
std::string ident = identHash.ToBase64 ();
|
||||
std::string path = m_ProfilesStorage.Path(ident);
|
||||
std::string path = g_ProfilesStorage.Path(ident);
|
||||
|
||||
try {
|
||||
boost::property_tree::write_ini (path, pt);
|
||||
@@ -68,7 +78,7 @@ namespace data
|
||||
void RouterProfile::Load (const IdentHash& identHash)
|
||||
{
|
||||
std::string ident = identHash.ToBase64 ();
|
||||
std::string path = m_ProfilesStorage.Path(ident);
|
||||
std::string path = g_ProfilesStorage.Path(ident);
|
||||
boost::property_tree::ptree pt;
|
||||
|
||||
if (!i2p::fs::Exists(path))
|
||||
@@ -94,6 +104,7 @@ namespace data
|
||||
m_LastUpdateTime = boost::posix_time::time_from_string (t);
|
||||
if ((GetTime () - m_LastUpdateTime).hours () < PEER_PROFILE_EXPIRATION_TIMEOUT)
|
||||
{
|
||||
m_LastUnreachableTime = pt.get (PEER_PROFILE_LAST_UNREACHABLE_TIME, 0);
|
||||
try
|
||||
{
|
||||
// read participations
|
||||
@@ -131,15 +142,29 @@ 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 ();
|
||||
}
|
||||
|
||||
void RouterProfile::Unreachable ()
|
||||
{
|
||||
m_LastUnreachableTime = i2p::util::GetSecondsSinceEpoch ();
|
||||
UpdateTime ();
|
||||
}
|
||||
|
||||
bool RouterProfile::IsLowPartcipationRate () const
|
||||
@@ -153,8 +178,19 @@ 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 ||
|
||||
ts + PEER_PROFILE_DECLINED_RECENTLY_INTERVAL < m_LastDeclineTime)
|
||||
m_LastDeclineTime = 0;
|
||||
return (bool)m_LastDeclineTime;
|
||||
}
|
||||
|
||||
bool RouterProfile::IsBad ()
|
||||
{
|
||||
if (IsDeclinedRecently () || IsUnreachable ()) return true;
|
||||
auto isBad = IsAlwaysDeclining () || IsLowPartcipationRate () /*|| IsLowReplyRate ()*/;
|
||||
if (isBad && m_NumTimesRejected > 10*(m_NumTimesTaken + 1))
|
||||
{
|
||||
@@ -168,32 +204,98 @@ namespace data
|
||||
return isBad;
|
||||
}
|
||||
|
||||
bool RouterProfile::IsUnreachable ()
|
||||
{
|
||||
if (!m_LastUnreachableTime) return false;
|
||||
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
if (ts > m_LastUnreachableTime + PEER_PROFILE_UNREACHABLE_INTERVAL ||
|
||||
ts + PEER_PROFILE_UNREACHABLE_INTERVAL < m_LastUnreachableTime)
|
||||
m_LastUnreachableTime = 0;
|
||||
return (bool)m_LastUnreachableTime;
|
||||
}
|
||||
|
||||
std::shared_ptr<RouterProfile> GetRouterProfile (const IdentHash& identHash)
|
||||
{
|
||||
{
|
||||
std::unique_lock<std::mutex> l(g_ProfilesMutex);
|
||||
auto it = g_Profiles.find (identHash);
|
||||
if (it != g_Profiles.end ())
|
||||
return it->second;
|
||||
}
|
||||
auto profile = std::make_shared<RouterProfile> ();
|
||||
profile->Load (identHash); // if possible
|
||||
std::unique_lock<std::mutex> l(g_ProfilesMutex);
|
||||
g_Profiles.emplace (identHash, profile);
|
||||
return profile;
|
||||
}
|
||||
|
||||
void InitProfilesStorage ()
|
||||
{
|
||||
m_ProfilesStorage.SetPlace(i2p::fs::GetDataDir());
|
||||
m_ProfilesStorage.Init(i2p::data::GetBase64SubstitutionTable(), 64);
|
||||
g_ProfilesStorage.SetPlace(i2p::fs::GetDataDir());
|
||||
g_ProfilesStorage.Init(i2p::data::GetBase64SubstitutionTable(), 64);
|
||||
}
|
||||
|
||||
void PersistProfiles ()
|
||||
{
|
||||
auto ts = GetTime ();
|
||||
std::list<std::pair<i2p::data::IdentHash, std::shared_ptr<RouterProfile> > > tmp;
|
||||
{
|
||||
std::unique_lock<std::mutex> l(g_ProfilesMutex);
|
||||
for (auto it = g_Profiles.begin (); it != g_Profiles.end ();)
|
||||
{
|
||||
if ((ts - it->second->GetLastUpdateTime ()).total_seconds () > PEER_PROFILE_PERSIST_INTERVAL)
|
||||
{
|
||||
if (it->second->IsUpdated ())
|
||||
tmp.push_back (std::make_pair (it->first, it->second));
|
||||
it = g_Profiles.erase (it);
|
||||
}
|
||||
else
|
||||
it++;
|
||||
}
|
||||
}
|
||||
for (auto& it: tmp)
|
||||
if (it.second) it.second->Save (it.first);
|
||||
}
|
||||
|
||||
void SaveProfiles ()
|
||||
{
|
||||
std::unordered_map<i2p::data::IdentHash, std::shared_ptr<RouterProfile> > tmp;
|
||||
{
|
||||
std::unique_lock<std::mutex> l(g_ProfilesMutex);
|
||||
tmp = g_Profiles;
|
||||
g_Profiles.clear ();
|
||||
}
|
||||
auto ts = GetTime ();
|
||||
for (auto& it: tmp)
|
||||
if (it.second->IsUpdated () && (ts - it.second->GetLastUpdateTime ()).total_seconds () < PEER_PROFILE_EXPIRATION_TIMEOUT*3600)
|
||||
it.second->Save (it.first);
|
||||
}
|
||||
|
||||
void DeleteObsoleteProfiles ()
|
||||
{
|
||||
{
|
||||
auto ts = GetTime ();
|
||||
std::unique_lock<std::mutex> l(g_ProfilesMutex);
|
||||
for (auto it = g_Profiles.begin (); it != g_Profiles.end ();)
|
||||
{
|
||||
if ((ts - it->second->GetLastUpdateTime ()).total_seconds () >= PEER_PROFILE_EXPIRATION_TIMEOUT*3600)
|
||||
it = g_Profiles.erase (it);
|
||||
else
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
struct stat st;
|
||||
std::time_t now = std::time(nullptr);
|
||||
|
||||
std::vector<std::string> files;
|
||||
m_ProfilesStorage.Traverse(files);
|
||||
g_ProfilesStorage.Traverse(files);
|
||||
for (const auto& path: files) {
|
||||
if (stat(path.c_str(), &st) != 0) {
|
||||
LogPrint(eLogWarning, "Profiling: Can't stat(): ", path);
|
||||
continue;
|
||||
}
|
||||
if (((now - st.st_mtime) / 3600) >= PEER_PROFILE_EXPIRATION_TIMEOUT) {
|
||||
if (now - st.st_mtime >= PEER_PROFILE_EXPIRATION_TIMEOUT*3600) {
|
||||
LogPrint(eLogDebug, "Profiling: Removing expired peer profile: ", path);
|
||||
i2p::fs::Remove(path);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2023, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -22,15 +22,19 @@ namespace data
|
||||
const char PEER_PROFILE_SECTION_USAGE[] = "usage";
|
||||
// params
|
||||
const char PEER_PROFILE_LAST_UPDATE_TIME[] = "lastupdatetime";
|
||||
const char PEER_PROFILE_LAST_UNREACHABLE_TIME[] = "lastunreachabletime";
|
||||
const char PEER_PROFILE_PARTICIPATION_AGREED[] = "agreed";
|
||||
const char PEER_PROFILE_PARTICIPATION_DECLINED[] = "declined";
|
||||
const char PEER_PROFILE_PARTICIPATION_NON_REPLIED[] = "nonreplied";
|
||||
const char PEER_PROFILE_USAGE_TAKEN[] = "taken";
|
||||
const char PEER_PROFILE_USAGE_REJECTED[] = "rejected";
|
||||
|
||||
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_EXPIRATION_TIMEOUT = 36; // in hours (1.5 days)
|
||||
const int PEER_PROFILE_AUTOCLEAN_TIMEOUT = 6 * 3600; // in seconds (6 hours)
|
||||
const int PEER_PROFILE_AUTOCLEAN_VARIANCE = 3600; // in seconds (1 hour)
|
||||
const int PEER_PROFILE_DECLINED_RECENTLY_INTERVAL = 150; // in seconds (2.5 minutes)
|
||||
const int PEER_PROFILE_PERSIST_INTERVAL = 3300; // in seconds (55 minutes)
|
||||
const int PEER_PROFILE_UNREACHABLE_INTERVAL = 2*3600; // on seconds (2 hours)
|
||||
|
||||
class RouterProfile
|
||||
{
|
||||
@@ -43,22 +47,30 @@ namespace data
|
||||
void Load (const IdentHash& identHash);
|
||||
|
||||
bool IsBad ();
|
||||
bool IsUnreachable ();
|
||||
|
||||
void TunnelBuildResponse (uint8_t ret);
|
||||
void TunnelNonReplied ();
|
||||
|
||||
void Unreachable ();
|
||||
|
||||
boost::posix_time::ptime GetLastUpdateTime () const { return m_LastUpdateTime; };
|
||||
bool IsUpdated () const { return m_IsUpdated; };
|
||||
|
||||
private:
|
||||
|
||||
boost::posix_time::ptime GetTime () const;
|
||||
void UpdateTime ();
|
||||
|
||||
bool IsAlwaysDeclining () const { return !m_NumTunnelsAgreed && m_NumTunnelsDeclined >= 5; };
|
||||
bool IsLowPartcipationRate () const;
|
||||
bool IsLowReplyRate () const;
|
||||
bool IsDeclinedRecently ();
|
||||
|
||||
private:
|
||||
|
||||
boost::posix_time::ptime m_LastUpdateTime;
|
||||
boost::posix_time::ptime m_LastUpdateTime; // TODO: use std::chrono
|
||||
bool m_IsUpdated;
|
||||
uint64_t m_LastDeclineTime, m_LastUnreachableTime; // in seconds
|
||||
// participation
|
||||
uint32_t m_NumTunnelsAgreed;
|
||||
uint32_t m_NumTunnelsDeclined;
|
||||
@@ -71,6 +83,8 @@ namespace data
|
||||
std::shared_ptr<RouterProfile> GetRouterProfile (const IdentHash& identHash);
|
||||
void InitProfilesStorage ();
|
||||
void DeleteObsoleteProfiles ();
|
||||
void SaveProfiles ();
|
||||
void PersistProfiles ();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2023, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -37,10 +37,9 @@ namespace garlic
|
||||
eRouterStatusOK = 0,
|
||||
eRouterStatusTesting = 1,
|
||||
eRouterStatusFirewalled = 2,
|
||||
eRouterStatusError = 3,
|
||||
eRouterStatusUnknown = 4,
|
||||
eRouterStatusProxy = 5,
|
||||
eRouterStatusMesh = 6
|
||||
eRouterStatusUnknown = 3,
|
||||
eRouterStatusProxy = 4,
|
||||
eRouterStatusMesh = 5
|
||||
};
|
||||
|
||||
enum RouterError
|
||||
@@ -48,7 +47,9 @@ namespace garlic
|
||||
eRouterErrorNone = 0,
|
||||
eRouterErrorClockSkew = 1,
|
||||
eRouterErrorOffline = 2,
|
||||
eRouterErrorSymmetricNAT = 3
|
||||
eRouterErrorSymmetricNAT = 3,
|
||||
eRouterErrorFullConeNAT = 4,
|
||||
eRouterErrorNoDescriptors = 5
|
||||
};
|
||||
|
||||
class RouterContext: public i2p::garlic::GarlicDestination
|
||||
@@ -103,35 +104,26 @@ namespace garlic
|
||||
uint64_t GetTransitBandwidthLimit () const { return (m_BandwidthLimit*m_ShareRatio)/100LL; };
|
||||
RouterStatus GetStatus () const { return m_Status; };
|
||||
void SetStatus (RouterStatus status);
|
||||
void SetStatusSSU2 (RouterStatus status);
|
||||
RouterError GetError () const { return m_Error; };
|
||||
void SetError (RouterError error) { m_Status = eRouterStatusError; m_Error = error; };
|
||||
void SetError (RouterError error) { m_Error = error; };
|
||||
RouterStatus GetStatusV6 () const { return m_StatusV6; };
|
||||
void SetStatusV6 (RouterStatus status);
|
||||
void SetStatusV6SSU2 (RouterStatus status);
|
||||
RouterError GetErrorV6 () const { return m_ErrorV6; };
|
||||
void SetErrorV6 (RouterError error) { m_StatusV6 = eRouterStatusError; m_ErrorV6 = error; };
|
||||
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);
|
||||
bool DecryptTunnelShortRequestRecord (const uint8_t * encrypted, uint8_t * data);
|
||||
|
||||
void UpdatePort (int port); // called from Daemon
|
||||
void UpdateAddress (const boost::asio::ip::address& host); // called from SSU or Daemon
|
||||
void UpdateAddress (const boost::asio::ip::address& host); // called from SSU2 or Daemon
|
||||
void PublishNTCP2Address (int port, bool publish, bool v4, bool v6, bool ygg);
|
||||
void UpdateNTCP2Address (bool enable);
|
||||
void PublishSSU2Address (int port, bool publish, bool v4, bool v6);
|
||||
void UpdateSSU2Address (bool enable);
|
||||
void RemoveNTCPAddress (bool v4only = true); // delete NTCP address for older routers. TODO: remove later
|
||||
void RemoveSSUAddress (); // delete SSU address for older routers
|
||||
bool AddIntroducer (const i2p::data::RouterInfo::Introducer& introducer);
|
||||
void RemoveIntroducer (const boost::asio::ip::udp::endpoint& e);
|
||||
bool AddSSU2Introducer (const i2p::data::RouterInfo::Introducer& introducer, bool v4);
|
||||
void RemoveSSU2Introducer (const i2p::data::IdentHash& h, bool v4);
|
||||
void ClearSSU2Introducers (bool v4);
|
||||
bool IsUnreachable () const;
|
||||
void SetUnreachable (bool v4, bool v6);
|
||||
void SetUnreachableSSU2 (bool v4, bool v6);
|
||||
void SetReachable (bool v4, bool v6);
|
||||
bool IsFloodfill () const { return m_IsFloodfill; };
|
||||
void SetFloodfill (bool floodfill);
|
||||
@@ -183,10 +175,12 @@ namespace garlic
|
||||
void UpdateRouterInfo ();
|
||||
void NewNTCP2Keys ();
|
||||
void NewSSU2Keys ();
|
||||
bool IsSSU2Only () const; // SSU2 and no SSU
|
||||
void UpdateNTCP2Keys ();
|
||||
void UpdateSSU2Keys ();
|
||||
bool Load ();
|
||||
void SaveKeys ();
|
||||
uint16_t SelectRandomPort () const;
|
||||
void PublishNTCP2Address (std::shared_ptr<i2p::data::RouterInfo::Address> address, int port, bool publish) const;
|
||||
|
||||
bool DecryptECIESTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data, size_t clearTextSize);
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2023, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -47,14 +47,12 @@ namespace data
|
||||
|
||||
const char CAPS_FLAG_V4 = '4';
|
||||
const char CAPS_FLAG_V6 = '6';
|
||||
const char CAPS_FLAG_SSU_TESTING = 'B';
|
||||
const char CAPS_FLAG_SSU_INTRODUCER = 'C';
|
||||
const char CAPS_FLAG_SSU2_TESTING = 'B';
|
||||
const char CAPS_FLAG_SSU2_INTRODUCER = 'C';
|
||||
|
||||
const uint8_t COST_NTCP2_PUBLISHED = 3;
|
||||
const uint8_t COST_NTCP2_NON_PUBLISHED = 14;
|
||||
const uint8_t COST_SSU2_DIRECT = 8;
|
||||
const uint8_t COST_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
|
||||
@@ -62,15 +60,25 @@ namespace data
|
||||
{
|
||||
public:
|
||||
|
||||
enum SupportedTransportsIdx
|
||||
{
|
||||
eNTCP2V4Idx = 0,
|
||||
eNTCP2V6Idx,
|
||||
eSSU2V4Idx,
|
||||
eSSU2V6Idx,
|
||||
eNTCP2V6MeshIdx,
|
||||
eNumTransports
|
||||
};
|
||||
|
||||
#define TransportBit(tr) e##tr = (1 << e##tr##Idx)
|
||||
|
||||
enum SupportedTransports
|
||||
{
|
||||
eNTCP2V4 = 0x01,
|
||||
eNTCP2V6 = 0x02,
|
||||
eSSUV4 = 0x04,
|
||||
eSSUV6 = 0x08,
|
||||
eNTCP2V6Mesh = 0x10,
|
||||
eSSU2V4 = 0x20,
|
||||
eSSU2V6 = 0x40,
|
||||
TransportBit(NTCP2V4), // 0x01
|
||||
TransportBit(NTCP2V6), // 0x02
|
||||
TransportBit(SSU2V4), // 0x04
|
||||
TransportBit(SSU2V6), // 0x08
|
||||
TransportBit(NTCP2V6Mesh), // 0x10
|
||||
eAllTransports = 0xFF
|
||||
};
|
||||
typedef uint8_t CompatibleTransports;
|
||||
@@ -96,20 +104,17 @@ namespace data
|
||||
enum TransportStyle
|
||||
{
|
||||
eTransportUnknown = 0,
|
||||
eTransportNTCP,
|
||||
eTransportSSU,
|
||||
eTransportNTCP2,
|
||||
eTransportSSU2
|
||||
};
|
||||
|
||||
typedef Tag<32> IntroKey; // should be castable to MacKey and AESKey
|
||||
struct Introducer
|
||||
{
|
||||
Introducer (): iPort (0), iExp (0) {};
|
||||
boost::asio::ip::address iHost;
|
||||
int iPort;
|
||||
IntroKey iKey; // or ih for SSU2
|
||||
Introducer (): iTag (0), iExp (0), isH (false) {};
|
||||
IdentHash iH;
|
||||
uint32_t iTag;
|
||||
uint32_t iExp;
|
||||
bool isH; // TODO: remove later
|
||||
};
|
||||
|
||||
struct SSUExt
|
||||
@@ -146,7 +151,7 @@ namespace data
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
bool IsNTCP2 () const { return transportStyle == eTransportNTCP; };
|
||||
bool IsNTCP2 () const { return transportStyle == eTransportNTCP2; };
|
||||
bool IsSSU2 () const { return transportStyle == eTransportSSU2; };
|
||||
bool IsPublishedNTCP2 () const { return IsNTCP2 () && published; };
|
||||
bool IsReachableSSU () const { return (bool)ssu && (published || UsesIntroducer ()); };
|
||||
@@ -167,7 +172,7 @@ namespace data
|
||||
Buffer (const uint8_t * buf, size_t len);
|
||||
};
|
||||
|
||||
typedef std::vector<std::shared_ptr<Address> > Addresses;
|
||||
typedef std::array<std::shared_ptr<Address>, eNumTransports> Addresses;
|
||||
|
||||
RouterInfo (const std::string& fullPath);
|
||||
RouterInfo (const RouterInfo& ) = default;
|
||||
@@ -183,39 +188,35 @@ namespace data
|
||||
int GetVersion () const { return m_Version; };
|
||||
virtual void SetProperty (const std::string& key, const std::string& value) {};
|
||||
virtual void ClearProperties () {};
|
||||
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;
|
||||
boost::shared_ptr<Addresses> GetAddresses () const; // should be called for local RI only, otherwise must return shared_ptr
|
||||
std::shared_ptr<const Address> GetNTCP2V4Address () const;
|
||||
std::shared_ptr<const Address> GetNTCP2V6Address () 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,int port, uint8_t caps); // non published
|
||||
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
|
||||
const boost::asio::ip::address& host, int port); // published
|
||||
void RemoveNTCP2Address (bool v4);
|
||||
void AddSSU2Address (const uint8_t * staticKey, const uint8_t * introKey, int port, uint8_t caps); // non published
|
||||
void AddSSU2Address (const uint8_t * staticKey, const uint8_t * introKey,
|
||||
const boost::asio::ip::address& host, int port); // published
|
||||
bool AddIntroducer (const Introducer& introducer);
|
||||
bool RemoveIntroducer (const boost::asio::ip::udp::endpoint& e);
|
||||
void RemoveSSU2Address (bool v4);
|
||||
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 & (eSSUV6 | eNTCP2V6 | eSSU2V6); };
|
||||
bool IsV4 () const { return m_SupportedTransports & (eSSUV4 | eNTCP2V4 | eSSU2V4); };
|
||||
bool IsV6 () const { return m_SupportedTransports & (eNTCP2V6 | eSSU2V6); };
|
||||
bool IsV4 () const { return m_SupportedTransports & (eNTCP2V4 | eSSU2V4); };
|
||||
bool IsMesh () const { return m_SupportedTransports & eNTCP2V6Mesh; };
|
||||
void EnableV6 ();
|
||||
void DisableV6 ();
|
||||
@@ -232,9 +233,7 @@ 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 IsIntroducer (bool v4) const;
|
||||
bool IsSSU2Introducer (bool v4) const;
|
||||
|
||||
uint8_t GetCaps () const { return m_Caps; };
|
||||
@@ -243,7 +242,7 @@ namespace data
|
||||
void SetUnreachable (bool unreachable) { m_IsUnreachable = unreachable; };
|
||||
bool IsUnreachable () const { return m_IsUnreachable; };
|
||||
|
||||
const uint8_t * GetBuffer () const { return m_Buffer->data (); };
|
||||
const uint8_t * GetBuffer () const { return m_Buffer ? m_Buffer->data () : nullptr; };
|
||||
const uint8_t * LoadBuffer (const std::string& fullPath); // load if necessary
|
||||
size_t GetBufferLen () const { return m_BufferLen; };
|
||||
|
||||
@@ -252,9 +251,9 @@ namespace data
|
||||
bool SaveToFile (const std::string& fullPath);
|
||||
|
||||
std::shared_ptr<RouterProfile> GetProfile () const;
|
||||
void SaveProfile () { if (m_Profile) m_Profile->Save (GetIdentHash ()); };
|
||||
void DropProfile () { m_Profile = nullptr; };
|
||||
|
||||
void Update (const uint8_t * buf, size_t len);
|
||||
bool Update (const uint8_t * buf, size_t len);
|
||||
void DeleteBuffer () { m_Buffer = nullptr; };
|
||||
bool IsNewer (const uint8_t * buf, size_t len) const;
|
||||
|
||||
@@ -274,9 +273,8 @@ namespace data
|
||||
void UpdateBuffer (const uint8_t * buf, size_t len);
|
||||
void SetBufferLen (size_t len) { m_BufferLen = len; };
|
||||
void RefreshTimestamp ();
|
||||
const Addresses& GetAddresses () const { return *m_Addresses; };
|
||||
CompatibleTransports GetReachableTransports () const { return m_ReachableTransports; };
|
||||
void SetReachableTransports (CompatibleTransports transports) { m_ReachableTransports = transports; };
|
||||
void SetReachableTransports (CompatibleTransports transports) { m_ReachableTransports = transports; };
|
||||
|
||||
private:
|
||||
|
||||
@@ -290,6 +288,8 @@ 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:
|
||||
|
||||
@@ -311,6 +311,7 @@ namespace data
|
||||
public:
|
||||
|
||||
LocalRouterInfo () = default;
|
||||
LocalRouterInfo (const std::string& fullPath): RouterInfo (fullPath) {};
|
||||
void CreateBuffer (const PrivateKeys& privateKeys);
|
||||
void UpdateCaps (uint8_t caps);
|
||||
|
||||
@@ -321,13 +322,15 @@ namespace data
|
||||
|
||||
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
996
libi2pd/SSU.cpp
@@ -1,996 +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 <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
159
libi2pd/SSU.h
@@ -1,159 +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 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
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user