Merge pull request #1662 from PurpleI2P/openssl

recent changes
This commit is contained in:
orignal 2021-06-16 18:12:21 -04:00 committed by GitHub
commit 88145eaf94
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
58 changed files with 2533 additions and 548 deletions

View File

@ -10,7 +10,7 @@ jobs:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Test in FreeBSD - name: Test in FreeBSD
id: test id: test
uses: vmactions/freebsd-vm@v0.1.2 uses: vmactions/freebsd-vm@v0.1.4
with: with:
usesh: true usesh: true
prepare: pkg install -y devel/cmake devel/gmake devel/boost-libs security/openssl net/miniupnpc prepare: pkg install -y devel/cmake devel/gmake devel/boost-libs security/openssl net/miniupnpc

View File

@ -3,9 +3,9 @@ name: Build on Ubuntu
on: [push, pull_request] on: [push, pull_request]
jobs: jobs:
build: build-make:
name: With USE_UPNP=${{ matrix.with_upnp }} name: Make with USE_UPNP=${{ matrix.with_upnp }}
runs-on: ubuntu-16.04 runs-on: ubuntu-18.04
strategy: strategy:
fail-fast: true fail-fast: true
matrix: matrix:
@ -19,3 +19,70 @@ jobs:
sudo apt-get install build-essential libboost1.74-dev libminiupnpc-dev libssl-dev zlib1g-dev sudo apt-get install build-essential libboost1.74-dev libminiupnpc-dev libssl-dev zlib1g-dev
- name: build application - name: build application
run: make USE_UPNP=${{ matrix.with_upnp }} -j3 run: make USE_UPNP=${{ matrix.with_upnp }} -j3
build-cmake:
name: CMake with -DWITH_UPNP=${{ matrix.with_upnp }}
runs-on: ubuntu-18.04
strategy:
fail-fast: true
matrix:
with_upnp: ['ON', 'OFF']
steps:
- uses: actions/checkout@v2
- name: install packages
run: |
sudo add-apt-repository ppa:mhier/libboost-latest
sudo apt-get update
sudo apt-get install build-essential cmake libboost1.74-dev libminiupnpc-dev libssl-dev zlib1g-dev
- name: build application
run: |
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" -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" -M --distribution buster "trunk build"
- uses: singingwolfboy/build-dpkg-buster@v1
id: build
with:
args: --unsigned-source --unsigned-changes -b
- uses: actions/upload-artifact@v1
with:
name: ${{ steps.build.outputs.filename }}
path: ${{ steps.build.outputs.filename }}
- uses: actions/upload-artifact@v1
with:
name: ${{ steps.build.outputs.filename-dbgsym }}
path: ${{ steps.build.outputs.filename-dbgsym }}

View File

@ -7,6 +7,7 @@ I2PD := i2pd
LIB_SRC_DIR := libi2pd LIB_SRC_DIR := libi2pd
LIB_CLIENT_SRC_DIR := libi2pd_client LIB_CLIENT_SRC_DIR := libi2pd_client
LANG_SRC_DIR := i18n
DAEMON_SRC_DIR := daemon DAEMON_SRC_DIR := daemon
# import source files lists # import source files lists
@ -49,12 +50,13 @@ ifeq ($(USE_MESHNET),yes)
NEEDED_CXXFLAGS += -DMESHNET NEEDED_CXXFLAGS += -DMESHNET
endif endif
NEEDED_CXXFLAGS += -MMD -MP -I$(LIB_SRC_DIR) -I$(LIB_CLIENT_SRC_DIR) NEEDED_CXXFLAGS += -MMD -MP -I$(LIB_SRC_DIR) -I$(LIB_CLIENT_SRC_DIR) -I$(LANG_SRC_DIR)
LIB_OBJS += $(patsubst %.cpp,obj/%.o,$(LIB_SRC)) LIB_OBJS += $(patsubst %.cpp,obj/%.o,$(LIB_SRC))
LIB_CLIENT_OBJS += $(patsubst %.cpp,obj/%.o,$(LIB_CLIENT_SRC)) LIB_CLIENT_OBJS += $(patsubst %.cpp,obj/%.o,$(LIB_CLIENT_SRC))
LANG_OBJS += $(patsubst %.cpp,obj/%.o,$(LANG_SRC))
DAEMON_OBJS += $(patsubst %.cpp,obj/%.o,$(DAEMON_SRC)) DAEMON_OBJS += $(patsubst %.cpp,obj/%.o,$(DAEMON_SRC))
DEPS += $(LIB_OBJS:.o=.d) $(LIB_CLIENT_OBJS:.o=.d) $(DAEMON_OBJS:.o=.d) DEPS += $(LIB_OBJS:.o=.d) $(LIB_CLIENT_OBJS:.o=.d) $(LANG_OBJS:.o=.d) $(DAEMON_OBJS:.o=.d)
all: mk_obj_dir $(ARLIB) $(ARLIB_CLIENT) $(I2PD) all: mk_obj_dir $(ARLIB) $(ARLIB_CLIENT) $(I2PD)
@ -63,6 +65,7 @@ mk_obj_dir:
@mkdir -p obj/Win32 @mkdir -p obj/Win32
@mkdir -p obj/$(LIB_SRC_DIR) @mkdir -p obj/$(LIB_SRC_DIR)
@mkdir -p obj/$(LIB_CLIENT_SRC_DIR) @mkdir -p obj/$(LIB_CLIENT_SRC_DIR)
@mkdir -p obj/$(LANG_SRC_DIR)
@mkdir -p obj/$(DAEMON_SRC_DIR) @mkdir -p obj/$(DAEMON_SRC_DIR)
api: mk_obj_dir $(SHLIB) $(ARLIB) api: mk_obj_dir $(SHLIB) $(ARLIB)
@ -82,7 +85,7 @@ obj/%.o: %.cpp
# '-' is 'ignore if missing' on first run # '-' is 'ignore if missing' on first run
-include $(DEPS) -include $(DEPS)
$(I2PD): $(DAEMON_OBJS) $(ARLIB) $(ARLIB_CLIENT) $(I2PD): $(LANG_OBJS) $(DAEMON_OBJS) $(ARLIB) $(ARLIB_CLIENT)
$(CXX) -o $@ $(LDFLAGS) $^ $(LDLIBS) $(CXX) -o $@ $(LDFLAGS) $^ $(LDLIBS)
$(SHLIB): $(LIB_OBJS) $(SHLIB): $(LIB_OBJS)

View File

@ -68,15 +68,15 @@ Build instructions:
**Supported systems:** **Supported systems:**
* GNU/Linux - [![Build Status](https://travis-ci.org/PurpleI2P/i2pd.svg?branch=openssl)](https://travis-ci.org/PurpleI2P/i2pd) * GNU/Linux - [![Build on Ubuntu](https://github.com/PurpleI2P/i2pd/actions/workflows/build.yml/badge.svg)](https://github.com/PurpleI2P/i2pd/actions/workflows/build.yml)
* CentOS / Fedora / Mageia - [![Build Status](https://copr.fedorainfracloud.org/coprs/supervillain/i2pd/package/i2pd-git/status_image/last_build.png)](https://copr.fedorainfracloud.org/coprs/supervillain/i2pd/package/i2pd-git/) * CentOS / Fedora / Mageia - [![Build Status](https://copr.fedorainfracloud.org/coprs/supervillain/i2pd/package/i2pd-git/status_image/last_build.png)](https://copr.fedorainfracloud.org/coprs/supervillain/i2pd/package/i2pd-git/)
* Alpine, ArchLinux, openSUSE, Gentoo, Debian, Ubuntu, etc. * Alpine, ArchLinux, openSUSE, Gentoo, Debian, Ubuntu, etc.
* Windows - [![Build status](https://ci.appveyor.com/api/projects/status/1908qe4p48ff1x23?svg=true)](https://ci.appveyor.com/project/PurpleI2P/i2pd) * Windows - [![Build on Windows](https://github.com/PurpleI2P/i2pd/actions/workflows/build-windows.yml/badge.svg)](https://github.com/PurpleI2P/i2pd/actions/workflows/build-windows.yml)
* Mac OS X - [![Build Status](https://travis-ci.org/PurpleI2P/i2pd.svg?branch=openssl)](https://travis-ci.org/PurpleI2P/i2pd) * Mac OS X - [![Build on OSX](https://github.com/PurpleI2P/i2pd/actions/workflows/build-osx.yml/badge.svg)](https://github.com/PurpleI2P/i2pd/actions/workflows/build-osx.yml)
* Docker image - [![Build Status](https://img.shields.io/docker/cloud/build/purplei2p/i2pd)](https://hub.docker.com/r/purplei2p/i2pd/builds/) * Docker image - [![Build Status](https://img.shields.io/docker/cloud/build/purplei2p/i2pd)](https://hub.docker.com/r/purplei2p/i2pd/builds/)
* Snap * Snap - [![i2pd](https://snapcraft.io/i2pd/badge.svg)](https://snapcraft.io/i2pd) [![i2pd](https://snapcraft.io/i2pd/trending.svg?name=0)](https://snapcraft.io/i2pd)
* FreeBSD * FreeBSD - [![Build on FreeBSD](https://github.com/PurpleI2P/i2pd/actions/workflows/build-freebsd.yml/badge.svg)](https://github.com/PurpleI2P/i2pd/actions/workflows/build-freebsd.yml)
* Android * Android - [![Android CI](https://github.com/PurpleI2P/i2pd-android/actions/workflows/android.yml/badge.svg)](https://github.com/PurpleI2P/i2pd-android/actions/workflows/android.yml)
* iOS * iOS
Using i2pd Using i2pd
@ -94,6 +94,7 @@ ETH: 0x9e5bac70d20d1079ceaa111127f4fb3bccce379d
DASH: Xw8YUrQpYzP9tZBmbjqxS3M97Q7v3vJKUF DASH: Xw8YUrQpYzP9tZBmbjqxS3M97Q7v3vJKUF
ZEC: t1cTckLuXsr1dwVrK4NDzfhehss4NvMadAJ ZEC: t1cTckLuXsr1dwVrK4NDzfhehss4NvMadAJ
GST: GbD2JSQHBHCKLa9WTHmigJRpyFgmBj4woG GST: GbD2JSQHBHCKLa9WTHmigJRpyFgmBj4woG
XMR: 497pJc7X4xqKvcLBLpSUtRgWqMMyo24u4btCos3cak6gbMkpobgSU6492ztUcUBghyeHpYeczB55s38NpuHoH5WGNSPDRMH
License License
------- -------

View File

@ -131,7 +131,7 @@ namespace win32
transfer >>= 10; transfer >>= 10;
auto mbytes = transfer & 0x03ff; auto mbytes = transfer & 0x03ff;
transfer >>= 10; transfer >>= 10;
auto gbytes = transfer & 0x03ff; auto gbytes = transfer;
if (gbytes) if (gbytes)
s << gbytes << " GB, "; s << gbytes << " GB, ";

View File

@ -33,61 +33,15 @@ target_architecture(ARCHITECTURE)
set(LIBI2PD_SRC_DIR ../libi2pd) set(LIBI2PD_SRC_DIR ../libi2pd)
set(LIBI2PD_CLIENT_SRC_DIR ../libi2pd_client) set(LIBI2PD_CLIENT_SRC_DIR ../libi2pd_client)
set(LANG_SRC_DIR ../i18n)
set(DAEMON_SRC_DIR ../daemon) set(DAEMON_SRC_DIR ../daemon)
include_directories(${LIBI2PD_SRC_DIR}) include_directories(${LIBI2PD_SRC_DIR})
include_directories(${LIBI2PD_CLIENT_SRC_DIR}) include_directories(${LIBI2PD_CLIENT_SRC_DIR})
include_directories(${LANG_SRC_DIR})
include_directories(${DAEMON_SRC_DIR}) include_directories(${DAEMON_SRC_DIR})
set(LIBI2PD_SRC FILE(GLOB LIBI2PD_SRC ${LIBI2PD_SRC_DIR}/*.cpp)
"${LIBI2PD_SRC_DIR}/api.cpp"
"${LIBI2PD_SRC_DIR}/Base.cpp"
"${LIBI2PD_SRC_DIR}/Blinding.cpp"
"${LIBI2PD_SRC_DIR}/BloomFilter.cpp"
"${LIBI2PD_SRC_DIR}/ChaCha20.cpp"
"${LIBI2PD_SRC_DIR}/Config.cpp"
"${LIBI2PD_SRC_DIR}/CPU.cpp"
"${LIBI2PD_SRC_DIR}/Crypto.cpp"
"${LIBI2PD_SRC_DIR}/CryptoKey.cpp"
"${LIBI2PD_SRC_DIR}/Datagram.cpp"
"${LIBI2PD_SRC_DIR}/Destination.cpp"
"${LIBI2PD_SRC_DIR}/ECIESX25519AEADRatchetSession.cpp"
"${LIBI2PD_SRC_DIR}/Ed25519.cpp"
"${LIBI2PD_SRC_DIR}/Elligator.cpp"
"${LIBI2PD_SRC_DIR}/Family.cpp"
"${LIBI2PD_SRC_DIR}/FS.cpp"
"${LIBI2PD_SRC_DIR}/Garlic.cpp"
"${LIBI2PD_SRC_DIR}/Gost.cpp"
"${LIBI2PD_SRC_DIR}/Gzip.cpp"
"${LIBI2PD_SRC_DIR}/HTTP.cpp"
"${LIBI2PD_SRC_DIR}/I2NPProtocol.cpp"
"${LIBI2PD_SRC_DIR}/Identity.cpp"
"${LIBI2PD_SRC_DIR}/LeaseSet.cpp"
"${LIBI2PD_SRC_DIR}/Log.cpp"
"${LIBI2PD_SRC_DIR}/NetDb.cpp"
"${LIBI2PD_SRC_DIR}/NetDbRequests.cpp"
"${LIBI2PD_SRC_DIR}/NTCP2.cpp"
"${LIBI2PD_SRC_DIR}/Poly1305.cpp"
"${LIBI2PD_SRC_DIR}/Profiling.cpp"
"${LIBI2PD_SRC_DIR}/Reseed.cpp"
"${LIBI2PD_SRC_DIR}/RouterContext.cpp"
"${LIBI2PD_SRC_DIR}/RouterInfo.cpp"
"${LIBI2PD_SRC_DIR}/Signature.cpp"
"${LIBI2PD_SRC_DIR}/SSU.cpp"
"${LIBI2PD_SRC_DIR}/SSUData.cpp"
"${LIBI2PD_SRC_DIR}/SSUSession.cpp"
"${LIBI2PD_SRC_DIR}/Streaming.cpp"
"${LIBI2PD_SRC_DIR}/Timestamp.cpp"
"${LIBI2PD_SRC_DIR}/TransitTunnel.cpp"
"${LIBI2PD_SRC_DIR}/Transports.cpp"
"${LIBI2PD_SRC_DIR}/Tunnel.cpp"
"${LIBI2PD_SRC_DIR}/TunnelEndpoint.cpp"
"${LIBI2PD_SRC_DIR}/TunnelGateway.cpp"
"${LIBI2PD_SRC_DIR}/TunnelPool.cpp"
"${LIBI2PD_SRC_DIR}/TunnelConfig.cpp"
"${LIBI2PD_SRC_DIR}/util.cpp"
)
add_library(libi2pd ${LIBI2PD_SRC}) add_library(libi2pd ${LIBI2PD_SRC})
set_target_properties(libi2pd PROPERTIES PREFIX "") set_target_properties(libi2pd PROPERTIES PREFIX "")
@ -102,19 +56,7 @@ if(WITH_LIBRARY)
# install(EXPORT libi2pd DESTINATION ${CMAKE_INSTALL_LIBDIR}) # install(EXPORT libi2pd DESTINATION ${CMAKE_INSTALL_LIBDIR})
endif() endif()
set(CLIENT_SRC FILE(GLOB CLIENT_SRC ${LIBI2PD_CLIENT_SRC_DIR}/*.cpp)
"${LIBI2PD_CLIENT_SRC_DIR}/AddressBook.cpp"
"${LIBI2PD_CLIENT_SRC_DIR}/BOB.cpp"
"${LIBI2PD_CLIENT_SRC_DIR}/ClientContext.cpp"
"${LIBI2PD_CLIENT_SRC_DIR}/MatchedDestination.cpp"
"${LIBI2PD_CLIENT_SRC_DIR}/I2PTunnel.cpp"
"${LIBI2PD_CLIENT_SRC_DIR}/I2PService.cpp"
"${LIBI2PD_CLIENT_SRC_DIR}/SAM.cpp"
"${LIBI2PD_CLIENT_SRC_DIR}/SOCKS.cpp"
"${LIBI2PD_CLIENT_SRC_DIR}/HTTPProxy.cpp"
"${LIBI2PD_CLIENT_SRC_DIR}/I2CP.cpp"
)
add_library(libi2pdclient ${CLIENT_SRC}) add_library(libi2pdclient ${CLIENT_SRC})
set_target_properties(libi2pdclient PROPERTIES PREFIX "") set_target_properties(libi2pdclient PROPERTIES PREFIX "")
@ -126,6 +68,8 @@ if(WITH_LIBRARY)
COMPONENT Libraries) COMPONENT Libraries)
endif() endif()
FILE(GLOB LANG_SRC ${LANG_SRC_DIR}/*.cpp)
set(DAEMON_SRC set(DAEMON_SRC
"${DAEMON_SRC_DIR}/Daemon.cpp" "${DAEMON_SRC_DIR}/Daemon.cpp"
"${DAEMON_SRC_DIR}/HTTPServer.cpp" "${DAEMON_SRC_DIR}/HTTPServer.cpp"
@ -321,7 +265,7 @@ message(STATUS "---------------------------------------")
include(GNUInstallDirs) include(GNUInstallDirs)
if(WITH_BINARY) if(WITH_BINARY)
add_executable("${PROJECT_NAME}" ${DAEMON_SRC}) add_executable("${PROJECT_NAME}" ${LANG_SRC} ${DAEMON_SRC})
if(WITH_STATIC) if(WITH_STATIC)
set_target_properties("${PROJECT_NAME}" PROPERTIES LINK_FLAGS "-static") set_target_properties("${PROJECT_NAME}" PROPERTIES LINK_FLAGS "-static")

View File

@ -25,24 +25,24 @@ RUN mkdir -p "$I2PD_HOME" "$DATA_DIR" \
# 1. install deps, clone and build. # 1. install deps, clone and build.
# 2. strip binaries. # 2. strip binaries.
# 3. Purge all dependencies and other unrelated packages, including build directory. # 3. Purge all dependencies and other unrelated packages, including build directory.
RUN apk --no-cache --virtual build-dependendencies add make gcc g++ libtool zlib-dev boost-dev build-base openssl-dev openssl git \ RUN apk --no-cache --virtual build-dependendencies add make gcc g++ libtool zlib-dev boost-dev build-base openssl-dev openssl miniupnpc-dev git \
&& mkdir -p /tmp/build \ && mkdir -p /tmp/build \
&& cd /tmp/build && git clone -b ${GIT_BRANCH} ${REPO_URL} \ && cd /tmp/build && git clone -b ${GIT_BRANCH} ${REPO_URL} \
&& cd i2pd \ && cd i2pd \
&& if [ -n "${GIT_TAG}" ]; then git checkout tags/${GIT_TAG}; fi \ && if [ -n "${GIT_TAG}" ]; then git checkout tags/${GIT_TAG}; fi \
&& make \ && make USE_UPNP=yes \
&& cp -R contrib/certificates /i2pd_certificates \ && cp -R contrib/certificates /i2pd_certificates \
&& mkdir -p /usr/local/bin \ && mkdir -p /usr/local/bin \
&& mv i2pd /usr/local/bin \ && mv i2pd /usr/local/bin \
&& cd /usr/local/bin \ && cd /usr/local/bin \
&& strip i2pd \ && strip i2pd \
&& rm -fr /tmp/build && apk --no-cache --purge del build-dependendencies build-base fortify-headers boost-dev zlib-dev openssl-dev \ && rm -fr /tmp/build && apk --no-cache --purge del build-dependendencies build-base fortify-headers boost-dev zlib-dev openssl-dev \
boost-python3 python3 gdbm boost-unit_test_framework linux-headers boost-prg_exec_monitor \ miniupnpc-dev boost-python3 python3 gdbm boost-unit_test_framework linux-headers boost-prg_exec_monitor \
boost-serialization boost-wave boost-wserialization boost-math boost-graph boost-regex git pcre2 \ boost-serialization boost-wave boost-wserialization boost-math boost-graph boost-regex git pcre2 \
libtool g++ gcc libtool g++ gcc
# 2. Adding required libraries to run i2pd to ensure it will run. # 2. Adding required libraries to run i2pd to ensure it will run.
RUN apk --no-cache add boost-filesystem boost-system boost-program_options boost-date_time boost-thread boost-iostreams openssl musl-utils libstdc++ RUN apk --no-cache add boost-filesystem boost-system boost-program_options boost-date_time boost-thread boost-iostreams openssl miniupnpc musl-utils libstdc++
COPY entrypoint.sh /entrypoint.sh COPY entrypoint.sh /entrypoint.sh
RUN chmod a+x /entrypoint.sh RUN chmod a+x /entrypoint.sh

724
contrib/i18n/English.po Normal file
View File

@ -0,0 +1,724 @@
# i2pd
# Copyright (C) 2021 PurpleI2P team
# This file is distributed under the same license as the i2pd package.
# R4SAS <r4sas@i2pmail.org>, 2021.
#
msgid ""
msgstr ""
"Project-Id-Version: i2pd\n"
"Report-Msgid-Bugs-To: https://github.com/PurpleI2P/i2pd/issues\n"
"POT-Creation-Date: 2021-06-15 17:40\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 3.0\n"
"X-Poedit-SourceCharset: UTF-8\n"
"X-Poedit-Basepath: .\n"
"X-Poedit-KeywordsList: ;tr\n"
"X-Poedit-SearchPath-0: daemon/HTTPServer.cpp\n"
"X-Poedit-SearchPath-1: libi2pd_client/HTTPProxy.cpp\n"
#: daemon/HTTPServer.cpp:85
msgid "Disabled"
msgstr ""
#: daemon/HTTPServer.cpp:86
msgid "Enabled"
msgstr ""
#: daemon/HTTPServer.cpp:141
msgid "day"
msgid_plural "days"
msgstr[0] ""
msgstr[1] ""
#: daemon/HTTPServer.cpp:145
msgid "hour"
msgid_plural "hours"
msgstr[0] ""
msgstr[1] ""
#: daemon/HTTPServer.cpp:149
msgid "minute"
msgid_plural "minutes"
msgstr[0] ""
msgstr[1] ""
#: daemon/HTTPServer.cpp:152
msgid "second"
msgid_plural "seconds"
msgstr[0] ""
msgstr[1] ""
#: daemon/HTTPServer.cpp:160 daemon/HTTPServer.cpp:188
msgid "KiB"
msgstr ""
#: daemon/HTTPServer.cpp:162
msgid "MiB"
msgstr ""
#: daemon/HTTPServer.cpp:164
msgid "GiB"
msgstr ""
#: daemon/HTTPServer.cpp:181
msgid "building"
msgstr ""
#: daemon/HTTPServer.cpp:182
msgid "failed"
msgstr ""
#: daemon/HTTPServer.cpp:183
msgid "expiring"
msgstr ""
#: daemon/HTTPServer.cpp:184
msgid "established"
msgstr ""
#: daemon/HTTPServer.cpp:185
msgid "unknown"
msgstr ""
#: daemon/HTTPServer.cpp:187
msgid "exploratory"
msgstr ""
#: daemon/HTTPServer.cpp:223
msgid "<b>i2pd</b> webconsole"
msgstr ""
#: daemon/HTTPServer.cpp:226
msgid "Main page"
msgstr ""
#: daemon/HTTPServer.cpp:227 daemon/HTTPServer.cpp:683
msgid "Router commands"
msgstr ""
#: daemon/HTTPServer.cpp:228
msgid "Local destinations"
msgstr ""
#: daemon/HTTPServer.cpp:230 daemon/HTTPServer.cpp:382
#: daemon/HTTPServer.cpp:463 daemon/HTTPServer.cpp:469
#: daemon/HTTPServer.cpp:599 daemon/HTTPServer.cpp:642
#: daemon/HTTPServer.cpp:646
msgid "LeaseSets"
msgstr ""
#: daemon/HTTPServer.cpp:232 daemon/HTTPServer.cpp:652
msgid "Tunnels"
msgstr ""
#: daemon/HTTPServer.cpp:233 daemon/HTTPServer.cpp:727
#: daemon/HTTPServer.cpp:743
msgid "Transit tunnels"
msgstr ""
#: daemon/HTTPServer.cpp:234 daemon/HTTPServer.cpp:792
msgid "Transports"
msgstr ""
#: daemon/HTTPServer.cpp:235
msgid "I2P tunnels"
msgstr ""
#: daemon/HTTPServer.cpp:237 daemon/HTTPServer.cpp:854
#: daemon/HTTPServer.cpp:864
msgid "SAM sessions"
msgstr ""
#: daemon/HTTPServer.cpp:253 daemon/HTTPServer.cpp:1254
#: daemon/HTTPServer.cpp:1257 daemon/HTTPServer.cpp:1260
#: daemon/HTTPServer.cpp:1274 daemon/HTTPServer.cpp:1319
#: daemon/HTTPServer.cpp:1322 daemon/HTTPServer.cpp:1325
msgid "ERROR"
msgstr ""
#: daemon/HTTPServer.cpp:260
msgid "OK"
msgstr ""
#: daemon/HTTPServer.cpp:261
msgid "Testing"
msgstr ""
#: daemon/HTTPServer.cpp:262
msgid "Firewalled"
msgstr ""
#: daemon/HTTPServer.cpp:263 daemon/HTTPServer.cpp:284
#: daemon/HTTPServer.cpp:370
msgid "Unknown"
msgstr ""
#: daemon/HTTPServer.cpp:264 daemon/HTTPServer.cpp:394
#: daemon/HTTPServer.cpp:395 daemon/HTTPServer.cpp:922
#: daemon/HTTPServer.cpp:931
msgid "Proxy"
msgstr ""
#: daemon/HTTPServer.cpp:265
msgid "Mesh"
msgstr ""
#: daemon/HTTPServer.cpp:268
msgid "Error"
msgstr ""
#: daemon/HTTPServer.cpp:272
msgid "Clock skew"
msgstr ""
#: daemon/HTTPServer.cpp:275
msgid "Offline"
msgstr ""
#: daemon/HTTPServer.cpp:278
msgid "Symmetric NAT"
msgstr ""
#: daemon/HTTPServer.cpp:290
msgid "Uptime"
msgstr ""
#: daemon/HTTPServer.cpp:293
msgid "Network status"
msgstr ""
#: daemon/HTTPServer.cpp:298
msgid "Network status v6"
msgstr ""
#: daemon/HTTPServer.cpp:304 daemon/HTTPServer.cpp:311
msgid "Stopping in"
msgstr ""
#: daemon/HTTPServer.cpp:318
msgid "Family"
msgstr ""
#: daemon/HTTPServer.cpp:319
msgid "Tunnel creation success rate"
msgstr ""
#: daemon/HTTPServer.cpp:320
msgid "Received"
msgstr ""
#: daemon/HTTPServer.cpp:322 daemon/HTTPServer.cpp:325
#: daemon/HTTPServer.cpp:328
msgid "KiB/s"
msgstr ""
#: daemon/HTTPServer.cpp:323
msgid "Sent"
msgstr ""
#: daemon/HTTPServer.cpp:326
msgid "Transit"
msgstr ""
#: daemon/HTTPServer.cpp:329
msgid "Data path"
msgstr ""
#: daemon/HTTPServer.cpp:332
msgid "Hidden content. Press on text to see."
msgstr ""
#: daemon/HTTPServer.cpp:335
msgid "Router Ident"
msgstr ""
#: daemon/HTTPServer.cpp:337
msgid "Router Family"
msgstr ""
#: daemon/HTTPServer.cpp:338
msgid "Router Caps"
msgstr ""
#: daemon/HTTPServer.cpp:339
msgid "Version"
msgstr ""
#: daemon/HTTPServer.cpp:340
msgid "Our external address"
msgstr ""
#: daemon/HTTPServer.cpp:348
msgid "supported"
msgstr ""
#: daemon/HTTPServer.cpp:380
msgid "Routers"
msgstr ""
#: daemon/HTTPServer.cpp:381
msgid "Floodfills"
msgstr ""
#: daemon/HTTPServer.cpp:388 daemon/HTTPServer.cpp:908
msgid "Client Tunnels"
msgstr ""
#: daemon/HTTPServer.cpp:389
msgid "Transit Tunnels"
msgstr ""
#: daemon/HTTPServer.cpp:393
msgid "Services"
msgstr ""
#: daemon/HTTPServer.cpp:407 daemon/HTTPServer.cpp:419
msgid "Local Destinations"
msgstr ""
#: daemon/HTTPServer.cpp:442
msgid "Encrypted B33 address"
msgstr ""
#: daemon/HTTPServer.cpp:451
msgid "Address registration line"
msgstr ""
#: daemon/HTTPServer.cpp:456
msgid "Domain"
msgstr ""
#: daemon/HTTPServer.cpp:457
msgid "Generate"
msgstr ""
#: daemon/HTTPServer.cpp:458
msgid ""
"<b>Note:</b> result string can be used only for registering 2LD domains "
"(example.i2p). For registering subdomains please use i2pd-tools."
msgstr ""
#: daemon/HTTPServer.cpp:464
msgid "Address"
msgstr ""
#: daemon/HTTPServer.cpp:464
msgid "Type"
msgstr ""
#: daemon/HTTPServer.cpp:464
msgid "EncType"
msgstr ""
#: daemon/HTTPServer.cpp:474 daemon/HTTPServer.cpp:657
msgid "Inbound tunnels"
msgstr ""
#: daemon/HTTPServer.cpp:479 daemon/HTTPServer.cpp:489
#: daemon/HTTPServer.cpp:662 daemon/HTTPServer.cpp:672
#: Means milliseconds
msgid "ms"
msgstr ""
#: daemon/HTTPServer.cpp:484 daemon/HTTPServer.cpp:667
msgid "Outbound tunnels"
msgstr ""
#: daemon/HTTPServer.cpp:496
msgid "Tags"
msgstr ""
#: daemon/HTTPServer.cpp:496
msgid "Incoming"
msgstr ""
#: daemon/HTTPServer.cpp:503 daemon/HTTPServer.cpp:506
msgid "Outgoing"
msgstr ""
#: daemon/HTTPServer.cpp:504 daemon/HTTPServer.cpp:520
msgid "Destination"
msgstr ""
#: daemon/HTTPServer.cpp:504
msgid "Amount"
msgstr ""
#: daemon/HTTPServer.cpp:511
msgid "Incoming Tags"
msgstr ""
#: daemon/HTTPServer.cpp:519 daemon/HTTPServer.cpp:522
msgid "Tags sessions"
msgstr ""
#: daemon/HTTPServer.cpp:520
msgid "Status"
msgstr ""
#: daemon/HTTPServer.cpp:529 daemon/HTTPServer.cpp:584
msgid "Local Destination"
msgstr ""
#: daemon/HTTPServer.cpp:538 daemon/HTTPServer.cpp:887
msgid "Streams"
msgstr ""
#: daemon/HTTPServer.cpp:560
msgid "Close stream"
msgstr ""
#: daemon/HTTPServer.cpp:589
msgid "I2CP session not found"
msgstr ""
#: daemon/HTTPServer.cpp:592
msgid "I2CP is not enabled"
msgstr ""
#: daemon/HTTPServer.cpp:618
msgid "Invalid"
msgstr ""
#: daemon/HTTPServer.cpp:621
msgid "Store type"
msgstr ""
#: daemon/HTTPServer.cpp:622
msgid "Expires"
msgstr ""
#: daemon/HTTPServer.cpp:627
msgid "Non Expired Leases"
msgstr ""
#: daemon/HTTPServer.cpp:630
msgid "Gateway"
msgstr ""
#: daemon/HTTPServer.cpp:631
msgid "TunnelID"
msgstr ""
#: daemon/HTTPServer.cpp:632
msgid "EndDate"
msgstr ""
#: daemon/HTTPServer.cpp:642
msgid "not floodfill"
msgstr ""
#: daemon/HTTPServer.cpp:653
msgid "Queue size"
msgstr ""
#: daemon/HTTPServer.cpp:684
msgid "Run peer test"
msgstr ""
#: daemon/HTTPServer.cpp:687
msgid "Decline transit tunnels"
msgstr ""
#: daemon/HTTPServer.cpp:689
msgid "Accept transit tunnels"
msgstr ""
#: daemon/HTTPServer.cpp:692 daemon/HTTPServer.cpp:697
msgid "Cancel graceful shutdown"
msgstr ""
#: daemon/HTTPServer.cpp:694 daemon/HTTPServer.cpp:699
msgid "Start graceful shutdown"
msgstr ""
#: daemon/HTTPServer.cpp:701
msgid "Force shutdown"
msgstr ""
#: daemon/HTTPServer.cpp:704
msgid ""
"<b>Note:</b> any action done here are not persistent and not changes your "
"config files."
msgstr ""
#: daemon/HTTPServer.cpp:706
msgid "Logging level"
msgstr ""
#: daemon/HTTPServer.cpp:714
msgid "Transit tunnels limit"
msgstr ""
#: daemon/HTTPServer.cpp:719
msgid "Change"
msgstr ""
#: daemon/HTTPServer.cpp:743
msgid "no transit tunnels currently built"
msgstr ""
#: daemon/HTTPServer.cpp:848 daemon/HTTPServer.cpp:871
msgid "SAM disabled"
msgstr ""
#: daemon/HTTPServer.cpp:864
msgid "no sessions currently running"
msgstr ""
#: daemon/HTTPServer.cpp:877
msgid "SAM session not found"
msgstr ""
#: daemon/HTTPServer.cpp:882
msgid "SAM Session"
msgstr ""
#: daemon/HTTPServer.cpp:939
msgid "Server Tunnels"
msgstr ""
#: daemon/HTTPServer.cpp:955
msgid "Client Forwards"
msgstr ""
#: daemon/HTTPServer.cpp:969
msgid "Server Forwards"
msgstr ""
#: daemon/HTTPServer.cpp:1175
msgid "Unknown page"
msgstr ""
#: daemon/HTTPServer.cpp:1194
msgid "Invalid token"
msgstr ""
#: daemon/HTTPServer.cpp:1252 daemon/HTTPServer.cpp:1309
#: daemon/HTTPServer.cpp:1337
msgid "SUCCESS"
msgstr ""
#: daemon/HTTPServer.cpp:1252
msgid "Stream closed"
msgstr ""
#: daemon/HTTPServer.cpp:1254
msgid "Stream not found or already was closed"
msgstr ""
#: daemon/HTTPServer.cpp:1257
msgid "Destination not found"
msgstr ""
#: daemon/HTTPServer.cpp:1260
msgid "StreamID can't be null"
msgstr ""
#: daemon/HTTPServer.cpp:1262 daemon/HTTPServer.cpp:1327
msgid "Return to destination page"
msgstr ""
#: daemon/HTTPServer.cpp:1263 daemon/HTTPServer.cpp:1276
msgid "You will be redirected back in 5 seconds"
msgstr ""
#: daemon/HTTPServer.cpp:1274
msgid "Transit tunnels count must not exceed 65535"
msgstr ""
#: daemon/HTTPServer.cpp:1275 daemon/HTTPServer.cpp:1338
msgid "Back to commands list"
msgstr ""
#: daemon/HTTPServer.cpp:1311
msgid "Register at reg.i2p"
msgstr ""
#: daemon/HTTPServer.cpp:1312
msgid "Description"
msgstr ""
#: daemon/HTTPServer.cpp:1312
msgid "A bit information about service on domain"
msgstr ""
#: daemon/HTTPServer.cpp:1313
msgid "Submit"
msgstr ""
#: daemon/HTTPServer.cpp:1319
msgid "Domain can't end with .b32.i2p"
msgstr ""
#: daemon/HTTPServer.cpp:1322
msgid "Domain must end with .i2p"
msgstr ""
#: daemon/HTTPServer.cpp:1325
msgid "Such destination is not found"
msgstr ""
#: daemon/HTTPServer.cpp:1333
msgid "Unknown command"
msgstr ""
#: daemon/HTTPServer.cpp:1337
msgid "Command accepted"
msgstr ""
#: daemon/HTTPServer.cpp:1339
msgid "You will be redirected in 5 seconds"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:157
msgid "Proxy error"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:165
msgid "Proxy info"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:173
msgid "Proxy error: Host not found"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:174
msgid "Remote host not found in router's addressbook"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:175
msgid "You may try to find this host on jump services below"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:273 libi2pd_client/HTTPProxy.cpp:288
#: libi2pd_client/HTTPProxy.cpp:365
msgid "Invalid request"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:273
msgid "Proxy unable to parse your request"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:288
msgid "addresshelper is not supported"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:297 libi2pd_client/HTTPProxy.cpp:306
#: libi2pd_client/HTTPProxy.cpp:385
msgid "Host"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:297
msgid "added to router's addressbook from helper"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:298 libi2pd_client/HTTPProxy.cpp:307
msgid "Click"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:298 libi2pd_client/HTTPProxy.cpp:308
msgid "here"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:298
msgid "to proceed"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:299 libi2pd_client/HTTPProxy.cpp:309
msgid "Addresshelper found"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:306
msgid "already in router's addressbook"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:308
msgid "to update record"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:322
msgid "Invalid Request"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:322
msgid "invalid request uri"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:365
msgid "Can't detect destination host from request"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:382 libi2pd_client/HTTPProxy.cpp:386
msgid "Outproxy failure"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:382
msgid "bad outproxy settings"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:385
msgid "not inside I2P network, but outproxy is not enabled"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:474
msgid "unknown outproxy url"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:480
msgid "cannot resolve upstream proxy"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:488
msgid "hostname too long"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:515
msgid "cannot connect to upstream socks proxy"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:521
msgid "Cannot negotiate with socks proxy"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:563
msgid "CONNECT error"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:563
msgid "Failed to Connect"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:574 libi2pd_client/HTTPProxy.cpp:600
msgid "socks proxy error"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:582
msgid "failed to send request to upstream"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:603
msgid "No Reply From socks proxy"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:610
msgid "cannot connect"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:610
msgid "http out proxy not implemented"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:611
msgid "cannot connect to upstream http proxy"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:644
msgid "Host is down"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:644
msgid ""
"Can't create connection to requested host, it may be down. Please try again "
"later."
msgstr ""

7
contrib/i18n/regex.txt Normal file
View File

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

View File

@ -103,6 +103,9 @@ port = 7070
# auth = true # auth = true
# user = i2pd # user = i2pd
# pass = changeme # pass = changeme
## Select webconsole language
## Currently supported english (default), russian, turkmen and ukrainian languages
# lang = english
[httpproxy] [httpproxy]
## Uncomment and set to 'false' to disable HTTP Proxy ## Uncomment and set to 'false' to disable HTTP Proxy

View File

@ -32,6 +32,7 @@
#include "UPnP.h" #include "UPnP.h"
#include "Timestamp.h" #include "Timestamp.h"
#include "util.h" #include "util.h"
#include "I18N.h"
namespace i2p namespace i2p
{ {
@ -343,6 +344,10 @@ namespace util
LogPrint(eLogInfo, "Daemon: using hidden mode"); LogPrint(eLogInfo, "Daemon: using hidden mode");
i2p::data::netdb.SetHidden(true); i2p::data::netdb.SetHidden(true);
} }
std::string httpLang; i2p::config::GetOption("http.lang", httpLang);
i2p::i18n::SetLanguage(httpLang);
return true; return true;
} }

View File

@ -30,6 +30,7 @@
#include "Daemon.h" #include "Daemon.h"
#include "util.h" #include "util.h"
#include "ECIESX25519AEADRatchetSession.h" #include "ECIESX25519AEADRatchetSession.h"
#include "I18N.h"
#ifdef WIN32_APP #ifdef WIN32_APP
#include "Win32App.h" #include "Win32App.h"
@ -40,7 +41,7 @@
namespace i2p { namespace i2p {
namespace http { namespace http {
const char *itoopieFavicon = const std::string itoopieFavicon =
"data:image/png;base64," "data:image/png;base64,"
"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACx" "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACx"
"jwv8YQUAAAAJcEhZcwAALiIAAC4iAari3ZIAAAAHdElNRQfgCQsUNSZrkhi1AAAAGXRFWHRTb2Z0" "jwv8YQUAAAAJcEhZcwAALiIAAC4iAari3ZIAAAAHdElNRQfgCQsUNSZrkhi1AAAAGXRFWHRTb2Z0"
@ -58,49 +59,51 @@ namespace http {
"JHYnlIsfzJjIp9xZKswL5YKBHL+coKJoRDaUSzoozxHVrygQU4JykQADAwAT5b1NHtwZugAAAABJ" "JHYnlIsfzJjIp9xZKswL5YKBHL+coKJoRDaUSzoozxHVrygQU4JykQADAwAT5b1NHtwZugAAAABJ"
"RU5ErkJggg=="; "RU5ErkJggg==";
const char *cssStyles = static void GetStyles (std::stringstream& s)
"<style>\r\n" {
" body { font: 100%/1.5em sans-serif; margin: 0; padding: 1.5em; background: #FAFAFA; color: #103456; }\r\n" s << "<style>\r\n"
" a, .slide label { text-decoration: none; color: #894C84; }\r\n" << " body { font: 100%/1.5em sans-serif; margin: 0; padding: 1.5em; background: #FAFAFA; color: #103456; }\r\n"
" a:hover, .slide label:hover { color: #FAFAFA; background: #894C84; }\r\n" << " a, .slide label { text-decoration: none; color: #894C84; }\r\n"
" a.button { -webkit-appearance: button; -moz-appearance: button; appearance: button; text-decoration: none;\r\n" << " a:hover, .slide label:hover { color: #FAFAFA; background: #894C84; }\r\n"
" color: initial; padding: 0 5px; border: 1px solid #894C84; }\r\n" << " a.button { -webkit-appearance: button; -moz-appearance: button; appearance: button; text-decoration: none;\r\n"
" .header { font-size: 2.5em; text-align: center; margin: 1em 0; color: #894C84; }\r\n" << " color: initial; padding: 0 5px; border: 1px solid #894C84; }\r\n"
" .wrapper { margin: 0 auto; padding: 1em; max-width: 58em; }\r\n" << " .header { font-size: 2.5em; text-align: center; margin: 1em 0; color: #894C84; }\r\n"
" .menu { float: left; } .menu a, .commands a { display: block; }\r\n" << " .wrapper { margin: 0 auto; padding: 1em; max-width: 58em; }\r\n"
" .listitem { display: block; font-family: monospace; font-size: 1.2em; white-space: nowrap; }\r\n" << " .menu { float: left; } .menu a, .commands a { display: block; }\r\n"
" .tableitem { font-family: monospace; font-size: 1.2em; white-space: nowrap; }\r\n" << " .listitem { display: block; font-family: monospace; font-size: 1.2em; white-space: nowrap; }\r\n"
" .content { float: left; font-size: 1em; margin-left: 4em; max-width: 45em; overflow: auto; }\r\n" << " .tableitem { font-family: monospace; font-size: 1.2em; white-space: nowrap; }\r\n"
" .tunnel.established { color: #56B734; } .tunnel.expiring { color: #D3AE3F; }\r\n" << " .content { float: left; font-size: 1em; margin-left: 4em; max-width: 45em; overflow: auto; }\r\n"
" .tunnel.failed { color: #D33F3F; } .tunnel.building { color: #434343; }\r\n" << " .tunnel.established { color: #56B734; } .tunnel.expiring { color: #D3AE3F; }\r\n"
" caption { font-size: 1.5em; text-align: center; color: #894C84; }\r\n" << " .tunnel.failed { color: #D33F3F; } .tunnel.building { color: #434343; }\r\n"
" table { display: table; border-collapse: collapse; text-align: center; }\r\n" << " caption { font-size: 1.5em; text-align: center; color: #894C84; }\r\n"
" table.extaddr { text-align: left; } table.services { width: 100%; }\r\n" << " table { display: table; border-collapse: collapse; text-align: center; }\r\n"
" textarea { word-break: break-all; }\r\n" << " table.extaddr { text-align: left; } table.services { width: 100%; }\r\n"
" .streamdest { width: 120px; max-width: 240px; overflow: hidden; text-overflow: ellipsis;}\r\n" << " textarea { word-break: break-all; }\r\n"
" .slide div.slidecontent, .slide [type=\"checkbox\"] { display: none; }\r\n" << " .streamdest { width: 120px; max-width: 240px; overflow: hidden; text-overflow: ellipsis;}\r\n"
" .slide [type=\"checkbox\"]:checked ~ div.slidecontent { display: block; margin-top: 0; padding: 0; }\r\n" << " .slide div.slidecontent, .slide [type=\"checkbox\"] { display: none; }\r\n"
" .disabled:after { color: #D33F3F; content: \"Disabled\" }\r\n" << " .slide [type=\"checkbox\"]:checked ~ div.slidecontent { display: block; margin-top: 0; padding: 0; }\r\n"
" .enabled:after { color: #56B734; content: \"Enabled\" }\r\n" << " .disabled:after { color: #D33F3F; content: \"" << tr("Disabled") << "\" }\r\n"
" @media screen and (max-width: 980px) {\r\n" /* adaptive style */ << " .enabled:after { color: #56B734; content: \"" << tr("Enabled") << "\" }\r\n"
" body { padding: 1.5em 0 0 0; }\r\n" << " @media screen and (max-width: 980px) {\r\n" /* adaptive style */
" .menu { width: 100%; display: block; float: none; position: unset; font-size: 16px;\r\n" << " body { padding: 1.5em 0 0 0; }\r\n"
" text-align: center; }\r\n" << " .menu { width: 100%; display: block; float: none; position: unset; font-size: 16px;\r\n"
" .menu a, .commands a { padding: 2px; }\r\n" << " text-align: center; }\r\n"
" .content { float: none; margin-left: unset; margin-top: 16px; max-width: 100%; width: 100%;\r\n" << " .menu a, .commands a { padding: 2px; }\r\n"
" text-align: center; }\r\n" << " .content { float: none; margin-left: unset; margin-top: 16px; max-width: 100%; width: 100%;\r\n"
" a, .slide label { /* margin-right: 10px; */ display: block; /* font-size: 18px; */ }\r\n" << " text-align: center; }\r\n"
" .header { margin: unset; font-size: 1.5em; } small {display: block}\r\n" << " a, .slide label { /* margin-right: 10px; */ display: block; /* font-size: 18px; */ }\r\n"
" a.button { -webkit-appearance: button; -moz-appearance: button; appearance: button; text-decoration: none;\r\n" << " .header { margin: unset; font-size: 1.5em; } small {display: block}\r\n"
" color: initial; margin-top: 10px; padding: 6px; border: 1px solid #894c84; width: -webkit-fill-available; }\r\n" << " a.button { -webkit-appearance: button; -moz-appearance: button; appearance: button; text-decoration: none;\r\n"
" input { width: 35%; text-align: center; padding: 5px;\r\n" << " color: initial; margin-top: 10px; padding: 6px; border: 1px solid #894c84; width: -webkit-fill-available; }\r\n"
" border: 2px solid #ccc; -webkit-border-radius: 5px; border-radius: 5px; font-size: 18px; }\r\n" << " input { width: 35%; text-align: center; padding: 5px;\r\n"
" textarea { width: -webkit-fill-available; height: auto; padding:5px; border:2px solid #ccc;\r\n" << " border: 2px solid #ccc; -webkit-border-radius: 5px; border-radius: 5px; font-size: 18px; }\r\n"
" -webkit-border-radius: 5px; border-radius: 5px; font-size: 12px; }\r\n" << " textarea { width: -webkit-fill-available; height: auto; padding:5px; border:2px solid #ccc;\r\n"
" button[type=submit] { padding: 5px 15px; background: #ccc; border: 0 none; cursor: pointer;\r\n" << " -webkit-border-radius: 5px; border-radius: 5px; font-size: 12px; }\r\n"
" -webkit-border-radius: 5px; border-radius: 5px; position: relative; height: 36px; display: -webkit-inline-box; margin-top: 10px; }\r\n" << " button[type=submit] { padding: 5px 15px; background: #ccc; border: 0 none; cursor: pointer;\r\n"
" }\r\n" /* adaptive style */ << " -webkit-border-radius: 5px; border-radius: 5px; position: relative; height: 36px; display: -webkit-inline-box; margin-top: 10px; }\r\n"
"</style>\r\n"; << " }\r\n" /* adaptive style */
<< "</style>\r\n";
}
const char HTTP_PAGE_TUNNELS[] = "tunnels"; const char HTTP_PAGE_TUNNELS[] = "tunnels";
const char HTTP_PAGE_TRANSIT_TUNNELS[] = "transit_tunnels"; const char HTTP_PAGE_TRANSIT_TUNNELS[] = "transit_tunnels";
@ -135,18 +138,18 @@ namespace http {
int num; int num;
if ((num = seconds / 86400) > 0) { if ((num = seconds / 86400) > 0) {
s << num << " days, "; s << num << " " << tr("day", "days", num) << ", ";
seconds -= num * 86400; seconds -= num * 86400;
} }
if ((num = seconds / 3600) > 0) { if ((num = seconds / 3600) > 0) {
s << num << " hours, "; s << num << " " << tr("hour", "hours", num) << ", ";
seconds -= num * 3600; seconds -= num * 3600;
} }
if ((num = seconds / 60) > 0) { if ((num = seconds / 60) > 0) {
s << num << " min, "; s << num << " " << tr("minute", "minutes", num) << ", ";
seconds -= num * 60; seconds -= num * 60;
} }
s << seconds << " seconds"; s << seconds << " " << tr("second", "seconds", seconds);
} }
static void ShowTraffic (std::stringstream& s, uint64_t bytes) static void ShowTraffic (std::stringstream& s, uint64_t bytes)
@ -154,28 +157,35 @@ namespace http {
s << std::fixed << std::setprecision(2); s << std::fixed << std::setprecision(2);
auto numKBytes = (double) bytes / 1024; auto numKBytes = (double) bytes / 1024;
if (numKBytes < 1024) if (numKBytes < 1024)
s << numKBytes << " KiB"; s << numKBytes << " " << tr("KiB");
else if (numKBytes < 1024 * 1024) else if (numKBytes < 1024 * 1024)
s << numKBytes / 1024 << " MiB"; s << numKBytes / 1024 << " " << tr("MiB");
else else
s << numKBytes / 1024 / 1024 << " GiB"; s << numKBytes / 1024 / 1024 << " " << tr("GiB");
} }
static void ShowTunnelDetails (std::stringstream& s, enum i2p::tunnel::TunnelState eState, bool explr, int bytes) static void ShowTunnelDetails (std::stringstream& s, enum i2p::tunnel::TunnelState eState, bool explr, int bytes)
{ {
std::string state; std::string state, stateText;
switch (eState) { switch (eState) {
case i2p::tunnel::eTunnelStateBuildReplyReceived : case i2p::tunnel::eTunnelStateBuildReplyReceived :
case i2p::tunnel::eTunnelStatePending : state = "building"; break; case i2p::tunnel::eTunnelStatePending : state = "building"; break;
case i2p::tunnel::eTunnelStateBuildFailed : case i2p::tunnel::eTunnelStateBuildFailed :
case i2p::tunnel::eTunnelStateTestFailed : case i2p::tunnel::eTunnelStateTestFailed :
case i2p::tunnel::eTunnelStateFailed : state = "failed"; break; case i2p::tunnel::eTunnelStateFailed : state = "failed"; break;
case i2p::tunnel::eTunnelStateExpiring : state = "expiring"; break; case i2p::tunnel::eTunnelStateExpiring : state = "expiring"; break;
case i2p::tunnel::eTunnelStateEstablished : state = "established"; break; case i2p::tunnel::eTunnelStateEstablished : state = "established"; break;
default: state = "unknown"; break; default: state = "unknown"; break;
} }
s << "<span class=\"tunnel " << state << "\"> " << state << ((explr) ? " (exploratory)" : "") << "</span>, ";
s << " " << (int) (bytes / 1024) << "&nbsp;KiB\r\n"; if (state == "building") stateText = tr("building");
else if (state == "failed") stateText = tr("failed");
else if (state == "expiring") stateText = tr("expiring");
else if (state == "established") stateText = tr("established");
else stateText = tr("unknown");
s << "<span class=\"tunnel " << state << "\"> " << stateText << ((explr) ? " (" + tr("exploratory") + ")" : "") << "</span>, ";
s << " " << (int) (bytes / 1024) << "&nbsp;" << tr("KiB") << "\r\n";
} }
static void SetLogLevel (const std::string& level) static void SetLogLevel (const std::string& level)
@ -191,39 +201,40 @@ namespace http {
static void ShowPageHead (std::stringstream& s) static void ShowPageHead (std::stringstream& s)
{ {
std::string webroot; std::string webroot; i2p::config::GetOption("http.webroot", webroot);
i2p::config::GetOption("http.webroot", webroot);
// Page language
std::string lang, langCode; i2p::config::GetOption("http.lang", lang);
if (lang == "russian") langCode = "ru";
else langCode = "en";
s << s <<
"<!DOCTYPE html>\r\n" "<!DOCTYPE html>\r\n"
"<html lang=\"en\">\r\n" /* TODO: Add support for locale */ "<html lang=\"" << langCode << "\">\r\n"
" <head>\r\n" /* TODO: Find something to parse html/template system. This is horrible. */ " <head>\r\n" /* TODO: Find something to parse html/template system. This is horrible. */
#if (!defined(WIN32))
" <meta charset=\"UTF-8\">\r\n" " <meta charset=\"UTF-8\">\r\n"
#else
" <meta charset=\"windows-1251\">\r\n"
#endif
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n" " <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n"
" <link rel=\"shortcut icon\" href=\"" << itoopieFavicon << "\">\r\n" " <link rel=\"shortcut icon\" href=\"" << itoopieFavicon << "\">\r\n"
" <title>Purple I2P " VERSION " Webconsole</title>\r\n" " <title>Purple I2P " VERSION " Webconsole</title>\r\n";
<< cssStyles << GetStyles(s);
"</head>\r\n";
s << s <<
"</head>\r\n"
"<body>\r\n" "<body>\r\n"
"<div class=\"header\"><b>i2pd</b> webconsole</div>\r\n" "<div class=\"header\">" << tr("<b>i2pd</b> webconsole") << "</div>\r\n"
"<div class=\"wrapper\">\r\n" "<div class=\"wrapper\">\r\n"
"<div class=\"menu\">\r\n" "<div class=\"menu\">\r\n"
" <a href=\"" << webroot << "\">Main page</a><br>\r\n" " <a href=\"" << webroot << "\">" << tr("Main page") << "</a><br>\r\n"
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_COMMANDS << "\">Router commands</a>\r\n" " <a href=\"" << webroot << "?page=" << HTTP_PAGE_COMMANDS << "\">" << tr("Router commands") << "</a>\r\n"
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_LOCAL_DESTINATIONS << "\">Local destinations</a>\r\n"; " <a href=\"" << webroot << "?page=" << HTTP_PAGE_LOCAL_DESTINATIONS << "\">" << tr("Local destinations") << "</a>\r\n";
if (i2p::context.IsFloodfill ()) if (i2p::context.IsFloodfill ())
s << " <a href=\"" << webroot << "?page=" << HTTP_PAGE_LEASESETS << "\">LeaseSets</a>\r\n"; s << " <a href=\"" << webroot << "?page=" << HTTP_PAGE_LEASESETS << "\">" << tr("LeaseSets") << "</a>\r\n";
s << s <<
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_TUNNELS << "\">Tunnels</a>\r\n" " <a href=\"" << webroot << "?page=" << HTTP_PAGE_TUNNELS << "\">" << tr("Tunnels") << "</a>\r\n"
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_TRANSIT_TUNNELS << "\">Transit tunnels</a>\r\n" " <a href=\"" << webroot << "?page=" << HTTP_PAGE_TRANSIT_TUNNELS << "\">" << tr("Transit tunnels") << "</a>\r\n"
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_TRANSPORTS << "\">Transports</a>\r\n" " <a href=\"" << webroot << "?page=" << HTTP_PAGE_TRANSPORTS << "\">" << tr ("Transports") << "</a>\r\n"
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_I2P_TUNNELS << "\">I2P tunnels</a>\r\n"; " <a href=\"" << webroot << "?page=" << HTTP_PAGE_I2P_TUNNELS << "\">" << tr("I2P tunnels") << "</a>\r\n";
if (i2p::client::context.GetSAMBridge ()) if (i2p::client::context.GetSAMBridge ())
s << " <a href=\"" << webroot << "?page=" << HTTP_PAGE_SAM_SESSIONS << "\">SAM sessions</a>\r\n"; s << " <a href=\"" << webroot << "?page=" << HTTP_PAGE_SAM_SESSIONS << "\">" << tr("SAM sessions") << "</a>\r\n";
s << s <<
"</div>\r\n" "</div>\r\n"
"<div class=\"content\">"; "<div class=\"content\">";
@ -239,94 +250,94 @@ namespace http {
static void ShowError(std::stringstream& s, const std::string& string) static void ShowError(std::stringstream& s, const std::string& string)
{ {
s << "<b>ERROR:</b>&nbsp;" << string << "<br>\r\n"; s << "<b>" << tr("ERROR") << ":</b>&nbsp;" << string << "<br>\r\n";
} }
static void ShowNetworkStatus (std::stringstream& s, RouterStatus status) static void ShowNetworkStatus (std::stringstream& s, RouterStatus status)
{ {
switch (status) switch (status)
{ {
case eRouterStatusOK: s << "OK"; break; case eRouterStatusOK: s << tr("OK"); break;
case eRouterStatusTesting: s << "Testing"; break; case eRouterStatusTesting: s << tr("Testing"); break;
case eRouterStatusFirewalled: s << "Firewalled"; break; case eRouterStatusFirewalled: s << tr("Firewalled"); break;
case eRouterStatusUnknown: s << "Unknown"; break; case eRouterStatusUnknown: s << tr("Unknown"); break;
case eRouterStatusProxy: s << "Proxy"; break; case eRouterStatusProxy: s << tr("Proxy"); break;
case eRouterStatusMesh: s << "Mesh"; break; case eRouterStatusMesh: s << tr("Mesh"); break;
case eRouterStatusError: case eRouterStatusError:
{ {
s << "Error"; s << tr("Error");
switch (i2p::context.GetError ()) switch (i2p::context.GetError ())
{ {
case eRouterErrorClockSkew: case eRouterErrorClockSkew:
s << " - Clock skew"; s << " - " << tr("Clock skew");
break; break;
case eRouterErrorOffline: case eRouterErrorOffline:
s << " - Offline"; s << " - " << tr("Offline");
break; break;
case eRouterErrorSymmetricNAT: case eRouterErrorSymmetricNAT:
s << " - Symmetric NAT"; s << " - " << tr("Symmetric NAT");
break; break;
default: ; default: ;
} }
break; break;
} }
default: s << "Unknown"; default: s << tr("Unknown");
} }
} }
void ShowStatus (std::stringstream& s, bool includeHiddenContent, i2p::http::OutputFormatEnum outputFormat) void ShowStatus (std::stringstream& s, bool includeHiddenContent, i2p::http::OutputFormatEnum outputFormat)
{ {
s << "<b>Uptime:</b> "; s << "<b>" << tr("Uptime") << ":</b> ";
ShowUptime(s, i2p::context.GetUptime ()); ShowUptime(s, i2p::context.GetUptime ());
s << "<br>\r\n"; s << "<br>\r\n";
s << "<b>Network status:</b> "; s << "<b>" << tr("Network status") << ":</b> ";
ShowNetworkStatus (s, i2p::context.GetStatus ()); ShowNetworkStatus (s, i2p::context.GetStatus ());
s << "<br>\r\n"; s << "<br>\r\n";
if (i2p::context.SupportsV6 ()) if (i2p::context.SupportsV6 ())
{ {
s << "<b>Network status 6:</b> "; s << "<b>" << tr("Network status v6") << ":</b> ";
ShowNetworkStatus (s, i2p::context.GetStatusV6 ()); ShowNetworkStatus (s, i2p::context.GetStatusV6 ());
s << "<br>\r\n"; s << "<br>\r\n";
} }
#if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY)) #if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY))
if (auto remains = Daemon.gracefulShutdownInterval) { if (auto remains = Daemon.gracefulShutdownInterval) {
s << "<b>Stopping in:</b> "; s << "<b>" << tr("Stopping in") << ":</b> ";
ShowUptime(s, remains); ShowUptime(s, remains);
s << "<br>\r\n"; s << "<br>\r\n";
} }
#elif defined(WIN32_APP) #elif defined(WIN32_APP)
if (i2p::win32::g_GracefulShutdownEndtime != 0) { if (i2p::win32::g_GracefulShutdownEndtime != 0) {
uint16_t remains = (i2p::win32::g_GracefulShutdownEndtime - GetTickCount()) / 1000; uint16_t remains = (i2p::win32::g_GracefulShutdownEndtime - GetTickCount()) / 1000;
s << "<b>Stopping in:</b> "; s << "<b>" << tr("Stopping in") << ":</b> ";
ShowUptime(s, remains); ShowUptime(s, remains);
s << "<br>\r\n"; s << "<br>\r\n";
} }
#endif #endif
auto family = i2p::context.GetFamily (); auto family = i2p::context.GetFamily ();
if (family.length () > 0) if (family.length () > 0)
s << "<b>Family:</b> " << family << "<br>\r\n"; s << "<b>"<< tr("Family") << ":</b> " << family << "<br>\r\n";
s << "<b>Tunnel creation success rate:</b> " << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate () << "%<br>\r\n"; s << "<b>" << tr("Tunnel creation success rate") << ":</b> " << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate () << "%<br>\r\n";
s << "<b>Received:</b> "; s << "<b>" << tr("Received") << ":</b> ";
ShowTraffic (s, i2p::transport::transports.GetTotalReceivedBytes ()); ShowTraffic (s, i2p::transport::transports.GetTotalReceivedBytes ());
s << " (" << (double) i2p::transport::transports.GetInBandwidth () / 1024 << " KiB/s)<br>\r\n"; s << " (" << (double) i2p::transport::transports.GetInBandwidth () / 1024 << " " << tr("KiB/s") << ")<br>\r\n";
s << "<b>Sent:</b> "; s << "<b>" << tr("Sent") << ":</b> ";
ShowTraffic (s, i2p::transport::transports.GetTotalSentBytes ()); ShowTraffic (s, i2p::transport::transports.GetTotalSentBytes ());
s << " (" << (double) i2p::transport::transports.GetOutBandwidth () / 1024 << " KiB/s)<br>\r\n"; s << " (" << (double) i2p::transport::transports.GetOutBandwidth () / 1024 << " " << tr("KiB/s") << ")<br>\r\n";
s << "<b>Transit:</b> "; s << "<b>" << tr("Transit") << ":</b> ";
ShowTraffic (s, i2p::transport::transports.GetTotalTransitTransmittedBytes ()); ShowTraffic (s, i2p::transport::transports.GetTotalTransitTransmittedBytes ());
s << " (" << (double) i2p::transport::transports.GetTransitBandwidth () / 1024 << " KiB/s)<br>\r\n"; s << " (" << (double) i2p::transport::transports.GetTransitBandwidth () / 1024 << " " << tr("KiB/s") << ")<br>\r\n";
s << "<b>Data path:</b> " << i2p::fs::GetDataDir() << "<br>\r\n"; s << "<b>" << tr("Data path") << ":</b> " << i2p::fs::GetUTF8DataDir() << "<br>\r\n";
s << "<div class='slide'>"; s << "<div class='slide'>";
if((outputFormat==OutputFormatEnum::forWebConsole)||!includeHiddenContent) { if((outputFormat == OutputFormatEnum::forWebConsole) || !includeHiddenContent) {
s << "<label for=\"slide-info\">Hidden content. Press on text to see.</label>\r\n<input type=\"checkbox\" id=\"slide-info\" />\r\n<div class=\"slidecontent\">\r\n"; 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>Router Ident:</b> " << i2p::context.GetRouterInfo().GetIdentHashBase64() << "<br>\r\n"; s << "<b>" << tr("Router Ident") << ":</b> " << i2p::context.GetRouterInfo().GetIdentHashBase64() << "<br>\r\n";
if (!i2p::context.GetRouterInfo().GetProperty("family").empty()) if (!i2p::context.GetRouterInfo().GetProperty("family").empty())
s << "<b>Router Family:</b> " << i2p::context.GetRouterInfo().GetProperty("family") << "<br>\r\n"; s << "<b>" << tr("Router Family") << ":</b> " << i2p::context.GetRouterInfo().GetProperty("family") << "<br>\r\n";
s << "<b>Router Caps:</b> " << i2p::context.GetRouterInfo().GetProperty("caps") << "<br>\r\n"; s << "<b>" << tr("Router Caps") << ":</b> " << i2p::context.GetRouterInfo().GetProperty("caps") << "<br>\r\n";
s << "<b>Version:</b> " VERSION "<br>\r\n"; s << "<b>" << tr("Version") << ":</b> " VERSION "<br>\r\n";
s << "<b>Our external address:</b>" << "<br>\r\n<table class=\"extaddr\"><tbody>\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()) for (const auto& address : i2p::context.GetRouterInfo().GetAddresses())
{ {
s << "<tr>\r\n"; s << "<tr>\r\n";
@ -334,7 +345,7 @@ namespace http {
{ {
s << "<td>NTCP2"; s << "<td>NTCP2";
if (address->host.is_v6 ()) s << "v6"; if (address->host.is_v6 ()) s << "v6";
s << "</td><td>supported</td>\r\n</tr>\r\n"; s << "</td><td>" << tr("supported") << "</td>\r\n</tr>\r\n";
continue; continue;
} }
switch (address->transportStyle) switch (address->transportStyle)
@ -356,32 +367,32 @@ namespace http {
break; break;
} }
default: default:
s << "<td>Unknown</td>\r\n"; s << "<td>" << tr("Unknown") << "</td>\r\n";
} }
s << "<td>" << address->host.to_string() << ":" << address->port << "</td>\r\n</tr>\r\n"; s << "<td>" << address->host.to_string() << ":" << address->port << "</td>\r\n</tr>\r\n";
} }
s << "</tbody></table>\r\n"; s << "</tbody></table>\r\n";
} }
s << "</div>\r\n</div>\r\n"; s << "</div>\r\n</div>\r\n";
if(outputFormat==OutputFormatEnum::forQtUi) { if(outputFormat == OutputFormatEnum::forQtUi) {
s << "<br>"; s << "<br>";
} }
s << "<b>Routers:</b> " << i2p::data::netdb.GetNumRouters () << " "; s << "<b>" << tr("Routers") << ":</b> " << i2p::data::netdb.GetNumRouters () << " ";
s << "<b>Floodfills:</b> " << i2p::data::netdb.GetNumFloodfills () << " "; s << "<b>" << tr("Floodfills") << ":</b> " << i2p::data::netdb.GetNumFloodfills () << " ";
s << "<b>LeaseSets:</b> " << i2p::data::netdb.GetNumLeaseSets () << "<br>\r\n"; s << "<b>" << tr("LeaseSets") << ":</b> " << i2p::data::netdb.GetNumLeaseSets () << "<br>\r\n";
size_t clientTunnelCount = i2p::tunnel::tunnels.CountOutboundTunnels(); size_t clientTunnelCount = i2p::tunnel::tunnels.CountOutboundTunnels();
clientTunnelCount += i2p::tunnel::tunnels.CountInboundTunnels(); clientTunnelCount += i2p::tunnel::tunnels.CountInboundTunnels();
size_t transitTunnelCount = i2p::tunnel::tunnels.CountTransitTunnels(); size_t transitTunnelCount = i2p::tunnel::tunnels.CountTransitTunnels();
s << "<b>Client Tunnels:</b> " << std::to_string(clientTunnelCount) << " "; s << "<b>" << tr("Client Tunnels") << ":</b> " << std::to_string(clientTunnelCount) << " ";
s << "<b>Transit Tunnels:</b> " << std::to_string(transitTunnelCount) << "<br>\r\n<br>\r\n"; s << "<b>" << tr("Transit Tunnels") << ":</b> " << std::to_string(transitTunnelCount) << "<br>\r\n<br>\r\n";
if(outputFormat==OutputFormatEnum::forWebConsole) { if(outputFormat==OutputFormatEnum::forWebConsole) {
bool i2pcontrol; i2p::config::GetOption("i2pcontrol.enabled", i2pcontrol); bool i2pcontrol; i2p::config::GetOption("i2pcontrol.enabled", i2pcontrol);
s << "<table class=\"services\"><caption>Services</caption><tbody>\r\n"; s << "<table class=\"services\"><caption>" << tr("Services") << "</caption><tbody>\r\n";
s << "<tr><td>" << "HTTP Proxy" << "</td><td><div class='" << ((i2p::client::context.GetHttpProxy ()) ? "enabled" : "disabled") << "'></div></td></tr>\r\n"; s << "<tr><td>" << "HTTP " << tr("Proxy") << "</td><td><div class='" << ((i2p::client::context.GetHttpProxy ()) ? "enabled" : "disabled") << "'></div></td></tr>\r\n";
s << "<tr><td>" << "SOCKS Proxy" << "</td><td><div class='" << ((i2p::client::context.GetSocksProxy ()) ? "enabled" : "disabled") << "'></div></td></tr>\r\n"; s << "<tr><td>" << "SOCKS " << tr("Proxy") << "</td><td><div class='" << ((i2p::client::context.GetSocksProxy ()) ? "enabled" : "disabled") << "'></div></td></tr>\r\n";
s << "<tr><td>" << "BOB" << "</td><td><div class='" << ((i2p::client::context.GetBOBCommandChannel ()) ? "enabled" : "disabled") << "'></div></td></tr>\r\n"; s << "<tr><td>" << "BOB" << "</td><td><div class='" << ((i2p::client::context.GetBOBCommandChannel ()) ? "enabled" : "disabled") << "'></div></td></tr>\r\n";
s << "<tr><td>" << "SAM" << "</td><td><div class='" << ((i2p::client::context.GetSAMBridge ()) ? "enabled" : "disabled") << "'></div></td></tr>\r\n"; s << "<tr><td>" << "SAM" << "</td><td><div class='" << ((i2p::client::context.GetSAMBridge ()) ? "enabled" : "disabled") << "'></div></td></tr>\r\n";
s << "<tr><td>" << "I2CP" << "</td><td><div class='" << ((i2p::client::context.GetI2CPServer ()) ? "enabled" : "disabled") << "'></div></td></tr>\r\n"; s << "<tr><td>" << "I2CP" << "</td><td><div class='" << ((i2p::client::context.GetI2CPServer ()) ? "enabled" : "disabled") << "'></div></td></tr>\r\n";
@ -393,7 +404,7 @@ namespace http {
void ShowLocalDestinations (std::stringstream& s) void ShowLocalDestinations (std::stringstream& s)
{ {
std::string webroot; i2p::config::GetOption("http.webroot", webroot); std::string webroot; i2p::config::GetOption("http.webroot", webroot);
s << "<b>Local Destinations:</b><br>\r\n<div class=\"list\">\r\n"; s << "<b>" << tr("Local Destinations") << ":</b><br>\r\n<div class=\"list\">\r\n";
for (auto& it: i2p::client::context.GetDestinations ()) for (auto& it: i2p::client::context.GetDestinations ())
{ {
auto ident = it.second->GetIdentHash (); auto ident = it.second->GetIdentHash ();
@ -405,7 +416,7 @@ namespace http {
auto i2cpServer = i2p::client::context.GetI2CPServer (); auto i2cpServer = i2p::client::context.GetI2CPServer ();
if (i2cpServer && !(i2cpServer->GetSessions ().empty ())) if (i2cpServer && !(i2cpServer->GetSessions ().empty ()))
{ {
s << "<br><b>I2CP Local Destinations:</b><br>\r\n<div class=\"list\">\r\n"; s << "<br><b>I2CP "<< tr("Local Destinations") << ":</b><br>\r\n<div class=\"list\">\r\n";
for (auto& it: i2cpServer->GetSessions ()) for (auto& it: i2cpServer->GetSessions ())
{ {
auto dest = it.second->GetDestination (); auto dest = it.second->GetDestination ();
@ -428,7 +439,7 @@ namespace http {
if (dest->IsEncryptedLeaseSet ()) if (dest->IsEncryptedLeaseSet ())
{ {
i2p::data::BlindedPublicKey blinded (dest->GetIdentity (), dest->IsPerClientAuth ()); i2p::data::BlindedPublicKey blinded (dest->GetIdentity (), dest->IsPerClientAuth ());
s << "<div class='slide'><label for='slide-b33'><b>Encrypted B33 address:</b></label>\r\n<input type=\"checkbox\" id=\"slide-b33\" />\r\n<div class=\"slidecontent\">\r\n"; s << "<div class='slide'><label for='slide-b33'><b>" << tr("Encrypted B33 address") << ":</b></label>\r\n<input type=\"checkbox\" id=\"slide-b33\" />\r\n<div class=\"slidecontent\">\r\n";
s << blinded.ToB33 () << ".b32.i2p<br>\r\n"; s << blinded.ToB33 () << ".b32.i2p<br>\r\n";
s << "</div>\r\n</div>\r\n"; s << "</div>\r\n</div>\r\n";
} }
@ -437,67 +448,67 @@ namespace http {
{ {
std::string webroot; i2p::config::GetOption("http.webroot", webroot); std::string webroot; i2p::config::GetOption("http.webroot", webroot);
auto base32 = dest->GetIdentHash ().ToBase32 (); auto base32 = dest->GetIdentHash ().ToBase32 ();
s << "<div class='slide'><label for='slide-regaddr'><b>Address registration line</b></label>\r\n<input type=\"checkbox\" id=\"slide-regaddr\" />\r\n<div class=\"slidecontent\">\r\n" s << "<div class='slide'><label for='slide-regaddr'><b>" << tr("Address registration line") << "</b></label>\r\n<input type=\"checkbox\" id=\"slide-regaddr\" />\r\n<div class=\"slidecontent\">\r\n"
"<form method=\"get\" action=\"" << webroot << "\">\r\n" "<form method=\"get\" action=\"" << webroot << "\">\r\n"
" <input type=\"hidden\" name=\"cmd\" value=\"" << HTTP_COMMAND_GET_REG_STRING << "\">\r\n" " <input type=\"hidden\" name=\"cmd\" value=\"" << HTTP_COMMAND_GET_REG_STRING << "\">\r\n"
" <input type=\"hidden\" name=\"token\" value=\"" << token << "\">\r\n" " <input type=\"hidden\" name=\"token\" value=\"" << token << "\">\r\n"
" <input type=\"hidden\" name=\"b32\" value=\"" << base32 << "\">\r\n" " <input type=\"hidden\" name=\"b32\" value=\"" << base32 << "\">\r\n"
" <b>Domain:</b>\r\n<input type=\"text\" maxlength=\"67\" name=\"name\" placeholder=\"domain.i2p\" required>\r\n" " <b>" << tr("Domain") << ":</b>\r\n<input type=\"text\" maxlength=\"67\" name=\"name\" placeholder=\"domain.i2p\" required>\r\n"
" <button type=\"submit\">Generate</button>\r\n" " <button type=\"submit\">" << tr("Generate") << "</button>\r\n"
"</form>\r\n<small><b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.</small>\r\n</div>\r\n</div>\r\n<br>\r\n"; "</form>\r\n<small>" << tr("<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.") << "</small>\r\n</div>\r\n</div>\r\n<br>\r\n";
} }
if(dest->GetNumRemoteLeaseSets()) if(dest->GetNumRemoteLeaseSets())
{ {
s << "<div class='slide'><label for='slide-lease'><b>LeaseSets:</b> <i>" << dest->GetNumRemoteLeaseSets () s << "<div class='slide'><label for='slide-lease'><b>" << tr("LeaseSets") << ":</b> <i>" << dest->GetNumRemoteLeaseSets ()
<< "</i></label>\r\n<input type=\"checkbox\" id=\"slide-lease\" />\r\n<div class=\"slidecontent\">\r\n<table><thead><th>Address</th><th>Type</th><th>EncType</th></thead><tbody class=\"tableitem\">"; << "</i></label>\r\n<input type=\"checkbox\" id=\"slide-lease\" />\r\n<div class=\"slidecontent\">\r\n<table><thead><th>"<< tr("Address") << "</th><th>" << tr("Type") << "</th><th>" << tr("EncType") << "</th></thead><tbody class=\"tableitem\">";
for(auto& it: dest->GetLeaseSets ()) for(auto& it: dest->GetLeaseSets ())
s << "<tr><td>" << it.first.ToBase32 () << "</td><td>" << (int)it.second->GetStoreType () << "</td><td>" << (int)it.second->GetEncryptionType () <<"</td></tr>\r\n"; s << "<tr><td>" << it.first.ToBase32 () << "</td><td>" << (int)it.second->GetStoreType () << "</td><td>" << (int)it.second->GetEncryptionType () <<"</td></tr>\r\n";
s << "</tbody></table>\r\n</div>\r\n</div>\r\n<br>\r\n"; s << "</tbody></table>\r\n</div>\r\n</div>\r\n<br>\r\n";
} else } else
s << "<b>LeaseSets:</b> <i>0</i><br>\r\n<br>\r\n"; s << "<b>" << tr("LeaseSets") << ":</b> <i>0</i><br>\r\n<br>\r\n";
auto pool = dest->GetTunnelPool (); auto pool = dest->GetTunnelPool ();
if (pool) if (pool)
{ {
s << "<b>Inbound tunnels:</b><br>\r\n<div class=\"list\">\r\n"; s << "<b>" << tr("Inbound tunnels") << ":</b><br>\r\n<div class=\"list\">\r\n";
for (auto & it : pool->GetInboundTunnels ()) { for (auto & it : pool->GetInboundTunnels ()) {
s << "<div class=\"listitem\">"; s << "<div class=\"listitem\">";
it->Print(s); it->Print(s);
if(it->LatencyIsKnown()) if(it->LatencyIsKnown())
s << " ( " << it->GetMeanLatency() << "ms )"; s << " ( " << it->GetMeanLatency() << tr("ms") << " )";
ShowTunnelDetails(s, it->GetState (), false, it->GetNumReceivedBytes ()); ShowTunnelDetails(s, it->GetState (), false, it->GetNumReceivedBytes ());
s << "</div>\r\n"; s << "</div>\r\n";
} }
s << "<br>\r\n"; s << "<br>\r\n";
s << "<b>Outbound tunnels:</b><br>\r\n<div class=\"list\">\r\n"; s << "<b>" << tr("Outbound tunnels") << ":</b><br>\r\n<div class=\"list\">\r\n";
for (auto & it : pool->GetOutboundTunnels ()) { for (auto & it : pool->GetOutboundTunnels ()) {
s << "<div class=\"listitem\">"; s << "<div class=\"listitem\">";
it->Print(s); it->Print(s);
if(it->LatencyIsKnown()) if(it->LatencyIsKnown())
s << " ( " << it->GetMeanLatency() << "ms )"; s << " ( " << it->GetMeanLatency() << tr("ms") << " )";
ShowTunnelDetails(s, it->GetState (), false, it->GetNumSentBytes ()); ShowTunnelDetails(s, it->GetState (), false, it->GetNumSentBytes ());
s << "</div>\r\n"; s << "</div>\r\n";
} }
} }
s << "<br>\r\n"; s << "<br>\r\n";
s << "<b>Tags</b><br>\r\nIncoming: <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 ()) { if (!dest->GetSessions ().empty ()) {
std::stringstream tmp_s; uint32_t out_tags = 0; std::stringstream tmp_s; uint32_t out_tags = 0;
for (const auto& it: dest->GetSessions ()) { 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"; 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 (); out_tags += it.second->GetNumOutgoingTags ();
} }
s << "<div class='slide'><label for='slide-tags'>Outgoing: <i>" << out_tags << "</i></label>\r\n<input type=\"checkbox\" id=\"slide-tags\" />\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>Destination</th><th>Amount</th></thead>\r\n<tbody class=\"tableitem\">\r\n" << tmp_s.str () << "</tbody></table>\r\n</div>\r\n</div>\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 } else
s << "Outgoing: <i>0</i><br>\r\n"; s << tr("Outgoing") << ": <i>0</i><br>\r\n";
s << "<br>\r\n"; s << "<br>\r\n";
auto numECIESx25519Tags = dest->GetNumIncomingECIESx25519Tags (); auto numECIESx25519Tags = dest->GetNumIncomingECIESx25519Tags ();
if (numECIESx25519Tags > 0) { if (numECIESx25519Tags > 0) {
s << "<b>ECIESx25519</b><br>\r\nIncoming Tags: <i>" << numECIESx25519Tags << "</i><br>\r\n"; s << "<b>ECIESx25519</b><br>\r\n" << tr("Incoming Tags") << ": <i>" << numECIESx25519Tags << "</i><br>\r\n";
if (!dest->GetECIESx25519Sessions ().empty ()) if (!dest->GetECIESx25519Sessions ().empty ())
{ {
std::stringstream tmp_s; uint32_t ecies_sessions = 0; std::stringstream tmp_s; uint32_t ecies_sessions = 0;
@ -505,17 +516,17 @@ namespace http {
tmp_s << "<tr><td>" << i2p::client::context.GetAddressBook ().ToAddress(it.second->GetDestination ()) << "</td><td>" << it.second->GetState () << "</td></tr>\r\n"; tmp_s << "<tr><td>" << i2p::client::context.GetAddressBook ().ToAddress(it.second->GetDestination ()) << "</td><td>" << it.second->GetState () << "</td></tr>\r\n";
ecies_sessions++; ecies_sessions++;
} }
s << "<div class='slide'><label for='slide-ecies-sessions'>Tags sessions: <i>" << ecies_sessions << "</i></label>\r\n<input type=\"checkbox\" id=\"slide-ecies-sessions\" />\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>Destination</th><th>Status</th></thead>\r\n<tbody class=\"tableitem\">\r\n" << tmp_s.str () << "</tbody></table>\r\n</div>\r\n</div>\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 } else
s << "Tags sessions: <i>0</i><br>\r\n"; s << tr("Tags sessions") << ": <i>0</i><br>\r\n";
s << "<br>\r\n"; s << "<br>\r\n";
} }
} }
void ShowLocalDestination (std::stringstream& s, const std::string& b32, uint32_t token) void ShowLocalDestination (std::stringstream& s, const std::string& b32, uint32_t token)
{ {
s << "<b>Local Destination:</b><br>\r\n<br>\r\n"; s << "<b>" << tr("Local Destination") << ":</b><br>\r\n<br>\r\n";
i2p::data::IdentHash ident; i2p::data::IdentHash ident;
ident.FromBase32 (b32); ident.FromBase32 (b32);
auto dest = i2p::client::context.FindLocalDestination (ident); auto dest = i2p::client::context.FindLocalDestination (ident);
@ -524,7 +535,7 @@ namespace http {
{ {
ShowLeaseSetDestination (s, dest, token); ShowLeaseSetDestination (s, dest, token);
// show streams // show streams
s << "<table>\r\n<caption>Streams</caption>\r\n<thead>\r\n<tr>"; s << "<table>\r\n<caption>" << tr("Streams") << "</caption>\r\n<thead>\r\n<tr>";
s << "<th style=\"width:25px;\">StreamID</th>"; s << "<th style=\"width:25px;\">StreamID</th>";
s << "<th style=\"width:5px;\" \\>"; // Stream closing button column s << "<th style=\"width:5px;\" \\>"; // Stream closing button column
s << "<th class=\"streamdest\">Destination</th>"; s << "<th class=\"streamdest\">Destination</th>";
@ -546,7 +557,7 @@ namespace http {
s << "<td>" << it->GetRecvStreamID () << "</td>"; s << "<td>" << it->GetRecvStreamID () << "</td>";
if (it->GetRecvStreamID ()) { if (it->GetRecvStreamID ()) {
s << "<td><a class=\"button\" href=\"/?cmd=" << HTTP_COMMAND_KILLSTREAM << "&b32=" << b32 << "&streamID=" s << "<td><a class=\"button\" href=\"/?cmd=" << HTTP_COMMAND_KILLSTREAM << "&b32=" << b32 << "&streamID="
<< it->GetRecvStreamID () << "&token=" << token << "\" title=\"Close stream\"> &#10008; </a></td>"; << it->GetRecvStreamID () << "&token=" << token << "\" title=\"" << tr("Close stream") << "\"> &#10008; </a></td>";
} else { } else {
s << "<td \\>"; s << "<td \\>";
} }
@ -570,22 +581,22 @@ namespace http {
auto i2cpServer = i2p::client::context.GetI2CPServer (); auto i2cpServer = i2p::client::context.GetI2CPServer ();
if (i2cpServer) if (i2cpServer)
{ {
s << "<b>I2CP Local Destination:</b><br>\r\n<br>\r\n"; s << "<b>I2CP " << tr("Local Destination") << ":</b><br>\r\n<br>\r\n";
auto it = i2cpServer->GetSessions ().find (std::stoi (id)); auto it = i2cpServer->GetSessions ().find (std::stoi (id));
if (it != i2cpServer->GetSessions ().end ()) if (it != i2cpServer->GetSessions ().end ())
ShowLeaseSetDestination (s, it->second->GetDestination (), 0); ShowLeaseSetDestination (s, it->second->GetDestination (), 0);
else else
ShowError(s, "I2CP session not found"); ShowError(s, tr("I2CP session not found"));
} }
else else
ShowError(s, "I2CP is not enabled"); ShowError(s, tr("I2CP is not enabled"));
} }
void ShowLeasesSets(std::stringstream& s) void ShowLeasesSets(std::stringstream& s)
{ {
if (i2p::data::netdb.GetNumLeaseSets ()) if (i2p::data::netdb.GetNumLeaseSets ())
{ {
s << "<b>LeaseSets:</b><br>\r\n<div class=\"list\">\r\n"; s << "<b>" << tr("LeaseSets") << ":</b><br>\r\n<div class=\"list\">\r\n";
int counter = 1; int counter = 1;
// for each lease set // for each lease set
i2p::data::netdb.VisitLeaseSets( i2p::data::netdb.VisitLeaseSets(
@ -604,21 +615,21 @@ namespace http {
s << " expired"; // additional css class for expired s << " expired"; // additional css class for expired
s << "\">\r\n"; s << "\">\r\n";
if (!ls->IsValid()) if (!ls->IsValid())
s << "<div class=\"invalid\">!! Invalid !! </div>\r\n"; s << "<div class=\"invalid\">!! " << tr("Invalid") << " !! </div>\r\n";
s << "<div class=\"slide\"><label for=\"slide" << counter << "\">" << dest.ToBase32() << "</label>\r\n"; s << "<div class=\"slide\"><label for=\"slide" << counter << "\">" << dest.ToBase32() << "</label>\r\n";
s << "<input type=\"checkbox\" id=\"slide" << (counter++) << "\" />\r\n<div class=\"slidecontent\">\r\n"; s << "<input type=\"checkbox\" id=\"slide" << (counter++) << "\" />\r\n<div class=\"slidecontent\">\r\n";
s << "<b>Store type:</b> " << (int)storeType << "<br>\r\n"; s << "<b>" << tr("Store type") << ":</b> " << (int)storeType << "<br>\r\n";
s << "<b>Expires:</b> " << ConvertTime(ls->GetExpirationTime()) << "<br>\r\n"; s << "<b>" << tr("Expires") << ":</b> " << ConvertTime(ls->GetExpirationTime()) << "<br>\r\n";
if (storeType == i2p::data::NETDB_STORE_TYPE_LEASESET || storeType == i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2) if (storeType == i2p::data::NETDB_STORE_TYPE_LEASESET || storeType == i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2)
{ {
// leases information is available // leases information is available
auto leases = ls->GetNonExpiredLeases(); auto leases = ls->GetNonExpiredLeases();
s << "<b>Non Expired Leases: " << leases.size() << "</b><br>\r\n"; s << "<b>" << tr("Non Expired Leases") << ": " << leases.size() << "</b><br>\r\n";
for ( auto & l : leases ) for ( auto & l : leases )
{ {
s << "<b>Gateway:</b> " << l->tunnelGateway.ToBase64() << "<br>\r\n"; s << "<b>" << tr("Gateway") << ":</b> " << l->tunnelGateway.ToBase64() << "<br>\r\n";
s << "<b>TunnelID:</b> " << l->tunnelID << "<br>\r\n"; s << "<b>" << tr("TunnelID") << ":</b> " << l->tunnelID << "<br>\r\n";
s << "<b>EndDate:</b> " << ConvertTime(l->endDate) << "<br>\r\n"; s << "<b>" << tr("EndDate") << ":</b> " << ConvertTime(l->endDate) << "<br>\r\n";
} }
} }
s << "</div>\r\n</div>\r\n</div>\r\n"; s << "</div>\r\n</div>\r\n</div>\r\n";
@ -628,37 +639,37 @@ namespace http {
} }
else if (!i2p::context.IsFloodfill ()) else if (!i2p::context.IsFloodfill ())
{ {
s << "<b>LeaseSets:</b> not floodfill.<br>\r\n"; s << "<b>" << tr("LeaseSets") << ":</b> " << tr("not floodfill") << ".<br>\r\n";
} }
else else
{ {
s << "<b>LeaseSets:</b> 0<br>\r\n"; s << "<b>" << tr("LeaseSets") << ":</b> 0<br>\r\n";
} }
} }
void ShowTunnels (std::stringstream& s) void ShowTunnels (std::stringstream& s)
{ {
s << "<b>Tunnels:</b><br>\r\n"; s << "<b>" << tr("Tunnels") << ":</b><br>\r\n";
s << "<b>Queue size:</b> " << i2p::tunnel::tunnels.GetQueueSize () << "<br>\r\n<br>\r\n"; s << "<b>" << tr("Queue size") << ":</b> " << i2p::tunnel::tunnels.GetQueueSize () << "<br>\r\n<br>\r\n";
auto ExplPool = i2p::tunnel::tunnels.GetExploratoryPool (); auto ExplPool = i2p::tunnel::tunnels.GetExploratoryPool ();
s << "<b>Inbound tunnels:</b><br>\r\n<div class=\"list\">\r\n"; s << "<b>" << tr("Inbound tunnels") << ":</b><br>\r\n<div class=\"list\">\r\n";
for (auto & it : i2p::tunnel::tunnels.GetInboundTunnels ()) { for (auto & it : i2p::tunnel::tunnels.GetInboundTunnels ()) {
s << "<div class=\"listitem\">"; s << "<div class=\"listitem\">";
it->Print(s); it->Print(s);
if(it->LatencyIsKnown()) if(it->LatencyIsKnown())
s << " ( " << it->GetMeanLatency() << "ms )"; s << " ( " << it->GetMeanLatency() << tr("ms") << " )";
ShowTunnelDetails(s, it->GetState (), (it->GetTunnelPool () == ExplPool), it->GetNumReceivedBytes ()); ShowTunnelDetails(s, it->GetState (), (it->GetTunnelPool () == ExplPool), it->GetNumReceivedBytes ());
s << "</div>\r\n"; s << "</div>\r\n";
} }
s << "</div>\r\n<br>\r\n"; s << "</div>\r\n<br>\r\n";
s << "<b>Outbound tunnels:</b><br>\r\n<div class=\"list\">\r\n"; s << "<b>" << tr("Outbound tunnels") << ":</b><br>\r\n<div class=\"list\">\r\n";
for (auto & it : i2p::tunnel::tunnels.GetOutboundTunnels ()) { for (auto & it : i2p::tunnel::tunnels.GetOutboundTunnels ()) {
s << "<div class=\"listitem\">"; s << "<div class=\"listitem\">";
it->Print(s); it->Print(s);
if(it->LatencyIsKnown()) if(it->LatencyIsKnown())
s << " ( " << it->GetMeanLatency() << "ms )"; s << " ( " << it->GetMeanLatency() << tr("ms") << " )";
ShowTunnelDetails(s, it->GetState (), (it->GetTunnelPool () == ExplPool), it->GetNumSentBytes ()); ShowTunnelDetails(s, it->GetState (), (it->GetTunnelPool () == ExplPool), it->GetNumSentBytes ());
s << "</div>\r\n"; s << "</div>\r\n";
} }
@ -669,30 +680,30 @@ namespace http {
{ {
std::string webroot; i2p::config::GetOption("http.webroot", webroot); std::string webroot; i2p::config::GetOption("http.webroot", webroot);
/* commands */ /* commands */
s << "<b>Router Commands</b><br>\r\n<br>\r\n<div class=\"commands\">\r\n"; s << "<b>" << tr("Router commands") << "</b><br>\r\n<br>\r\n<div class=\"commands\">\r\n";
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_RUN_PEER_TEST << "&token=" << token << "\">Run peer test</a>\r\n"; s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_RUN_PEER_TEST << "&token=" << token << "\">" << tr("Run peer test") << "</a>\r\n";
//s << " <a href=\"/?cmd=" << HTTP_COMMAND_RELOAD_CONFIG << "\">Reload config</a><br>\r\n"; //s << " <a href=\"/?cmd=" << HTTP_COMMAND_RELOAD_CONFIG << "\">Reload config</a><br>\r\n";
if (i2p::context.AcceptsTunnels ()) if (i2p::context.AcceptsTunnels ())
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_DISABLE_TRANSIT << "&token=" << token << "\">Decline transit tunnels</a>\r\n"; s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_DISABLE_TRANSIT << "&token=" << token << "\">" << tr("Decline transit tunnels") << "</a>\r\n";
else else
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_ENABLE_TRANSIT << "&token=" << token << "\">Accept transit tunnels</a>\r\n"; s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_ENABLE_TRANSIT << "&token=" << token << "\">" << tr("Accept transit tunnels") << "</a>\r\n";
#if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY)) #if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY))
if (Daemon.gracefulShutdownInterval) if (Daemon.gracefulShutdownInterval)
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_CANCEL << "&token=" << token << "\">Cancel graceful shutdown</a>\r\n"; s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_CANCEL << "&token=" << token << "\">" << tr("Cancel graceful shutdown") << "</a>\r\n";
else else
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_START << "&token=" << token << "\">Start graceful shutdown</a><br>\r\n"; s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_START << "&token=" << token << "\">" << tr("Start graceful shutdown") << "</a>\r\n";
#elif defined(WIN32_APP) #elif defined(WIN32_APP)
if (i2p::util::DaemonWin32::Instance().isGraceful) if (i2p::util::DaemonWin32::Instance().isGraceful)
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_CANCEL << "&token=" << token << "\">Cancel graceful shutdown</a>\r\n"; s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_CANCEL << "&token=" << token << "\">" << tr("Cancel graceful shutdown") << "</a>\r\n";
else else
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_START << "&token=" << token << "\">Graceful shutdown</a>\r\n"; s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_START << "&token=" << token << "\">" << tr("Start graceful shutdown") << "</a>\r\n";
#endif #endif
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_NOW << "&token=" << token << "\">Force shutdown</a>\r\n"; s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_NOW << "&token=" << token << "\">" << tr("Force shutdown") << "</a>\r\n";
s << "</div>"; s << "</div>";
s << "<br>\r\n<small><b>Note:</b> any action done here are not persistent and not changes your config files.</small>\r\n<br>\r\n"; 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";
s << "<b>Logging level</b><br>\r\n"; 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=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=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=warn&token=" << token << "\"> warn </a> \r\n";
@ -700,12 +711,12 @@ namespace http {
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\" href=\"" << webroot << "?cmd=" << HTTP_COMMAND_LOGLEVEL << "&level=debug&token=" << token << "\"> debug </a><br>\r\n<br>\r\n";
uint16_t maxTunnels = GetMaxNumTransitTunnels (); uint16_t maxTunnels = GetMaxNumTransitTunnels ();
s << "<b>Transit tunnels limit</b><br>\r\n"; s << "<b>" << tr("Transit tunnels limit") << "</b><br>\r\n";
s << "<form method=\"get\" action=\"" << webroot << "\">\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=\"cmd\" value=\"" << HTTP_COMMAND_LIMITTRANSIT << "\">\r\n";
s << " <input type=\"hidden\" name=\"token\" value=\"" << token << "\">\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=\"65535\" name=\"limit\" value=\"" << maxTunnels << "\">\r\n";
s << " <button type=\"submit\">Change</button>\r\n"; s << " <button type=\"submit\">" << tr("Change") << "</button>\r\n";
s << "</form>\r\n<br>\r\n"; s << "</form>\r\n<br>\r\n";
} }
@ -713,7 +724,7 @@ namespace http {
{ {
if(i2p::tunnel::tunnels.CountTransitTunnels()) if(i2p::tunnel::tunnels.CountTransitTunnels())
{ {
s << "<b>Transit tunnels:</b><br>\r\n<div class=\"list\">\r\n"; s << "<b>" << tr("Transit tunnels") << ":</b><br>\r\n<div class=\"list\">\r\n";
for (const auto& it: i2p::tunnel::tunnels.GetTransitTunnels ()) for (const auto& it: i2p::tunnel::tunnels.GetTransitTunnels ())
{ {
s << "<div class=\"listitem\">\r\n"; s << "<div class=\"listitem\">\r\n";
@ -729,7 +740,7 @@ namespace http {
} }
else else
{ {
s << "<b>Transit tunnels:</b> no transit tunnels currently built.<br>\r\n"; s << "<b>" << tr("Transit tunnels") << ":</b> " << tr("no transit tunnels currently built") << ".<br>\r\n";
} }
} }
@ -778,7 +789,7 @@ namespace http {
void ShowTransports (std::stringstream& s) void ShowTransports (std::stringstream& s)
{ {
s << "<b>Transports:</b><br>\r\n"; s << "<b>" << tr("Transports") << ":</b><br>\r\n";
auto ntcp2Server = i2p::transport::transports.GetNTCP2Server (); auto ntcp2Server = i2p::transport::transports.GetNTCP2Server ();
if (ntcp2Server) if (ntcp2Server)
{ {
@ -834,13 +845,13 @@ namespace http {
auto sam = i2p::client::context.GetSAMBridge (); auto sam = i2p::client::context.GetSAMBridge ();
if (!sam) if (!sam)
{ {
ShowError(s, "SAM disabled"); ShowError(s, tr("SAM disabled"));
return; return;
} }
if(sam->GetSessions ().size ()) if(sam->GetSessions ().size ())
{ {
s << "<b>SAM Sessions:</b><br>\r\n<div class=\"list\">\r\n"; s << "<b>" << tr("SAM sessions") << ":</b><br>\r\n<div class=\"list\">\r\n";
for (auto& it: sam->GetSessions ()) for (auto& it: sam->GetSessions ())
{ {
auto& name = it.second->GetLocalDestination ()->GetNickname (); auto& name = it.second->GetLocalDestination ()->GetNickname ();
@ -850,30 +861,30 @@ namespace http {
s << "</div>\r\n"; s << "</div>\r\n";
} }
else else
s << "<b>SAM Sessions:</b> no sessions currently running.<br>\r\n"; s << "<b>" << tr("SAM sessions") << ":</b> " << tr("no sessions currently running") << ".<br>\r\n";
} }
void ShowSAMSession (std::stringstream& s, const std::string& id) void ShowSAMSession (std::stringstream& s, const std::string& id)
{ {
auto sam = i2p::client::context.GetSAMBridge (); auto sam = i2p::client::context.GetSAMBridge ();
if (!sam) { if (!sam) {
ShowError(s, "SAM disabled"); ShowError(s, tr("SAM disabled"));
return; return;
} }
auto session = sam->FindSession (id); auto session = sam->FindSession (id);
if (!session) { if (!session) {
ShowError(s, "SAM session not found"); ShowError(s, tr("SAM session not found"));
return; return;
} }
std::string webroot; i2p::config::GetOption("http.webroot", webroot); std::string webroot; i2p::config::GetOption("http.webroot", webroot);
s << "<b>SAM Session:</b><br>\r\n<div class=\"list\">\r\n"; s << "<b>" << tr("SAM Session") << ":</b><br>\r\n<div class=\"list\">\r\n";
auto& ident = session->GetLocalDestination ()->GetIdentHash(); auto& ident = session->GetLocalDestination ()->GetIdentHash();
s << "<div class=\"listitem\"><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 << i2p::client::context.GetAddressBook ().ToAddress(ident) << "</a></div>\r\n"; s << i2p::client::context.GetAddressBook ().ToAddress(ident) << "</a></div>\r\n";
s << "<br>\r\n"; s << "<br>\r\n";
s << "<b>Streams:</b><br>\r\n<div class=\"list\">\r\n"; s << "<b>" << tr("Streams") << ":</b><br>\r\n<div class=\"list\">\r\n";
for (const auto& it: sam->ListSockets(id)) for (const auto& it: sam->ListSockets(id))
{ {
s << "<div class=\"listitem\">"; s << "<div class=\"listitem\">";
@ -882,7 +893,7 @@ namespace http {
case i2p::client::eSAMSocketTypeSession : s << "session"; break; case i2p::client::eSAMSocketTypeSession : s << "session"; break;
case i2p::client::eSAMSocketTypeStream : s << "stream"; break; case i2p::client::eSAMSocketTypeStream : s << "stream"; break;
case i2p::client::eSAMSocketTypeAcceptor : s << "acceptor"; break; case i2p::client::eSAMSocketTypeAcceptor : s << "acceptor"; break;
case i2p::client::eSAMSocketTypeForward : s << "forward"; break; case i2p::client::eSAMSocketTypeForward : s << "forward"; break;
default: s << "unknown"; break; default: s << "unknown"; break;
} }
s << " [" << it->GetSocket ().remote_endpoint() << "]"; s << " [" << it->GetSocket ().remote_endpoint() << "]";
@ -894,7 +905,7 @@ namespace http {
void ShowI2PTunnels (std::stringstream& s) void ShowI2PTunnels (std::stringstream& s)
{ {
std::string webroot; i2p::config::GetOption("http.webroot", webroot); std::string webroot; i2p::config::GetOption("http.webroot", webroot);
s << "<b>Client Tunnels:</b><br>\r\n<div class=\"list\">\r\n"; s << "<b>" << tr("Client Tunnels") << ":</b><br>\r\n<div class=\"list\">\r\n";
for (auto& it: i2p::client::context.GetClientTunnels ()) for (auto& it: i2p::client::context.GetClientTunnels ())
{ {
auto& ident = it.second->GetLocalDestination ()->GetIdentHash(); auto& ident = it.second->GetLocalDestination ()->GetIdentHash();
@ -908,7 +919,7 @@ namespace http {
{ {
auto& ident = httpProxy->GetLocalDestination ()->GetIdentHash(); auto& ident = httpProxy->GetLocalDestination ()->GetIdentHash();
s << "<div class=\"listitem\"><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 << "HTTP Proxy" << "</a> &#8656; "; s << "HTTP " << tr("Proxy") << "</a> &#8656; ";
s << i2p::client::context.GetAddressBook ().ToAddress(ident); s << i2p::client::context.GetAddressBook ().ToAddress(ident);
s << "</div>\r\n"<< std::endl; s << "</div>\r\n"<< std::endl;
} }
@ -917,7 +928,7 @@ namespace http {
{ {
auto& ident = socksProxy->GetLocalDestination ()->GetIdentHash(); auto& ident = socksProxy->GetLocalDestination ()->GetIdentHash();
s << "<div class=\"listitem\"><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 << "SOCKS Proxy" << "</a> &#8656; "; s << "SOCKS " << tr("Proxy") << "</a> &#8656; ";
s << i2p::client::context.GetAddressBook ().ToAddress(ident); s << i2p::client::context.GetAddressBook ().ToAddress(ident);
s << "</div>\r\n"<< std::endl; s << "</div>\r\n"<< std::endl;
} }
@ -925,7 +936,7 @@ namespace http {
auto& serverTunnels = i2p::client::context.GetServerTunnels (); auto& serverTunnels = i2p::client::context.GetServerTunnels ();
if (!serverTunnels.empty ()) { if (!serverTunnels.empty ()) {
s << "<br>\r\n<b>Server Tunnels:</b><br>\r\n<div class=\"list\">\r\n"; s << "<br>\r\n<b>" << tr("Server Tunnels") << ":</b><br>\r\n<div class=\"list\">\r\n";
for (auto& it: serverTunnels) for (auto& it: serverTunnels)
{ {
auto& ident = it.second->GetLocalDestination ()->GetIdentHash(); auto& ident = it.second->GetLocalDestination ()->GetIdentHash();
@ -941,7 +952,7 @@ namespace http {
auto& clientForwards = i2p::client::context.GetClientForwards (); auto& clientForwards = i2p::client::context.GetClientForwards ();
if (!clientForwards.empty ()) if (!clientForwards.empty ())
{ {
s << "<br>\r\n<b>Client Forwards:</b><br>\r\n<div class=\"list\">\r\n"; s << "<br>\r\n<b>" << tr("Client Forwards") << ":</b><br>\r\n<div class=\"list\">\r\n";
for (auto& it: clientForwards) for (auto& it: clientForwards)
{ {
auto& ident = it.second->GetLocalDestination ()->GetIdentHash(); auto& ident = it.second->GetLocalDestination ()->GetIdentHash();
@ -955,7 +966,7 @@ namespace http {
auto& serverForwards = i2p::client::context.GetServerForwards (); auto& serverForwards = i2p::client::context.GetServerForwards ();
if (!serverForwards.empty ()) if (!serverForwards.empty ())
{ {
s << "<br>\r\n<b>Server Forwards:</b><br>\r\n<div class=\"list\">\r\n"; s << "<br>\r\n<b>" << tr("Server Forwards") << ":</b><br>\r\n<div class=\"list\">\r\n";
for (auto& it: serverForwards) for (auto& it: serverForwards)
{ {
auto& ident = it.second->GetLocalDestination ()->GetIdentHash(); auto& ident = it.second->GetLocalDestination ()->GetIdentHash();
@ -1087,7 +1098,7 @@ namespace http {
return; return;
} }
} }
// Html5 head start // HTML head start
ShowPageHead (s); ShowPageHead (s);
if (req.uri.find("page=") != std::string::npos) { if (req.uri.find("page=") != std::string::npos) {
HandlePage (req, res, s); HandlePage (req, res, s);
@ -1161,7 +1172,7 @@ namespace http {
ShowLeasesSets(s); ShowLeasesSets(s);
else { else {
res.code = 400; res.code = 400;
ShowError(s, "Unknown page: " + page); ShowError(s, tr("Unknown page") + ": " + page);
return; return;
} }
} }
@ -1180,7 +1191,7 @@ namespace http {
if (token.empty () || m_Tokens.find (std::stoi (token)) == m_Tokens.end ()) if (token.empty () || m_Tokens.find (std::stoi (token)) == m_Tokens.end ())
{ {
ShowError(s, "Invalid token"); ShowError(s, tr("Invalid token"));
return; return;
} }
@ -1238,18 +1249,18 @@ namespace http {
if (dest) if (dest)
{ {
if(dest->DeleteStream (streamID)) if(dest->DeleteStream (streamID))
s << "<b>SUCCESS</b>:&nbsp;Stream closed<br>\r\n<br>\r\n"; s << "<b>" << tr("SUCCESS") << "</b>:&nbsp;" << tr("Stream closed") << "<br>\r\n<br>\r\n";
else else
s << "<b>ERROR</b>:&nbsp;Stream not found or already was closed<br>\r\n<br>\r\n"; s << "<b>" << tr("ERROR") << "</b>:&nbsp;" << tr("Stream not found or already was closed") << "<br>\r\n<br>\r\n";
} }
else else
s << "<b>ERROR</b>:&nbsp;Destination not found<br>\r\n<br>\r\n"; s << "<b>" << tr("ERROR") << "</b>:&nbsp;" << tr("Destination not found") << "<br>\r\n<br>\r\n";
} }
else else
s << "<b>ERROR</b>:&nbsp;StreamID can be null<br>\r\n<br>\r\n"; s << "<b>" << tr("ERROR") << "</b>:&nbsp;" << tr("StreamID can't be null") << "<br>\r\n<br>\r\n";
s << "<a href=\"" << webroot << "?page=local_destination&b32=" << b32 << "\">Return to destination page</a><br>\r\n"; s << "<a href=\"" << webroot << "?page=local_destination&b32=" << b32 << "\">" << tr("Return to destination page") << "</a><br>\r\n";
s << "<p>You will be redirected back in 5 seconds</b>"; s << "<p>" << tr("You will be redirected back in 5 seconds") << "</b>";
redirect = "5; url=" + webroot + "?page=local_destination&b32=" + b32; redirect = "5; url=" + webroot + "?page=local_destination&b32=" + b32;
res.add_header("Refresh", redirect.c_str()); res.add_header("Refresh", redirect.c_str());
return; return;
@ -1260,9 +1271,9 @@ namespace http {
if (limit > 0 && limit <= 65535) if (limit > 0 && limit <= 65535)
SetMaxNumTransitTunnels (limit); SetMaxNumTransitTunnels (limit);
else { else {
s << "<b>ERROR</b>:&nbsp;Transit tunnels count must not exceed 65535\r\n<br>\r\n<br>\r\n"; s << "<b>" << tr("ERROR") << "</b>:&nbsp;" << tr("Transit tunnels count must not exceed 65535") << "\r\n<br>\r\n<br>\r\n";
s << "<a href=\"" << webroot << "?page=commands\">Back to commands list</a>\r\n<br>\r\n"; s << "<a href=\"" << webroot << "?page=commands\">" << tr("Back to commands list") << "</a>\r\n<br>\r\n";
s << "<p>You will be redirected back in 5 seconds</b>"; s << "<p>" << tr("You will be redirected back in 5 seconds") << "</b>";
res.add_header("Refresh", redirect.c_str()); res.add_header("Refresh", redirect.c_str());
return; return;
} }
@ -1295,37 +1306,37 @@ namespace http {
auto len = i2p::data::ByteStreamToBase64 (signature, signatureLen, sig, signatureLen*2); auto len = i2p::data::ByteStreamToBase64 (signature, signatureLen, sig, signatureLen*2);
sig[len] = 0; sig[len] = 0;
out << "#!sig=" << sig; out << "#!sig=" << sig;
s << "<b>SUCCESS</b>:<br>\r\n<form action=\"http://shx5vqsw7usdaunyzr2qmes2fq37oumybpudrd4jjj4e4vk4uusa.b32.i2p/add\" method=\"post\" rel=\"noreferrer\" target=\"_blank\">\r\n" s << "<b>" << tr("SUCCESS") << "</b>:<br>\r\n<form action=\"http://shx5vqsw7usdaunyzr2qmes2fq37oumybpudrd4jjj4e4vk4uusa.b32.i2p/add\" method=\"post\" rel=\"noreferrer\" target=\"_blank\">\r\n"
"<textarea readonly name=\"record\" cols=\"80\" rows=\"10\">" << out.str () << "</textarea>\r\n<br>\r\n<br>\r\n" "<textarea readonly name=\"record\" cols=\"80\" rows=\"10\">" << out.str () << "</textarea>\r\n<br>\r\n<br>\r\n"
"<b>Register at reg.i2p:</b>\r\n<br>\r\n" "<b>" << tr("Register at reg.i2p") << ":</b>\r\n<br>\r\n"
"<b>Description:</b>\r\n<input type=\"text\" maxlength=\"64\" name=\"desc\" placeholder=\"A bit information about service on domain\">\r\n" "<b>" << tr("Description") << ":</b>\r\n<input type=\"text\" maxlength=\"64\" name=\"desc\" placeholder=\"" << tr("A bit information about service on domain") << "\">\r\n"
"<input type=\"submit\" value=\"Submit\">\r\n" "<input type=\"submit\" value=\"" << tr("Submit") << "\">\r\n"
"</form>\r\n<br>\r\n"; "</form>\r\n<br>\r\n";
delete[] signature; delete[] signature;
delete[] sig; delete[] sig;
} }
else else
s << "<b>ERROR</b>:&nbsp;Domain can't end with .b32.i2p\r\n<br>\r\n<br>\r\n"; s << "<b>" << tr("ERROR") << "</b>:&nbsp;" << tr("Domain can't end with .b32.i2p") << "\r\n<br>\r\n<br>\r\n";
} }
else else
s << "<b>ERROR</b>:&nbsp;Domain must end with .i2p\r\n<br>\r\n<br>\r\n"; s << "<b>" << tr("ERROR") << "</b>:&nbsp;" << tr("Domain must end with .i2p") << "\r\n<br>\r\n<br>\r\n";
} }
else else
s << "<b>ERROR</b>:&nbsp;Such destination is not found\r\n<br>\r\n<br>\r\n"; s << "<b>" << tr("ERROR") << "</b>:&nbsp;" << tr("Such destination is not found") << "\r\n<br>\r\n<br>\r\n";
s << "<a href=\"" << webroot << "?page=local_destination&b32=" << b32 << "\">Return to destination page</a>\r\n"; s << "<a href=\"" << webroot << "?page=local_destination&b32=" << b32 << "\">" << tr("Return to destination page") << "</a>\r\n";
return; return;
} }
else else
{ {
res.code = 400; res.code = 400;
ShowError(s, "Unknown command: " + cmd); ShowError(s, tr("Unknown command") + ": " + cmd);
return; return;
} }
s << "<b>SUCCESS</b>:&nbsp;Command accepted<br><br>\r\n"; s << "<b>" << tr("SUCCESS") << "</b>:&nbsp;" << tr("Command accepted") << "<br><br>\r\n";
s << "<a href=\"" << webroot << "?page=commands\">Back to commands list</a><br>\r\n"; s << "<a href=\"" << webroot << "?page=commands\">" << tr("Back to commands list") << "</a><br>\r\n";
s << "<p>You will be redirected in 5 seconds</b>"; s << "<p>" << tr("You will be redirected in 5 seconds") << "</b>";
res.add_header("Refresh", redirect.c_str()); res.add_header("Refresh", redirect.c_str());
} }

2
debian/compat vendored
View File

@ -1 +1 @@
10 9

18
debian/control vendored
View File

@ -3,30 +3,16 @@ Section: net
Priority: optional Priority: optional
Maintainer: r4sas <r4sas@i2pmail.org> 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 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.6 Standards-Version: 3.9.8
Homepage: http://i2pd.website/ Homepage: http://i2pd.website/
Vcs-Git: git://github.com/PurpleI2P/i2pd.git Vcs-Git: git://github.com/PurpleI2P/i2pd.git
Vcs-Browser: https://github.com/PurpleI2P/i2pd Vcs-Browser: https://github.com/PurpleI2P/i2pd
Package: i2pd Package: i2pd
Architecture: any Architecture: any
Pre-Depends: adduser Pre-Depends: ${misc:Pre-Depends}, adduser
Depends: ${shlibs:Depends}, ${misc:Depends}, lsb-base, Depends: ${shlibs:Depends}, ${misc:Depends}, lsb-base,
Description: Full-featured C++ implementation of I2P client. Description: Full-featured C++ implementation of I2P client.
I2P (Invisible Internet Protocol) is a universal anonymous network layer. All I2P (Invisible Internet Protocol) is a universal anonymous network layer. All
communications over I2P are anonymous and end-to-end encrypted, participants communications over I2P are anonymous and end-to-end encrypted, participants
don't reveal their real IP addresses. don't reveal their real IP addresses.
.
This package contains the full-featured C++ implementation of I2P router.
Package: i2pd-dbg
Architecture: any
Priority: extra
Section: debug
Depends: i2pd (= ${binary:Version}), ${misc:Depends}
Description: i2pd debugging symbols
I2P (Invisible Internet Protocol) is a universal anonymous network layer. All
communications over I2P are anonymous and end-to-end encrypted, participants
don't reveal their real IP addresses.
.
This package contains symbols required for debugging.

29
debian/copyright vendored
View File

@ -6,13 +6,6 @@ Files: *
Copyright: 2013-2020 PurpleI2P Copyright: 2013-2020 PurpleI2P
License: BSD-3-clause License: BSD-3-clause
Files: qt/i2pd_qt/android/src/org/kde/necessitas/ministro/IMinistro.aidl
qt/i2pd_qt/android/src/org/kde/necessitas/ministro/IMinistroCallback.aidl
qt/i2pd_qt/android/src/org/qtproject/qt5/android/bindings/QtActivity.java
qt/i2pd_qt/android/src/org/qtproject/qt5/android/bindings/QtApplication.java
Copyright: 2011-2013 BogDan Vatra <bogdan@kde.org>
License: BSD-2-Clause
Files: debian/* Files: debian/*
Copyright: 2013-2015 Kill Your TV <killyourtv@i2pmail.org> Copyright: 2013-2015 Kill Your TV <killyourtv@i2pmail.org>
2014-2016 hagen <hagen@i2pmail.org> 2014-2016 hagen <hagen@i2pmail.org>
@ -49,28 +42,6 @@ License: BSD-3-clause
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
License: BSD-2-Clause
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE HOLDERS OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
License: GPL-2+ License: GPL-2+
This package is free software; you can redistribute it and/or modify This package is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by

4
debian/docs vendored
View File

@ -1,5 +1 @@
README.md README.md
contrib/i2pd.conf
contrib/subscriptions.txt
contrib/tunnels.conf
contrib/tunnels.d

2
debian/i2pd.dirs vendored
View File

@ -1,2 +0,0 @@
etc/i2pd
var/lib/i2pd

2
debian/i2pd.install vendored
View File

@ -1,5 +1,5 @@
i2pd usr/sbin/ i2pd usr/sbin/
contrib/i2pd.conf etc/i2pd/ contrib/i2pd.conf etc/i2pd/
contrib/tunnels.conf etc/i2pd/ contrib/tunnels.conf etc/i2pd/
contrib/subscriptions.txt etc/i2pd/ contrib/subscriptions.txt etc/i2pd/
contrib/certificates/ usr/share/i2pd/ contrib/certificates/ usr/share/i2pd/

3
debian/postinst vendored
View File

@ -12,7 +12,6 @@ case "$1" in
# Create user and group as a system user. # Create user and group as a system user.
if getent passwd $I2PDUSER > /dev/null 2>&1; then if getent passwd $I2PDUSER > /dev/null 2>&1; then
groupadd -f $I2PDUSER || true groupadd -f $I2PDUSER || true
usermod -s "/bin/false" -e 1 $I2PDUSER > /dev/null || true
else else
adduser --system --quiet --group --home $I2PDHOME $I2PDUSER adduser --system --quiet --group --home $I2PDHOME $I2PDUSER
fi fi
@ -23,7 +22,7 @@ case "$1" in
chmod 640 $LOGFILE chmod 640 $LOGFILE
chown -f ${I2PDUSER}:adm $LOGFILE chown -f ${I2PDUSER}:adm $LOGFILE
mkdir -p -m0750 $I2PDHOME mkdir -p -m0750 $I2PDHOME
chown -f -R -P ${I2PDUSER}:${I2PDUSER} ${I2PDHOME} chown -f -P ${I2PDUSER}:${I2PDUSER} ${I2PDHOME}
;; ;;
abort-upgrade|abort-remove|abort-deconfigure) abort-upgrade|abort-remove|abort-deconfigure)
echo "Aborting upgrade" echo "Aborting upgrade"

26
debian/rules vendored
View File

@ -1,22 +1,16 @@
#!/usr/bin/make -f #!/usr/bin/make -f
# -*- makefile -*-
# Uncomment this to turn on verbose mode.
#export DH_VERBOSE=1 #export DH_VERBOSE=1
DEB_BUILD_MAINT_OPTIONS=hardening=+bindnow
#DPKG_EXPORT_BUILDFLAGS = 1 export DEB_BUILD_MAINT_OPTIONS = hardening=+all
#include /usr/share/dpkg/buildflags.mk
#CXXFLAGS+=$(CPPFLAGS)
#PREFIX=/usr include /usr/share/dpkg/architecture.mk
export DEB_CXXFLAGS_MAINT_APPEND = -Wall -pedantic -O3
export DEB_LDFLAGS_MAINT_APPEND =
%: %:
dh $@ --parallel dh $@ --parallel
# dh_apparmor --profile-name=usr.sbin.i2pd -pi2pd
override_dh_strip:
dh_strip --dbg-package=i2pd-dbg
## uncomment this if you have "missing info" problem when building package
#override_dh_shlibdeps:
# dh_shlibdeps --dpkg-shlibdeps-params=--ignore-missing-info

6
debian/watch vendored
View File

@ -1,3 +1,3 @@
version=3 version=4 opts="filenamemangle=s%(?:.*?)?v?(\d[\d.]*)\.tar\.gz%i2pd-$1.tar.gz%" \
opts=filenamemangle=s/.+\/v?(\d\S*)\.tar\.gz/i2pd-$1\.tar\.gz/ \ https://github.com/PurpleI2P/i2pd/tags \
https://github.com/PurpleI2P/i2pd/tags .*/v?(\d\S*)\.tar\.gz (?:.*?/)?(\d[\d.]*)\.tar\.gz debian uupdate

View File

@ -19,4 +19,6 @@ LIB_CLIENT_SRC = $(wildcard $(LIB_CLIENT_SRC_DIR)/*.cpp)
#DAEMON_SRC = \ #DAEMON_SRC = \
# HTTPServer.cpp I2PControl.cpp UPnP.cpp Daemon.cpp i2pd.cpp # HTTPServer.cpp I2PControl.cpp UPnP.cpp Daemon.cpp i2pd.cpp
LANG_SRC = $(wildcard $(LANG_SRC_DIR)/*.cpp)
DAEMON_SRC = $(wildcard $(DAEMON_SRC_DIR)/*.cpp) DAEMON_SRC = $(wildcard $(DAEMON_SRC_DIR)/*.cpp)

74
i18n/Afrikaans.cpp Normal file
View File

@ -0,0 +1,74 @@
/*
* Copyright (c) 2021, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
* See full license text in LICENSE file at top of project tree
*/
#include <map>
#include <vector>
#include <string>
#include <memory>
#include "I18N.h"
// Afrikaans localization file
// This is an example translation file without strings in it.
namespace i2p
{
namespace i18n
{
namespace afrikaans // language
{
// 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
{
{"Disabled", "Gedeaktiveer"},
{"Enabled", "Geaktiveer"},
{"failed", "Het misluk"},
{"unknown", "onbekend"},
{"Tunnels", "Tonnels"},
{"Transit tunnels", "Deurgang tonnels"},
{"I2P tunnels", "I2P tonnels"},
{"SAM sessions", "SAM sessies"},
{"OK", "LEKKER"},
{"Testing", "Besig om te toets"},
{"Firewalled", "Vuurmuur'd"},
{"Unknown", "Onbekend"},
{"Error", "Fout"},
{"Offline", "Aflyn"},
{"Uptime", "Optyd"},
{"Network status", "Netwerk status"},
{"Network status v6", "Netwerk status v6"},
{"Family", "Familie"},
{"Received", "Ontvang"},
{"Sent", "Gestuur"},
{"Hidden content. Press on text to see.", "Hidden content. Druk om te sien."},
{"Router Ident", "Router Ident"},
{"Router Family", "Router Familie"},
{"", ""},
};
static std::map<std::string, std::vector<std::string>> plurals
{
{"days", {"dag", "dae"}},
{"hours", {"uur", "ure"}},
{"minutes", {"minuut", "minute"}},
{"seconds", {"seconde", "sekondes"}},
{"", {"", ""}},
};
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
{
return std::make_shared<i2p::i18n::Locale>(strings, plurals, [] (int n)->int { return plural(n); });
}
} // language
} // i18n
} // i2p

47
i18n/English.cpp Normal file
View File

@ -0,0 +1,47 @@
/*
* Copyright (c) 2021, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
* See full license text in LICENSE file at top of project tree
*/
#include <map>
#include <vector>
#include <string>
#include <memory>
#include "I18N.h"
// English localization file
// This is an example translation file without strings in it.
namespace i2p
{
namespace i18n
{
namespace english // language
{
// 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
{
{"", ""},
};
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>(strings, plurals, [] (int n)->int { return plural(n); });
}
} // language
} // i18n
} // i2p

50
i18n/I18N.h Normal file
View File

@ -0,0 +1,50 @@
/*
* Copyright (c) 2021, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
* See full license text in LICENSE file at top of project tree
*/
#ifndef __I18N_H__
#define __I18N_H__
#include "RouterContext.h"
namespace i2p
{
namespace i18n
{
inline void SetLanguage(const std::string &lang)
{
if (!lang.compare("afrikaans"))
i2p::context.SetLanguage (i2p::i18n::afrikaans::GetLocale());
else if (!lang.compare("russian"))
i2p::context.SetLanguage (i2p::i18n::russian::GetLocale());
else if (!lang.compare("turkmen"))
i2p::context.SetLanguage (i2p::i18n::turkmen::GetLocale());
else if (!lang.compare("ukrainian"))
i2p::context.SetLanguage (i2p::i18n::ukrainian::GetLocale());
else // fallback
i2p::context.SetLanguage (i2p::i18n::english::GetLocale());
}
inline std::string translate (const std::string& arg)
{
return i2p::context.GetLanguage ()->GetString (arg);
}
inline std::string translate (const std::string& arg, const std::string& arg2, const int& n)
{
return i2p::context.GetLanguage ()->GetPlural (arg, arg2, n);
}
} // i18n
} // i2p
template<typename... TArgs>
std::string tr (TArgs&&... args)
{
return i2p::i18n::translate(std::forward<TArgs>(args)...);
}
#endif // __I18N_H__

68
i18n/I18N_langs.h Normal file
View File

@ -0,0 +1,68 @@
/*
* Copyright (c) 2021, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
* See full license text in LICENSE file at top of project tree
*/
#ifndef __I18N_LANGS_H__
#define __I18N_LANGS_H__
namespace i2p
{
namespace i18n
{
class Locale
{
public:
Locale (
const std::map<std::string, std::string>& strings,
const std::map<std::string, std::vector<std::string>>& plurals,
std::function<int(int)> formula
): m_Strings (strings), m_Plurals (plurals), m_Formula (formula) { };
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::map<std::string, std::string> m_Strings;
const std::map<std::string, std::vector<std::string>> m_Plurals;
std::function<int(int)> m_Formula;
};
// Add localization here with language name as namespace
namespace afrikaans { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
namespace english { 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 (); }
} // i18n
} // i2p
#endif // __I18N_LANGS_H__

243
i18n/Russian.cpp Normal file
View File

@ -0,0 +1,243 @@
/*
* Copyright (c) 2021, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
* See full license text in LICENSE file at top of project tree
*/
#include <map>
#include <vector>
#include <string>
#include <memory>
#include "I18N.h"
// Russian localization file
namespace i2p
{
namespace i18n
{
namespace russian // language
{
// See for language plural forms here:
// https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
static int plural (int n) {
return n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;
}
static std::map<std::string, std::string> strings
{
// HTTP Proxy
{"Proxy error", "Ошибка прокси"},
{"Proxy info", "Информация прокси"},
{"Proxy error: Host not found", "Ошибка прокси: Адрес не найден"},
{"Remote host not found in router's addressbook", "Запрошенный адрес не найден в адресной книге роутера"},
{"You may try to find this host on jump services below", "Вы можете попробовать найти адрес на джамп сервисах ниже"},
{"Invalid request", "Некорректный запрос"},
{"Proxy unable to parse your request", "Прокси не может разобрать ваш запрос"},
{"addresshelper is not supported", "addresshelper не поддерживается"},
{"Host", "Адрес"},
{"added to router's addressbook from helper", "добавлен в адресную книгу роутера через хелпер"},
{"already in router's addressbook", "уже в адресной книге роутера"},
{"Click", "Нажмите"},
{"here", "здесь"},
{"to proceed", "чтобы продолжить"},
{"to update record", "чтобы обновить запись"},
{"Addresshelper found", "Найден 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 прокси"},
{"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 прокси серверу"},
{"Host is down", "Адрес недоступен"},
{"Can't create connection to requested host, it may be down. Please try again later.",
"Не удалось установить соединение к запрошенному адресу, возможно он не в сети. Попробуйте повторить запрос позже."},
// Webconsole //
// cssStyles
{"Disabled", "Выключено"},
{"Enabled", "Включено"},
// ShowTraffic
{"KiB", "КиБ"},
{"MiB", "МиБ"},
{"GiB", "ГиБ"},
// ShowTunnelDetails
{"building", "строится"},
{"failed", "неудачный"},
{"expiring", "истекает"},
{"established", "работает"},
{"exploratory", "исследовательский"},
{"unknown", "неизвестно"},
{"<b>i2pd</b> webconsole", "Веб-консоль <b>i2pd</b>"},
// ShowPageHead
{"Main page", "Главная"},
{"Router commands", "Команды роутера"},
{"Local destinations", "Локальные назнач."},
{"LeaseSets", "Лизсеты"},
{"Tunnels", "Туннели"},
{"Transit tunnels", "Транзит. туннели"},
{"Transports", "Транспорты"},
{"I2P tunnels", "I2P туннели"},
{"SAM sessions", "SAM сессии"},
// Network Status
{"OK", "OK"},
{"Testing", "Тестирование"},
{"Firewalled", "Заблокировано извне"},
{"Unknown", "Неизвестно"},
{"Proxy", "Прокси"},
{"Mesh", "MESH-сеть"},
{"Error", "Ошибка"},
{"Clock skew", "Не точное время"},
{"Offline", "Оффлайн"},
{"Symmetric NAT", "Симметричный NAT"},
// Status
{"Uptime", "В сети"},
{"Network status", "Сетевой статус"},
{"Network status v6", "Сетевой статус v6"},
{"Stopping in", "Остановка через"},
{"Family", "Семейство"},
{"Tunnel creation success rate", "Успешно построенных туннелей"},
{"Received", "Получено"},
{"Sent", "Отправлено"},
{"Transit", "Транзит"},
{"KiB/s", "КиБ/с"},
{"Data path", "Путь к данным"},
{"Hidden content. Press on text to see.", "Скрытый контент. Нажмите на текст чтобы отобразить."},
{"Router Ident", "Идентификатор роутера"},
{"Router Family", "Семейство роутера"},
{"Router Caps", "Флаги роутера"},
{"Version", "Версия"},
{"Our external address", "Наш внешний адрес"},
{"supported", "поддерживается"},
{"Routers", "Роутеры"},
{"Floodfills", "Флудфилы"},
{"LeaseSets", "Лизсеты"},
{"Client Tunnels", "Клиентские туннели"},
{"Transit Tunnels", "Транзитные туннели"},
{"Services", "Сервисы"},
// ShowLocalDestinations
{"Local Destinations", "Локальные назначения"},
// ShowLeaseSetDestination
{"Encrypted B33 address", "Шифрованные B33 адреса"},
{"Address registration line", "Строка регистрации адреса"},
{"Domain", "Домен"},
{"Generate", "Сгенерировать"},
{"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.",
"<b>Примечание:</b> полученная строка может быть использована только для регистрации доменов второго уровня. Для регистрации поддоменов используйте i2pd-tools."},
{"Address", "Адрес"},
{"Type", "Тип"},
{"EncType", "ТипШифр"},
{"Inbound tunnels", "Входящие туннели"},
{"Outbound tunnels", "Исходящие туннели"},
{"ms", "мс"}, // milliseconds
{"Tags", "Теги"},
{"Incoming", "Входящие"},
{"Outgoing", "Исходящие"},
{"Destination", "Назначение"},
{"Amount", "Количество"},
{"Incoming Tags", "Входящие Теги"},
{"Tags sessions", "Сессии Тегов"},
{"Status", "Статус"},
// ShowLocalDestination
{"Local Destination", "Локальное назначение"},
{"Streams", "Стримы"},
{"Close stream", "Закрыть стрим"},
// ShowI2CPLocalDestination
{"I2CP session not found", "I2CP сессия не найдена"},
{"I2CP is not enabled", "I2CP не включен"},
// ShowLeasesSets
{"Invalid", "Некорректный"},
{"Store type", "Тип хранилища"},
{"Expires", "Истекает"},
{"Non Expired Leases", "Не истекшие Lease-ы"},
{"Gateway", "Шлюз"},
{"TunnelID", "ID туннеля"},
{"EndDate", "Заканчивается"},
{"not floodfill", "не флудфил"},
// ShowTunnels
{"Queue size", "Размер очереди"},
// ShowCommands
{"Run peer test", "Запустить тестирование"},
{"Decline transit tunnels", "Отклонять транзитные туннели"},
{"Accept transit tunnels", "Принимать транзитные туннели"},
{"Cancel graceful shutdown", "Отменить плавную остановку"},
{"Start graceful shutdown", "Запустить плавную остановку"},
{"Force shutdown", "Принудительная остановка"},
{"<b>Note:</b> any action done here are not persistent and not changes your config files.",
"<b>Примечание:</b> любое действие произведенное здесь не является постоянным и не изменяет ваши конфигурационные файлы."},
{"Logging level", "Уровень логирования"},
{"Transit tunnels limit", "Лимит транзитных туннелей"},
{"Change", "Изменить"},
// ShowTransitTunnels
{"no transit tunnels currently built", "нет построенных транзитных туннелей"},
// ShowSAMSessions/ShowSAMSession
{"SAM disabled", "SAM выключен"},
{"SAM session not found", "SAM сессия не найдена"},
{"no sessions currently running", "нет запущенных сессий"},
{"SAM Session", "SAM сессия"},
// ShowI2PTunnels
{"Server Tunnels", "Серверные туннели"},
{"Client Forwards", "Клиентские перенаправления"},
{"Server Forwards", "Серверные перенаправления"},
// HandlePage
{"Unknown page", "Неизвестная страница"},
// HandleCommand, ShowError
{"Invalid token", "Неверный токен"},
{"SUCCESS", "УСПЕШНО"},
{"ERROR", "ОШИБКА"},
{"Unknown command", "Неизвестная команда"},
{"Command accepted", "Команда принята"},
{"Back to commands list", "Вернуться к списку команд"},
{"You will be redirected in 5 seconds", "Вы будете переадресованы через 5 секунд"},
// HTTP_COMMAND_KILLSTREAM
{"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 back in 5 seconds", "Вы будете переадресованы назад через 5 секунд"},
// HTTP_COMMAND_LIMITTRANSIT
{"Transit tunnels count must not exceed 65535", "Число транзитных туннелей не должно превышать 65535"},
// HTTP_COMMAND_GET_REG_STRING
{"Register at reg.i2p", "Зарегистрировать на reg.i2p"},
{"Description", "Описание"},
{"A bit information about service on domain", "Немного информации о сервисе на домене"},
{"Submit", "Отправить"},
{"Domain can't end with .b32.i2p", "Домен не может заканчиваться на .b32.i2p"},
{"Domain must end with .i2p", "Домен должен заканчиваться на .i2p"},
{"Such destination is not found", "Такая точка назначения не найдена"},
{"", ""},
};
static std::map<std::string, std::vector<std::string>> plurals
{
// ShowUptime
{"days", {"день", "дня", "дней"}},
{"hours", {"час", "часа", "часов"}},
{"minutes", {"минуту", "минуты", "минут"}},
{"seconds", {"секунду", "секунды", "секунд"}},
{"", {"", "", ""}},
};
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
{
return std::make_shared<i2p::i18n::Locale>(strings, plurals, [] (int n)->int { return plural(n); });
}
} // language
} // i18n
} // i2p

242
i18n/Turkmen.cpp Normal file
View File

@ -0,0 +1,242 @@
/*
* Copyright (c) 2021, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
* See full license text in LICENSE file at top of project tree
*/
#include <map>
#include <vector>
#include <string>
#include <memory>
#include "I18N.h"
// Turkmen localization file
namespace i2p
{
namespace i18n
{
namespace turkmen // language
{
// 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
{
// HTTP Proxy
{"Proxy error", "Proksi ýalňyşlygy"},
{"Proxy info", "Proksi maglumat"},
{"Proxy error: Host not found", "Proksi ýalňyşlygy: Host tapylmady"},
{"Remote host not found in router's addressbook", "Uzakdaky öý eýesi marşruteriň salgy kitabynda tapylmady"},
{"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"},
{"already in router's addressbook", "marşruteriň adres kitaby"},
{"Click", "Basyň"},
{"here", "bu ýerde"},
{"to proceed", "dowam etmek"},
{"to update record", "recordazgyny täzelemek üçin"},
{"Addresshelper found", "Forgelper tapyldy"},
{"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"},
{"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"},
{"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ň."},
// Webconsole //
// cssStyles
{"Disabled", "Öçürildi"},
{"Enabled", "Goşuldy"},
// ShowTraffic
{"KiB", "KiB"},
{"MiB", "MiB"},
{"GiB", "GiB"},
// ShowTunnelDetails
{"building", "bina"},
{"failed", "şowsuz"},
{"expiring", "möhleti gutarýar"},
{"established", "işleýär"},
{"exploratory", "gözleg"},
{"unknown", "näbelli"},
{"<b>i2pd</b> webconsole", "Web konsoly <b>i2pd</b>"},
// ShowPageHead
{"Main page", "Esasy sahypa"},
{"Router commands", "Marşrutizator buýruklary"},
{"Local destinations", "Ýerli ýerler"},
{"LeaseSets", "Lizset"},
{"Tunnels", "Tuneller"},
{"Transit tunnels", "Tranzit tunels"},
{"Transports", "Daşamak"},
{"I2P tunnels", "I2P tuneller"},
{"SAM sessions", "SAM Sessiýasy"},
// Network Status
{"OK", "OK"},
{"Testing", "Synag etmek"},
{"Firewalled", "Daşynda petiklendi"},
{"Unknown", "Näbelli"},
{"Proxy", "Proksi"},
{"Mesh", "MESH-tor"},
{"Error", "Ýalňyşlyk"},
{"Clock skew", "Takyk wagt däl"},
{"Offline", "Awtonom"},
{"Symmetric NAT", "Simmetriklik NAT"},
// Status
{"Uptime", "Onlaýn onlaýn sözlügi"},
{"Network status", "Tor ýagdaýy"},
{"Network status v6", "Tor ýagdaýy v6"},
{"Stopping in", "Soň duruň"},
{"Family", "Maşgala"},
{"Tunnel creation success rate", "Gurlan teneller üstünlikli gurlan teneller"},
{"Received", "Alnan"},
{"Sent", "Ýerleşdirildi"},
{"Transit", "Tranzit"},
{"KiB/s", "KiB/s"},
{"Data path", "Maglumat ýoly"},
{"Hidden content. Press on text to see.", "Gizlin mazmun. Görkezmek üçin tekste basyň."},
{"Router Ident", "Marşrutly kesgitleýji"},
{"Router Family", "Marşrutler maşgalasy"},
{"Router Caps", "Baýdaklar marşruteri"},
{"Version", "Wersiýasy"},
{"Our external address", "Daşarky salgymyz"},
{"supported", "goldanýar"},
{"Routers", "Marşrutizatorlar"},
{"Floodfills", "Fludfillar"},
{"Client Tunnels", "Müşderi tunelleri"},
{"Transit Tunnels", "Tranzit Tunelleri"},
{"Services", "Hyzmatlar"},
// ShowLocalDestinations
{"Local Destinations", "Ýerli ýerler"},
// ShowLeaseSetDestination
{"Encrypted B33 address", "Şifrlenen B33 salgylar"},
{"Address registration line", "Hasaba alyş salgysy"},
{"Domain", "Domen"},
{"Generate", "Öndürmek"},
{"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.",
"<b>Bellik:</b> Alnan setir diňe ikinji derejeli domenleri bellige almak üçin ulanylyp bilner. Subýutmalary hasaba almak üçin i2pd ulanyň-tools."},
{"Address", "Salgysy"},
{"Type", "Görnüş"},
{"EncType", "Şifrlemek görnüşi"},
{"Inbound tunnels", "Gelýän tuneller"},
{"Outbound tunnels", "Çykýan tuneller"},
{"ms", "ms"}, // milliseconds
{"Tags", "Bellikler"},
{"Incoming", "Gelýän"},
{"Outgoing", "Çykýan"},
{"Destination", "Maksat"},
{"Amount", "Sany"},
{"Incoming Tags", "Gelýän bellikler"},
{"Tags sessions", "Sapaklar bellikler"},
{"Status", "Ýagdaýy"},
// ShowLocalDestination
{"Local Destination", "Ýerli maksat"},
{"Streams", "Strimlary"},
{"Close stream", "Yap strim"},
// ShowI2CPLocalDestination
{"I2CP session not found", "I2CP Sessiýa tapylmady"},
{"I2CP is not enabled", "I2CP goşulmaýar"},
// ShowLeasesSets
{"Invalid", "Nädogry"},
{"Store type", "Ammar görnüşi"},
{"Expires", "Möhleti gutarýar"},
{"Non Expired Leases", "Möhleti gutarmady Lizsetlary"},
{"Gateway", "Derweze"},
{"TunnelID", "Tuneliň ID"},
{"EndDate", "Gutarýar"},
{"not floodfill", "fludfil däl"},
// ShowTunnels
{"Queue size", "Nobatyň ululygy"},
// ShowCommands
{"Run peer test", "Synag başlaň"},
{"Decline transit tunnels", "Tranzit tunellerini ret ediň"},
{"Accept transit tunnels", "Tranzit tunellerini alyň"},
{"Cancel graceful shutdown", "Tekiz durmagy ýatyryň"},
{"Start graceful shutdown", "Tekiz durmak"},
{"Force shutdown", "Mejbury duralga"},
{"<b>Note:</b> any action done here are not persistent and not changes your config files.",
"<b>Bellik:</b> Bu ýerde öndürilen islendik çäre hemişelik däl we konfigurasiýa faýllaryňyzy üýtgetmeýär."},
{"Logging level", "Giriş derejesi"},
{"Transit tunnels limit", "Tranzit tunelleriniň çägi"},
{"Change", "Üýtgetmek"},
// ShowTransitTunnels
{"no transit tunnels currently built", "gurlan tranzit tunelleri ýok"},
// ShowSAMSessions/ShowSAMSession
{"SAM disabled", "SAM öçürilen"},
{"SAM session not found", "SAM Sessiýa tapylmady"},
{"no sessions currently running", "başlamagyň sessiýalary ýok"},
{"SAM Session", "SAM Sessiýa"},
// ShowI2PTunnels
{"Server Tunnels", "Serwer tunelleri"},
{"Client Forwards", "Müşderi gönükdirýär"},
{"Server Forwards", "Serweriň täzeden düzlüleri"},
// HandlePage
{"Unknown page", "Näbelli sahypa"},
// HandleCommand, ShowError
{"Invalid token", "Nädogry token"},
{"SUCCESS", "Üstünlikli"},
{"ERROR", "Ýalňyşlyk"},
{"Unknown command", "Näbelli topar"},
{"Command accepted", "Topar kabul edilýär"},
{"Back to commands list", "Topar sanawyna dolan"},
{"You will be redirected in 5 seconds", "5 sekuntdan soň täzeden ugrukdyrylarsyňyz"},
// HTTP_COMMAND_KILLSTREAM
{"Stream closed", "Strim ýapyk"},
{"Stream not found or already was closed", "Strim tapylmady ýa-da eýýäm ýapyldy"},
{"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 back in 5 seconds", "5 sekuntda yzyna iberiler"},
// HTTP_COMMAND_LIMITTRANSIT
{"Transit tunnels count must not exceed 65535", "Tranzit tagtalaryň sany 65535-den geçmeli däldir"},
// HTTP_COMMAND_GET_REG_STRING
{"Register at reg.i2p", "Reg.i2P-de hasaba duruň"},
{"Description", "Beýany"},
{"A bit information about service on domain", "Domendäki hyzmat barada käbir maglumatlar"},
{"Submit", "Iber"},
{"Domain can't end with .b32.i2p", "Domain .b32.i2p bilen gutaryp bilmez"},
{"Domain must end with .i2p", "Domeni .i2p bilen gutarmaly"},
{"Such destination is not found", "Bu barmaly ýer tapylmady"},
{"", ""},
};
static std::map<std::string, std::vector<std::string>> plurals
{
// ShowUptime
{"days", {"gün", "gün"}},
{"hours", {"sagat", "sagat"}},
{"minutes", {"minut", "minut"}},
{"seconds", {"sekunt", "sekunt"}},
{"", {"", ""}},
};
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
{
return std::make_shared<i2p::i18n::Locale>(strings, plurals, [] (int n)->int { return plural(n); });
}
} // language
} // i18n
} // i2p

242
i18n/Ukrainian.cpp Normal file
View File

@ -0,0 +1,242 @@
/*
* Copyright (c) 2021, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
* See full license text in LICENSE file at top of project tree
*/
#include <map>
#include <vector>
#include <string>
#include <memory>
#include "I18N.h"
// Ukrainian localization file
namespace i2p
{
namespace i18n
{
namespace ukrainian // language
{
// See for language plural forms here:
// https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
static int plural (int n) {
return n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;
}
static std::map<std::string, std::string> strings
{
// HTTP Proxy
{"Proxy error", "Помилка проксі"},
{"Proxy info", "Інформація проксі"},
{"Proxy error: Host not found", "Помилка проксі: Адреса не знайдена"},
{"Remote host not found in router's addressbook", "Віддалена адреса не знайдена в адресній книзі роутера"},
{"You may try to find this host on jump services below", "Ви можете спробувати знайти дану адресу на джамп сервісах нижче"},
{"Invalid request", "Некоректний запит"},
{"Proxy unable to parse your request", "Проксі не може розібрати ваш запит"},
{"addresshelper is not supported", "addresshelper не підтримується"},
{"Host", "Адреса"},
{"added to router's addressbook from helper", "доданий в адресну книгу роутера через хелпер"},
{"already in router's addressbook", "вже в адресній книзі роутера"},
{"Click", "Натисніть"},
{"here", "тут"},
{"to proceed", "щоб продовжити"},
{"to update record", "щоб оновити запис"},
{"Addresshelper found", "Знайдено 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 проксі"},
{"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 проксі сервера"},
{"Host is down", "Вузол недоступний"},
{"Can't create connection to requested host, it may be down. Please try again later.",
"Не вдалося встановити з'єднання до запитаного вузла, можливо він не в мережі. Спробуйте повторити запит пізніше."},
// Webconsole //
// cssStyles
{"Disabled", "Вимкнуто"},
{"Enabled", "Увімкнуто"},
// ShowTraffic
{"KiB", "КіБ"},
{"MiB", "МіБ"},
{"GiB", "ГіБ"},
// ShowTunnelDetails
{"building", "будується"},
{"failed", "невдалий"},
{"expiring", "завершується"},
{"established", "працює"},
{"exploratory", "дослідницький"},
{"unknown", "невідомо"},
{"<b>i2pd</b> webconsole", "Веб-консоль <b>i2pd</b>"},
// ShowPageHead
{"Main page", "Головна"},
{"Router commands", "Команди роутера"},
{"Local destinations", "Локальні призначення"},
{"LeaseSets", "Лізсети"},
{"Tunnels", "Тунелі"},
{"Transit tunnels", "Транзитні тунелі"},
{"Transports", "Транспорти"},
{"I2P tunnels", "I2P тунелі"},
{"SAM sessions", "SAM сесії"},
// Network Status
{"OK", "OK"},
{"Testing", "Тестування"},
{"Firewalled", "Заблоковано ззовні"},
{"Unknown", "Невідомо"},
{"Proxy", "Проксі"},
{"Mesh", "MESH-мережа"},
{"Error", "Помилка"},
{"Clock skew", "Неточний час"},
{"Offline", "Офлайн"},
{"Symmetric NAT", "Симетричний NAT"},
// Status
{"Uptime", "В мережі"},
{"Network status", "Мережевий статус"},
{"Network status v6", "Мережевий статус v6"},
{"Stopping in", "Зупинка через"},
{"Family", "Сімейство"},
{"Tunnel creation success rate", "Успішно побудованих тунелів"},
{"Received", "Отримано"},
{"Sent", "Відправлено"},
{"Transit", "Транзит"},
{"KiB/s", "КіБ/с"},
{"Data path", "Шлях до даних"},
{"Hidden content. Press on text to see.", "Прихований вміст. Натисніть на текст щоб відобразити."},
{"Router Ident", "Ідентифікатор Роутера"},
{"Router Family", "Сімейство Роутера"},
{"Router Caps", "Прапорці Роутера"},
{"Version", "Версія"},
{"Our external address", "Наша зовнішня адреса"},
{"supported", "підтримується"},
{"Routers", "Роутери"},
{"Floodfills", "Флудфіли"},
{"Client Tunnels", "Клієнтські Тунелі"},
{"Transit Tunnels", "Транзитні Тунелі"},
{"Services", "Сервіси"},
// ShowLocalDestinations
{"Local Destinations", "Локальні Призначення"},
// ShowLeaseSetDestination
{"Encrypted B33 address", "Шифровані B33 адреси"},
{"Address registration line", "Рядок реєстрації адреси"},
{"Domain", "Домен"},
{"Generate", "Згенерувати"},
{"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.",
"<b>Примітка:</b> отриманий рядок може бути використаний тільки для реєстрації доменів другого рівня. Для реєстрації піддоменів використовуйте i2pd-tools."},
{"Address", "Адреса"},
{"Type", "Тип"},
{"EncType", "ТипШифр"},
{"Inbound tunnels", "Вхідні тунелі"},
{"Outbound tunnels", "Вихідні тунелі"},
{"ms", "мс"}, // milliseconds
{"Tags", "Теги"},
{"Incoming", "Вхідні"},
{"Outgoing", "Вихідні"},
{"Destination", "Призначення"},
{"Amount", "Кількість"},
{"Incoming Tags", "Вхідні Теги"},
{"Tags sessions", "Сесії тегів"},
{"Status", "Статус"},
// ShowLocalDestination
{"Local Destination", "Локальні Призначення"},
{"Streams", "Потоки"},
{"Close stream", "Закрити потік"},
// ShowI2CPLocalDestination
{"I2CP session not found", "I2CP сесія не знайдена"},
{"I2CP is not enabled", "I2CP не увікнуто"},
// ShowLeasesSets
{"Invalid", "Некоректний"},
{"Store type", "Тип сховища"},
{"Expires", "Завершується"},
{"Non Expired Leases", "Не завершені Lease-и"},
{"Gateway", "Шлюз"},
{"TunnelID", "ID тунеля"},
{"EndDate", "Закінчується"},
{"not floodfill", "не флудфіл"},
// ShowTunnels
{"Queue size", "Розмір черги"},
// ShowCommands
{"Run peer test", "Запустити тестування"},
{"Decline transit tunnels", "Відхиляти транзитні тунелі"},
{"Accept transit tunnels", "Ухвалювати транзитні тунелі"},
{"Cancel graceful shutdown", "Скасувати плавну зупинку"},
{"Start graceful shutdown", "Запустити плавну зупинку"},
{"Force shutdown", "Примусова зупинка"},
{"<b>Note:</b> any action done here are not persistent and not changes your config files.",
"<b>Примітка:</b> будь-яка зроблена тут дія не є постійною та не змінює ваші конфігураційні файли."},
{"Logging level", "Рівень логування"},
{"Transit tunnels limit", "Обмеження транзитних тунелів"},
{"Change", "Змінити"},
// ShowTransitTunnels
{"no transit tunnels currently built", "немає побудованих транзитних тунелів"},
// ShowSAMSessions/ShowSAMSession
{"SAM disabled", "SAM вимкнуто"},
{"SAM session not found", "SAM сесія не знайдена"},
{"no sessions currently running", "немає запущених сесій"},
{"SAM Session", "SAM сесія"},
// ShowI2PTunnels
{"Server Tunnels", "Серверні Тунелі"},
{"Client Forwards", "Клієнтські Переспрямування"},
{"Server Forwards", "Серверні Переспрямування"},
// HandlePage
{"Unknown page", "Невідома сторінка"},
// HandleCommand, ShowError
{"Invalid token", "Невірний токен"},
{"SUCCESS", "УСПІШНО"},
{"ERROR", "ПОМИЛКА"},
{"Unknown command", "Невідома команда"},
{"Command accepted", "Команда прийнята"},
{"Back to commands list", "Повернутися до списку команд"},
{"You will be redirected in 5 seconds", "Ви будете переадресовані через 5 секунд"},
// HTTP_COMMAND_KILLSTREAM
{"Stream closed", "Потік зачинений"},
{"Stream not found or already was closed", "Потік не знайдений або вже зачинений"},
{"Destination not found", "Точка призначення не знайдена"},
{"StreamID can't be null", "Ідентифікатор потоку не може бути порожнім"},
{"Return to destination page", "Повернутися на сторінку точки призначення"},
{"You will be redirected back in 5 seconds", "Ви будете переадресовані назад через 5 секунд"},
// HTTP_COMMAND_LIMITTRANSIT
{"Transit tunnels count must not exceed 65535", "Кількість транзитних тунелів не повинна перевищувати 65535"},
// HTTP_COMMAND_GET_REG_STRING
{"Register at reg.i2p", "Зареєструвати на reg.i2p"},
{"Description", "Опис"},
{"A bit information about service on domain", "Трохи інформації про сервіс на домені"},
{"Submit", "Надіслати"},
{"Domain can't end with .b32.i2p", "Домен не може закінчуватися на .b32.i2p"},
{"Domain must end with .i2p", "Домен повинен закінчуватися на .i2p"},
{"Such destination is not found", "Така точка призначення не знайдена"},
{"", ""},
};
static std::map<std::string, std::vector<std::string>> plurals
{
// ShowUptime
{"days", {"день", "дня", "днів"}},
{"hours", {"годину", "години", "годин"}},
{"minutes", {"хвилину", "хвилини", "хвилин"}},
{"seconds", {"секунду", "секунди", "секунд"}},
{"", {"", "", ""}},
};
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
{
return std::make_shared<i2p::i18n::Locale>(strings, plurals, [] (int n)->int { return plural(n); });
}
} // language
} // i18n
} // i2p

View File

@ -62,11 +62,11 @@ namespace config {
("floodfill", bool_switch()->default_value(false), "Router will be floodfill (default: disabled)") ("floodfill", bool_switch()->default_value(false), "Router will be floodfill (default: disabled)")
("bandwidth", value<std::string>()->default_value(""), "Bandwidth limit: integer in KBps or letters: L (32), O (256), P (2048), X (>9000)") ("bandwidth", value<std::string>()->default_value(""), "Bandwidth limit: integer in KBps or letters: L (32), O (256), P (2048), X (>9000)")
("share", value<int>()->default_value(100), "Limit of transit traffic from max bandwidth in percents. (default: 100)") ("share", value<int>()->default_value(100), "Limit of transit traffic from max bandwidth in percents. (default: 100)")
("ntcp", bool_switch()->default_value(false), "Deprecated option. Always false") ("ntcp", bool_switch()->default_value(false), "Ignored. Always false")
("ssu", bool_switch()->default_value(true), "Enable SSU transport (default: enabled)") ("ssu", bool_switch()->default_value(true), "Enable SSU transport (default: enabled)")
("ntcpproxy", value<std::string>()->default_value(""), "Deprecated option") ("ntcpproxy", value<std::string>()->default_value(""), "Ignored")
#ifdef _WIN32 #ifdef _WIN32
("svcctl", value<std::string>()->default_value(""), "Deprecated option") ("svcctl", value<std::string>()->default_value(""), "Ignored")
("insomnia", bool_switch()->default_value(false), "Prevent system from sleeping (default: disabled)") ("insomnia", bool_switch()->default_value(false), "Prevent system from sleeping (default: disabled)")
("close", value<std::string>()->default_value("ask"), "Action on close: minimize, exit, ask") ("close", value<std::string>()->default_value("ask"), "Action on close: minimize, exit, ask")
#endif #endif
@ -77,9 +77,9 @@ namespace config {
("limits.coresize", value<uint32_t>()->default_value(0), "Maximum size of corefile in Kb (0 - use system limit)") ("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.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(2500), "Maximum active transit sessions (default:2500)")
("limits.ntcpsoft", value<uint16_t>()->default_value(0), "Deprecated option") ("limits.ntcpsoft", value<uint16_t>()->default_value(0), "Threshold to start probabilistic backoff with ntcp sessions (default: use system limit)")
("limits.ntcphard", value<uint16_t>()->default_value(0), "Deprecated option") ("limits.ntcphard", value<uint16_t>()->default_value(0), "Maximum number of ntcp sessions (default: use system limit)")
("limits.ntcpthreads", value<uint16_t>()->default_value(1), "Deprecated option") ("limits.ntcpthreads", value<uint16_t>()->default_value(1), "Maximum number of threads used by NTCP DH worker (default: 1)")
; ;
options_description httpserver("HTTP Server options"); options_description httpserver("HTTP Server options");
@ -93,6 +93,7 @@ namespace config {
("http.strictheaders", value<bool>()->default_value(true), "Enable strict host checking on WebUI") ("http.strictheaders", value<bool>()->default_value(true), "Enable strict host checking on WebUI")
("http.hostname", value<std::string>()->default_value("localhost"), "Expected hostname for WebUI") ("http.hostname", value<std::string>()->default_value("localhost"), "Expected hostname for WebUI")
("http.webroot", value<std::string>()->default_value("/"), "WebUI root path (default: / )") ("http.webroot", value<std::string>()->default_value("/"), "WebUI root path (default: / )")
("http.lang", value<std::string>()->default_value("english"), "WebUI language (default: english )")
; ;
options_description httpproxy("HTTP Proxy options"); options_description httpproxy("HTTP Proxy options");
@ -113,6 +114,7 @@ namespace config {
("httpproxy.addresshelper", value<bool>()->default_value(true), "Enable or disable addresshelper") ("httpproxy.addresshelper", value<bool>()->default_value(true), "Enable or disable addresshelper")
("httpproxy.i2cp.leaseSetType", value<std::string>()->default_value("3"), "Local destination's LeaseSet type") ("httpproxy.i2cp.leaseSetType", value<std::string>()->default_value("3"), "Local destination's LeaseSet type")
("httpproxy.i2cp.leaseSetEncType", value<std::string>()->default_value("0,4"), "Local destination's LeaseSet encryption type") ("httpproxy.i2cp.leaseSetEncType", value<std::string>()->default_value("0,4"), "Local destination's LeaseSet encryption type")
("httpproxy.i2cp.leaseSetPrivKey", value<std::string>()->default_value(""), "LeaseSet private key")
; ;
options_description socksproxy("SOCKS Proxy options"); options_description socksproxy("SOCKS Proxy options");
@ -134,6 +136,7 @@ namespace config {
("socksproxy.outproxyport", value<uint16_t>()->default_value(9050), "Upstream outproxy port for SOCKS Proxy") ("socksproxy.outproxyport", value<uint16_t>()->default_value(9050), "Upstream outproxy port for SOCKS Proxy")
("socksproxy.i2cp.leaseSetType", value<std::string>()->default_value("3"), "Local destination's LeaseSet type") ("socksproxy.i2cp.leaseSetType", value<std::string>()->default_value("3"), "Local destination's LeaseSet type")
("socksproxy.i2cp.leaseSetEncType", value<std::string>()->default_value("0,4"), "Local destination's LeaseSet encryption type") ("socksproxy.i2cp.leaseSetEncType", value<std::string>()->default_value("0,4"), "Local destination's LeaseSet encryption type")
("socksproxy.i2cp.leaseSetPrivKey", value<std::string>()->default_value(""), "LeaseSet private key")
; ;
options_description sam("SAM bridge options"); options_description sam("SAM bridge options");
@ -218,7 +221,7 @@ namespace config {
("addressbook.defaulturl", value<std::string>()->default_value( ("addressbook.defaulturl", value<std::string>()->default_value(
"http://shx5vqsw7usdaunyzr2qmes2fq37oumybpudrd4jjj4e4vk4uusa.b32.i2p/hosts.txt" "http://shx5vqsw7usdaunyzr2qmes2fq37oumybpudrd4jjj4e4vk4uusa.b32.i2p/hosts.txt"
), "AddressBook subscription URL for initial setup") ), "AddressBook subscription URL for initial setup")
("addressbook.subscriptions", value<std::string>()->default_value(""), "AddressBook subscriptions URLs, separated by comma") ("addressbook.subscriptions", value<std::string>()->default_value("http://reg.i2p/hosts.txt"), "AddressBook subscriptions URLs, separated by comma")
("addressbook.hostsfile", value<std::string>()->default_value(""), "File to dump addresses in hosts.txt format"); ("addressbook.hostsfile", value<std::string>()->default_value(""), "File to dump addresses in hosts.txt format");
options_description trust("Trust options"); options_description trust("Trust options");
@ -281,7 +284,7 @@ namespace config {
options_description meshnets("Meshnet transports options"); options_description meshnets("Meshnet transports options");
meshnets.add_options() meshnets.add_options()
("meshnets.yggdrasil", bool_switch()->default_value(false), "Support transports through the Yggdrasil (default: false)") ("meshnets.yggdrasil", bool_switch()->default_value(false), "Support transports through the Yggdrasil (deafult: false)")
("meshnets.yggaddress", value<std::string>()->default_value(""), "Yggdrasil address to publish") ("meshnets.yggaddress", value<std::string>()->default_value(""), "Yggdrasil address to publish")
; ;

View File

@ -82,6 +82,14 @@ namespace client
if (it != params->end ()) m_Nickname = it->second; if (it != params->end ()) m_Nickname = it->second;
// otherwise we set default nickname in Start when we know local address // otherwise we set default nickname in Start when we know local address
} }
it = params->find (I2CP_PARAM_DONT_PUBLISH_LEASESET);
if (it != params->end ())
{
// oveeride isPublic
bool dontpublish = false;
i2p::config::GetOption (it->second, dontpublish);
m_IsPublic = !dontpublish;
}
it = params->find (I2CP_PARAM_LEASESET_TYPE); it = params->find (I2CP_PARAM_LEASESET_TYPE);
if (it != params->end ()) if (it != params->end ())
m_LeaseSetType = std::stoi(it->second); m_LeaseSetType = std::stoi(it->second);
@ -509,7 +517,7 @@ namespace client
// schedule verification // schedule verification
m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_VERIFICATION_TIMEOUT)); m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_VERIFICATION_TIMEOUT));
m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer, m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer,
shared_from_this (), std::placeholders::_1)); shared_from_this (), std::placeholders::_1));
} }
else else
i2p::garlic::GarlicDestination::HandleDeliveryStatusMessage (msgID); i2p::garlic::GarlicDestination::HandleDeliveryStatusMessage (msgID);
@ -592,7 +600,8 @@ namespace client
// assume it successive and try to verify // assume it successive and try to verify
m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_VERIFICATION_TIMEOUT)); m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_VERIFICATION_TIMEOUT));
m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer, m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer,
shared_from_this (), std::placeholders::_1)); shared_from_this (), std::placeholders::_1));
} }
} }
} }

View File

@ -61,14 +61,15 @@ namespace client
const char I2CP_PARAM_RATCHET_OUTBOUND_TAGS[] = "crypto.ratchet.outboundTags"; // not used yet const char I2CP_PARAM_RATCHET_OUTBOUND_TAGS[] = "crypto.ratchet.outboundTags"; // not used yet
const char I2CP_PARAM_INBOUND_NICKNAME[] = "inbound.nickname"; const char I2CP_PARAM_INBOUND_NICKNAME[] = "inbound.nickname";
const char I2CP_PARAM_OUTBOUND_NICKNAME[] = "outbound.nickname"; const char I2CP_PARAM_OUTBOUND_NICKNAME[] = "outbound.nickname";
const char I2CP_PARAM_DONT_PUBLISH_LEASESET[] = "i2cp.dontPublishLeaseSet";
const char I2CP_PARAM_LEASESET_TYPE[] = "i2cp.leaseSetType"; const char I2CP_PARAM_LEASESET_TYPE[] = "i2cp.leaseSetType";
const int DEFAULT_LEASESET_TYPE = 1; const int DEFAULT_LEASESET_TYPE = 3;
const char I2CP_PARAM_LEASESET_ENCRYPTION_TYPE[] = "i2cp.leaseSetEncType"; const char I2CP_PARAM_LEASESET_ENCRYPTION_TYPE[] = "i2cp.leaseSetEncType";
const char I2CP_PARAM_LEASESET_PRIV_KEY[] = "i2cp.leaseSetPrivKey"; // PSK decryption key, base64 const char I2CP_PARAM_LEASESET_PRIV_KEY[] = "i2cp.leaseSetPrivKey"; // PSK decryption key, base64
const char I2CP_PARAM_LEASESET_AUTH_TYPE[] = "i2cp.leaseSetAuthType"; const char I2CP_PARAM_LEASESET_AUTH_TYPE[] = "i2cp.leaseSetAuthType";
const char I2CP_PARAM_LEASESET_CLIENT_DH[] = "i2cp.leaseSetClient.dh"; // group of i2cp.leaseSetClient.dh.nnn const char I2CP_PARAM_LEASESET_CLIENT_DH[] = "i2cp.leaseSetClient.dh"; // group of i2cp.leaseSetClient.dh.nnn
const char I2CP_PARAM_LEASESET_CLIENT_PSK[] = "i2cp.leaseSetClient.psk"; // group of i2cp.leaseSetClient.psk.nnn const char I2CP_PARAM_LEASESET_CLIENT_PSK[] = "i2cp.leaseSetClient.psk"; // group of i2cp.leaseSetClient.psk.nnn
// latency // latency
const char I2CP_PARAM_MIN_TUNNEL_LATENCY[] = "latency.min"; const char I2CP_PARAM_MIN_TUNNEL_LATENCY[] = "latency.min";
const int DEFAULT_MIN_TUNNEL_LATENCY = 0; const int DEFAULT_MIN_TUNNEL_LATENCY = 0;

View File

@ -117,14 +117,14 @@ namespace garlic
return session->HandleNextMessage (buf, len, shared_from_this (), index); return session->HandleNextMessage (buf, len, shared_from_this (), index);
} }
DatabaseLookupTagSet::DatabaseLookupTagSet (GarlicDestination * destination, const uint8_t * key): SymmetricKeyTagSet::SymmetricKeyTagSet (GarlicDestination * destination, const uint8_t * key):
ReceiveRatchetTagSet (nullptr), m_Destination (destination) ReceiveRatchetTagSet (nullptr), m_Destination (destination)
{ {
memcpy (m_Key, key, 32); memcpy (m_Key, key, 32);
Expire (); Expire ();
} }
bool DatabaseLookupTagSet::HandleNextMessage (uint8_t * buf, size_t len, int index) bool SymmetricKeyTagSet::HandleNextMessage (uint8_t * buf, size_t len, int index)
{ {
if (len < 24) return false; if (len < 24) return false;
uint8_t nonce[12]; uint8_t nonce[12];
@ -133,18 +133,18 @@ namespace garlic
len -= 16; // poly1305 len -= 16; // poly1305
if (!i2p::crypto::AEADChaCha20Poly1305 (buf + offset, len - offset, buf, 8, m_Key, nonce, buf + offset, len - offset, false)) // decrypt if (!i2p::crypto::AEADChaCha20Poly1305 (buf + offset, len - offset, buf, 8, m_Key, nonce, buf + offset, len - offset, false)) // decrypt
{ {
LogPrint (eLogWarning, "Garlic: Lookup reply AEAD decryption failed"); LogPrint (eLogWarning, "Garlic: Symmetric key tagset AEAD decryption failed");
return false; return false;
} }
// we assume 1 I2NP block with delivery type local // we assume 1 I2NP block with delivery type local
if (offset + 3 > len) if (offset + 3 > len)
{ {
LogPrint (eLogWarning, "Garlic: Lookup reply is too short ", len); LogPrint (eLogWarning, "Garlic: Symmetric key tagset is too short ", len);
return false; return false;
} }
if (buf[offset] != eECIESx25519BlkGalicClove) if (buf[offset] != eECIESx25519BlkGalicClove)
{ {
LogPrint (eLogWarning, "Garlic: Lookup reply unexpected block ", (int)buf[offset]); LogPrint (eLogWarning, "Garlic: Symmetric key tagset unexpected block ", (int)buf[offset]);
return false; return false;
} }
offset++; offset++;
@ -152,7 +152,7 @@ namespace garlic
offset += 2; offset += 2;
if (offset + size > len) if (offset + size > len)
{ {
LogPrint (eLogWarning, "Garlic: Lookup reply block is too long ", size); LogPrint (eLogWarning, "Garlic: Symmetric key tagset block is too long ", size);
return false; return false;
} }
if (m_Destination) if (m_Destination)
@ -1109,21 +1109,22 @@ namespace garlic
bool RouterIncomingRatchetSession::HandleNextMessage (const uint8_t * buf, size_t len) bool RouterIncomingRatchetSession::HandleNextMessage (const uint8_t * buf, size_t len)
{ {
if (!GetOwner ()) return false; if (!GetOwner ()) return false;
i2p::crypto::NoiseSymmetricState state (GetNoiseState ()); m_CurrentNoiseState = GetNoiseState ();
// we are Bob // we are Bob
state.MixHash (buf, 32); m_CurrentNoiseState.MixHash (buf, 32);
uint8_t sharedSecret[32]; uint8_t sharedSecret[32];
if (!GetOwner ()->Decrypt (buf, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) // x25519(bsk, aepk) if (!GetOwner ()->Decrypt (buf, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) // x25519(bsk, aepk)
{ {
LogPrint (eLogWarning, "Garlic: Incorrect N ephemeral public key"); LogPrint (eLogWarning, "Garlic: Incorrect N ephemeral public key");
return false; return false;
} }
state.MixKey (sharedSecret); m_CurrentNoiseState.MixKey (sharedSecret);
buf += 32; len -= 32; buf += 32; len -= 32;
uint8_t nonce[12]; uint8_t nonce[12];
CreateNonce (0, nonce); CreateNonce (0, nonce);
std::vector<uint8_t> payload (len - 16); std::vector<uint8_t> payload (len - 16);
if (!i2p::crypto::AEADChaCha20Poly1305 (buf, len - 16, state.m_H, 32, state.m_CK + 32, nonce, payload.data (), len - 16, false)) // decrypt if (!i2p::crypto::AEADChaCha20Poly1305 (buf, len - 16, m_CurrentNoiseState.m_H, 32,
m_CurrentNoiseState.m_CK + 32, nonce, payload.data (), len - 16, false)) // decrypt
{ {
LogPrint (eLogWarning, "Garlic: Payload for router AEAD verification failed"); LogPrint (eLogWarning, "Garlic: Payload for router AEAD verification failed");
return false; return false;

View File

@ -104,11 +104,11 @@ namespace garlic
uint64_t m_ExpirationTimestamp = 0; uint64_t m_ExpirationTimestamp = 0;
}; };
class DatabaseLookupTagSet: public ReceiveRatchetTagSet class SymmetricKeyTagSet: public ReceiveRatchetTagSet
{ {
public: public:
DatabaseLookupTagSet (GarlicDestination * destination, const uint8_t * key); SymmetricKeyTagSet (GarlicDestination * destination, const uint8_t * key);
bool IsIndexExpired (int index) const { return false; }; bool IsIndexExpired (int index) const { return false; };
bool HandleNextMessage (uint8_t * buf, size_t len, int index); bool HandleNextMessage (uint8_t * buf, size_t len, int index);
@ -249,6 +249,11 @@ namespace garlic
RouterIncomingRatchetSession (const i2p::crypto::NoiseSymmetricState& initState); RouterIncomingRatchetSession (const i2p::crypto::NoiseSymmetricState& initState);
bool HandleNextMessage (const uint8_t * buf, size_t len); bool HandleNextMessage (const uint8_t * buf, size_t len);
i2p::crypto::NoiseSymmetricState& GetCurrentNoiseState () { return m_CurrentNoiseState; };
private:
i2p::crypto::NoiseSymmetricState m_CurrentNoiseState;
}; };
std::shared_ptr<I2NPMessage> WrapECIESX25519AEADRatchetMessage (std::shared_ptr<const I2NPMessage> msg, const uint8_t * key, uint64_t tag); std::shared_ptr<I2NPMessage> WrapECIESX25519AEADRatchetMessage (std::shared_ptr<const I2NPMessage> msg, const uint8_t * key, uint64_t tag);

View File

@ -12,6 +12,7 @@
#ifdef _WIN32 #ifdef _WIN32
#include <shlobj.h> #include <shlobj.h>
#include <windows.h> #include <windows.h>
#include <codecvt>
#endif #endif
#include "Base.h" #include "Base.h"
@ -41,16 +42,28 @@ namespace fs {
return dataDir; return dataDir;
} }
const std::string GetUTF8DataDir () {
#ifdef _WIN32
boost::filesystem::wpath path (dataDir);
auto loc = boost::filesystem::path::imbue(std::locale( std::locale(), new std::codecvt_utf8_utf16<wchar_t>() ) ); // convert path to UTF-8
auto dataDirUTF8 = path.string();
boost::filesystem::path::imbue(loc); // Return locale settings back
return dataDirUTF8;
#else
return dataDir; // linux, osx, android uses UTF-8 by default
#endif
}
void DetectDataDir(const std::string & cmdline_param, bool isService) { void DetectDataDir(const std::string & cmdline_param, bool isService) {
if (cmdline_param != "") { if (cmdline_param != "") {
dataDir = cmdline_param; dataDir = cmdline_param;
return; return;
} }
#ifdef _WIN32 #ifdef _WIN32
char localAppData[MAX_PATH]; wchar_t localAppData[MAX_PATH];
// check executable directory first // check executable directory first
if(!GetModuleFileName(NULL, localAppData, MAX_PATH)) if(!GetModuleFileNameW(NULL, localAppData, MAX_PATH))
{ {
#ifdef WIN32_APP #ifdef WIN32_APP
MessageBox(NULL, TEXT("Unable to get application path!"), TEXT("I2Pd: error"), MB_ICONERROR | MB_OK); MessageBox(NULL, TEXT("Unable to get application path!"), TEXT("I2Pd: error"), MB_ICONERROR | MB_OK);
@ -61,14 +74,15 @@ namespace fs {
} }
else else
{ {
auto execPath = boost::filesystem::path(localAppData).parent_path(); auto execPath = boost::filesystem::wpath(localAppData).parent_path();
// if config file exists in .exe's folder use it // if config file exists in .exe's folder use it
if(boost::filesystem::exists(execPath/"i2pd.conf")) // TODO: magic string if(boost::filesystem::exists(execPath/"i2pd.conf")) // TODO: magic string
dataDir = execPath.string ();
else // otherwise %appdata%
{ {
if(SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, localAppData) != S_OK) dataDir = execPath.string ();
} else // otherwise %appdata%
{
if(SHGetFolderPathW(NULL, CSIDL_APPDATA, NULL, 0, localAppData) != S_OK)
{ {
#ifdef WIN32_APP #ifdef WIN32_APP
MessageBox(NULL, TEXT("Unable to get AppData path!"), TEXT("I2Pd: error"), MB_ICONERROR | MB_OK); MessageBox(NULL, TEXT("Unable to get AppData path!"), TEXT("I2Pd: error"), MB_ICONERROR | MB_OK);
@ -78,7 +92,9 @@ namespace fs {
exit(1); exit(1);
} }
else else
dataDir = std::string(localAppData) + "\\" + appName; {
dataDir = boost::filesystem::wpath(localAppData).string() + "\\" + appName;
}
} }
} }
return; return;

View File

@ -75,6 +75,9 @@ namespace fs {
/** @brief Returns datadir path */ /** @brief Returns datadir path */
const std::string & GetDataDir(); const std::string & GetDataDir();
/** @brief Returns datadir path in UTF-8 encoding */
const std::string GetUTF8DataDir();
/** /**
* @brief Set datadir either from cmdline option or using autodetection * @brief Set datadir either from cmdline option or using autodetection
* @param cmdline_param Value of cmdline parameter --datadir=<something> * @param cmdline_param Value of cmdline parameter --datadir=<something>

View File

@ -471,7 +471,7 @@ namespace garlic
{ {
uint64_t t; uint64_t t;
memcpy (&t, tag, 8); memcpy (&t, tag, 8);
auto tagset = std::make_shared<DatabaseLookupTagSet>(this, key); auto tagset = std::make_shared<SymmetricKeyTagSet>(this, key);
m_ECIESx25519Tags.emplace (t, ECIESX25519AEADRatchetIndexTagset{0, tagset}); m_ECIESx25519Tags.emplace (t, ECIESX25519AEADRatchetIndexTagset{0, tagset});
} }

View File

@ -187,6 +187,8 @@ namespace http
params.clear(); params.clear();
for (const auto& it : tokens) { for (const auto& it : tokens) {
if (!it.length()) // empty
continue;
std::size_t eq = it.find ('='); std::size_t eq = it.find ('=');
if (eq != std::string::npos) { if (eq != std::string::npos) {
auto e = std::pair<std::string, std::string>(it.substr(0, eq), it.substr(eq + 1)); auto e = std::pair<std::string, std::string>(it.substr(0, eq), it.substr(eq + 1));

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2021, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -18,6 +18,7 @@
#include "Tunnel.h" #include "Tunnel.h"
#include "Transports.h" #include "Transports.h"
#include "Garlic.h" #include "Garlic.h"
#include "ECIESX25519AEADRatchetSession.h"
#include "I2NPProtocol.h" #include "I2NPProtocol.h"
#include "version.h" #include "version.h"
@ -387,16 +388,16 @@ namespace i2p
bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
clearText + ECIES_BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET, clearText + ECIES_BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET,
clearText + ECIES_BUILD_REQUEST_RECORD_IV_KEY_OFFSET, clearText + ECIES_BUILD_REQUEST_RECORD_IV_KEY_OFFSET,
clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x80, clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_GATEWAY_FLAG,
clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x40) : clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG) :
i2p::tunnel::CreateTransitTunnel ( i2p::tunnel::CreateTransitTunnel (
bufbe32toh (clearText + BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET), bufbe32toh (clearText + BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET),
clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
bufbe32toh (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), bufbe32toh (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
clearText + BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET, clearText + BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET,
clearText + BUILD_REQUEST_RECORD_IV_KEY_OFFSET, clearText + BUILD_REQUEST_RECORD_IV_KEY_OFFSET,
clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x80, clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_GATEWAY_FLAG,
clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x40); clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG);
i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel); i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel);
} }
else else
@ -424,9 +425,9 @@ namespace i2p
{ {
uint8_t nonce[12]; uint8_t nonce[12];
memset (nonce, 0, 12); memset (nonce, 0, 12);
auto noiseState = std::move (i2p::context.GetCurrentNoiseState ()); auto& noiseState = i2p::context.GetCurrentNoiseState ();
if (!noiseState || !i2p::crypto::AEADChaCha20Poly1305 (reply, TUNNEL_BUILD_RECORD_SIZE - 16, if (!i2p::crypto::AEADChaCha20Poly1305 (reply, TUNNEL_BUILD_RECORD_SIZE - 16,
noiseState->m_H, 32, noiseState->m_CK, nonce, reply, TUNNEL_BUILD_RECORD_SIZE, true)) // encrypt noiseState.m_H, 32, noiseState.m_CK, nonce, reply, TUNNEL_BUILD_RECORD_SIZE, true)) // encrypt
{ {
LogPrint (eLogWarning, "I2NP: Reply AEAD encryption failed"); LogPrint (eLogWarning, "I2NP: Reply AEAD encryption failed");
return false; return false;
@ -458,7 +459,7 @@ namespace i2p
LogPrint (eLogDebug, "I2NP: VariableTunnelBuild ", num, " records"); LogPrint (eLogDebug, "I2NP: VariableTunnelBuild ", num, " records");
if (len < num*TUNNEL_BUILD_RECORD_SIZE + 1) if (len < num*TUNNEL_BUILD_RECORD_SIZE + 1)
{ {
LogPrint (eLogError, "VaribleTunnelBuild message of ", num, " records is too short ", len); LogPrint (eLogError, "I2NP: VaribleTunnelBuild message of ", num, " records is too short ", len);
return; return;
} }
@ -486,7 +487,7 @@ namespace i2p
uint8_t clearText[ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE]; uint8_t clearText[ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE];
if (HandleBuildRequestRecords (num, buf + 1, clearText)) if (HandleBuildRequestRecords (num, buf + 1, clearText))
{ {
if (clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x40) // we are endpoint of outboud tunnel if (clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG) // we are endpoint of outboud tunnel
{ {
// so we send it to reply tunnel // so we send it to reply tunnel
transports.SendMessage (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, transports.SendMessage (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
@ -505,7 +506,7 @@ namespace i2p
uint8_t clearText[BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE]; uint8_t clearText[BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE];
if (HandleBuildRequestRecords (num, buf + 1, clearText)) if (HandleBuildRequestRecords (num, buf + 1, clearText))
{ {
if (clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x40) // we are endpoint of outboud tunnel if (clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG) // we are endpoint of outboud tunnel
{ {
// so we send it to reply tunnel // so we send it to reply tunnel
transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
@ -526,18 +527,18 @@ namespace i2p
{ {
if (i2p::context.IsECIES ()) if (i2p::context.IsECIES ())
{ {
LogPrint (eLogWarning, "TunnelBuild is too old for ECIES router"); LogPrint (eLogWarning, "I2NP: TunnelBuild is too old for ECIES router");
return; return;
} }
if (len < NUM_TUNNEL_BUILD_RECORDS*TUNNEL_BUILD_RECORD_SIZE) if (len < NUM_TUNNEL_BUILD_RECORDS*TUNNEL_BUILD_RECORD_SIZE)
{ {
LogPrint (eLogError, "TunnelBuild message is too short ", len); LogPrint (eLogError, "I2NP: TunnelBuild message is too short ", len);
return; return;
} }
uint8_t clearText[BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE]; uint8_t clearText[BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE];
if (HandleBuildRequestRecords (NUM_TUNNEL_BUILD_RECORDS, buf, clearText)) if (HandleBuildRequestRecords (NUM_TUNNEL_BUILD_RECORDS, buf, clearText))
{ {
if (clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x40) // we are endpoint of outbound tunnel if (clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG) // we are endpoint of outbound tunnel
{ {
// so we send it to reply tunnel // so we send it to reply tunnel
transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
@ -558,7 +559,7 @@ namespace i2p
LogPrint (eLogDebug, "I2NP: VariableTunnelBuildReplyMsg of ", num, " records replyMsgID=", replyMsgID); LogPrint (eLogDebug, "I2NP: VariableTunnelBuildReplyMsg of ", num, " records replyMsgID=", replyMsgID);
if (len < num*BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE + 1) if (len < num*BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE + 1)
{ {
LogPrint (eLogError, "VaribleTunnelBuildReply message of ", num, " records is too short ", len); LogPrint (eLogError, "I2NP: VaribleTunnelBuildReply message of ", num, " records is too short ", len);
return; return;
} }
@ -582,7 +583,112 @@ namespace i2p
LogPrint (eLogWarning, "I2NP: Pending tunnel for message ", replyMsgID, " not found"); LogPrint (eLogWarning, "I2NP: Pending tunnel for message ", replyMsgID, " not found");
} }
void HandleShortTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len)
{
if (!i2p::context.IsECIES ())
{
LogPrint (eLogWarning, "I2NP: ShortTunnelBuild can be handled by ECIES router only");
return;
}
int num = buf[0];
LogPrint (eLogDebug, "I2NP: ShortTunnelBuild ", num, " records");
if (len < num*SHORT_TUNNEL_BUILD_RECORD_SIZE + 1)
{
LogPrint (eLogError, "I2NP: ShortTunnelBuild message of ", num, " records is too short ", len);
return;
}
// TODO: check replyMsgID
const uint8_t * record = buf + 1;
for (int i = 0; i < num; i++)
{
if (!memcmp (record, (const uint8_t *)i2p::context.GetRouterInfo ().GetIdentHash (), 16))
{
LogPrint (eLogDebug, "I2NP: Short request record ", i, " is ours");
uint8_t clearText[SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE];
if (!i2p::context.DecryptTunnelShortRequestRecord (record + SHORT_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText))
{
LogPrint (eLogWarning, "I2NP: Can't decrypt short request record ", i);
return;
}
auto& noiseState = i2p::context.GetCurrentNoiseState ();
uint8_t layerKeys[64]; // (layer key, iv key)
i2p::crypto::HKDF (noiseState.m_CK + 32, nullptr, 0, "LayerAndIVKeys", layerKeys); // TODO: correct domain
auto transitTunnel = i2p::tunnel::CreateTransitTunnel (
bufbe32toh (clearText + SHORT_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET),
clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET,
bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
layerKeys, layerKeys + 32,
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 (clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG)
{
// we are endpoint, create OutboundTunnelBuildReply
auto otbrm = NewI2NPShortMessage ();
auto payload = otbrm->GetPayload ();
payload[0] = num; // num
payload[1] = i; // slot
payload +=2;
// reply
htobe16buf (payload, 3); payload += 2; // length, TODO
memset (payload, 0, 3); payload += 3; // ClearText: no options, and zero ret code. TODO
// ShortBuildReplyRecords. Exclude ours
uint8_t * records = buf + 1;
if (i > 0)
{
memcpy (payload, records, i*SHORT_TUNNEL_BUILD_RECORD_SIZE);
payload += i*SHORT_TUNNEL_BUILD_RECORD_SIZE;
records += i*SHORT_TUNNEL_BUILD_RECORD_SIZE;
}
if (i < num-1)
{
memcpy (payload, records, (num-1-i)*SHORT_TUNNEL_BUILD_RECORD_SIZE);
payload += (num-1-i)*SHORT_TUNNEL_BUILD_RECORD_SIZE;
}
otbrm->len += (payload - otbrm->GetPayload ());
otbrm->FillI2NPMessageHeader (eI2NPOutboundTunnelBuildReply, bufbe32toh (clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET));
uint8_t replyKeys[64]; // (reply key, tag)
i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "ReplyKeyAndTag", replyKeys); // TODO: correct domain
uint64_t tag;
memcpy (&tag, replyKeys + 32, 8);
// send garlic to reply tunnel
transports.SendMessage (clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET,
CreateTunnelGatewayMsg (bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
i2p::garlic::WrapECIESX25519AEADRatchetMessage (otbrm, replyKeys, tag)));
}
else
{
// we are participant, encrypt reply
uint8_t nonce[12];
memset (nonce, 0, 12);
uint8_t * reply = buf + 1;
for (int j = 0; j < num; j++)
{
nonce[4] = j; // nonce is record #
if (j == i)
{
// TODO: fill reply
if (!i2p::crypto::AEADChaCha20Poly1305 (reply, SHORT_TUNNEL_BUILD_RECORD_SIZE - 16,
noiseState.m_H, 32, noiseState.m_CK, nonce, reply, SHORT_TUNNEL_BUILD_RECORD_SIZE, true)) // encrypt
{
LogPrint (eLogWarning, "I2NP: Short reply AEAD encryption failed");
return;
}
}
else
i2p::crypto::ChaCha20 (reply, SHORT_TUNNEL_BUILD_RECORD_SIZE, noiseState.m_CK, nonce, reply);
reply += SHORT_TUNNEL_BUILD_RECORD_SIZE;
}
transports.SendMessage (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET,
CreateI2NPMessage (eI2NPShortTunnelBuild, buf, len,
bufbe32toh (clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
}
return;
}
record += SHORT_TUNNEL_BUILD_RECORD_SIZE;
}
}
std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (const uint8_t * buf) std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (const uint8_t * buf)
{ {
auto msg = NewI2NPTunnelMessage (); auto msg = NewI2NPTunnelMessage ();
@ -700,6 +806,9 @@ namespace i2p
case eI2NPVariableTunnelBuildReply: case eI2NPVariableTunnelBuildReply:
HandleVariableTunnelBuildReplyMsg (msgID, buf, size); HandleVariableTunnelBuildReplyMsg (msgID, buf, size);
break; break;
case eI2NPShortTunnelBuild:
HandleShortTunnelBuildMsg (msgID, buf, size);
break;
case eI2NPTunnelBuild: case eI2NPTunnelBuild:
HandleTunnelBuildMsg (buf, size); HandleTunnelBuildMsg (buf, size);
break; break;
@ -756,6 +865,7 @@ namespace i2p
case eI2NPVariableTunnelBuildReply: case eI2NPVariableTunnelBuildReply:
case eI2NPTunnelBuild: case eI2NPTunnelBuild:
case eI2NPTunnelBuildReply: case eI2NPTunnelBuildReply:
case eI2NPShortTunnelBuild:
// forward to tunnel thread // forward to tunnel thread
i2p::tunnel::tunnels.PostTunnelData (msg); i2p::tunnel::tunnels.PostTunnelData (msg);
break; break;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2021, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -55,7 +55,8 @@ namespace i2p
// TunnelBuild // TunnelBuild
const size_t TUNNEL_BUILD_RECORD_SIZE = 528; const size_t TUNNEL_BUILD_RECORD_SIZE = 528;
const size_t SHORT_TUNNEL_BUILD_RECORD_SIZE = 236;
//BuildRequestRecordClearText //BuildRequestRecordClearText
const size_t BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET = 0; const size_t BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET = 0;
const size_t BUILD_REQUEST_RECORD_OUR_IDENT_OFFSET = BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET + 4; const size_t BUILD_REQUEST_RECORD_OUR_IDENT_OFFSET = BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET + 4;
@ -100,6 +101,19 @@ namespace i2p
// ECIES BuildResponseRecord // ECIES BuildResponseRecord
const size_t ECIES_BUILD_RESPONSE_RECORD_OPTIONS_OFFSET = 0; const size_t ECIES_BUILD_RESPONSE_RECORD_OPTIONS_OFFSET = 0;
const size_t ECIES_BUILD_RESPONSE_RECORD_RET_OFFSET = 511; const size_t ECIES_BUILD_RESPONSE_RECORD_RET_OFFSET = 511;
// ShortRequestRecordClearText
const size_t SHORT_REQUEST_RECORD_ENCRYPTED_OFFSET = 16;
const size_t SHORT_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET = 0;
const size_t SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET = SHORT_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET + 4;
const size_t SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET = SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET + 4;
const size_t SHORT_REQUEST_RECORD_FLAG_OFFSET = SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET + 32;
const size_t SHORT_REQUEST_RECORD_MORE_FLAGS_OFFSET = SHORT_REQUEST_RECORD_FLAG_OFFSET + 1;
const size_t SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE = SHORT_REQUEST_RECORD_MORE_FLAGS_OFFSET + 2;
const size_t SHORT_REQUEST_RECORD_REQUEST_TIME_OFFSET = SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE + 1;
const size_t SHORT_REQUEST_RECORD_REQUEST_EXPIRATION_OFFSET = SHORT_REQUEST_RECORD_REQUEST_TIME_OFFSET + 4;
const size_t SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET = SHORT_REQUEST_RECORD_REQUEST_EXPIRATION_OFFSET + 4;
const size_t SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE = 172;
enum I2NPMessageType enum I2NPMessageType
{ {
@ -115,9 +129,13 @@ namespace i2p
eI2NPTunnelBuild = 21, eI2NPTunnelBuild = 21,
eI2NPTunnelBuildReply = 22, eI2NPTunnelBuildReply = 22,
eI2NPVariableTunnelBuild = 23, eI2NPVariableTunnelBuild = 23,
eI2NPVariableTunnelBuildReply = 24 eI2NPVariableTunnelBuildReply = 24,
eI2NPShortTunnelBuild = 25,
eI2NPOutboundTunnelBuildReply = 26
}; };
const uint8_t TUNNEL_BUILD_RECORD_GATEWAY_FLAG = 0x80;
const uint8_t TUNNEL_BUILD_RECORD_ENDPOINT_FLAG = 0x40;
const int NUM_TUNNEL_BUILD_RECORDS = 8; const int NUM_TUNNEL_BUILD_RECORDS = 8;
// DatabaseLookup flags // DatabaseLookup flags
@ -284,6 +302,7 @@ namespace tunnel
bool HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * clearText); bool HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * clearText);
void HandleVariableTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len); void HandleVariableTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len);
void HandleVariableTunnelBuildReplyMsg (uint32_t replyMsgID, uint8_t * buf, size_t len); void HandleVariableTunnelBuildReplyMsg (uint32_t replyMsgID, uint8_t * buf, size_t len);
void HandleShortTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len);
void HandleTunnelBuildMsg (uint8_t * buf, size_t len); void HandleTunnelBuildMsg (uint8_t * buf, size_t len);
std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (const uint8_t * buf); std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (const uint8_t * buf);

View File

@ -23,6 +23,10 @@
#include "HTTP.h" #include "HTTP.h"
#include "util.h" #include "util.h"
#ifdef __linux__
#include <linux/in6.h>
#endif
namespace i2p namespace i2p
{ {
namespace transport namespace transport
@ -1166,7 +1170,7 @@ namespace transport
if (!address) continue; if (!address) continue;
if (address->IsPublishedNTCP2 () && address->port) if (address->IsPublishedNTCP2 () && address->port)
{ {
if (address->host.is_v4()) if (address->IsV4())
{ {
try try
{ {
@ -1185,7 +1189,7 @@ namespace transport
auto conn = std::make_shared<NTCP2Session>(*this); auto conn = std::make_shared<NTCP2Session>(*this);
m_NTCP2Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCP2Server::HandleAccept, this, conn, std::placeholders::_1)); m_NTCP2Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCP2Server::HandleAccept, this, conn, std::placeholders::_1));
} }
else if (address->host.is_v6() && (context.SupportsV6 () || context.SupportsMesh ())) else if (address->IsV6() && (context.SupportsV6 () || context.SupportsMesh ()))
{ {
m_NTCP2V6Acceptor.reset (new boost::asio::ip::tcp::acceptor (GetService ())); m_NTCP2V6Acceptor.reset (new boost::asio::ip::tcp::acceptor (GetService ()));
try try
@ -1193,6 +1197,18 @@ namespace transport
m_NTCP2V6Acceptor->open (boost::asio::ip::tcp::v6()); m_NTCP2V6Acceptor->open (boost::asio::ip::tcp::v6());
m_NTCP2V6Acceptor->set_option (boost::asio::ip::v6_only (true)); m_NTCP2V6Acceptor->set_option (boost::asio::ip::v6_only (true));
m_NTCP2V6Acceptor->set_option (boost::asio::socket_base::reuse_address (true)); m_NTCP2V6Acceptor->set_option (boost::asio::socket_base::reuse_address (true));
#ifdef __linux__
if (!m_Address6 && !m_YggdrasilAddress) // 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_NTCP2V6Acceptor->set_option (ipv6PreferAddr(IPV6_PREFER_SRC_PUBLIC | IPV6_PREFER_SRC_HOME | IPV6_PREFER_SRC_NONCGA));
}
#endif
auto ep = boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v6(), address->port); auto ep = boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v6(), address->port);
if (m_Address6 && !context.SupportsMesh ()) if (m_Address6 && !context.SupportsMesh ())
ep = boost::asio::ip::tcp::endpoint (m_Address6->address(), address->port); ep = boost::asio::ip::tcp::endpoint (m_Address6->address(), address->port);

View File

@ -45,10 +45,8 @@ namespace i2p
UpdateRouterInfo (); UpdateRouterInfo ();
if (IsECIES ()) if (IsECIES ())
{ {
auto initState = new i2p::crypto::NoiseSymmetricState (); i2p::crypto::InitNoiseNState (m_InitialNoiseState, GetIdentity ()->GetEncryptionPublicKey ());
i2p::crypto::InitNoiseNState (*initState, GetIdentity ()->GetEncryptionPublicKey ()); m_ECIESSession = std::make_shared<i2p::garlic::RouterIncomingRatchetSession>(m_InitialNoiseState);
m_InitialNoiseState.reset (initState);
m_ECIESSession = std::make_shared<i2p::garlic::RouterIncomingRatchetSession>(*initState);
} }
} }
@ -486,7 +484,7 @@ namespace i2p
addr->ssu->introducers.clear (); addr->ssu->introducers.clear ();
port = addr->port; port = addr->port;
} }
// unpiblish NTCP2 addreeses // unpublish NTCP2 addreeses
bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2); bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2);
if (ntcp2) if (ntcp2)
PublishNTCP2Address (port, false, v4, v6, false); PublishNTCP2Address (port, false, v4, v6, false);
@ -679,7 +677,7 @@ namespace i2p
if (addr->IsPublishedNTCP2 ()) if (addr->IsPublishedNTCP2 ())
{ {
bool isYgg1 = i2p::util::net::IsYggdrasilAddress (addr->host); bool isYgg1 = i2p::util::net::IsYggdrasilAddress (addr->host);
if (addr->host.is_v6 () && ((isYgg && isYgg1) || (!isYgg && !isYgg1))) if (addr->IsV6 () && ((isYgg && isYgg1) || (!isYgg && !isYgg1)))
{ {
if (addr->host != host) if (addr->host != host)
{ {
@ -736,14 +734,8 @@ namespace i2p
} }
} }
std::shared_ptr<const i2p::data::IdentityEx> oldIdentity; std::shared_ptr<const i2p::data::IdentityEx> oldIdentity;
bool rekey = m_Keys.GetPublic ()->GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1; if (m_Keys.GetPublic ()->GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1 ||
if (!rekey && m_Keys.GetPublic ()->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ELGAMAL) m_Keys.GetPublic ()->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ELGAMAL)
{
// rekey routers with bandwidth = L (or default) this time
bool isFloodfill; i2p::config::GetOption("floodfill", isFloodfill);
if (!isFloodfill) rekey = true;
}
if (rekey)
{ {
// update keys // update keys
LogPrint (eLogInfo, "Router: router keys are obsolete. Creating new"); LogPrint (eLogInfo, "Router: router keys are obsolete. Creating new");
@ -881,34 +873,11 @@ namespace i2p
bool RouterContext::DecryptTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data) bool RouterContext::DecryptTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data)
{ {
if (!m_TunnelDecryptor) return false;
if (IsECIES ()) if (IsECIES ())
{ return DecryptECIESTunnelBuildRecord (encrypted, data, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE);
if (!m_InitialNoiseState) return false;
// m_InitialNoiseState is h = SHA256(h || hepk)
m_CurrentNoiseState.reset (new i2p::crypto::NoiseSymmetricState (*m_InitialNoiseState));
m_CurrentNoiseState->MixHash (encrypted, 32); // h = SHA256(h || sepk)
uint8_t sharedSecret[32];
if (!m_TunnelDecryptor->Decrypt (encrypted, sharedSecret, nullptr, false))
{
LogPrint (eLogWarning, "Router: Incorrect ephemeral public key");
return false;
}
m_CurrentNoiseState->MixKey (sharedSecret);
encrypted += 32;
uint8_t nonce[12];
memset (nonce, 0, 12);
if (!i2p::crypto::AEADChaCha20Poly1305 (encrypted, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE,
m_CurrentNoiseState->m_H, 32, m_CurrentNoiseState->m_CK + 32, nonce, data, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE, false)) // decrypt
{
LogPrint (eLogWarning, "Router: Tunnel record AEAD decryption failed");
return false;
}
m_CurrentNoiseState->MixHash (encrypted, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE + 16); // h = SHA256(h || ciphertext)
return true;
}
else else
{ {
if (!m_TunnelDecryptor) return false;
BN_CTX * ctx = BN_CTX_new (); BN_CTX * ctx = BN_CTX_new ();
bool success = m_TunnelDecryptor->Decrypt (encrypted, data, ctx, false); bool success = m_TunnelDecryptor->Decrypt (encrypted, data, ctx, false);
BN_CTX_free (ctx); BN_CTX_free (ctx);
@ -916,6 +885,42 @@ namespace i2p
} }
} }
bool RouterContext::DecryptECIESTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data, size_t clearTextSize)
{
// m_InitialNoiseState is h = SHA256(h || hepk)
m_CurrentNoiseState = m_InitialNoiseState;
m_CurrentNoiseState.MixHash (encrypted, 32); // h = SHA256(h || sepk)
uint8_t sharedSecret[32];
if (!m_TunnelDecryptor->Decrypt (encrypted, sharedSecret, nullptr, false))
{
LogPrint (eLogWarning, "Router: Incorrect ephemeral public key");
return false;
}
m_CurrentNoiseState.MixKey (sharedSecret);
encrypted += 32;
uint8_t nonce[12];
memset (nonce, 0, 12);
if (!i2p::crypto::AEADChaCha20Poly1305 (encrypted, clearTextSize, m_CurrentNoiseState.m_H, 32,
m_CurrentNoiseState.m_CK + 32, nonce, data, clearTextSize, false)) // decrypt
{
LogPrint (eLogWarning, "Router: Tunnel record AEAD decryption failed");
return false;
}
m_CurrentNoiseState.MixHash (encrypted, clearTextSize + 16); // h = SHA256(h || ciphertext)
return true;
}
bool RouterContext::DecryptTunnelShortRequestRecord (const uint8_t * encrypted, uint8_t * data)
{
if (IsECIES ())
return DecryptECIESTunnelBuildRecord (encrypted, data, SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE);
else
{
LogPrint (eLogWarning, "Router: Can't decrypt short request record on non-ECIES router");
return false;
}
}
i2p::crypto::X25519Keys& RouterContext::GetStaticKeys () i2p::crypto::X25519Keys& RouterContext::GetStaticKeys ()
{ {
if (!m_StaticKeys) if (!m_StaticKeys)

View File

@ -18,13 +18,14 @@
#include "Identity.h" #include "Identity.h"
#include "RouterInfo.h" #include "RouterInfo.h"
#include "Garlic.h" #include "Garlic.h"
#include "I18N_langs.h"
namespace i2p namespace i2p
{ {
namespace garlic namespace garlic
{ {
class RouterIncomingRatchetSession; class RouterIncomingRatchetSession;
} }
const char ROUTER_INFO[] = "router.info"; const char ROUTER_INFO[] = "router.info";
const char ROUTER_KEYS[] = "router.keys"; const char ROUTER_KEYS[] = "router.keys";
@ -39,7 +40,7 @@ namespace garlic
eRouterStatusError = 3, eRouterStatusError = 3,
eRouterStatusUnknown = 4, eRouterStatusUnknown = 4,
eRouterStatusProxy = 5, eRouterStatusProxy = 5,
eRouterStatusMesh = 6 eRouterStatusMesh = 6
}; };
enum RouterError enum RouterError
@ -49,7 +50,7 @@ namespace garlic
eRouterErrorOffline = 2, eRouterErrorOffline = 2,
eRouterErrorSymmetricNAT = 3 eRouterErrorSymmetricNAT = 3
}; };
class RouterContext: public i2p::garlic::GarlicDestination class RouterContext: public i2p::garlic::GarlicDestination
{ {
private: private:
@ -96,9 +97,10 @@ namespace garlic
int GetNetID () const { return m_NetID; }; int GetNetID () const { return m_NetID; };
void SetNetID (int netID) { m_NetID = netID; }; void SetNetID (int netID) { m_NetID = netID; };
bool DecryptTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data); bool DecryptTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data);
bool DecryptTunnelShortRequestRecord (const uint8_t * encrypted, uint8_t * data);
void UpdatePort (int port); // called from Daemon 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 SSU or Daemon
void PublishNTCP2Address (int port, bool publish, bool v4, bool v6, bool ygg); void PublishNTCP2Address (int port, bool publish, bool v4, bool v6, bool ygg);
void UpdateNTCP2Address (bool enable); void UpdateNTCP2Address (bool enable);
void RemoveNTCPAddress (bool v4only = true); // delete NTCP address for older routers. TODO: remove later void RemoveNTCPAddress (bool v4only = true); // delete NTCP address for older routers. TODO: remove later
@ -123,12 +125,12 @@ namespace garlic
void SetSupportsV4 (bool supportsV4); void SetSupportsV4 (bool supportsV4);
void SetSupportsMesh (bool supportsmesh, const boost::asio::ip::address_v6& host); void SetSupportsMesh (bool supportsmesh, const boost::asio::ip::address_v6& host);
bool IsECIES () const { return GetIdentity ()->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD; }; bool IsECIES () const { return GetIdentity ()->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD; };
std::unique_ptr<i2p::crypto::NoiseSymmetricState>& GetCurrentNoiseState () { return m_CurrentNoiseState; }; i2p::crypto::NoiseSymmetricState& GetCurrentNoiseState () { return m_CurrentNoiseState; };
void UpdateNTCP2V6Address (const boost::asio::ip::address& host); // called from Daemon. TODO: remove void UpdateNTCP2V6Address (const boost::asio::ip::address& host); // called from Daemon. TODO: remove
void UpdateStats (); void UpdateStats ();
void UpdateTimestamp (uint64_t ts); // in seconds, called from NetDb before publishing void UpdateTimestamp (uint64_t ts); // in seconds, called from NetDb before publishing
void CleanupDestination (); // garlic destination void CleanupDestination (); // garlic destination
// implements LocalDestination // implements LocalDestination
std::shared_ptr<const i2p::data::IdentityEx> GetIdentity () const { return m_Keys.GetPublic (); }; std::shared_ptr<const i2p::data::IdentityEx> GetIdentity () const { return m_Keys.GetPublic (); };
@ -144,6 +146,10 @@ namespace garlic
void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg); void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg); void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
// i18n
std::shared_ptr<const i2p::i18n::Locale> GetLanguage () { return m_Language; };
void SetLanguage (const std::shared_ptr<const i2p::i18n::Locale> language) { m_Language = language; };
protected: protected:
// implements GarlicDestination // implements GarlicDestination
@ -159,6 +165,8 @@ namespace garlic
bool Load (); bool Load ();
void SaveKeys (); void SaveKeys ();
bool DecryptECIESTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data, size_t clearTextSize);
private: private:
i2p::data::RouterInfo m_RouterInfo; i2p::data::RouterInfo m_RouterInfo;
@ -177,7 +185,10 @@ namespace garlic
std::unique_ptr<NTCP2PrivateKeys> m_NTCP2Keys; std::unique_ptr<NTCP2PrivateKeys> m_NTCP2Keys;
std::unique_ptr<i2p::crypto::X25519Keys> m_StaticKeys; std::unique_ptr<i2p::crypto::X25519Keys> m_StaticKeys;
// for ECIESx25519 // for ECIESx25519
std::unique_ptr<i2p::crypto::NoiseSymmetricState> m_InitialNoiseState, m_CurrentNoiseState; i2p::crypto::NoiseSymmetricState m_InitialNoiseState, m_CurrentNoiseState;
// i18n
std::shared_ptr<const i2p::i18n::Locale> m_Language;
}; };
extern RouterContext context; extern RouterContext context;

View File

@ -36,7 +36,7 @@ namespace data
RouterInfo::RouterInfo (const std::string& fullPath): RouterInfo::RouterInfo (const std::string& fullPath):
m_FullPath (fullPath), m_IsUpdated (false), m_IsUnreachable (false), m_FullPath (fullPath), m_IsUpdated (false), m_IsUnreachable (false),
m_SupportedTransports (0), m_Caps (0), m_Version (0) m_SupportedTransports (0), m_ReachableTransports (0), m_Caps (0), m_Version (0)
{ {
m_Addresses = boost::make_shared<Addresses>(); // create empty list m_Addresses = boost::make_shared<Addresses>(); // create empty list
m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE]; m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE];
@ -45,7 +45,7 @@ namespace data
RouterInfo::RouterInfo (const uint8_t * buf, int len): RouterInfo::RouterInfo (const uint8_t * buf, int len):
m_IsUpdated (true), m_IsUnreachable (false), m_SupportedTransports (0), m_IsUpdated (true), m_IsUnreachable (false), m_SupportedTransports (0),
m_Caps (0), m_Version (0) m_ReachableTransports (0), m_Caps (0), m_Version (0)
{ {
m_Addresses = boost::make_shared<Addresses>(); // create empty list m_Addresses = boost::make_shared<Addresses>(); // create empty list
if (len <= MAX_RI_BUFFER_SIZE) if (len <= MAX_RI_BUFFER_SIZE)
@ -84,6 +84,7 @@ namespace data
m_IsUpdated = true; m_IsUpdated = true;
m_IsUnreachable = false; m_IsUnreachable = false;
m_SupportedTransports = 0; m_SupportedTransports = 0;
m_ReachableTransports = 0;
m_Caps = 0; m_Caps = 0;
// don't clean up m_Addresses, it will be replaced in ReadFromStream // don't clean up m_Addresses, it will be replaced in ReadFromStream
m_Properties.clear (); m_Properties.clear ();
@ -305,9 +306,10 @@ namespace data
if (isHost) if (isHost)
{ {
if (address->host.is_v6 ()) if (address->host.is_v6 ())
supportedTransports |= i2p::util::net::IsYggdrasilAddress (address->host) ? eNTCP2V6Mesh : eNTCP2V6; supportedTransports |= (i2p::util::net::IsYggdrasilAddress (address->host) ? eNTCP2V6Mesh : eNTCP2V6);
else else
supportedTransports |= eNTCP2V4; supportedTransports |= eNTCP2V4;
m_ReachableTransports |= supportedTransports;
} }
else if (!address->published) else if (!address->published)
{ {
@ -347,10 +349,16 @@ namespace data
else else
it.iPort = 0; it.iPort = 0;
} }
if (!numValid) address->ssu->introducers.resize (0); if (numValid)
m_ReachableTransports |= supportedTransports;
else
address->ssu->introducers.resize (0);
} }
else if (isHost && address->port) else if (isHost && address->port)
{
address->published = true; address->published = true;
m_ReachableTransports |= supportedTransports;
}
} }
} }
if (supportedTransports) if (supportedTransports)
@ -546,7 +554,7 @@ namespace data
if (address.IsNTCP2 ()) if (address.IsNTCP2 ())
{ {
WriteString ("NTCP2", s); WriteString ("NTCP2", s);
if (address.IsPublishedNTCP2 () && !address.host.is_unspecified ()) if (address.IsPublishedNTCP2 () && !address.host.is_unspecified () && address.port)
isPublished = true; isPublished = true;
else else
{ {
@ -860,6 +868,7 @@ namespace data
for (auto& intro: addr->ssu->introducers) for (auto& intro: addr->ssu->introducers)
if (intro.iTag == introducer.iTag) return false; // already presented if (intro.iTag == introducer.iTag) return false; // already presented
addr->ssu->introducers.push_back (introducer); addr->ssu->introducers.push_back (introducer);
m_ReachableTransports |= (addr->IsV4 () ? eSSUV4 : eSSUV6);
return true; return true;
} }
} }
@ -877,6 +886,8 @@ namespace data
if (boost::asio::ip::udp::endpoint (it->iHost, it->iPort) == e) if (boost::asio::ip::udp::endpoint (it->iHost, it->iPort) == e)
{ {
addr->ssu->introducers.erase (it); addr->ssu->introducers.erase (it);
if (addr->ssu->introducers.empty ())
m_ReachableTransports &= ~(addr->IsV4 () ? eSSUV4 : eSSUV6);
return true; return true;
} }
} }
@ -987,9 +998,16 @@ namespace data
for (auto it = m_Addresses->begin (); it != m_Addresses->end ();) for (auto it = m_Addresses->begin (); it != m_Addresses->end ();)
{ {
auto addr = *it; auto addr = *it;
addr->caps &= ~AddressCaps::eV6; if (addr->IsV6 ())
if (addr->host.is_v6 ()) {
it = m_Addresses->erase (it); if (addr->IsV4 ())
{
addr->caps &= ~AddressCaps::eV6;
++it;
}
else
it = m_Addresses->erase (it);
}
else else
++it; ++it;
} }
@ -1004,9 +1022,16 @@ namespace data
for (auto it = m_Addresses->begin (); it != m_Addresses->end ();) for (auto it = m_Addresses->begin (); it != m_Addresses->end ();)
{ {
auto addr = *it; auto addr = *it;
addr->caps &= ~AddressCaps::eV4; if (addr->IsV4 ())
if (addr->host.is_v4 ()) {
it = m_Addresses->erase (it); if (addr->IsV6 ())
{
addr->caps &= ~AddressCaps::eV4;
++it;
}
else
it = m_Addresses->erase (it);
}
else else
++it; ++it;
} }
@ -1177,12 +1202,38 @@ namespace data
for (auto& addr: *m_Addresses) for (auto& addr: *m_Addresses)
{ {
// TODO: implement SSU // TODO: implement SSU
if (addr->transportStyle == eTransportNTCP && (!addr->IsPublishedNTCP2 () || addr->port)) if (addr->transportStyle == eTransportNTCP && !addr->IsPublishedNTCP2 ())
{ {
addr->caps &= ~(eV4 | eV6); addr->caps &= ~(eV4 | eV6);
addr->caps |= transports; addr->caps |= transports;
} }
} }
} }
void RouterInfo::UpdateSupportedTransports ()
{
m_SupportedTransports = 0;
m_ReachableTransports = 0;
for (const auto& addr: *m_Addresses)
{
uint8_t transports = 0;
if (addr->transportStyle == eTransportNTCP)
{
if (addr->IsV4 ()) transports |= eNTCP2V4;
if (addr->IsV6 ())
transports |= (i2p::util::net::IsYggdrasilAddress (addr->host) ? eNTCP2V6Mesh : eNTCP2V6);
if (addr->IsPublishedNTCP2 ())
m_ReachableTransports |= transports;
}
else if (addr->transportStyle == eTransportSSU)
{
if (addr->IsV4 ()) transports |= eSSUV4;
if (addr->IsV6 ()) transports |= eSSUV6;
if (addr->IsReachableSSU ())
m_ReachableTransports |= transports;
}
m_SupportedTransports |= transports;
}
}
} }
} }

View File

@ -59,7 +59,7 @@ namespace data
{ {
public: public:
enum SupportedTranports enum SupportedTransports
{ {
eNTCP2V4 = 0x01, eNTCP2V4 = 0x01,
eNTCP2V6 = 0x02, eNTCP2V6 = 0x02,
@ -147,7 +147,7 @@ namespace data
bool IsNTCP2 () const { return (bool)ntcp2; }; bool IsNTCP2 () const { return (bool)ntcp2; };
bool IsPublishedNTCP2 () const { return IsNTCP2 () && published; }; bool IsPublishedNTCP2 () const { return IsNTCP2 () && published; };
bool IsReachableSSU () const { return (bool)ssu && (!host.is_unspecified () || !ssu->introducers.empty ()); }; bool IsReachableSSU () const { return (bool)ssu && (published || !ssu->introducers.empty ()); };
bool UsesIntroducer () const { return (bool)ssu && !ssu->introducers.empty (); }; bool UsesIntroducer () const { return (bool)ssu && !ssu->introducers.empty (); };
bool IsIntroducer () const { return caps & eSSUIntroducer; }; bool IsIntroducer () const { return caps & eSSUIntroducer; };
@ -187,6 +187,7 @@ namespace data
std::string GetProperty (const std::string& key) const; // called from RouterContext only std::string GetProperty (const std::string& key) const; // called from RouterContext only
void ClearProperties () { m_Properties.clear (); }; void ClearProperties () { m_Properties.clear (); };
void SetUnreachableAddressesTransportCaps (uint8_t transports); // bitmask of AddressCaps void SetUnreachableAddressesTransportCaps (uint8_t transports); // bitmask of AddressCaps
void UpdateSupportedTransports ();
bool IsFloodfill () const { return m_Caps & Caps::eFloodfill; }; bool IsFloodfill () const { return m_Caps & Caps::eFloodfill; };
bool IsReachable () const { return m_Caps & Caps::eReachable; }; bool IsReachable () const { return m_Caps & Caps::eReachable; };
bool IsSSU (bool v4only = true) const; bool IsSSU (bool v4only = true) const;
@ -269,7 +270,7 @@ namespace data
boost::shared_ptr<Addresses> m_Addresses; // TODO: use std::shared_ptr and std::atomic_store for gcc >= 4.9 boost::shared_ptr<Addresses> m_Addresses; // TODO: use std::shared_ptr and std::atomic_store for gcc >= 4.9
std::map<std::string, std::string> m_Properties; std::map<std::string, std::string> m_Properties;
bool m_IsUpdated, m_IsUnreachable; bool m_IsUpdated, m_IsUnreachable;
uint8_t m_SupportedTransports, m_Caps; uint8_t m_SupportedTransports, m_ReachableTransports, m_Caps;
int m_Version; int m_Version;
mutable std::shared_ptr<RouterProfile> m_Profile; mutable std::shared_ptr<RouterProfile> m_Profile;
}; };

View File

@ -14,6 +14,10 @@
#include "SSU.h" #include "SSU.h"
#include "util.h" #include "util.h"
#ifdef __linux__
#include <linux/in6.h>
#endif
#ifdef _WIN32 #ifdef _WIN32
#include <boost/winapi/error_codes.hpp> #include <boost/winapi/error_codes.hpp>
#endif #endif
@ -62,6 +66,18 @@ namespace transport
m_SocketV6.set_option (boost::asio::ip::v6_only (true)); 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::receive_buffer_size (SSU_SOCKET_RECEIVE_BUFFER_SIZE));
m_SocketV6.set_option (boost::asio::socket_base::send_buffer_size (SSU_SOCKET_SEND_BUFFER_SIZE)); m_SocketV6.set_option (boost::asio::socket_base::send_buffer_size (SSU_SOCKET_SEND_BUFFER_SIZE));
#ifdef __linux__
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); m_SocketV6.bind (m_EndpointV6);
LogPrint (eLogInfo, "SSU: Start listening v6 port ", m_EndpointV6.port()); LogPrint (eLogInfo, "SSU: Start listening v6 port ", m_EndpointV6.port());
} }

View File

@ -383,7 +383,7 @@ namespace transport
{ {
// tell out peer to now assign relay tag // tell out peer to now assign relay tag
flag = SSU_HEADER_EXTENDED_OPTIONS_INCLUDED; flag = SSU_HEADER_EXTENDED_OPTIONS_INCLUDED;
*payload = 2; payload++; // 1 byte length *payload = 2; payload++; // 1 byte length
uint16_t flags = 0; // clear EXTENDED_OPTIONS_FLAG_REQUEST_RELAY_TAG uint16_t flags = 0; // clear EXTENDED_OPTIONS_FLAG_REQUEST_RELAY_TAG
htobe16buf (payload, flags); htobe16buf (payload, flags);
payload += 2; payload += 2;
@ -1073,7 +1073,10 @@ namespace transport
LogPrint (eLogDebug, "SSU: peer test from Charlie. We are Bob"); LogPrint (eLogDebug, "SSU: peer test from Charlie. We are Bob");
auto session = m_Server.GetPeerTestSession (nonce); // session with Alice from PeerTest auto session = m_Server.GetPeerTestSession (nonce); // session with Alice from PeerTest
if (session && session->m_State == eSessionStateEstablished) if (session && session->m_State == eSessionStateEstablished)
session->Send (PAYLOAD_TYPE_PEER_TEST, buf, len); // back to Alice {
const auto& ep = session->GetRemoteEndpoint (); // Alice's endpoint as known to Bob
session->SendPeerTest (nonce, ep.address (), ep.port (), introKey, false, true); // send back to Alice
}
m_Server.RemovePeerTest (nonce); // nonce has been used m_Server.RemovePeerTest (nonce); // nonce has been used
break; break;
} }
@ -1093,9 +1096,12 @@ namespace transport
if (port) if (port)
{ {
LogPrint (eLogDebug, "SSU: peer test from Bob. We are Charlie"); LogPrint (eLogDebug, "SSU: peer test from Bob. We are Charlie");
m_Server.NewPeerTest (nonce, ePeerTestParticipantCharlie);
Send (PAYLOAD_TYPE_PEER_TEST, buf, len); // back to Bob Send (PAYLOAD_TYPE_PEER_TEST, buf, len); // back to Bob
SendPeerTest (nonce, addr, port, introKey); // to Alice with her address received from Bob if (!addr.is_unspecified () && !i2p::util::net::IsInReservedRange(addr))
{
m_Server.NewPeerTest (nonce, ePeerTestParticipantCharlie);
SendPeerTest (nonce, addr, port, introKey); // to Alice with her address received from Bob
}
} }
else else
{ {

View File

@ -38,6 +38,7 @@ namespace tunnel
const int TUNNEL_CREATION_TIMEOUT = 30; // 30 seconds const int TUNNEL_CREATION_TIMEOUT = 30; // 30 seconds
const int STANDARD_NUM_RECORDS = 4; // in VariableTunnelBuild message const int STANDARD_NUM_RECORDS = 4; // in VariableTunnelBuild message
const int MAX_NUM_RECORDS = 8; const int MAX_NUM_RECORDS = 8;
const int HIGH_LATENCY_PER_HOP = 250; // in milliseconds
enum TunnelState enum TunnelState
{ {
@ -98,6 +99,8 @@ namespace tunnel
bool LatencyFitsRange(uint64_t lowerbound, uint64_t upperbound) const; bool LatencyFitsRange(uint64_t lowerbound, uint64_t upperbound) const;
bool LatencyIsKnown() const { return m_Latency > 0; } bool LatencyIsKnown() const { return m_Latency > 0; }
bool IsSlow () const { return LatencyIsKnown() && (int)m_Latency > HIGH_LATENCY_PER_HOP*GetNumHops (); }
protected: protected:
void PrintHops (std::stringstream& s) const; void PrintHops (std::stringstream& s) const;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2021, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -81,8 +81,8 @@ namespace tunnel
void TunnelHopConfig::CreateBuildRequestRecord (uint8_t * record, uint32_t replyMsgID, BN_CTX * ctx) void TunnelHopConfig::CreateBuildRequestRecord (uint8_t * record, uint32_t replyMsgID, BN_CTX * ctx)
{ {
uint8_t flag = 0; uint8_t flag = 0;
if (isGateway) flag |= 0x80; if (isGateway) flag |= TUNNEL_BUILD_RECORD_GATEWAY_FLAG;
if (isEndpoint) flag |= 0x40; if (isEndpoint) flag |= TUNNEL_BUILD_RECORD_ENDPOINT_FLAG;
auto encryptor = ident->CreateEncryptor (nullptr); auto encryptor = ident->CreateEncryptor (nullptr);
if (IsECIES ()) if (IsECIES ())
{ {

View File

@ -142,16 +142,24 @@ namespace tunnel
{ {
std::vector<std::shared_ptr<InboundTunnel> > v; std::vector<std::shared_ptr<InboundTunnel> > v;
int i = 0; int i = 0;
std::shared_ptr<InboundTunnel> slowTunnel;
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex); std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
for (const auto& it : m_InboundTunnels) for (const auto& it : m_InboundTunnels)
{ {
if (i >= num) break; if (i >= num) break;
if (it->IsEstablished ()) if (it->IsEstablished ())
{ {
v.push_back (it); if (it->IsSlow () && !slowTunnel)
i++; slowTunnel = it;
else
{
v.push_back (it);
i++;
}
} }
} }
if (slowTunnel && (int)v.size () < (num/2+1))
v.push_back (slowTunnel);
return v; return v;
} }
@ -172,13 +180,16 @@ namespace tunnel
{ {
if (tunnels.empty ()) return nullptr; if (tunnels.empty ()) return nullptr;
uint32_t ind = rand () % (tunnels.size ()/2 + 1), i = 0; uint32_t ind = rand () % (tunnels.size ()/2 + 1), i = 0;
bool skipped = false;
typename TTunnels::value_type tunnel = nullptr; typename TTunnels::value_type tunnel = nullptr;
for (const auto& it: tunnels) for (const auto& it: tunnels)
{ {
if (it->IsEstablished () && it != excluded) if (it->IsEstablished () && it != excluded)
{ {
if(HasLatencyRequirement() && it->LatencyIsKnown() && !it->LatencyFitsRange(m_MinLatency, m_MaxLatency)) { if (it->IsSlow () || (HasLatencyRequirement() && it->LatencyIsKnown() &&
i ++; !it->LatencyFitsRange(m_MinLatency, m_MaxLatency)))
{
i++; skipped = true;
continue; continue;
} }
tunnel = it; tunnel = it;
@ -186,7 +197,8 @@ namespace tunnel
} }
if (i > ind && tunnel) break; if (i > ind && tunnel) break;
} }
if(HasLatencyRequirement() && !tunnel) { if (!tunnel && skipped)
{
ind = rand () % (tunnels.size ()/2 + 1), i = 0; ind = rand () % (tunnels.size ()/2 + 1), i = 0;
for (const auto& it: tunnels) for (const auto& it: tunnels)
{ {
@ -393,10 +405,14 @@ namespace tunnel
} }
} }
bool TunnelPool::IsExploratory () const
{
return i2p::tunnel::tunnels.GetExploratoryPool () == shared_from_this ();
}
std::shared_ptr<const i2p::data::RouterInfo> TunnelPool::SelectNextHop (std::shared_ptr<const i2p::data::RouterInfo> prevHop, bool reverse) const std::shared_ptr<const i2p::data::RouterInfo> TunnelPool::SelectNextHop (std::shared_ptr<const i2p::data::RouterInfo> prevHop, bool reverse) const
{ {
bool isExploratory = (i2p::tunnel::tunnels.GetExploratoryPool () == shared_from_this ()); auto hop = IsExploratory () ? i2p::data::netdb.GetRandomRouter (prevHop, reverse):
auto hop = isExploratory ? i2p::data::netdb.GetRandomRouter (prevHop, reverse):
i2p::data::netdb.GetHighBandwidthRandomRouter (prevHop, reverse); i2p::data::netdb.GetHighBandwidthRandomRouter (prevHop, reverse);
if (!hop || hop->GetProfile ()->IsBad ()) if (!hop || hop->GetProfile ()->IsBad ())
@ -521,6 +537,11 @@ namespace tunnel
void TunnelPool::RecreateInboundTunnel (std::shared_ptr<InboundTunnel> tunnel) void TunnelPool::RecreateInboundTunnel (std::shared_ptr<InboundTunnel> tunnel)
{ {
if (IsExploratory () || tunnel->IsSlow ()) // always create new exploratory tunnel or if slow
{
CreateInboundTunnel ();
return;
}
auto outboundTunnel = GetNextOutboundTunnel (); auto outboundTunnel = GetNextOutboundTunnel ();
if (!outboundTunnel) if (!outboundTunnel)
outboundTunnel = tunnels.GetNextOutboundTunnel (); outboundTunnel = tunnels.GetNextOutboundTunnel ();
@ -567,6 +588,11 @@ namespace tunnel
void TunnelPool::RecreateOutboundTunnel (std::shared_ptr<OutboundTunnel> tunnel) void TunnelPool::RecreateOutboundTunnel (std::shared_ptr<OutboundTunnel> tunnel)
{ {
if (IsExploratory () || tunnel->IsSlow ()) // always create new exploratory tunnel or if slow
{
CreateOutboundTunnel ();
return;
}
auto inboundTunnel = GetNextInboundTunnel (); auto inboundTunnel = GetNextInboundTunnel ();
if (!inboundTunnel) if (!inboundTunnel)
inboundTunnel = tunnels.GetNextInboundTunnel (); inboundTunnel = tunnels.GetNextInboundTunnel ();

View File

@ -77,6 +77,7 @@ namespace tunnel
void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg); void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
void ProcessDeliveryStatus (std::shared_ptr<I2NPMessage> msg); void ProcessDeliveryStatus (std::shared_ptr<I2NPMessage> msg);
bool IsExploratory () const;
bool IsActive () const { return m_IsActive; }; bool IsActive () const { return m_IsActive; };
void SetActive (bool isActive) { m_IsActive = isActive; }; void SetActive (bool isActive) { m_IsActive = isActive; };
void DetachTunnels (); void DetachTunnels ();

View File

@ -177,13 +177,13 @@ namespace util
SaveStateHelper (T& orig): m_Original (orig), m_Copy (orig) {}; SaveStateHelper (T& orig): m_Original (orig), m_Copy (orig) {};
~SaveStateHelper () { m_Original = m_Copy; }; ~SaveStateHelper () { m_Original = m_Copy; };
private: private:
T& m_Original; T& m_Original;
T m_Copy; T m_Copy;
}; };
namespace net namespace net
{ {
int GetMTU (const boost::asio::ip::address& localAddress); int GetMTU (const boost::asio::ip::address& localAddress);

View File

@ -495,6 +495,8 @@ namespace client
options[I2CP_PARAM_LEASESET_TYPE] = value; options[I2CP_PARAM_LEASESET_TYPE] = value;
if (i2p::config::GetOption(prefix + I2CP_PARAM_LEASESET_ENCRYPTION_TYPE, value)) if (i2p::config::GetOption(prefix + I2CP_PARAM_LEASESET_ENCRYPTION_TYPE, value))
options[I2CP_PARAM_LEASESET_ENCRYPTION_TYPE] = value; options[I2CP_PARAM_LEASESET_ENCRYPTION_TYPE] = value;
if (i2p::config::GetOption(prefix + I2CP_PARAM_LEASESET_PRIV_KEY, value) && !value.empty ())
options[I2CP_PARAM_LEASESET_PRIV_KEY] = value;
} }
void ClientContext::ReadTunnels () void ClientContext::ReadTunnels ()

View File

@ -28,6 +28,7 @@
#include "I2PTunnel.h" #include "I2PTunnel.h"
#include "Config.h" #include "Config.h"
#include "HTTP.h" #include "HTTP.h"
#include "I18N.h"
namespace i2p { namespace i2p {
namespace proxy { namespace proxy {
@ -71,8 +72,8 @@ namespace proxy {
void SentHTTPFailed(const boost::system::error_code & ecode); void SentHTTPFailed(const boost::system::error_code & ecode);
void HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream); void HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream);
/* error helpers */ /* error helpers */
void GenericProxyError(const char *title, const char *description); void GenericProxyError(const std::string& title, const std::string& description);
void GenericProxyInfo(const char *title, const char *description); void GenericProxyInfo(const std::string& title, const std::string& description);
void HostNotFound(std::string & host); void HostNotFound(std::string & host);
void SendProxyError(std::string & content); void SendProxyError(std::string & content);
@ -151,17 +152,17 @@ namespace proxy {
Done(shared_from_this()); Done(shared_from_this());
} }
void HTTPReqHandler::GenericProxyError(const char *title, const char *description) { void HTTPReqHandler::GenericProxyError(const std::string& title, const std::string& description) {
std::stringstream ss; std::stringstream ss;
ss << "<h1>Proxy error: " << title << "</h1>\r\n"; ss << "<h1>" << tr("Proxy error") << ": " << title << "</h1>\r\n";
ss << "<p>" << description << "</p>\r\n"; ss << "<p>" << description << "</p>\r\n";
std::string content = ss.str(); std::string content = ss.str();
SendProxyError(content); SendProxyError(content);
} }
void HTTPReqHandler::GenericProxyInfo(const char *title, const char *description) { void HTTPReqHandler::GenericProxyInfo(const std::string& title, const std::string& description) {
std::stringstream ss; std::stringstream ss;
ss << "<h1>Proxy info: " << title << "</h1>\r\n"; ss << "<h1>" << tr("Proxy info") << ": " << title << "</h1>\r\n";
ss << "<p>" << description << "</p>\r\n"; ss << "<p>" << description << "</p>\r\n";
std::string content = ss.str(); std::string content = ss.str();
SendProxyError(content); SendProxyError(content);
@ -169,9 +170,9 @@ namespace proxy {
void HTTPReqHandler::HostNotFound(std::string & host) { void HTTPReqHandler::HostNotFound(std::string & host) {
std::stringstream ss; std::stringstream ss;
ss << "<h1>Proxy error: Host not found</h1>\r\n" ss << "<h1>" << tr("Proxy error: Host not found") << "</h1>\r\n"
<< "<p>Remote host not found in router's addressbook</p>\r\n" << "<p>" << tr("Remote host not found in router's addressbook") << "</p>\r\n"
<< "<p>You may try to find this host on jump services below:</p>\r\n" << "<p>" << tr("You may try to find this host on jump services below") << ":</p>\r\n"
<< "<ul>\r\n"; << "<ul>\r\n";
for (const auto& js : jumpservices) { for (const auto& js : jumpservices) {
ss << " <li><a href=\"" << js.second << host << "\">" << js.first << "</a></li>\r\n"; ss << " <li><a href=\"" << js.second << host << "\">" << js.first << "</a></li>\r\n";
@ -216,6 +217,7 @@ namespace proxy {
b64 = i2p::http::UrlDecode(value); b64 = i2p::http::UrlDecode(value);
// if we need update exists, request formed with update param // if we need update exists, request formed with update param
if (params["update"] == "true") { len += std::strlen("&update=true"); confirm = true; } if (params["update"] == "true") { len += std::strlen("&update=true"); confirm = true; }
if (pos != 0 && url.query[pos-1] == '&') { pos--; len++; } // if helper is not only one query option
url.query.replace(pos, len, ""); url.query.replace(pos, len, "");
return true; return true;
} }
@ -268,7 +270,7 @@ namespace proxy {
if (m_req_len < 0) { if (m_req_len < 0) {
LogPrint(eLogError, "HTTPProxy: unable to parse request"); LogPrint(eLogError, "HTTPProxy: unable to parse request");
GenericProxyError("Invalid request", "Proxy unable to parse your request"); GenericProxyError(tr("Invalid request"), tr("Proxy unable to parse your request"));
return true; /* parse error */ return true; /* parse error */
} }
@ -283,7 +285,7 @@ namespace proxy {
if (!m_Addresshelper) if (!m_Addresshelper)
{ {
LogPrint(eLogWarning, "HTTPProxy: addresshelper request rejected"); LogPrint(eLogWarning, "HTTPProxy: addresshelper request rejected");
GenericProxyError("Invalid request", "addresshelper is not supported"); GenericProxyError(tr("Invalid request"), tr("addresshelper is not supported"));
return true; return true;
} }
if (!i2p::client::context.GetAddressBook ().FindAddress (m_RequestURL.host) || m_Confirm) if (!i2p::client::context.GetAddressBook ().FindAddress (m_RequestURL.host) || m_Confirm)
@ -292,17 +294,19 @@ namespace proxy {
LogPrint (eLogInfo, "HTTPProxy: added address from addresshelper for ", m_RequestURL.host); LogPrint (eLogInfo, "HTTPProxy: added address from addresshelper for ", m_RequestURL.host);
std::string full_url = m_RequestURL.to_string(); std::string full_url = m_RequestURL.to_string();
std::stringstream ss; std::stringstream ss;
ss << "Host " << m_RequestURL.host << " added to router's addressbook from helper. " ss << tr("Host") <<" " << m_RequestURL.host << " " << tr("added to router's addressbook from helper") << ". ";
<< "Click <a href=\"" << full_url << "\">here</a> to proceed."; ss << tr("Click") << " <a href=\"" << full_url << "\">" << tr("here") << "</a> " << tr("to proceed") << ".";
GenericProxyInfo("Addresshelper found", ss.str().c_str()); GenericProxyInfo(tr("Addresshelper found"), ss.str());
return true; /* request processed */ return true; /* request processed */
} }
else else
{ {
std::string full_url = m_RequestURL.to_string();
std::stringstream ss; std::stringstream ss;
ss << "Host " << m_RequestURL.host << " <font color=red>already in router's addressbook</font>. " ss << tr("Host") << " " << m_RequestURL.host << " <font color=red>" << tr("already in router's addressbook") << "</font>. ";
<< "Click <a href=\"" << m_RequestURL.query << "?i2paddresshelper=" << jump << "&update=true\">here</a> to update record."; ss << tr("Click") << " <a href=\"" << full_url << (full_url.find('?') != std::string::npos ? "&i2paddresshelper=" : "?i2paddresshelper=");
GenericProxyInfo("Addresshelper found", ss.str().c_str()); ss << jump << "&update=true\">" << tr("here") << "</a> " << tr("to update record") << ".";
GenericProxyInfo(tr("Addresshelper found"), ss.str());
return true; /* request processed */ return true; /* request processed */
} }
} }
@ -315,7 +319,7 @@ namespace proxy {
auto pos = uri.find(":"); auto pos = uri.find(":");
if(pos == std::string::npos || pos == uri.size() - 1) if(pos == std::string::npos || pos == uri.size() - 1)
{ {
GenericProxyError("Invalid Request", "invalid request uri"); GenericProxyError(tr("Invalid Request"), tr("invalid request uri"));
return true; return true;
} }
else else
@ -358,7 +362,7 @@ namespace proxy {
else else
{ {
/* relative url and missing 'Host' header */ /* relative url and missing 'Host' header */
GenericProxyError("Invalid request", "Can't detect destination host from request"); GenericProxyError(tr("Invalid request"), tr("Can't detect destination host from request"));
return true; return true;
} }
} }
@ -375,11 +379,11 @@ namespace proxy {
if(m_ProxyURL.parse(m_OutproxyUrl)) if(m_ProxyURL.parse(m_OutproxyUrl))
ForwardToUpstreamProxy(); ForwardToUpstreamProxy();
else else
GenericProxyError("Outproxy failure", "bad outproxy settings"); GenericProxyError(tr("Outproxy failure"), tr("bad outproxy settings"));
} else { } else {
LogPrint (eLogWarning, "HTTPProxy: outproxy failure for ", dest_host, ": no outproxy enabled"); LogPrint (eLogWarning, "HTTPProxy: outproxy failure for ", dest_host, ": no outproxy enabled");
std::string message = "Host " + dest_host + " not inside I2P network, but outproxy is not enabled"; std::stringstream ss; ss << tr("Host") << " " << dest_host << " " << tr("not inside I2P network, but outproxy is not enabled");
GenericProxyError("Outproxy failure", message.c_str()); GenericProxyError(tr("Outproxy failure"), ss.str());
} }
return true; return true;
} }
@ -467,13 +471,13 @@ namespace proxy {
else else
{ {
// unknown type, complain // unknown type, complain
GenericProxyError("unknown outproxy url", m_ProxyURL.to_string().c_str()); GenericProxyError(tr("unknown outproxy url"), m_ProxyURL.to_string());
} }
} }
void HTTPReqHandler::HandleUpstreamProxyResolved(const boost::system::error_code & ec, boost::asio::ip::tcp::resolver::iterator it, ProxyResolvedHandler handler) void HTTPReqHandler::HandleUpstreamProxyResolved(const boost::system::error_code & ec, boost::asio::ip::tcp::resolver::iterator it, ProxyResolvedHandler handler)
{ {
if(ec) GenericProxyError("cannot resolve upstream proxy", ec.message().c_str()); if(ec) GenericProxyError(tr("cannot resolve upstream proxy"), ec.message());
else handler(*it); else handler(*it);
} }
@ -481,7 +485,7 @@ namespace proxy {
{ {
if(!ec) { if(!ec) {
if(m_RequestURL.host.size() > 255) { if(m_RequestURL.host.size() > 255) {
GenericProxyError("hostname too long", m_RequestURL.host.c_str()); GenericProxyError(tr("hostname too long"), m_RequestURL.host);
return; return;
} }
uint16_t port = m_RequestURL.port; uint16_t port = m_RequestURL.port;
@ -508,13 +512,13 @@ namespace proxy {
reqsize += host.size(); reqsize += host.size();
m_socks_buf[++reqsize] = 0; m_socks_buf[++reqsize] = 0;
boost::asio::async_write(*m_proxysock, boost::asio::buffer(m_socks_buf, reqsize), boost::asio::transfer_all(), std::bind(&HTTPReqHandler::HandleSocksProxySendHandshake, this, std::placeholders::_1, std::placeholders::_2)); boost::asio::async_write(*m_proxysock, boost::asio::buffer(m_socks_buf, reqsize), boost::asio::transfer_all(), std::bind(&HTTPReqHandler::HandleSocksProxySendHandshake, this, std::placeholders::_1, std::placeholders::_2));
} else GenericProxyError("cannot connect to upstream socks proxy", ec.message().c_str()); } else GenericProxyError(tr("cannot connect to upstream socks proxy"), ec.message());
} }
void HTTPReqHandler::HandleSocksProxySendHandshake(const boost::system::error_code & ec, std::size_t bytes_transferred) void HTTPReqHandler::HandleSocksProxySendHandshake(const boost::system::error_code & ec, std::size_t bytes_transferred)
{ {
LogPrint(eLogDebug, "HTTPProxy: upstream socks handshake sent"); LogPrint(eLogDebug, "HTTPProxy: upstream socks handshake sent");
if(ec) GenericProxyError("Cannot negotiate with socks proxy", ec.message().c_str()); if(ec) GenericProxyError(tr("Cannot negotiate with socks proxy"), ec.message());
else m_proxysock->async_read_some(boost::asio::buffer(m_socks_buf, 8), std::bind(&HTTPReqHandler::HandleSocksProxyReply, this, std::placeholders::_1, std::placeholders::_2)); else m_proxysock->async_read_some(boost::asio::buffer(m_socks_buf, 8), std::bind(&HTTPReqHandler::HandleSocksProxyReply, this, std::placeholders::_1, std::placeholders::_2));
} }
@ -556,7 +560,7 @@ namespace proxy {
} }
else else
{ {
GenericProxyError("CONNECT error", "Failed to Connect"); GenericProxyError(tr("CONNECT error"), tr("Failed to Connect"));
} }
} }
@ -567,7 +571,7 @@ namespace proxy {
m_send_buf = m_ClientResponse.to_string(); m_send_buf = m_ClientResponse.to_string();
boost::asio::async_write(*m_sock, boost::asio::buffer(m_send_buf), boost::asio::transfer_all(), [&] (const boost::system::error_code & ec, std::size_t transferred) boost::asio::async_write(*m_sock, boost::asio::buffer(m_send_buf), boost::asio::transfer_all(), [&] (const boost::system::error_code & ec, std::size_t transferred)
{ {
if(ec) GenericProxyError("socks proxy error", ec.message().c_str()); if(ec) GenericProxyError(tr("socks proxy error"), ec.message());
else HandoverToUpstreamProxy(); else HandoverToUpstreamProxy();
}); });
} else { } else {
@ -575,7 +579,7 @@ namespace proxy {
LogPrint(eLogDebug, "HTTPProxy: send ", m_send_buf.size(), " bytes"); LogPrint(eLogDebug, "HTTPProxy: send ", m_send_buf.size(), " bytes");
boost::asio::async_write(*m_proxysock, boost::asio::buffer(m_send_buf), boost::asio::transfer_all(), [&](const boost::system::error_code & ec, std::size_t transferred) boost::asio::async_write(*m_proxysock, boost::asio::buffer(m_send_buf), boost::asio::transfer_all(), [&](const boost::system::error_code & ec, std::size_t transferred)
{ {
if(ec) GenericProxyError("failed to send request to upstream", ec.message().c_str()); if(ec) GenericProxyError(tr("failed to send request to upstream"), ec.message());
else HandoverToUpstreamProxy(); else HandoverToUpstreamProxy();
}); });
} }
@ -593,18 +597,18 @@ namespace proxy {
ss << "error code: "; ss << "error code: ";
ss << (int) m_socks_buf[1]; ss << (int) m_socks_buf[1];
std::string msg = ss.str(); std::string msg = ss.str();
GenericProxyError("Socks Proxy error", msg.c_str()); GenericProxyError(tr("socks proxy error"), msg);
} }
} }
else GenericProxyError("No Reply From socks proxy", ec.message().c_str()); else GenericProxyError(tr("No Reply From socks proxy"), ec.message());
} }
void HTTPReqHandler::HandleUpstreamHTTPProxyConnect(const boost::system::error_code & ec) void HTTPReqHandler::HandleUpstreamHTTPProxyConnect(const boost::system::error_code & ec)
{ {
if(!ec) { if(!ec) {
LogPrint(eLogDebug, "HTTPProxy: connected to http upstream"); LogPrint(eLogDebug, "HTTPProxy: connected to http upstream");
GenericProxyError("cannot connect", "http out proxy not implemented"); GenericProxyError(tr("cannot connect"), tr("http out proxy not implemented"));
} else GenericProxyError("cannot connect to upstream http proxy", ec.message().c_str()); } else GenericProxyError(tr("cannot connect to upstream http proxy"), ec.message());
} }
/* will be called after some data received from client */ /* will be called after some data received from client */
@ -637,7 +641,7 @@ namespace proxy {
{ {
if (!stream) { if (!stream) {
LogPrint (eLogError, "HTTPProxy: error when creating the stream, check the previous warnings for more info"); LogPrint (eLogError, "HTTPProxy: error when creating the stream, check the previous warnings for more info");
GenericProxyError("Host is down", "Can't create connection to requested host, it may be down. Please try again later."); GenericProxyError(tr("Host is down"), tr("Can't create connection to requested host, it may be down. Please try again later."));
return; return;
} }
if (Kill()) if (Kill())

View File

@ -544,13 +544,11 @@ namespace client
offset += 8; // date offset += 8; // date
if (identity->Verify (buf, offset, buf + offset)) // signature if (identity->Verify (buf, offset, buf + offset)) // signature
{ {
bool isPublic = true;
if (params[I2CP_PARAM_DONT_PUBLISH_LEASESET] == "true") isPublic = false;
if (!m_Destination) if (!m_Destination)
{ {
m_Destination = m_Owner.IsSingleThread () ? m_Destination = m_Owner.IsSingleThread () ?
std::make_shared<I2CPDestination>(m_Owner.GetService (), shared_from_this (), identity, isPublic, params): std::make_shared<I2CPDestination>(m_Owner.GetService (), shared_from_this (), identity, true, params):
std::make_shared<RunnableI2CPDestination>(shared_from_this (), identity, isPublic, params); std::make_shared<RunnableI2CPDestination>(shared_from_this (), identity, true, params);
SendSessionStatusMessage (1); // created SendSessionStatusMessage (1); // created
LogPrint (eLogDebug, "I2CP: session ", m_SessionID, " created"); LogPrint (eLogDebug, "I2CP: session ", m_SessionID, " created");
m_Destination->Start (); m_Destination->Start ();

View File

@ -62,7 +62,6 @@ namespace client
}; };
// params // params
const char I2CP_PARAM_DONT_PUBLISH_LEASESET[] = "i2cp.dontPublishLeaseSet";
const char I2CP_PARAM_MESSAGE_RELIABILITY[] = "i2cp.messageReliability"; const char I2CP_PARAM_MESSAGE_RELIABILITY[] = "i2cp.messageReliability";
class I2CPSession; class I2CPSession;