Merge pull request #1550 from PurpleI2P/openssl

2.33.0
This commit is contained in:
orignal 2020-08-24 13:02:22 -04:00 committed by GitHub
commit 305a654a37
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
48 changed files with 934 additions and 553 deletions

View File

@ -1,6 +1,31 @@
# for this file format description, # for this file format description,
# see https://github.com/olivierlacan/keep-a-changelog # see https://github.com/olivierlacan/keep-a-changelog
## [2.33.0] - 2020-08-24
### Added
- Shared transient addresses
- crypto.ratchet.inboundTags paramater
- Multiple encryption keys through I2CP
- Pre-calculated x25519 ephemeral keys
- Change datagram routing path if nothing comes back in 10 seconds
- Shared routing path for datagram session
### Changed
- UDP tunnels send mix of repliable and raw datagrams in bulk
- Encrypt SSU packet again upon resend
- Start new tunnel message if remaining buffer is too small
- Use LeaseSet2 for ECIES-X25519-AEAD-Ratchet automatically
- Save new ECIES-X25519-AEAD-Ratchet session with NSR tagset
- Generate random padding lengths for ECIES-X25519-AEAD-Ratchet in bulk
- Webconsole layout
- Reseed servers list
### Fixed
- Don't connect through terminated SAM destination
- Differentiate UDP server sessions by port
- ECIES-X25519-AEAD-Ratchet through I2CP
- Don't save invalid address to AddressBook
- ECDSA signatures names in SAM
- AppArmor profile
## [2.32.1] - 2020-06-02 ## [2.32.1] - 2020-06-02
### Added ### Added
- Read explicit peers in tunnels config - Read explicit peers in tunnels config

View File

@ -1,5 +1,5 @@
#define I2Pd_AppName "i2pd" #define I2Pd_AppName "i2pd"
#define I2Pd_ver "2.32.1" #define I2Pd_ver "2.33.0"
#define I2Pd_Publisher "PurpleI2P" #define I2Pd_Publisher "PurpleI2P"
[Setup] [Setup]

View File

@ -1,4 +1,4 @@
[IRC-IRC2P] #[IRC-IRC2P]
#type = client #type = client
#address = 127.0.0.1 #address = 127.0.0.1
#port = 6668 #port = 6668

View File

@ -30,8 +30,8 @@ android {
applicationId "org.purplei2p.i2pd" applicationId "org.purplei2p.i2pd"
targetSdkVersion 29 targetSdkVersion 29
minSdkVersion 14 minSdkVersion 14
versionCode 2321 versionCode 2330
versionName "2.32.1" versionName "2.33.0"
setProperty("archivesBaseName", archivesBaseName + "-" + versionName) setProperty("archivesBaseName", archivesBaseName + "-" + versionName)
ndk { ndk {
abiFilters 'armeabi-v7a' abiFilters 'armeabi-v7a'

View File

@ -53,15 +53,15 @@ include $(PREBUILT_STATIC_LIBRARY)
LOCAL_PATH := $(call my-dir) LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS) include $(CLEAR_VARS)
LOCAL_MODULE := crypto LOCAL_MODULE := crypto
LOCAL_SRC_FILES := $(OPENSSL_PATH)/openssl-1.1.1a-clang/$(TARGET_ARCH_ABI)/lib/libcrypto.a LOCAL_SRC_FILES := $(OPENSSL_PATH)/openssl-1.1.1d-clang/$(TARGET_ARCH_ABI)/lib/libcrypto.a
LOCAL_EXPORT_C_INCLUDES := $(OPENSSL_PATH)/openssl-1.1.1a-clang/include LOCAL_EXPORT_C_INCLUDES := $(OPENSSL_PATH)/openssl-1.1.1d-clang/include
include $(PREBUILT_STATIC_LIBRARY) include $(PREBUILT_STATIC_LIBRARY)
LOCAL_PATH := $(call my-dir) LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS) include $(CLEAR_VARS)
LOCAL_MODULE := ssl LOCAL_MODULE := ssl
LOCAL_SRC_FILES := $(OPENSSL_PATH)/openssl-1.1.1a-clang/$(TARGET_ARCH_ABI)/lib/libssl.a LOCAL_SRC_FILES := $(OPENSSL_PATH)/openssl-1.1.1d-clang/$(TARGET_ARCH_ABI)/lib/libssl.a
LOCAL_EXPORT_C_INCLUDES := $(OPENSSL_PATH)/openssl-1.1.1a-clang/include LOCAL_EXPORT_C_INCLUDES := $(OPENSSL_PATH)/openssl-1.1.1d-clang/include
LOCAL_STATIC_LIBRARIES := crypto LOCAL_STATIC_LIBRARIES := crypto
include $(PREBUILT_STATIC_LIBRARY) include $(PREBUILT_STATIC_LIBRARY)

View File

@ -1,4 +1,4 @@
version: 2.32.1.{build} version: 2.33.0.{build}
pull_requests: pull_requests:
do_not_increment_build_number: true do_not_increment_build_number: true
branches: branches:

View File

@ -4,34 +4,22 @@
# #
#include <tunables/global> #include <tunables/global>
/usr/sbin/i2pd { profile i2pd /{usr/,}sbin/i2pd {
#include <abstractions/base> #include <abstractions/base>
#include <abstractions/openssl>
network inet dgram, #include <abstractions/nameservice>
network inet stream,
network inet6 dgram,
network inet6 stream,
network netlink raw,
/etc/gai.conf r,
/etc/host.conf r,
/etc/hosts r,
/etc/nsswitch.conf r,
/etc/resolv.conf r,
/run/resolvconf/resolv.conf r,
/run/systemd/resolve/resolv.conf r,
/run/systemd/resolve/stub-resolv.conf r,
# path specific (feel free to modify if you have another paths) # path specific (feel free to modify if you have another paths)
/etc/i2pd/** r, /etc/i2pd/** r,
/run/i2pd/i2pd.pid rwk,
/var/lib/i2pd/** rw, /var/lib/i2pd/** rw,
/var/log/i2pd/i2pd.log w, /var/log/i2pd/i2pd.log w,
/var/run/i2pd/i2pd.pid rwk, /{var/,}run/i2pd/i2pd.pid rwk,
/usr/sbin/i2pd mr, /{usr/,}sbin/i2pd mr,
/usr/share/i2pd/** r, @{system_share_dirs}/i2pd/** r,
# user homedir (if started not by init.d or systemd) # user homedir (if started not by init.d or systemd)
owner @{HOME}/.i2pd/ rw, owner @{HOME}/.i2pd/ rw,
owner @{HOME}/.i2pd/** rwk, owner @{HOME}/.i2pd/** rwk,
#include if exists <local/usr.sbin.i2pd>
} }

View File

@ -1,32 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIFezCCA2OgAwIBAgIEUQYyQjANBgkqhkiG9w0BAQ0FADBuMQswCQYDVQQGEwJY
WDELMAkGA1UECBMCWFgxCzAJBgNVBAcTAlhYMR4wHAYDVQQKExVJMlAgQW5vbnlt
b3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEXMBUGA1UEAwwOYnVnbWVAbWFpbC5p
MnAwHhcNMTQxMTA2MDkxMTE0WhcNMjQxMTA1MDkxMTE0WjBuMQswCQYDVQQGEwJY
WDELMAkGA1UECBMCWFgxCzAJBgNVBAcTAlhYMR4wHAYDVQQKExVJMlAgQW5vbnlt
b3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEXMBUGA1UEAwwOYnVnbWVAbWFpbC5p
MnAwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCrThOH0eSDT0VnCSBC
sqYmAydWH+O8eNttDXr2mSvZLhvAW+6/xHTkKhaWvkIvvS0Vh8hujMnD90Cgp4Fk
TKCxMj9K527o5xIZwWW05OevbjlBwIpVLO1PjmsfsoD1nIX14eEzJSEoAulKsv7V
jGUC/6hC11mmVvH9buQLSRv6sCjuAcMszmw3TAD+XYBIs+z57KuwYXtX3+OA543c
l1/ZKLYkkwY8cwzZqWDVWqTKP5TfVae58t40HhJk3bOsr21FZsaOjlmao3GO+d/3
exKuUGJRcolSqskL3sZ1ovFqko81obvvx0upI0YA0iMr/NRGl3VPuf/LJvRppYGc
LsJHgy9TIgtHvaXRi5Nt4CbKl9sZh/7WkkTTI5YGvevu00btlabAN+DSAZZqdsB3
wY8HhM1MHiA9SWsqwU65TwErcRrjNna2FiDHEu0xk5+/iAGl6CSKHZBmNcYKXSv8
cwShB0jjmciK0a05nC638RPgj0fng7KRrSglyzfjXRrljmZ40LSBL/GGMZMWpOM7
mEsBH5UZJ/2BEmjc9X9257zBdx8BK8y1TXpAligpNBsERcTw1WP1PJ35einZvlXW
qI3GwMf0sl26sn+evcK0gDl27jVDZ45MtNQEq64M4NV3Tn9zq0eg/39YvjVeqrI5
l7sxmYqYGR6BuSncwdc4x+t6swIDAQABoyEwHzAdBgNVHQ4EFgQU/REZ7NMbVZHr
Xkao6Q8Ccqv2kAMwDQYJKoZIhvcNAQENBQADggIBACc2YjLVNbl1kJUdg2klCLJt
5LjNTiIZa2Cha5GStlC/lyoRRge6+q/y9TN3tTptlzLPS9pI9EE1GfIQaE+HAk+e
/bC3KUOAHgVuETvsNAbfpaVsPCdWpFuXmp/4b9iDN7qZy4afTKUPA/Ir/cLfNp14
JULfP4z2yFOsCQZ5viNFAs1u99FrwobV2LBzUSIJQewsksuOwj96zIyau0Y629oJ
k+og88Tifd9EH3MVZNGhdpojQDDdwHQSITnCDgfRP5yER1WIA4jg6l+mM90QkvLY
5NjWTna5kJ3X6UizvgCk365yzT2sbN3R9UGXfCJa9GBcnnviJtJF3+/gC0abwY2f
NtVYp32Xky45NY/NdRhDg0bjHP3psxmX+Sc0M9NuQcDQ+fUR+CzM0IGeiszkzXOs
RG+bOou2cZ81G4oxWdAALHIRrn7VvLGlkFMxiIZyhYcTGQZzsTPT6n18dY99+DAV
yQWZfIRdm8DOnt0G+cwfeohc/9ZwDmj4jJAAi0aeTXdY6NEGIVydk6MAycEhg2Hx
9EV96kRwZNIW0AGY8CozECFL3Eyo2ClQVV4Q35SsBibsitDjM03usc2DJ/qjynXA
C8HoOSWgbddiBvqZueqK8GdhykOy3J3ysr+MNN/lbG48LqkQr1OWxev9rGGQ6RJT
wpBgPyAFAwouPy1whmnx
-----END CERTIFICATE-----

View File

@ -0,0 +1,34 @@
-----BEGIN CERTIFICATE-----
MIIF0zCCA7ugAwIBAgIQWjHyC+NRh3emuuAwcEnKSjANBgkqhkiG9w0BAQsFADB0
MQswCQYDVQQGEwJYWDELMAkGA1UEBxMCWFgxCzAJBgNVBAkTAlhYMR4wHAYDVQQK
ExVJMlAgQW5vbnltb3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEdMBsGA1UEAwwU
cmVzZWVkQGRpdmEuZXhjaGFuZ2UwHhcNMjAwNjA5MDUzNjQ1WhcNMzAwNjA5MDUz
NjQ1WjB0MQswCQYDVQQGEwJYWDELMAkGA1UEBxMCWFgxCzAJBgNVBAkTAlhYMR4w
HAYDVQQKExVJMlAgQW5vbnltb3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEdMBsG
A1UEAwwUcmVzZWVkQGRpdmEuZXhjaGFuZ2UwggIiMA0GCSqGSIb3DQEBAQUAA4IC
DwAwggIKAoICAQC6BJGeMEgoXk9dlzKVfmwHrT2VpwTT+wRJvh3eAM746u4uDT2y
NPHXhdGcQ9dRRZ63T98IshWCwOmWSlm1kdWkmKkVVb93GUoMQ3gziCi0apLJMAau
gEu/sPCbORS2dPsQeAPW2eIsJO7dSjTRiQAuquW//NcIXG4gnxDA52lgke1BvpKr
83SJlCrqECAy6OKtZ49yn75CqmPPWFn0b/E8bxruN5ffeipTTospvdEtT41gXUqk
hOz3k8ang+QTWiP//jOjk31KXZ2dbh0LOlNJOvRxCqQmBZafNxxCR4DH8RewfPlL
qOiOJVzbLSP9RjqPLwnny5BOjbLWXcaybN5Qv2Pyd4mKtN3EpqBwRu7VnzXpsuuG
gRbxNmfKJ/vBEGrZAHAxi0NkHHEEne3B7pPDc2dVZHOfTfCu31m9uDHZ4eHEsNOJ
SJRiGjq74l0chCSlBGLrD1Y9LPyqadjdwuB9bzM0tMFC1wPflanQCflhhnEzAfbN
BaU2GRXo/I1UCDW/dH1FIkqEe61eMW1Lwqr5tdlrUpdr5VIddTyNJRBJogbZ+HZE
8mcoJW2lXRAkYi7KEm4b4EQNe7sbRNTF0j+fAJ+3ZOZ3O3SMHss6ignlSa+giVim
VvL+Joc6wpSzxpeNPf6m82cEO/UvifFYeOC9TpiRriSt+vvgQVzQtfQ+fQIDAQAB
o2EwXzAOBgNVHQ8BAf8EBAMCAoQwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUF
BwMBMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFHJlc2VlZEBkaXZhLmV4Y2hh
bmdlMA0GCSqGSIb3DQEBCwUAA4ICAQCFGOb1dHlwjmgFHEER6oMiGWl1mI3Hb7GX
NNI6QUhZQ+iEWGYtsOTk3Q8xejL8t6AG/ZLXfZviLIJXZc5XZfPXk0ezDSC2cYxQ
ZAyYPw2dRP14brI86sCSqNAFIax/U5SM3zXhCbBiTfaEoBPfDpvKjx+VliaITUnc
sHTRn+C5ID5M8cZIqUSGECPEMU/bDtuRNJLTKYaJ98yXtYuS2CWsMEM4o0GGcnYQ
5HOZT/lbbwfq1Ks7IyJpeIpRaS5qckGcfgkxFY4eGujDuaFeWC+HCIh9RzBJrqZR
73Aly4Pyu7Jjg8xCCf9MswDjtqAjEHgWCmRLWL7p3H6cPipFKNMY6yomYZl5urE7
q6DUAZFKwPqlZpyeaY4/SVvaHTxuPp7484s3db4kPhdmuQS/DOB/7d+cn/S580Vy
ALqlFQjtjLEaT16upceAV0gYktDInE6Rtym/OsqilrtYks/Sc0GROSz8lJhDDWbr
W3t92muSXDh0rYrEUYWl+xl1gSTpbIP75zzU+cUr1E/qlRY9qZn66FsJpOuN0I0q
UXsQS/bPDcA+IW48Hd9LfO9gtTWZslwFTimjEvQ2nJAnUlUQP6OfuPUKHoYX/CwY
2LCN8+pv2bKPDVHvp0lf6xrbbZNvFtzfR0G3AprZjYpuu2XgjVB5nJnwmbH74b9w
LD8d2z2Lgg==
-----END CERTIFICATE-----

View File

@ -36,7 +36,7 @@ RUN apk --no-cache --virtual build-dependendencies add make gcc g++ libtool zlib
&& 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 boost-python2 linux-headers boost-prg_exec_monitor \ 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

View File

@ -1,7 +1,7 @@
%define git_hash %(git rev-parse HEAD | cut -c -7) %define git_hash %(git rev-parse HEAD | cut -c -7)
Name: i2pd-git Name: i2pd-git
Version: 2.32.1 Version: 2.33.0
Release: git%{git_hash}%{?dist} Release: git%{git_hash}%{?dist}
Summary: I2P router written in C++ Summary: I2P router written in C++
Conflicts: i2pd Conflicts: i2pd
@ -124,6 +124,9 @@ getent passwd i2pd >/dev/null || \
%changelog %changelog
* Mon Aug 24 2020 orignal <i2porignal@yandex.ru> - 2.33.0
- update to 2.33.0
* Tue Jun 02 2020 r4sas <r4sas@i2pmail.org> - 2.32.1 * Tue Jun 02 2020 r4sas <r4sas@i2pmail.org> - 2.32.1
- update to 2.32.1 - update to 2.32.1

View File

@ -1,5 +1,5 @@
Name: i2pd Name: i2pd
Version: 2.32.1 Version: 2.33.0
Release: 1%{?dist} Release: 1%{?dist}
Summary: I2P router written in C++ Summary: I2P router written in C++
Conflicts: i2pd-git Conflicts: i2pd-git
@ -122,6 +122,9 @@ getent passwd i2pd >/dev/null || \
%changelog %changelog
* Mon Aug 24 2020 orignal <i2porignal@yandex.ru> - 2.33.0
- update to 2.33.0
* Tue Jun 02 2020 r4sas <r4sas@i2pmail.org> - 2.32.1 * Tue Jun 02 2020 r4sas <r4sas@i2pmail.org> - 2.32.1
- update to 2.32.1 - update to 2.32.1

View File

@ -63,23 +63,43 @@ namespace http {
" body { font: 100%/1.5em sans-serif; margin: 0; padding: 1.5em; background: #FAFAFA; color: #103456; }\r\n" " body { font: 100%/1.5em sans-serif; margin: 0; padding: 1.5em; background: #FAFAFA; color: #103456; }\r\n"
" a, .slide label { text-decoration: none; color: #894C84; }\r\n" " a, .slide label { text-decoration: none; color: #894C84; }\r\n"
" a:hover, .slide label:hover { color: #FAFAFA; background: #894C84; }\r\n" " a:hover, .slide label:hover { color: #FAFAFA; background: #894C84; }\r\n"
" a.button { -webkit-appearance: button; -moz-appearance: button; appearance: button; text-decoration: none; color: initial; padding: 0 5px; }\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: 1.5em 0; color: #894C84; }\r\n" " color: initial; padding: 0 5px; border: 1px solid #894C84; }\r\n"
" .wrapper { margin: 0 auto; padding: 1em; max-width: 60em; }\r\n" " .header { font-size: 2.5em; text-align: center; margin: 1em 0; color: #894C84; }\r\n"
" .left { float: left; position: absolute; }\r\n" " .wrapper { margin: 0 auto; padding: 1em; max-width: 58em; }\r\n"
" .right { float: left; font-size: 1em; margin-left: 13em; max-width: 46em; overflow: auto; }\r\n" " .menu { float: left; } .menu a, .commands a { display: block; }\r\n"
" .tunnel.established { color: #56B734; }\r\n" " .listitem { display: block; font-family: monospace; font-size: 1.2em; white-space: nowrap; }\r\n"
" .tunnel.expiring { color: #D3AE3F; }\r\n" " .tableitem { font-family: monospace; font-size: 1.2em; white-space: nowrap; }\r\n"
" .tunnel.failed { color: #D33F3F; }\r\n" " .content { float: left; font-size: 1em; margin-left: 4em; max-width: 46em; overflow: auto; }\r\n"
" .tunnel.building { color: #434343; }\r\n" " .tunnel.established { color: #56B734; } .tunnel.expiring { color: #D3AE3F; }\r\n"
" .tunnel.failed { color: #D33F3F; } .tunnel.building { color: #434343; }\r\n"
" caption { font-size: 1.5em; text-align: center; color: #894C84; }\r\n" " caption { font-size: 1.5em; text-align: center; color: #894C84; }\r\n"
" table { display: table; border-collapse: collapse; text-align: center; }\r\n" " table { display: table; border-collapse: collapse; text-align: center; }\r\n"
" table.extaddr { text-align: left; }\r\n table.services { width: 100%; }" " table.extaddr { text-align: left; } table.services { width: 100%; }\r\n"
" textarea { word-break: break-all; }\r\n"
" .streamdest { width: 120px; max-width: 240px; overflow: hidden; text-overflow: ellipsis;}\r\n" " .streamdest { width: 120px; max-width: 240px; overflow: hidden; text-overflow: ellipsis;}\r\n"
" .slide div.content, .slide [type='checkbox'] { display: none; }\r\n" " .slide div.slidecontent, .slide [type=\"checkbox\"] { display: none; }\r\n"
" .slide [type='checkbox']:checked ~ div.content { display: block; margin-top: 0; padding: 0; }\r\n" " .slide [type=\"checkbox\"]:checked ~ div.slidecontent { display: block; margin-top: 0; padding: 0; }\r\n"
" .disabled:after { color: #D33F3F; content: \"Disabled\" }\r\n" " .disabled:after { color: #D33F3F; content: \"Disabled\" }\r\n"
" .enabled:after { color: #56B734; content: \"Enabled\" }\r\n" " .enabled:after { color: #56B734; content: \"Enabled\" }\r\n"
" @media screen and (max-width: 980px) {\r\n" /* adaptive style */
" body { padding: 1.5em 0 0 0; }\r\n"
" .menu { width: 100%; display: block; float: none; position: unset; font-size: 16px;\r\n"
" text-align: center; }\r\n"
" .menu a, .commands a { padding: 2px; }\r\n"
" .content { float: none; margin-left: unset; margin-top: 16px; max-width: 100%; width: 100%;\r\n"
" text-align: center; }\r\n"
" a, .slide label { /* margin-right: 10px; */ display: block; /* font-size: 18px; */ }\r\n"
" .header { margin: unset; font-size: 1.5em; } small {display: block}\r\n"
" a.button { -webkit-appearance: button; -moz-appearance: button; appearance: button; text-decoration: none;\r\n"
" color: initial; margin-top: 10px; padding: 6px; border: 1px solid #894c84; width: -webkit-fill-available; }\r\n"
" input { width: 35%; text-align: center; padding: 5px;\r\n"
" border: 2px solid #ccc; -webkit-border-radius: 5px; border-radius: 5px; font-size: 18px; }\r\n"
" textarea { width: -webkit-fill-available; height: auto; padding:5px; border:2px solid #ccc;\r\n"
" -webkit-border-radius: 5px; border-radius: 5px; font-size: 12px; }\r\n"
" button[type=submit] { padding: 5px 15px; background: #ccc; border: 0 none; cursor: pointer;\r\n"
" -webkit-border-radius: 5px; border-radius: 5px; position: relative; height: 36px; display: -webkit-inline-box; margin-top: 10px; }\r\n"
" }\r\n" /* adaptive style */
"</style>\r\n"; "</style>\r\n";
const char HTTP_PAGE_TUNNELS[] = "tunnels"; const char HTTP_PAGE_TUNNELS[] = "tunnels";
@ -154,7 +174,7 @@ namespace http {
default: state = "unknown"; break; default: state = "unknown"; break;
} }
s << "<span class=\"tunnel " << state << "\"> " << state << ((explr) ? " (exploratory)" : "") << "</span>, "; s << "<span class=\"tunnel " << state << "\"> " << state << ((explr) ? " (exploratory)" : "") << "</span>, ";
s << " " << (int) (bytes / 1024) << "&nbsp;KiB<br>\r\n"; s << " " << (int) (bytes / 1024) << "&nbsp;KiB\r\n";
} }
static void SetLogLevel (const std::string& level) static void SetLogLevel (const std::string& level)
@ -181,36 +201,37 @@ namespace http {
#else #else
" <meta charset=\"windows-1251\">\r\n" " <meta charset=\"windows-1251\">\r\n"
#endif #endif
" <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 << << cssStyles <<
"</head>\r\n"; "</head>\r\n";
s << s <<
"<body>\r\n" "<body>\r\n"
"<div class=header><b>i2pd</b> webconsole</div>\r\n" "<div class=\"header\"><b>i2pd</b> webconsole</div>\r\n"
"<div class=wrapper>\r\n" "<div class=\"wrapper\">\r\n"
"<div class=left>\r\n" "<div class=\"menu\">\r\n"
" <a href=\"" << webroot << "\">Main page</a><br>\r\n<br>\r\n" " <a href=\"" << webroot << "\">Main page</a><br>\r\n"
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_COMMANDS << "\">Router commands</a><br>\r\n" " <a href=\"" << webroot << "?page=" << HTTP_PAGE_COMMANDS << "\">Router commands</a>\r\n"
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_LOCAL_DESTINATIONS << "\">Local destinations</a><br>\r\n"; " <a href=\"" << webroot << "?page=" << HTTP_PAGE_LOCAL_DESTINATIONS << "\">Local destinations</a>\r\n";
if (i2p::context.IsFloodfill ()) if (i2p::context.IsFloodfill ())
s << " <a href=\"" << webroot << "?page=" << HTTP_PAGE_LEASESETS << "\">LeaseSets</a><br>\r\n"; s << " <a href=\"" << webroot << "?page=" << HTTP_PAGE_LEASESETS << "\">LeaseSets</a>\r\n";
s << s <<
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_TUNNELS << "\">Tunnels</a><br>\r\n" " <a href=\"" << webroot << "?page=" << HTTP_PAGE_TUNNELS << "\">Tunnels</a>\r\n"
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_TRANSIT_TUNNELS << "\">Transit tunnels</a><br>\r\n" " <a href=\"" << webroot << "?page=" << HTTP_PAGE_TRANSIT_TUNNELS << "\">Transit tunnels</a>\r\n"
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_TRANSPORTS << "\">Transports</a><br>\r\n" " <a href=\"" << webroot << "?page=" << HTTP_PAGE_TRANSPORTS << "\">Transports</a>\r\n"
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_I2P_TUNNELS << "\">I2P tunnels</a><br>\r\n"; " <a href=\"" << webroot << "?page=" << HTTP_PAGE_I2P_TUNNELS << "\">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><br>\r\n"; s << " <a href=\"" << webroot << "?page=" << HTTP_PAGE_SAM_SESSIONS << "\">SAM sessions</a>\r\n";
s << s <<
"</div>\r\n" "</div>\r\n"
"<div class=right>"; "<div class=\"content\">";
} }
static void ShowPageTail (std::stringstream& s) static void ShowPageTail (std::stringstream& s)
{ {
s << s <<
"</div></div>\r\n" "</div>\r\n</div>\r\n"
"</body>\r\n" "</body>\r\n"
"</html>\r\n"; "</html>\r\n";
} }
@ -266,7 +287,7 @@ namespace http {
s << "<b>Data path:</b> " << i2p::fs::GetDataDir() << "<br>\r\n"; s << "<b>Data path:</b> " << i2p::fs::GetDataDir() << "<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='content'>\r\n"; 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";
} }
if(includeHiddenContent) { if(includeHiddenContent) {
s << "<b>Router Ident:</b> " << i2p::context.GetRouterInfo().GetIdentHashBase64() << "<br>\r\n"; s << "<b>Router Ident:</b> " << i2p::context.GetRouterInfo().GetIdentHashBase64() << "<br>\r\n";
@ -340,18 +361,19 @@ 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<br>\r\n"; s << "<b>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 ();
s << "<a href=\"" << webroot << "?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">"; s << "<div class=\"listitem\"><a href=\"" << webroot << "?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
s << i2p::client::context.GetAddressBook ().ToAddress(ident) << "</a><br>\r\n" << std::endl; s << i2p::client::context.GetAddressBook ().ToAddress(ident) << "</a></div>\r\n" << std::endl;
} }
s << "</div>\r\n";
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<br>\r\n"; s << "<br><b>I2CP 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 ();
@ -359,21 +381,22 @@ namespace http {
{ {
auto ident = dest->GetIdentHash (); auto ident = dest->GetIdentHash ();
auto& name = dest->GetNickname (); auto& name = dest->GetNickname ();
s << "<a href=\"" << webroot << "?page=" << HTTP_PAGE_I2CP_LOCAL_DESTINATION << "&i2cp_id=" << it.first << "\">[ "; s << "<div class=\"listitem\"><a href=\"" << webroot << "?page=" << HTTP_PAGE_I2CP_LOCAL_DESTINATION << "&i2cp_id=" << it.first << "\">[ ";
s << name << " ]</a> &#8660; " << i2p::client::context.GetAddressBook ().ToAddress(ident) <<"<br>\r\n" << std::endl; s << name << " ]</a> &#8660; " << i2p::client::context.GetAddressBook ().ToAddress(ident) <<"</div>\r\n" << std::endl;
} }
} }
s << "</div>\r\n";
} }
} }
static void ShowLeaseSetDestination (std::stringstream& s, std::shared_ptr<const i2p::client::LeaseSetDestination> dest) static void ShowLeaseSetDestination (std::stringstream& s, std::shared_ptr<const i2p::client::LeaseSetDestination> dest)
{ {
s << "<b>Base64:</b><br>\r\n<textarea readonly cols=\"80\" rows=\"11\" wrap=\"on\">"; s << "<b>Base64:</b><br>\r\n<textarea readonly cols=\"80\" rows=\"8\" wrap=\"on\">";
s << dest->GetIdentity ()->ToBase64 () << "</textarea><br>\r\n<br>\r\n"; s << dest->GetIdentity ()->ToBase64 () << "</textarea><br>\r\n<br>\r\n";
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='content'>\r\n"; 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 << 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";
} }
@ -381,29 +404,34 @@ namespace http {
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>LeaseSets:</b> <i>" << dest->GetNumRemoteLeaseSets ()
<< "</i></label>\r\n<input type='checkbox' id='slide-lease'/>\r\n<div class='content'>\r\n<table><thead><th>Address</th><th>Type</th><th>EncType</th></thead><tbody>"; << "</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\">";
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>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"; s << "<b>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\">";
it->Print(s); it->Print(s);
if(it->LatencyIsKnown()) if(it->LatencyIsKnown())
s << " ( " << it->GetMeanLatency() << "ms )"; s << " ( " << it->GetMeanLatency() << "ms )";
ShowTunnelDetails(s, it->GetState (), false, it->GetNumReceivedBytes ()); ShowTunnelDetails(s, it->GetState (), false, it->GetNumReceivedBytes ());
s << "</div>\r\n";
} }
s << "<br>\r\n"; s << "<br>\r\n";
s << "<b>Outbound tunnels:</b><br>\r\n"; s << "<b>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\">";
it->Print(s); it->Print(s);
if(it->LatencyIsKnown()) if(it->LatencyIsKnown())
s << " ( " << it->GetMeanLatency() << "ms )"; s << " ( " << it->GetMeanLatency() << "ms )";
ShowTunnelDetails(s, it->GetState (), false, it->GetNumSentBytes ()); ShowTunnelDetails(s, it->GetState (), false, it->GetNumSentBytes ());
s << "</div>\r\n";
} }
} }
s << "<br>\r\n"; s << "<br>\r\n";
@ -415,8 +443,8 @@ namespace http {
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'>Outgoing: <i>" << out_tags << "</i></label>\r\n<input type=\"checkbox\" id=\"slide-tags\" />\r\n"
<< "<div class='content'>\r\n<table><tbody><thead><th>Destination</th><th>Amount</th></thead>\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>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";
} else } else
s << "Outgoing: <i>0</i><br>\r\n"; s << "Outgoing: <i>0</i><br>\r\n";
s << "<br>\r\n"; s << "<br>\r\n";
@ -431,8 +459,8 @@ 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'>Tags sessions: <i>" << ecies_sessions << "</i></label>\r\n<input type=\"checkbox\" id=\"slide-ecies-sessions\" />\r\n"
<< "<div class='content'>\r\n<table><tbody><thead><th>Destination</th><th>Status</th></thead>\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>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";
} else } else
s << "Tags sessions: <i>0</i><br>\r\n"; s << "Tags sessions: <i>0</i><br>\r\n";
s << "<br>\r\n"; s << "<br>\r\n";
@ -462,11 +490,12 @@ namespace http {
s << "<th>RTT</th>"; s << "<th>RTT</th>";
s << "<th>Window</th>"; s << "<th>Window</th>";
s << "<th>Status</th>"; s << "<th>Status</th>";
s << "</tr>\r\n</thead>\r\n<tbody>\r\n"; s << "</tr>\r\n</thead>\r\n<tbody class=\"tableitem\">\r\n";
for (const auto& it: dest->GetAllStreams ()) for (const auto& it: dest->GetAllStreams ())
{ {
auto streamDest = i2p::client::context.GetAddressBook ().ToAddress(it->GetRemoteIdentity ()); auto streamDest = i2p::client::context.GetAddressBook ().ToAddress(it->GetRemoteIdentity ());
std::string streamDestShort = streamDest.substr(0,12) + "&hellip;.b32.i2p";
s << "<tr>"; s << "<tr>";
s << "<td>" << it->GetRecvStreamID () << "</td>"; s << "<td>" << it->GetRecvStreamID () << "</td>";
if (it->GetRecvStreamID ()) { if (it->GetRecvStreamID ()) {
@ -475,7 +504,7 @@ namespace http {
} else { } else {
s << "<td \\>"; s << "<td \\>";
} }
s << "<td class=\"streamdest\" title=\"" << streamDest << "\">" << streamDest << "</td>"; s << "<td class=\"streamdest\" title=\"" << streamDest << "\">" << streamDestShort << "</td>";
s << "<td>" << it->GetNumSentBytes () << "</td>"; s << "<td>" << it->GetNumSentBytes () << "</td>";
s << "<td>" << it->GetNumReceivedBytes () << "</td>"; s << "<td>" << it->GetNumReceivedBytes () << "</td>";
s << "<td>" << it->GetSendQueueSize () << "</td>"; s << "<td>" << it->GetSendQueueSize () << "</td>";
@ -510,7 +539,7 @@ namespace http {
{ {
if (i2p::data::netdb.GetNumLeaseSets ()) if (i2p::data::netdb.GetNumLeaseSets ())
{ {
s << "<b>LeaseSets:</b><br>\r\n<br>\r\n"; s << "<b>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(
@ -524,14 +553,14 @@ namespace http {
else else
ls.reset (new i2p::data::LeaseSet2 (storeType, leaseSet->GetBuffer(), leaseSet->GetBufferLen())); ls.reset (new i2p::data::LeaseSet2 (storeType, leaseSet->GetBuffer(), leaseSet->GetBufferLen()));
if (!ls) return; if (!ls) return;
s << "<div class='leaseset"; s << "<div class=\"leaseset listitem";
if (ls->IsExpired()) if (ls->IsExpired())
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\">!! 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='content'>\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>Store type:</b> " << (int)storeType << "<br>\r\n";
s << "<b>Expires:</b> " << ConvertTime(ls->GetExpirationTime()) << "<br>\r\n"; s << "<b>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)
@ -563,54 +592,59 @@ namespace http {
void ShowTunnels (std::stringstream& s) void ShowTunnels (std::stringstream& s)
{ {
s << "<b>Tunnels:</b><br>\r\n<br>\r\n"; s << "<b>Tunnels:</b><br>\r\n";
s << "<b>Queue size:</b> " << i2p::tunnel::tunnels.GetQueueSize () << "<br>\r\n"; s << "<b>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"; s << "<b>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\">";
it->Print(s); it->Print(s);
if(it->LatencyIsKnown()) if(it->LatencyIsKnown())
s << " ( " << it->GetMeanLatency() << "ms )"; s << " ( " << it->GetMeanLatency() << "ms )";
ShowTunnelDetails(s, it->GetState (), (it->GetTunnelPool () == ExplPool), it->GetNumReceivedBytes ()); ShowTunnelDetails(s, it->GetState (), (it->GetTunnelPool () == ExplPool), it->GetNumReceivedBytes ());
s << "</div>\r\n";
} }
s << "<br>\r\n"; s << "</div>\r\n<br>\r\n";
s << "<b>Outbound tunnels:</b><br>\r\n"; s << "<b>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\">";
it->Print(s); it->Print(s);
if(it->LatencyIsKnown()) if(it->LatencyIsKnown())
s << " ( " << it->GetMeanLatency() << "ms )"; s << " ( " << it->GetMeanLatency() << "ms )";
ShowTunnelDetails(s, it->GetState (), (it->GetTunnelPool () == ExplPool), it->GetNumSentBytes ()); ShowTunnelDetails(s, it->GetState (), (it->GetTunnelPool () == ExplPool), it->GetNumSentBytes ());
s << "</div>\r\n";
} }
s << "<br>\r\n"; s << "</div>\r\n";
} }
static void ShowCommands (std::stringstream& s, uint32_t token) static void ShowCommands (std::stringstream& s, uint32_t token)
{ {
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"; s << "<b>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><br>\r\n"; s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_RUN_PEER_TEST << "&token=" << token << "\">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><br>\r\n"; s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_DISABLE_TRANSIT << "&token=" << token << "\">Decline transit tunnels</a>\r\n";
else else
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_ENABLE_TRANSIT << "&token=" << token << "\">Accept transit tunnels</a><br>\r\n"; s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_ENABLE_TRANSIT << "&token=" << token << "\">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><br>"; s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_CANCEL << "&token=" << token << "\">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 << "\">Start graceful shutdown</a><br>\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><br>"; s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_CANCEL << "&token=" << token << "\">Cancel graceful shutdown</a>\r\n";
else else
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_START << "&token=" << token << "\">Graceful shutdown</a><br>\r\n"; s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_START << "&token=" << token << "\">Graceful shutdown</a>\r\n";
#endif #endif
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_NOW << "&token=" << token << "\">Force shutdown</a><br>\r\n<br>\r\n"; s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_NOW << "&token=" << token << "\">Force shutdown</a>\r\n";
s << "</div>";
s << "<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><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>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";
@ -633,17 +667,19 @@ namespace http {
{ {
if(i2p::tunnel::tunnels.CountTransitTunnels()) if(i2p::tunnel::tunnels.CountTransitTunnels())
{ {
s << "<b>Transit tunnels:</b><br>\r\n<br>\r\n"; s << "<b>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";
if (std::dynamic_pointer_cast<i2p::tunnel::TransitTunnelGateway>(it)) if (std::dynamic_pointer_cast<i2p::tunnel::TransitTunnelGateway>(it))
s << it->GetTunnelID () << " &#8658; "; s << it->GetTunnelID () << " &#8658; ";
else if (std::dynamic_pointer_cast<i2p::tunnel::TransitTunnelEndpoint>(it)) else if (std::dynamic_pointer_cast<i2p::tunnel::TransitTunnelEndpoint>(it))
s << " &#8658; " << it->GetTunnelID (); s << " &#8658; " << it->GetTunnelID ();
else else
s << " &#8658; " << it->GetTunnelID () << " &#8658; "; s << " &#8658; " << it->GetTunnelID () << " &#8658; ";
s << " " << it->GetNumTransmittedBytes () << "<br>\r\n"; s << " " << it->GetNumTransmittedBytes () << "</div>\r\n";
} }
s << "</div>\r\n";
} }
else else
{ {
@ -659,43 +695,44 @@ namespace http {
{ {
if (it.second && it.second->IsEstablished () && !it.second->GetSocket ().remote_endpoint ().address ().is_v6 ()) if (it.second && it.second->IsEstablished () && !it.second->GetSocket ().remote_endpoint ().address ().is_v6 ())
{ {
// incoming connection doesn't have remote RI tmp_s << "<div class=\"listitem\">\r\n";
if (it.second->IsOutgoing ()) tmp_s << " &#8658; "; if (it.second->IsOutgoing ()) tmp_s << " &#8658; ";
tmp_s << i2p::data::GetIdentHashAbbreviation (it.second->GetRemoteIdentity ()->GetIdentHash ()) << ": " tmp_s << i2p::data::GetIdentHashAbbreviation (it.second->GetRemoteIdentity ()->GetIdentHash ()) << ": "
<< it.second->GetSocket ().remote_endpoint().address ().to_string (); << it.second->GetSocket ().remote_endpoint().address ().to_string ();
if (!it.second->IsOutgoing ()) tmp_s << " &#8658; "; if (!it.second->IsOutgoing ()) tmp_s << " &#8658; ";
tmp_s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]"; tmp_s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
tmp_s << "<br>\r\n" << std::endl; tmp_s << "</div>\r\n" << std::endl;
cnt++; cnt++;
} }
if (it.second && it.second->IsEstablished () && it.second->GetSocket ().remote_endpoint ().address ().is_v6 ()) if (it.second && it.second->IsEstablished () && it.second->GetSocket ().remote_endpoint ().address ().is_v6 ())
{ {
tmp_s6 << "<div class=\"listitem\">\r\n";
if (it.second->IsOutgoing ()) tmp_s6 << " &#8658; "; if (it.second->IsOutgoing ()) tmp_s6 << " &#8658; ";
tmp_s6 << i2p::data::GetIdentHashAbbreviation (it.second->GetRemoteIdentity ()->GetIdentHash ()) << ": " tmp_s6 << i2p::data::GetIdentHashAbbreviation (it.second->GetRemoteIdentity ()->GetIdentHash ()) << ": "
<< "[" << it.second->GetSocket ().remote_endpoint().address ().to_string () << "]"; << "[" << it.second->GetSocket ().remote_endpoint().address ().to_string () << "]";
if (!it.second->IsOutgoing ()) tmp_s6 << " &#8658; "; if (!it.second->IsOutgoing ()) tmp_s6 << " &#8658; ";
tmp_s6 << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]"; tmp_s6 << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
tmp_s6 << "<br>\r\n" << std::endl; tmp_s6 << "</div>\r\n" << std::endl;
cnt6++; cnt6++;
} }
} }
if (!tmp_s.str ().empty ()) if (!tmp_s.str ().empty ())
{ {
s << "<div class='slide'><label for='slide_" << boost::algorithm::to_lower_copy(name) << "'><b>" << name s << "<div class='slide'><label for='slide_" << boost::algorithm::to_lower_copy(name) << "'><b>" << name
<< "</b> ( " << cnt << " )</label>\r\n<input type='checkbox' id='slide_" << boost::algorithm::to_lower_copy(name) << "'/>\r\n<div class='content'>" << "</b> ( " << cnt << " )</label>\r\n<input type=\"checkbox\" id=\"slide_" << boost::algorithm::to_lower_copy(name) << "\" />\r\n<div class=\"slidecontent list\">"
<< tmp_s.str () << "</div>\r\n</div>\r\n"; << tmp_s.str () << "</div>\r\n</div>\r\n";
} }
if (!tmp_s6.str ().empty ()) if (!tmp_s6.str ().empty ())
{ {
s << "<div class='slide'><label for='slide_" << boost::algorithm::to_lower_copy(name) << "v6'><b>" << name s << "<div class='slide'><label for='slide_" << boost::algorithm::to_lower_copy(name) << "v6'><b>" << name
<< "v6</b> ( " << cnt6 << " )</label>\r\n<input type='checkbox' id='slide_" << boost::algorithm::to_lower_copy(name) << "v6'/>\r\n<div class='content'>" << "v6</b> ( " << cnt6 << " )</label>\r\n<input type=\"checkbox\" id=\"slide_" << boost::algorithm::to_lower_copy(name) << "v6\" />\r\n<div class=\"slidecontent list\">"
<< tmp_s6.str () << "</div>\r\n</div>\r\n"; << tmp_s6.str () << "</div>\r\n</div>\r\n";
} }
} }
void ShowTransports (std::stringstream& s) void ShowTransports (std::stringstream& s)
{ {
s << "<b>Transports:</b><br>\r\n<br>\r\n"; s << "<b>Transports:</b><br>\r\n";
auto ntcpServer = i2p::transport::transports.GetNTCPServer (); auto ntcpServer = i2p::transport::transports.GetNTCPServer ();
if (ntcpServer) if (ntcpServer)
{ {
@ -716,9 +753,10 @@ namespace http {
auto sessions = ssuServer->GetSessions (); auto sessions = ssuServer->GetSessions ();
if (!sessions.empty ()) if (!sessions.empty ())
{ {
s << "<div class='slide'><label for='slide_ssu'><b>SSU</b> ( " << (int) sessions.size() << " )</label>\r\n<input type='checkbox' id='slide_ssu'/>\r\n<div class='content'>"; s << "<div class='slide'><label for='slide_ssu'><b>SSU</b> ( " << (int) sessions.size() << " )</label>\r\n<input type=\"checkbox\" id=\"slide_ssu\" />\r\n<div class=\"slidecontent list\">";
for (const auto& it: sessions) for (const auto& it: sessions)
{ {
s << "<div class=\"listitem\">\r\n";
auto endpoint = it.second->GetRemoteEndpoint (); auto endpoint = it.second->GetRemoteEndpoint ();
if (it.second->IsOutgoing ()) s << " &#8658; "; if (it.second->IsOutgoing ()) s << " &#8658; ";
s << endpoint.address ().to_string () << ":" << endpoint.port (); s << endpoint.address ().to_string () << ":" << endpoint.port ();
@ -726,16 +764,17 @@ namespace http {
s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]"; s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
if (it.second->GetRelayTag ()) if (it.second->GetRelayTag ())
s << " [itag:" << it.second->GetRelayTag () << "]"; s << " [itag:" << it.second->GetRelayTag () << "]";
s << "<br>\r\n" << std::endl; s << "</div>\r\n" << std::endl;
} }
s << "</div>\r\n</div>\r\n"; s << "</div>\r\n</div>\r\n";
} }
auto sessions6 = ssuServer->GetSessionsV6 (); auto sessions6 = ssuServer->GetSessionsV6 ();
if (!sessions6.empty ()) if (!sessions6.empty ())
{ {
s << "<div class='slide'><label for='slide_ssuv6'><b>SSUv6</b> ( " << (int) sessions6.size() << " )</label>\r\n<input type='checkbox' id='slide_ssuv6'/>\r\n<div class='content'>"; s << "<div class='slide'><label for='slide_ssuv6'><b>SSUv6</b> ( " << (int) sessions6.size() << " )</label>\r\n<input type=\"checkbox\" id=\"slide_ssuv6\" />\r\n<div class=\"slidecontent list\">";
for (const auto& it: sessions6) for (const auto& it: sessions6)
{ {
s << "<div class=\"listitem\">\r\n";
auto endpoint = it.second->GetRemoteEndpoint (); auto endpoint = it.second->GetRemoteEndpoint ();
if (it.second->IsOutgoing ()) s << " &#8658; "; if (it.second->IsOutgoing ()) s << " &#8658; ";
s << "[" << endpoint.address ().to_string () << "]:" << endpoint.port (); s << "[" << endpoint.address ().to_string () << "]:" << endpoint.port ();
@ -743,7 +782,7 @@ namespace http {
s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]"; s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
if (it.second->GetRelayTag ()) if (it.second->GetRelayTag ())
s << " [itag:" << it.second->GetRelayTag () << "]"; s << " [itag:" << it.second->GetRelayTag () << "]";
s << "<br>\r\n" << std::endl; s << "</div>\r\n" << std::endl;
} }
s << "</div>\r\n</div>\r\n"; s << "</div>\r\n</div>\r\n";
} }
@ -762,13 +801,14 @@ namespace http {
if(sam->GetSessions ().size ()) if(sam->GetSessions ().size ())
{ {
s << "<b>SAM Sessions:</b><br>\r\n<br>\r\n"; s << "<b>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->localDestination->GetNickname (); auto& name = it.second->localDestination->GetNickname ();
s << "<a href=\"" << webroot << "?page=" << HTTP_PAGE_SAM_SESSION << "&sam_id=" << it.first << "\">"; s << "<div class=\"listitem\"><a href=\"" << webroot << "?page=" << HTTP_PAGE_SAM_SESSION << "&sam_id=" << it.first << "\">";
s << name << " (" << it.first << ")</a><br>\r\n" << std::endl; s << name << " (" << it.first << ")</a></div>\r\n" << std::endl;
} }
s << "</div>\r\n";
} }
else else
s << "<b>SAM Sessions:</b> no sessions currently running.<br>\r\n"; s << "<b>SAM Sessions:</b> no sessions currently running.<br>\r\n";
@ -776,25 +816,28 @@ namespace http {
static void ShowSAMSession (std::stringstream& s, const std::string& id) static void ShowSAMSession (std::stringstream& s, const std::string& id)
{ {
std::string webroot; i2p::config::GetOption("http.webroot", webroot);
s << "<b>SAM Session:</b><br>\r\n<br>\r\n";
auto sam = i2p::client::context.GetSAMBridge (); auto sam = i2p::client::context.GetSAMBridge ();
if (!sam) { if (!sam) {
ShowError(s, "SAM disabled"); ShowError(s, "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, "SAM session not found");
return; return;
} }
std::string webroot; i2p::config::GetOption("http.webroot", webroot);
s << "<b>SAM Session:</b><br>\r\n<div class=\"list\">\r\n";
auto& ident = session->localDestination->GetIdentHash(); auto& ident = session->localDestination->GetIdentHash();
s << "<a href=\"" << webroot << "?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">"; s << "<div class=\"listitem\"><a href=\"" << webroot << "?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
s << i2p::client::context.GetAddressBook ().ToAddress(ident) << "</a><br>\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"; s << "<b>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\">";
switch (it->GetSocketType ()) switch (it->GetSocketType ())
{ {
case i2p::client::eSAMSocketTypeSession : s << "session"; break; case i2p::client::eSAMSocketTypeSession : s << "session"; break;
@ -803,88 +846,95 @@ namespace http {
default: s << "unknown"; break; default: s << "unknown"; break;
} }
s << " [" << it->GetSocket ().remote_endpoint() << "]"; s << " [" << it->GetSocket ().remote_endpoint() << "]";
s << "<br>\r\n"; s << "</div>\r\n";
} }
s << "</div>\r\n";
} }
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<br>\r\n"; s << "<b>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();
s << "<a href=\"" << webroot << "?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">"; s << "<div class=\"listitem\"><a href=\"" << webroot << "?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
s << it.second->GetName () << "</a> &#8656; "; s << it.second->GetName () << "</a> &#8656; ";
s << i2p::client::context.GetAddressBook ().ToAddress(ident); s << i2p::client::context.GetAddressBook ().ToAddress(ident);
s << "<br>\r\n"<< std::endl; s << "</div>\r\n"<< std::endl;
} }
auto httpProxy = i2p::client::context.GetHttpProxy (); auto httpProxy = i2p::client::context.GetHttpProxy ();
if (httpProxy) if (httpProxy)
{ {
auto& ident = httpProxy->GetLocalDestination ()->GetIdentHash(); auto& ident = httpProxy->GetLocalDestination ()->GetIdentHash();
s << "<a href=\"" << webroot << "?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">"; s << "<div class=\"listitem\"><a href=\"" << webroot << "?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
s << "HTTP Proxy" << "</a> &#8656; "; s << "HTTP Proxy" << "</a> &#8656; ";
s << i2p::client::context.GetAddressBook ().ToAddress(ident); s << i2p::client::context.GetAddressBook ().ToAddress(ident);
s << "<br>\r\n"<< std::endl; s << "</div>\r\n"<< std::endl;
} }
auto socksProxy = i2p::client::context.GetSocksProxy (); auto socksProxy = i2p::client::context.GetSocksProxy ();
if (socksProxy) if (socksProxy)
{ {
auto& ident = socksProxy->GetLocalDestination ()->GetIdentHash(); auto& ident = socksProxy->GetLocalDestination ()->GetIdentHash();
s << "<a href=\"" << webroot << "?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">"; s << "<div class=\"listitem\"><a href=\"" << webroot << "?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
s << "SOCKS Proxy" << "</a> &#8656; "; s << "SOCKS Proxy" << "</a> &#8656; ";
s << i2p::client::context.GetAddressBook ().ToAddress(ident); s << i2p::client::context.GetAddressBook ().ToAddress(ident);
s << "<br>\r\n"<< std::endl; s << "</div>\r\n"<< std::endl;
} }
s << "</div>\r\n";
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<br>\r\n"; s << "<br>\r\n<b>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();
s << "<a href=\"" << webroot << "?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">"; s << "<div class=\"listitem\"><a href=\"" << webroot << "?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
s << it.second->GetName () << "</a> &#8658; "; s << it.second->GetName () << "</a> &#8658; ";
s << i2p::client::context.GetAddressBook ().ToAddress(ident); s << i2p::client::context.GetAddressBook ().ToAddress(ident);
s << ":" << it.second->GetLocalPort (); s << ":" << it.second->GetLocalPort ();
s << "</a><br>\r\n"<< std::endl; s << "</a></div>\r\n"<< std::endl;
} }
s << "</div>\r\n";
} }
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<br>\r\n"; s << "<br>\r\n<b>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();
s << "<a href=\"" << webroot << "?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">"; s << "<div class=\"listitem\"><a href=\"" << webroot << "?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
s << it.second->GetName () << "</a> &#8656; "; s << it.second->GetName () << "</a> &#8656; ";
s << i2p::client::context.GetAddressBook ().ToAddress(ident); s << i2p::client::context.GetAddressBook ().ToAddress(ident);
s << "<br>\r\n"<< std::endl; s << "</div>\r\n"<< std::endl;
} }
s << "</div>\r\n";
} }
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<br>\r\n"; s << "<br>\r\n<b>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();
s << "<a href=\"" << webroot << "?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">"; s << "<a href=\"" << webroot << "?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
s << it.second->GetName () << "</a> &#8656; "; s << it.second->GetName () << "</a> &#8656; ";
s << i2p::client::context.GetAddressBook ().ToAddress(ident); s << i2p::client::context.GetAddressBook ().ToAddress(ident);
s << "<br>\r\n"<< std::endl; s << "</div>\r\n"<< std::endl;
} }
s << "</div>\r\n";
} }
} }
std::string ConvertTime (uint64_t time) std::string ConvertTime (uint64_t time)
{ {
ldiv_t divTime = ldiv(time,1000); lldiv_t divTime = lldiv(time, 1000);
time_t t = divTime.quot; time_t t = divTime.quot;
struct tm *tm = localtime(&t); struct tm *tm = localtime(&t);
char date[128]; char date[128];
snprintf(date, sizeof(date), "%02d/%02d/%d %02d:%02d:%02d.%03ld", tm->tm_mday, tm->tm_mon + 1, tm->tm_year + 1900, tm->tm_hour, tm->tm_min, tm->tm_sec, divTime.rem); snprintf(date, sizeof(date), "%02d/%02d/%d %02d:%02d:%02d.%03lld", tm->tm_mday, tm->tm_mon + 1, tm->tm_year + 1900, tm->tm_hour, tm->tm_min, tm->tm_sec, divTime.rem);
return date; return date;
} }

6
debian/changelog vendored
View File

@ -1,3 +1,9 @@
i2pd (2.33.0-1) unstable; urgency=medium
* updated to version 2.33.0/0.9.47
-- orignal <orignal@i2pmail.org> Mon, 24 Aug 2020 16:00:00 +0000
i2pd (2.32.1-1) unstable; urgency=high i2pd (2.32.1-1) unstable; urgency=high
* updated to version 2.32.1 * updated to version 2.32.1

View File

@ -195,7 +195,7 @@ namespace config {
("reseed.proxy", value<std::string>()->default_value(""), "url for reseed proxy, supports http/socks") ("reseed.proxy", value<std::string>()->default_value(""), "url for reseed proxy, supports http/socks")
("reseed.urls", value<std::string>()->default_value( ("reseed.urls", value<std::string>()->default_value(
"https://reseed.i2p-projekt.de/," "https://reseed.i2p-projekt.de/,"
"https://i2p.mooo.com/netDb/," "https://reseed.diva.exchange/,"
"https://reseed.i2p2.no/," "https://reseed.i2p2.no/,"
"https://reseed-fr.i2pd.xyz/," "https://reseed-fr.i2pd.xyz/,"
"https://reseed.memcpy.io/," "https://reseed.memcpy.io/,"

View File

@ -375,15 +375,22 @@ namespace crypto
#endif #endif
} }
void X25519Keys::SetPrivateKey (const uint8_t * priv) void X25519Keys::SetPrivateKey (const uint8_t * priv, bool calculatePublic)
{ {
#if OPENSSL_X25519 #if OPENSSL_X25519
if (m_Ctx) EVP_PKEY_CTX_free (m_Ctx); if (m_Ctx) EVP_PKEY_CTX_free (m_Ctx);
if (m_Pkey) EVP_PKEY_free (m_Pkey); if (m_Pkey) EVP_PKEY_free (m_Pkey);
m_Pkey = EVP_PKEY_new_raw_private_key (EVP_PKEY_X25519, NULL, priv, 32); m_Pkey = EVP_PKEY_new_raw_private_key (EVP_PKEY_X25519, NULL, priv, 32);
m_Ctx = EVP_PKEY_CTX_new (m_Pkey, NULL); m_Ctx = EVP_PKEY_CTX_new (m_Pkey, NULL);
if (calculatePublic)
{
size_t len = 32;
EVP_PKEY_get_raw_public_key (m_Pkey, m_PublicKey, &len);
}
#else #else
memcpy (m_PrivateKey, priv, 32); memcpy (m_PrivateKey, priv, 32);
if (calculatePublic)
GetEd25519 ()->ScalarMulB (m_PrivateKey, m_PublicKey, m_Ctx);
#endif #endif
} }

View File

@ -88,9 +88,12 @@ namespace crypto
void GenerateKeys (); void GenerateKeys ();
const uint8_t * GetPublicKey () const { return m_PublicKey; }; const uint8_t * GetPublicKey () const { return m_PublicKey; };
void GetPrivateKey (uint8_t * priv) const; void GetPrivateKey (uint8_t * priv) const;
void SetPrivateKey (const uint8_t * priv); // wihout calculating public void SetPrivateKey (const uint8_t * priv, bool calculatePublic = false);
void Agree (const uint8_t * pub, uint8_t * shared); void Agree (const uint8_t * pub, uint8_t * shared);
bool IsElligatorIneligible () const { return m_IsElligatorIneligible; }
void SetElligatorIneligible () { m_IsElligatorIneligible = true; }
private: private:
uint8_t m_PublicKey[32]; uint8_t m_PublicKey[32];
@ -101,6 +104,7 @@ namespace crypto
BN_CTX * m_Ctx; BN_CTX * m_Ctx;
uint8_t m_PrivateKey[32]; uint8_t m_PrivateKey[32];
#endif #endif
bool m_IsElligatorIneligible = false; // true if definitly ineligible
}; };
// ElGamal // ElGamal

View File

@ -166,9 +166,9 @@ namespace crypto
memcpy (pub, m_PublicKey, 32); memcpy (pub, m_PublicKey, 32);
} }
ECIESX25519AEADRatchetDecryptor::ECIESX25519AEADRatchetDecryptor (const uint8_t * priv) ECIESX25519AEADRatchetDecryptor::ECIESX25519AEADRatchetDecryptor (const uint8_t * priv, bool calculatePublic)
{ {
m_StaticKeys.SetPrivateKey (priv); m_StaticKeys.SetPrivateKey (priv, calculatePublic);
} }
bool ECIESX25519AEADRatchetDecryptor::Decrypt (const uint8_t * epub, uint8_t * sharedSecret, BN_CTX * ctx, bool zeroPadding) bool ECIESX25519AEADRatchetDecryptor::Decrypt (const uint8_t * epub, uint8_t * sharedSecret, BN_CTX * ctx, bool zeroPadding)

View File

@ -145,11 +145,12 @@ namespace crypto
{ {
public: public:
ECIESX25519AEADRatchetDecryptor (const uint8_t * priv); ECIESX25519AEADRatchetDecryptor (const uint8_t * priv, bool calculatePublic = false);
~ECIESX25519AEADRatchetDecryptor () {}; ~ECIESX25519AEADRatchetDecryptor () {};
bool Decrypt (const uint8_t * epub, uint8_t * sharedSecret, BN_CTX * ctx, bool zeroPadding); bool Decrypt (const uint8_t * epub, uint8_t * sharedSecret, BN_CTX * ctx, bool zeroPadding);
// agree with static and return in sharedSecret (32 bytes) // agree with static and return in sharedSecret (32 bytes)
size_t GetPublicKeyLen () const { return 32; }; size_t GetPublicKeyLen () const { return 32; };
const uint8_t * GetPubicKey () const { return m_StaticKeys.GetPublicKey (); };
private: private:

View File

@ -34,26 +34,52 @@ namespace datagram
void DatagramDestination::SendDatagramTo(const uint8_t * payload, size_t len, const i2p::data::IdentHash & identity, uint16_t fromPort, uint16_t toPort) void DatagramDestination::SendDatagramTo(const uint8_t * payload, size_t len, const i2p::data::IdentHash & identity, uint16_t fromPort, uint16_t toPort)
{ {
if (m_Owner->GetIdentity ()->GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1)
{
uint8_t hash[32];
SHA256(payload, len, hash);
m_Owner->Sign (hash, 32, m_Signature.data ());
}
else
m_Owner->Sign (payload, len, m_Signature.data ());
auto session = ObtainSession(identity); auto session = ObtainSession(identity);
auto msg = CreateDataMessage ({{m_From.data (), m_From.size ()}, {m_Signature.data (), m_Signature.size ()}, {payload, len}}, SendDatagram (session, payload, len, fromPort, toPort);
fromPort, toPort, false, !session->IsRatchets ()); // datagram FlushSendQueue (session);
session->SendMsg(msg);
} }
void DatagramDestination::SendRawDatagramTo(const uint8_t * payload, size_t len, const i2p::data::IdentHash & identity, uint16_t fromPort, uint16_t toPort) void DatagramDestination::SendRawDatagramTo(const uint8_t * payload, size_t len, const i2p::data::IdentHash & identity, uint16_t fromPort, uint16_t toPort)
{ {
auto session = ObtainSession(identity); auto session = ObtainSession(identity);
auto msg = CreateDataMessage ({{payload, len}}, fromPort, toPort, true, !session->IsRatchets ()); // raw SendRawDatagram (session, payload, len, fromPort, toPort);
session->SendMsg(msg); FlushSendQueue (session);
}
std::shared_ptr<DatagramSession> DatagramDestination::GetSession(const i2p::data::IdentHash & ident)
{
return ObtainSession(ident);
}
void DatagramDestination::SendDatagram (std::shared_ptr<DatagramSession> session, const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort)
{
if (session)
{
if (m_Owner->GetIdentity ()->GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1)
{
uint8_t hash[32];
SHA256(payload, len, hash);
m_Owner->Sign (hash, 32, m_Signature.data ());
}
else
m_Owner->Sign (payload, len, m_Signature.data ());
auto msg = CreateDataMessage ({{m_From.data (), m_From.size ()}, {m_Signature.data (), m_Signature.size ()}, {payload, len}},
fromPort, toPort, false, !session->IsRatchets ()); // datagram
session->SendMsg(msg);
}
}
void DatagramDestination::SendRawDatagram (std::shared_ptr<DatagramSession> session, const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort)
{
if (session)
session->SendMsg(CreateDataMessage ({{payload, len}}, fromPort, toPort, true, !session->IsRatchets ())); // raw
}
void DatagramDestination::FlushSendQueue (std::shared_ptr<DatagramSession> session)
{
if (session)
session->FlushSendQueue ();
} }
void DatagramDestination::HandleDatagram (uint16_t fromPort, uint16_t toPort,uint8_t * const &buf, size_t len) void DatagramDestination::HandleDatagram (uint16_t fromPort, uint16_t toPort,uint8_t * const &buf, size_t len)
@ -126,7 +152,7 @@ namespace datagram
const std::vector<std::pair<const uint8_t *, size_t> >& payloads, const std::vector<std::pair<const uint8_t *, size_t> >& payloads,
uint16_t fromPort, uint16_t toPort, bool isRaw, bool checksum) uint16_t fromPort, uint16_t toPort, bool isRaw, bool checksum)
{ {
auto msg = NewI2NPMessage (); auto msg = m_I2NPMsgsPool.AcquireShared ();
uint8_t * buf = msg->GetPayload (); uint8_t * buf = msg->GetPayload ();
buf += 4; // reserve for length buf += 4; // reserve for length
size_t size = m_Gzip ? m_Deflator.Deflate (payloads, buf, msg->maxLen - msg->len) : size_t size = m_Gzip ? m_Deflator.Deflate (payloads, buf, msg->maxLen - msg->len) :
@ -196,7 +222,6 @@ namespace datagram
const i2p::data::IdentHash & remoteIdent) : const i2p::data::IdentHash & remoteIdent) :
m_LocalDestination(localDestination), m_LocalDestination(localDestination),
m_RemoteIdent(remoteIdent), m_RemoteIdent(remoteIdent),
m_SendQueueTimer(localDestination->GetService()),
m_RequestingLS(false) m_RequestingLS(false)
{ {
} }
@ -204,21 +229,21 @@ namespace datagram
void DatagramSession::Start () void DatagramSession::Start ()
{ {
m_LastUse = i2p::util::GetMillisecondsSinceEpoch (); m_LastUse = i2p::util::GetMillisecondsSinceEpoch ();
ScheduleFlushSendQueue();
} }
void DatagramSession::Stop () void DatagramSession::Stop ()
{ {
m_SendQueueTimer.cancel ();
} }
void DatagramSession::SendMsg(std::shared_ptr<I2NPMessage> msg) void DatagramSession::SendMsg(std::shared_ptr<I2NPMessage> msg)
{ {
// we used this session // we used this session
m_LastUse = i2p::util::GetMillisecondsSinceEpoch(); m_LastUse = i2p::util::GetMillisecondsSinceEpoch();
// schedule send if (msg || m_SendQueue.empty ())
auto self = shared_from_this(); m_SendQueue.push_back(msg);
m_LocalDestination->GetService().post(std::bind(&DatagramSession::HandleSend, self, msg)); // flush queue right away if full
if (!msg || m_SendQueue.size() >= DATAGRAM_SEND_QUEUE_MAX_SIZE)
FlushSendQueue();
} }
DatagramSession::Info DatagramSession::GetSessionInfo() const DatagramSession::Info DatagramSession::GetSessionInfo() const
@ -256,94 +281,113 @@ namespace datagram
std::shared_ptr<i2p::garlic::GarlicRoutingPath> DatagramSession::GetSharedRoutingPath () std::shared_ptr<i2p::garlic::GarlicRoutingPath> DatagramSession::GetSharedRoutingPath ()
{ {
if (!m_RoutingSession || !m_RoutingSession->GetOwner ()) if (!m_RemoteLeaseSet || m_RemoteLeaseSet->IsExpired ())
{ {
if(!m_RemoteLeaseSet) { m_RemoteLeaseSet = m_LocalDestination->FindLeaseSet(m_RemoteIdent);
m_RemoteLeaseSet = m_LocalDestination->FindLeaseSet(m_RemoteIdent); if (!m_RemoteLeaseSet)
} {
if(!m_RemoteLeaseSet) { if(!m_RequestingLS)
// no remote lease set {
if(!m_RequestingLS) {
m_RequestingLS = true; m_RequestingLS = true;
m_LocalDestination->RequestDestination(m_RemoteIdent, std::bind(&DatagramSession::HandleLeaseSetUpdated, this, std::placeholders::_1)); m_LocalDestination->RequestDestination(m_RemoteIdent, std::bind(&DatagramSession::HandleLeaseSetUpdated, this, std::placeholders::_1));
} }
return nullptr; return nullptr;
} }
m_RoutingSession = m_LocalDestination->GetRoutingSession(m_RemoteLeaseSet, true);
} }
if (!m_RoutingSession || !m_RoutingSession->GetOwner ())
{
bool found = false;
for (auto& it: m_PendingRoutingSessions)
if (it->GetOwner ()) // found established session
{
m_RoutingSession = it;
m_PendingRoutingSessions.clear ();
found = true;
break;
}
if (!found)
{
m_RoutingSession = m_LocalDestination->GetRoutingSession(m_RemoteLeaseSet, true);
if (!m_RoutingSession->GetOwner ())
m_PendingRoutingSessions.push_back (m_RoutingSession);
}
}
auto path = m_RoutingSession->GetSharedRoutingPath(); auto path = m_RoutingSession->GetSharedRoutingPath();
if(path) { if (path && m_RoutingSession->IsRatchets () &&
if (m_CurrentOutboundTunnel && !m_CurrentOutboundTunnel->IsEstablished()) { m_LastUse > m_RoutingSession->GetLastActivityTimestamp ()*1000 + DATAGRAM_SESSION_PATH_TIMEOUT)
{
m_RoutingSession->SetSharedRoutingPath (nullptr);
path = nullptr;
}
if (path)
{
if (path->outboundTunnel && !path->outboundTunnel->IsEstablished ())
{
// bad outbound tunnel, switch outbound tunnel // bad outbound tunnel, switch outbound tunnel
m_CurrentOutboundTunnel = m_LocalDestination->GetTunnelPool()->GetNextOutboundTunnel(m_CurrentOutboundTunnel); path->outboundTunnel = m_LocalDestination->GetTunnelPool()->GetNextOutboundTunnel(path->outboundTunnel);
path->outboundTunnel = m_CurrentOutboundTunnel; if (!path->outboundTunnel)
m_RoutingSession->SetSharedRoutingPath (nullptr);
} }
if(m_CurrentRemoteLease && m_CurrentRemoteLease->ExpiresWithin(DATAGRAM_SESSION_LEASE_HANDOVER_WINDOW)) {
if (path->remoteLease && path->remoteLease->ExpiresWithin(DATAGRAM_SESSION_LEASE_HANDOVER_WINDOW))
{
// bad lease, switch to next one // bad lease, switch to next one
if(m_RemoteLeaseSet && m_RemoteLeaseSet->IsExpired())
m_RemoteLeaseSet = m_LocalDestination->FindLeaseSet(m_RemoteIdent);
if(m_RemoteLeaseSet) {
auto ls = m_RemoteLeaseSet->GetNonExpiredLeasesExcluding([&](const i2p::data::Lease& l) -> bool {
return l.tunnelID == m_CurrentRemoteLease->tunnelID;
});
auto sz = ls.size();
if (sz) {
auto idx = rand() % sz;
m_CurrentRemoteLease = ls[idx];
}
} else {
// no remote lease set?
LogPrint(eLogWarning, "DatagramSession: no cached remote lease set for ", m_RemoteIdent.ToBase32());
}
path->remoteLease = m_CurrentRemoteLease;
}
} else {
// no current path, make one
path = std::make_shared<i2p::garlic::GarlicRoutingPath>();
// switch outbound tunnel if bad
if(m_CurrentOutboundTunnel == nullptr || ! m_CurrentOutboundTunnel->IsEstablished()) {
m_CurrentOutboundTunnel = m_LocalDestination->GetTunnelPool()->GetNextOutboundTunnel(m_CurrentOutboundTunnel);
}
// switch lease if bad
if(m_CurrentRemoteLease && m_CurrentRemoteLease->ExpiresWithin(DATAGRAM_SESSION_LEASE_HANDOVER_WINDOW)) {
if(!m_RemoteLeaseSet) {
m_RemoteLeaseSet = m_LocalDestination->FindLeaseSet(m_RemoteIdent);
}
if(m_RemoteLeaseSet) {
// pick random next good lease
auto ls = m_RemoteLeaseSet->GetNonExpiredLeasesExcluding([&] (const i2p::data::Lease & l) -> bool {
if(m_CurrentRemoteLease)
return l.tunnelGateway == m_CurrentRemoteLease->tunnelGateway;
return false;
});
auto sz = ls.size();
if(sz) {
auto idx = rand() % sz;
m_CurrentRemoteLease = ls[idx];
}
} else {
// no remote lease set currently, bail
LogPrint(eLogWarning, "DatagramSession: no remote lease set found for ", m_RemoteIdent.ToBase32());
return nullptr;
}
} else if (!m_CurrentRemoteLease) {
if(!m_RemoteLeaseSet) m_RemoteLeaseSet = m_LocalDestination->FindLeaseSet(m_RemoteIdent);
if (m_RemoteLeaseSet) if (m_RemoteLeaseSet)
{ {
auto ls = m_RemoteLeaseSet->GetNonExpiredLeases(); auto ls = m_RemoteLeaseSet->GetNonExpiredLeasesExcluding(
[&](const i2p::data::Lease& l) -> bool
{
return l.tunnelID == path->remoteLease->tunnelID;
});
auto sz = ls.size(); auto sz = ls.size();
if (sz) { if (sz)
{
auto idx = rand() % sz; auto idx = rand() % sz;
m_CurrentRemoteLease = ls[idx]; path->remoteLease = ls[idx];
} }
else
m_RoutingSession->SetSharedRoutingPath (nullptr);
}
else
{
// no remote lease set?
LogPrint(eLogWarning, "DatagramSession: no cached remote lease set for ", m_RemoteIdent.ToBase32());
m_RoutingSession->SetSharedRoutingPath (nullptr);
} }
} }
path->outboundTunnel = m_CurrentOutboundTunnel; }
path->remoteLease = m_CurrentRemoteLease; else
{
// no current path, make one
path = std::make_shared<i2p::garlic::GarlicRoutingPath>();
path->outboundTunnel = m_LocalDestination->GetTunnelPool()->GetNextOutboundTunnel();
if (!path->outboundTunnel) return nullptr;
if (m_RemoteLeaseSet)
{
// pick random next good lease
auto ls = m_RemoteLeaseSet->GetNonExpiredLeases();
auto sz = ls.size();
if (sz)
{
auto idx = rand() % sz;
path->remoteLease = ls[idx];
}
else
return nullptr;
}
else
{
// no remote lease set currently, bail
LogPrint(eLogWarning, "DatagramSession: no remote lease set found for ", m_RemoteIdent.ToBase32());
return nullptr;
}
m_RoutingSession->SetSharedRoutingPath(path); m_RoutingSession->SetSharedRoutingPath(path);
} }
return path; return path;
} }
void DatagramSession::HandleLeaseSetUpdated(std::shared_ptr<i2p::data::LeaseSet> ls) void DatagramSession::HandleLeaseSetUpdated(std::shared_ptr<i2p::data::LeaseSet> ls)
@ -356,16 +400,9 @@ namespace datagram
if(ls && ls->GetExpirationTime() > oldExpire) m_RemoteLeaseSet = ls; if(ls && ls->GetExpirationTime() > oldExpire) m_RemoteLeaseSet = ls;
} }
void DatagramSession::HandleSend(std::shared_ptr<I2NPMessage> msg)
{
if (msg || m_SendQueue.empty ())
m_SendQueue.push_back(msg);
// flush queue right away if full
if(m_SendQueue.size() >= DATAGRAM_SEND_QUEUE_MAX_SIZE) FlushSendQueue();
}
void DatagramSession::FlushSendQueue () void DatagramSession::FlushSendQueue ()
{ {
if (m_SendQueue.empty ()) return;
std::vector<i2p::tunnel::TunnelMessageBlock> send; std::vector<i2p::tunnel::TunnelMessageBlock> send;
auto routingPath = GetSharedRoutingPath(); auto routingPath = GetSharedRoutingPath();
// if we don't have a routing path we will drop all queued messages // if we don't have a routing path we will drop all queued messages
@ -380,15 +417,6 @@ namespace datagram
routingPath->outboundTunnel->SendTunnelDataMsg(send); routingPath->outboundTunnel->SendTunnelDataMsg(send);
} }
m_SendQueue.clear(); m_SendQueue.clear();
ScheduleFlushSendQueue();
}
void DatagramSession::ScheduleFlushSendQueue()
{
boost::posix_time::milliseconds dlt(10);
m_SendQueueTimer.expires_from_now(dlt);
auto self = shared_from_this();
m_SendQueueTimer.async_wait([self](const boost::system::error_code & ec) { if(ec) return; self->FlushSendQueue(); });
} }
} }
} }

View File

@ -59,6 +59,7 @@ namespace datagram
/** send an i2np message to remote endpoint for this session */ /** send an i2np message to remote endpoint for this session */
void SendMsg(std::shared_ptr<I2NPMessage> msg); void SendMsg(std::shared_ptr<I2NPMessage> msg);
void FlushSendQueue();
/** get the last time in milliseconds for when we used this datagram session */ /** get the last time in milliseconds for when we used this datagram session */
uint64_t LastActivity() const { return m_LastUse; } uint64_t LastActivity() const { return m_LastUse; }
@ -84,11 +85,6 @@ namespace datagram
private: private:
void FlushSendQueue();
void ScheduleFlushSendQueue();
void HandleSend(std::shared_ptr<I2NPMessage> msg);
std::shared_ptr<i2p::garlic::GarlicRoutingPath> GetSharedRoutingPath(); std::shared_ptr<i2p::garlic::GarlicRoutingPath> GetSharedRoutingPath();
void HandleLeaseSetUpdated(std::shared_ptr<i2p::data::LeaseSet> ls); void HandleLeaseSetUpdated(std::shared_ptr<i2p::data::LeaseSet> ls);
@ -99,9 +95,7 @@ namespace datagram
i2p::data::IdentHash m_RemoteIdent; i2p::data::IdentHash m_RemoteIdent;
std::shared_ptr<const i2p::data::LeaseSet> m_RemoteLeaseSet; std::shared_ptr<const i2p::data::LeaseSet> m_RemoteLeaseSet;
std::shared_ptr<i2p::garlic::GarlicRoutingSession> m_RoutingSession; std::shared_ptr<i2p::garlic::GarlicRoutingSession> m_RoutingSession;
std::shared_ptr<const i2p::data::Lease> m_CurrentRemoteLease; std::vector<std::shared_ptr<i2p::garlic::GarlicRoutingSession> > m_PendingRoutingSessions;
std::shared_ptr<i2p::tunnel::OutboundTunnel> m_CurrentOutboundTunnel;
boost::asio::deadline_timer m_SendQueueTimer;
std::vector<std::shared_ptr<I2NPMessage> > m_SendQueue; std::vector<std::shared_ptr<I2NPMessage> > m_SendQueue;
uint64_t m_LastUse; uint64_t m_LastUse;
bool m_RequestingLS; bool m_RequestingLS;
@ -122,6 +116,13 @@ namespace datagram
void SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash & ident, uint16_t fromPort = 0, uint16_t toPort = 0); void SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash & ident, uint16_t fromPort = 0, uint16_t toPort = 0);
void SendRawDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash & ident, uint16_t fromPort = 0, uint16_t toPort = 0); void SendRawDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash & ident, uint16_t fromPort = 0, uint16_t toPort = 0);
// TODO: implement calls from other thread from SAM
std::shared_ptr<DatagramSession> GetSession(const i2p::data::IdentHash & ident);
void SendDatagram (std::shared_ptr<DatagramSession> session, const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort);
void SendRawDatagram (std::shared_ptr<DatagramSession> session, const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort);
void FlushSendQueue (std::shared_ptr<DatagramSession> session);
void HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len, bool isRaw = false); void HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len, bool isRaw = false);
void SetReceiver (const Receiver& receiver) { m_Receiver = receiver; }; void SetReceiver (const Receiver& receiver) { m_Receiver = receiver; };
@ -165,6 +166,7 @@ namespace datagram
i2p::data::GzipInflator m_Inflator; i2p::data::GzipInflator m_Inflator;
i2p::data::GzipDeflator m_Deflator; i2p::data::GzipDeflator m_Deflator;
std::vector<uint8_t> m_From, m_Signature; std::vector<uint8_t> m_From, m_Signature;
i2p::util::MemoryPool<I2NPMessageBuffer<I2NP_MAX_MESSAGE_SIZE> > m_I2NPMsgsPool;
}; };
} }
} }

View File

@ -56,6 +56,9 @@ namespace client
if (it != params->end ()) if (it != params->end ())
numTags = std::stoi(it->second); numTags = std::stoi(it->second);
LogPrint (eLogInfo, "Destination: parameters for tunnel set to: ", inQty, " inbound (", inLen, " hops), ", outQty, " outbound (", outLen, " hops), ", numTags, " tags"); LogPrint (eLogInfo, "Destination: parameters for tunnel set to: ", inQty, " inbound (", inLen, " hops), ", outQty, " outbound (", outLen, " hops), ", numTags, " tags");
it = params->find (I2CP_PARAM_RATCHET_INBOUND_TAGS);
if (it != params->end ())
SetNumRatchetInboundTags (std::stoi(it->second));
it = params->find (I2CP_PARAM_EXPLICIT_PEERS); it = params->find (I2CP_PARAM_EXPLICIT_PEERS);
if (it != params->end ()) if (it != params->end ())
{ {
@ -892,7 +895,11 @@ namespace client
encryptionKey->GenerateKeys (); encryptionKey->GenerateKeys ();
encryptionKey->CreateDecryptor (); encryptionKey->CreateDecryptor ();
if (it == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET) if (it == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET)
{
m_ECIESx25519EncryptionKey.reset (encryptionKey); m_ECIESx25519EncryptionKey.reset (encryptionKey);
if (GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_LEASESET)
SetLeaseSetType (i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2); // Rathets must use LeaseSet2
}
else else
m_StandardEncryptionKey.reset (encryptionKey); m_StandardEncryptionKey.reset (encryptionKey);
} }

View File

@ -57,6 +57,8 @@ namespace client
const int STREAM_REQUEST_TIMEOUT = 60; //in seconds const int STREAM_REQUEST_TIMEOUT = 60; //in seconds
const char I2CP_PARAM_TAGS_TO_SEND[] = "crypto.tagsToSend"; const char I2CP_PARAM_TAGS_TO_SEND[] = "crypto.tagsToSend";
const int DEFAULT_TAGS_TO_SEND = 40; const int DEFAULT_TAGS_TO_SEND = 40;
const char I2CP_PARAM_RATCHET_INBOUND_TAGS[] = "crypto.ratchet.inboundTags";
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_LEASESET_TYPE[] = "i2cp.leaseSetType"; const char I2CP_PARAM_LEASESET_TYPE[] = "i2cp.leaseSetType";

View File

@ -16,6 +16,7 @@
#include "Timestamp.h" #include "Timestamp.h"
#include "Tunnel.h" #include "Tunnel.h"
#include "TunnelPool.h" #include "TunnelPool.h"
#include "Transports.h"
#include "ECIESX25519AEADRatchetSession.h" #include "ECIESX25519AEADRatchetSession.h"
namespace i2p namespace i2p
@ -96,6 +97,7 @@ namespace garlic
ECIESX25519AEADRatchetSession::ECIESX25519AEADRatchetSession (GarlicDestination * owner, bool attachLeaseSet): ECIESX25519AEADRatchetSession::ECIESX25519AEADRatchetSession (GarlicDestination * owner, bool attachLeaseSet):
GarlicRoutingSession (owner, attachLeaseSet) GarlicRoutingSession (owner, attachLeaseSet)
{ {
RAND_bytes (m_PaddingSizes, 32); m_NextPaddingSize = 0;
ResetKeys (); ResetKeys ();
} }
@ -136,12 +138,38 @@ namespace garlic
bool ECIESX25519AEADRatchetSession::GenerateEphemeralKeysAndEncode (uint8_t * buf) bool ECIESX25519AEADRatchetSession::GenerateEphemeralKeysAndEncode (uint8_t * buf)
{ {
bool ineligible = false;
while (!ineligible)
{
m_EphemeralKeys = i2p::transport::transports.GetNextX25519KeysPair ();
ineligible = m_EphemeralKeys->IsElligatorIneligible ();
if (!ineligible) // we haven't tried it yet
{
if (i2p::crypto::GetElligator ()->Encode (m_EphemeralKeys->GetPublicKey (), buf))
return true; // success
// otherwise return back
m_EphemeralKeys->SetElligatorIneligible ();
i2p::transport::transports.ReuseX25519KeysPair (m_EphemeralKeys);
}
else
i2p::transport::transports.ReuseX25519KeysPair (m_EphemeralKeys);
}
// we still didn't find elligator eligible pair
for (int i = 0; i < 10; i++) for (int i = 0; i < 10; i++)
{ {
m_EphemeralKeys.GenerateKeys (); // create new
if (i2p::crypto::GetElligator ()->Encode (m_EphemeralKeys.GetPublicKey (), buf)) m_EphemeralKeys = std::make_shared<i2p::crypto::X25519Keys>();
m_EphemeralKeys->GenerateKeys ();
if (i2p::crypto::GetElligator ()->Encode (m_EphemeralKeys->GetPublicKey (), buf))
return true; // success return true; // success
else
{
// let NTCP2 use it
m_EphemeralKeys->SetElligatorIneligible ();
i2p::transport::transports.ReuseX25519KeysPair (m_EphemeralKeys);
}
} }
LogPrint (eLogError, "Garlic: Can't generate elligator eligible x25519 keys");
return false; return false;
} }
@ -150,7 +178,8 @@ namespace garlic
uint8_t tagsetKey[32]; uint8_t tagsetKey[32];
i2p::crypto::HKDF (m_CK, nullptr, 0, "SessionReplyTags", tagsetKey, 32); // tagsetKey = HKDF(chainKey, ZEROLEN, "SessionReplyTags", 32) i2p::crypto::HKDF (m_CK, nullptr, 0, "SessionReplyTags", tagsetKey, 32); // tagsetKey = HKDF(chainKey, ZEROLEN, "SessionReplyTags", 32)
// Session Tag Ratchet // Session Tag Ratchet
auto tagsetNsr = std::make_shared<RatchetTagSet>(shared_from_this ()); auto tagsetNsr = (m_State == eSessionStateNewSessionReceived) ? std::make_shared<RatchetTagSet>(shared_from_this ()):
std::make_shared<NSRatchetTagSet>(shared_from_this ());
tagsetNsr->DHInitialize (m_CK, tagsetKey); // tagset_nsr = DH_INITIALIZE(chainKey, tagsetKey) tagsetNsr->DHInitialize (m_CK, tagsetKey); // tagset_nsr = DH_INITIALIZE(chainKey, tagsetKey)
tagsetNsr->NextSessionTagRatchet (); tagsetNsr->NextSessionTagRatchet ();
return tagsetNsr; return tagsetNsr;
@ -292,7 +321,7 @@ namespace garlic
if (flag & ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG) if (flag & ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG)
memcpy (m_NextSendRatchet->remote, buf, 32); memcpy (m_NextSendRatchet->remote, buf, 32);
uint8_t sharedSecret[32], tagsetKey[32]; uint8_t sharedSecret[32], tagsetKey[32];
m_NextSendRatchet->key.Agree (m_NextSendRatchet->remote, sharedSecret); m_NextSendRatchet->key->Agree (m_NextSendRatchet->remote, sharedSecret);
i2p::crypto::HKDF (sharedSecret, nullptr, 0, "XDHRatchetTagSet", tagsetKey, 32); // tagsetKey = HKDF(sharedSecret, ZEROLEN, "XDHRatchetTagSet", 32) i2p::crypto::HKDF (sharedSecret, nullptr, 0, "XDHRatchetTagSet", tagsetKey, 32); // tagsetKey = HKDF(sharedSecret, ZEROLEN, "XDHRatchetTagSet", 32)
auto newTagset = std::make_shared<RatchetTagSet> (shared_from_this ()); auto newTagset = std::make_shared<RatchetTagSet> (shared_from_this ());
newTagset->SetTagSetID (1 + m_NextSendRatchet->keyID + keyID); newTagset->SetTagSetID (1 + m_NextSendRatchet->keyID + keyID);
@ -324,7 +353,7 @@ namespace garlic
int tagsetID = 2*keyID; int tagsetID = 2*keyID;
if (newKey) if (newKey)
{ {
m_NextReceiveRatchet->key.GenerateKeys (); m_NextReceiveRatchet->key = i2p::transport::transports.GetNextX25519KeysPair ();
m_NextReceiveRatchet->newKey = true; m_NextReceiveRatchet->newKey = true;
tagsetID++; tagsetID++;
} }
@ -334,13 +363,14 @@ namespace garlic
memcpy (m_NextReceiveRatchet->remote, buf, 32); memcpy (m_NextReceiveRatchet->remote, buf, 32);
uint8_t sharedSecret[32], tagsetKey[32]; uint8_t sharedSecret[32], tagsetKey[32];
m_NextReceiveRatchet->key.Agree (m_NextReceiveRatchet->remote, sharedSecret); m_NextReceiveRatchet->key->Agree (m_NextReceiveRatchet->remote, sharedSecret);
i2p::crypto::HKDF (sharedSecret, nullptr, 0, "XDHRatchetTagSet", tagsetKey, 32); // tagsetKey = HKDF(sharedSecret, ZEROLEN, "XDHRatchetTagSet", 32) i2p::crypto::HKDF (sharedSecret, nullptr, 0, "XDHRatchetTagSet", tagsetKey, 32); // tagsetKey = HKDF(sharedSecret, ZEROLEN, "XDHRatchetTagSet", 32)
auto newTagset = std::make_shared<RatchetTagSet>(shared_from_this ()); auto newTagset = std::make_shared<RatchetTagSet>(shared_from_this ());
newTagset->SetTagSetID (tagsetID); newTagset->SetTagSetID (tagsetID);
newTagset->DHInitialize (receiveTagset->GetNextRootKey (), tagsetKey); newTagset->DHInitialize (receiveTagset->GetNextRootKey (), tagsetKey);
newTagset->NextSessionTagRatchet (); newTagset->NextSessionTagRatchet ();
GenerateMoreReceiveTags (newTagset, ECIESX25519_MAX_NUM_GENERATED_TAGS); GenerateMoreReceiveTags (newTagset, (GetOwner () && GetOwner ()->GetNumRatchetInboundTags () > 0) ?
GetOwner ()->GetNumRatchetInboundTags () : ECIESX25519_MAX_NUM_GENERATED_TAGS);
receiveTagset->Expire (); receiveTagset->Expire ();
LogPrint (eLogDebug, "Garlic: next receive tagset ", tagsetID, " created"); LogPrint (eLogDebug, "Garlic: next receive tagset ", tagsetID, " created");
} }
@ -361,7 +391,7 @@ namespace garlic
else else
m_NextSendRatchet.reset (new DHRatchet ()); m_NextSendRatchet.reset (new DHRatchet ());
if (m_NextSendRatchet->newKey) if (m_NextSendRatchet->newKey)
m_NextSendRatchet->key.GenerateKeys (); m_NextSendRatchet->key = i2p::transport::transports.GetNextX25519KeysPair ();
m_SendForwardKey = true; m_SendForwardKey = true;
LogPrint (eLogDebug, "Garlic: new send ratchet ", m_NextSendRatchet->newKey ? "new" : "old", " key ", m_NextSendRatchet->keyID, " created"); LogPrint (eLogDebug, "Garlic: new send ratchet ", m_NextSendRatchet->newKey ? "new" : "old", " key ", m_NextSendRatchet->keyID, " created");
@ -381,9 +411,9 @@ namespace garlic
// KDF1 // KDF1
MixHash (m_RemoteStaticKey, 32); // h = SHA256(h || bpk) MixHash (m_RemoteStaticKey, 32); // h = SHA256(h || bpk)
MixHash (m_EphemeralKeys.GetPublicKey (), 32); // h = SHA256(h || aepk) MixHash (m_EphemeralKeys->GetPublicKey (), 32); // h = SHA256(h || aepk)
uint8_t sharedSecret[32]; uint8_t sharedSecret[32];
m_EphemeralKeys.Agree (m_RemoteStaticKey, sharedSecret); // x25519(aesk, bpk) m_EphemeralKeys->Agree (m_RemoteStaticKey, sharedSecret); // x25519(aesk, bpk)
i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64) i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64)
// encrypt static key section // encrypt static key section
uint8_t nonce[12]; uint8_t nonce[12];
@ -416,8 +446,8 @@ namespace garlic
bool ECIESX25519AEADRatchetSession::NewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen) bool ECIESX25519AEADRatchetSession::NewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen)
{ {
// we are Bob // we are Bob
m_NSRTagset = CreateNewSessionTagset (); m_NSRSendTagset = CreateNewSessionTagset ();
uint64_t tag = m_NSRTagset->GetNextSessionTag (); uint64_t tag = m_NSRSendTagset->GetNextSessionTag ();
size_t offset = 0; size_t offset = 0;
memcpy (out + offset, &tag, 8); memcpy (out + offset, &tag, 8);
@ -432,11 +462,11 @@ namespace garlic
offset += 32; offset += 32;
// KDF for Reply Key Section // KDF for Reply Key Section
MixHash ((const uint8_t *)&tag, 8); // h = SHA256(h || tag) MixHash ((const uint8_t *)&tag, 8); // h = SHA256(h || tag)
MixHash (m_EphemeralKeys.GetPublicKey (), 32); // h = SHA256(h || bepk) MixHash (m_EphemeralKeys->GetPublicKey (), 32); // h = SHA256(h || bepk)
uint8_t sharedSecret[32]; uint8_t sharedSecret[32];
m_EphemeralKeys.Agree (m_Aepk, sharedSecret); // sharedSecret = x25519(besk, aepk) m_EphemeralKeys->Agree (m_Aepk, sharedSecret); // sharedSecret = x25519(besk, aepk)
i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK, 32); // chainKey = HKDF(chainKey, sharedSecret, "", 32) i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK, 32); // chainKey = HKDF(chainKey, sharedSecret, "", 32)
m_EphemeralKeys.Agree (m_RemoteStaticKey, sharedSecret); // sharedSecret = x25519(besk, apk) m_EphemeralKeys->Agree (m_RemoteStaticKey, sharedSecret); // sharedSecret = x25519(besk, apk)
i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64) i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64)
uint8_t nonce[12]; uint8_t nonce[12];
CreateNonce (0, nonce); CreateNonce (0, nonce);
@ -458,7 +488,8 @@ namespace garlic
m_SendTagset = std::make_shared<RatchetTagSet>(shared_from_this ()); m_SendTagset = std::make_shared<RatchetTagSet>(shared_from_this ());
m_SendTagset->DHInitialize (m_CK, keydata + 32); // tagset_ba = DH_INITIALIZE(chainKey, k_ba) m_SendTagset->DHInitialize (m_CK, keydata + 32); // tagset_ba = DH_INITIALIZE(chainKey, k_ba)
m_SendTagset->NextSessionTagRatchet (); m_SendTagset->NextSessionTagRatchet ();
GenerateMoreReceiveTags (receiveTagset, ECIESX25519_MIN_NUM_GENERATED_TAGS); GenerateMoreReceiveTags (receiveTagset, (GetOwner () && GetOwner ()->GetNumRatchetInboundTags () > 0) ?
GetOwner ()->GetNumRatchetInboundTags () : ECIESX25519_MIN_NUM_GENERATED_TAGS);
i2p::crypto::HKDF (keydata + 32, nullptr, 0, "AttachPayloadKDF", m_NSRKey, 32); // k = HKDF(k_ba, ZEROLEN, "AttachPayloadKDF", 32) i2p::crypto::HKDF (keydata + 32, nullptr, 0, "AttachPayloadKDF", m_NSRKey, 32); // k = HKDF(k_ba, ZEROLEN, "AttachPayloadKDF", 32)
// encrypt payload // encrypt payload
if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, m_H, 32, m_NSRKey, nonce, out + offset, len + 16, true)) // encrypt if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, m_H, 32, m_NSRKey, nonce, out + offset, len + 16, true)) // encrypt
@ -475,13 +506,13 @@ namespace garlic
bool ECIESX25519AEADRatchetSession::NextNewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen) bool ECIESX25519AEADRatchetSession::NextNewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen)
{ {
// we are Bob and sent NSR already // we are Bob and sent NSR already
uint64_t tag = m_NSRTagset->GetNextSessionTag (); // next tag uint64_t tag = m_NSRSendTagset->GetNextSessionTag (); // next tag
memcpy (out, &tag, 8); memcpy (out, &tag, 8);
memcpy (out + 8, m_NSREncodedKey, 32); memcpy (out + 8, m_NSREncodedKey, 32);
// recalculate h with new tag // recalculate h with new tag
memcpy (m_H, m_NSRH, 32); memcpy (m_H, m_NSRH, 32);
MixHash ((const uint8_t *)&tag, 8); // h = SHA256(h || tag) MixHash ((const uint8_t *)&tag, 8); // h = SHA256(h || tag)
MixHash (m_EphemeralKeys.GetPublicKey (), 32); // h = SHA256(h || bepk) MixHash (m_EphemeralKeys->GetPublicKey (), 32); // h = SHA256(h || bepk)
uint8_t nonce[12]; uint8_t nonce[12];
CreateNonce (0, nonce); CreateNonce (0, nonce);
if (!i2p::crypto::AEADChaCha20Poly1305 (nonce /* can be anything */, 0, m_H, 32, m_CK + 32, nonce, out + 40, 16, true)) // encrypt, ciphertext = ENCRYPT(k, n, ZEROLEN, ad) if (!i2p::crypto::AEADChaCha20Poly1305 (nonce /* can be anything */, 0, m_H, 32, m_CK + 32, nonce, out + 40, 16, true)) // encrypt, ciphertext = ENCRYPT(k, n, ZEROLEN, ad)
@ -520,7 +551,7 @@ namespace garlic
if (m_State == eSessionStateNewSessionSent) if (m_State == eSessionStateNewSessionSent)
{ {
// only fist time, we assume ephemeral keys the same // only fist time, we assume ephemeral keys the same
m_EphemeralKeys.Agree (bepk, sharedSecret); // sharedSecret = x25519(aesk, bepk) m_EphemeralKeys->Agree (bepk, sharedSecret); // sharedSecret = x25519(aesk, bepk)
i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK, 32); // chainKey = HKDF(chainKey, sharedSecret, "", 32) i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK, 32); // chainKey = HKDF(chainKey, sharedSecret, "", 32)
GetOwner ()->Decrypt (bepk, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET); // x25519 (ask, bepk) GetOwner ()->Decrypt (bepk, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET); // x25519 (ask, bepk)
i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64) i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64)
@ -547,7 +578,8 @@ namespace garlic
auto receiveTagset = std::make_shared<RatchetTagSet>(shared_from_this ()); auto receiveTagset = std::make_shared<RatchetTagSet>(shared_from_this ());
receiveTagset->DHInitialize (m_CK, keydata + 32); // tagset_ba = DH_INITIALIZE(chainKey, k_ba) receiveTagset->DHInitialize (m_CK, keydata + 32); // tagset_ba = DH_INITIALIZE(chainKey, k_ba)
receiveTagset->NextSessionTagRatchet (); receiveTagset->NextSessionTagRatchet ();
GenerateMoreReceiveTags (receiveTagset, ECIESX25519_MIN_NUM_GENERATED_TAGS); GenerateMoreReceiveTags (receiveTagset, (GetOwner () && GetOwner ()->GetNumRatchetInboundTags () > 0) ?
GetOwner ()->GetNumRatchetInboundTags () : ECIESX25519_MIN_NUM_GENERATED_TAGS);
} }
i2p::crypto::HKDF (keydata + 32, nullptr, 0, "AttachPayloadKDF", keydata, 32); // k = HKDF(k_ba, ZEROLEN, "AttachPayloadKDF", 32) i2p::crypto::HKDF (keydata + 32, nullptr, 0, "AttachPayloadKDF", keydata, 32); // k = HKDF(k_ba, ZEROLEN, "AttachPayloadKDF", 32)
// decrypt payload // decrypt payload
@ -560,6 +592,7 @@ namespace garlic
if (m_State == eSessionStateNewSessionSent) if (m_State == eSessionStateNewSessionSent)
{ {
m_State = eSessionStateEstablished; m_State = eSessionStateEstablished;
m_EphemeralKeys = nullptr;
m_SessionCreatedTimestamp = i2p::util::GetSecondsSinceEpoch (); m_SessionCreatedTimestamp = i2p::util::GetSecondsSinceEpoch ();
GetOwner ()->AddECIESx25519Session (m_RemoteStaticKey, shared_from_this ()); GetOwner ()->AddECIESx25519Session (m_RemoteStaticKey, shared_from_this ());
} }
@ -609,11 +642,23 @@ namespace garlic
return false; return false;
} }
HandlePayload (payload, len - 16, receiveTagset, index); HandlePayload (payload, len - 16, receiveTagset, index);
int moreTags = ECIESX25519_MIN_NUM_GENERATED_TAGS + (index >> 2); // N/4 if (GetOwner ())
if (moreTags > ECIESX25519_MAX_NUM_GENERATED_TAGS) moreTags = ECIESX25519_MAX_NUM_GENERATED_TAGS; {
moreTags -= (receiveTagset->GetNextIndex () - index); int moreTags = 0;
if (moreTags > 0 && GetOwner ()) if (GetOwner ()->GetNumRatchetInboundTags () > 0) // override in settings?
GenerateMoreReceiveTags (receiveTagset, moreTags); {
if (receiveTagset->GetNextIndex () - index < GetOwner ()->GetNumRatchetInboundTags ()/2)
moreTags = GetOwner ()->GetNumRatchetInboundTags ();
}
else
{
moreTags = ECIESX25519_MIN_NUM_GENERATED_TAGS + (index >> 2); // N/4
if (moreTags > ECIESX25519_MAX_NUM_GENERATED_TAGS) moreTags = ECIESX25519_MAX_NUM_GENERATED_TAGS;
moreTags -= (receiveTagset->GetNextIndex () - index);
}
if (moreTags > 0)
GenerateMoreReceiveTags (receiveTagset, moreTags);
}
return true; return true;
} }
@ -625,7 +670,8 @@ namespace garlic
{ {
case eSessionStateNewSessionReplySent: case eSessionStateNewSessionReplySent:
m_State = eSessionStateEstablished; m_State = eSessionStateEstablished;
m_NSRTagset = nullptr; m_NSRSendTagset = nullptr;
m_EphemeralKeys = nullptr;
#if (__cplusplus >= 201703L) // C++ 17 or higher #if (__cplusplus >= 201703L) // C++ 17 or higher
[[fallthrough]]; [[fallthrough]];
#endif #endif
@ -693,8 +739,11 @@ namespace garlic
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch (); uint64_t ts = i2p::util::GetMillisecondsSinceEpoch ();
size_t payloadLen = 0; size_t payloadLen = 0;
if (first) payloadLen += 7;// datatime if (first) payloadLen += 7;// datatime
if (msg && m_Destination) if (msg)
payloadLen += msg->GetPayloadLength () + 13 + 32; {
payloadLen += msg->GetPayloadLength () + 13;
if (m_Destination) payloadLen += 32;
}
auto leaseSet = (GetLeaseSetUpdateStatus () == eLeaseSetUpdated || auto leaseSet = (GetLeaseSetUpdateStatus () == eLeaseSetUpdated ||
(GetLeaseSetUpdateStatus () == eLeaseSetSubmitted && (GetLeaseSetUpdateStatus () == eLeaseSetSubmitted &&
ts > GetLeaseSetSubmissionTime () + LEASET_CONFIRMATION_TIMEOUT)) ? ts > GetLeaseSetSubmissionTime () + LEASET_CONFIRMATION_TIMEOUT)) ?
@ -724,13 +773,17 @@ namespace garlic
if (m_NextSendRatchet->newKey) payloadLen += 32; if (m_NextSendRatchet->newKey) payloadLen += 32;
} }
uint8_t paddingSize = 0; uint8_t paddingSize = 0;
if (payloadLen) if (payloadLen || ts > m_LastSentTimestamp + ECIESX25519_SEND_INACTIVITY_TIMEOUT)
{ {
int delta = (int)ECIESX25519_OPTIMAL_PAYLOAD_SIZE - (int)payloadLen; int delta = (int)ECIESX25519_OPTIMAL_PAYLOAD_SIZE - (int)payloadLen;
if (delta < 0 || delta > 3) // don't create padding if we are close to optimal size if (delta < 0 || delta > 3) // don't create padding if we are close to optimal size
{ {
RAND_bytes (&paddingSize, 1); paddingSize = m_PaddingSizes[m_NextPaddingSize++] & 0x0F; // 0 - 15
paddingSize &= 0x0F; // 0 - 15 if (m_NextPaddingSize >= 32)
{
RAND_bytes (m_PaddingSizes, 32);
m_NextPaddingSize = 0;
}
if (delta > 3) if (delta > 3)
{ {
delta -= 3; delta -= 3;
@ -741,96 +794,100 @@ namespace garlic
} }
} }
std::vector<uint8_t> v(payloadLen); std::vector<uint8_t> v(payloadLen);
size_t offset = 0; if (payloadLen)
// DateTime
if (first)
{ {
v[offset] = eECIESx25519BlkDateTime; offset++; m_LastSentTimestamp = ts;
htobe16buf (v.data () + offset, 4); offset += 2; size_t offset = 0;
htobe32buf (v.data () + offset, ts/1000); offset += 4; // in seconds // DateTime
} if (first)
// LeaseSet
if (leaseSet)
{
offset += CreateLeaseSetClove (leaseSet, ts, v.data () + offset, payloadLen - offset);
if (!first)
{ {
// ack request v[offset] = eECIESx25519BlkDateTime; offset++;
v[offset] = eECIESx25519BlkAckRequest; offset++; htobe16buf (v.data () + offset, 4); offset += 2;
htobe16buf (v.data () + offset, 1); offset += 2; htobe32buf (v.data () + offset, ts/1000); offset += 4; // in seconds
v[offset] = 0; offset++; // flags
} }
} // LeaseSet
// msg if (leaseSet)
if (msg && m_Destination)
offset += CreateGarlicClove (msg, v.data () + offset, payloadLen - offset, true);
// ack
if (m_AckRequests.size () > 0)
{
v[offset] = eECIESx25519BlkAck; offset++;
htobe16buf (v.data () + offset, m_AckRequests.size () * 4); offset += 2;
for (auto& it: m_AckRequests)
{ {
htobe16buf (v.data () + offset, it.first); offset += 2; offset += CreateLeaseSetClove (leaseSet, ts, v.data () + offset, payloadLen - offset);
htobe16buf (v.data () + offset, it.second); offset += 2; if (!first)
{
// ack request
v[offset] = eECIESx25519BlkAckRequest; offset++;
htobe16buf (v.data () + offset, 1); offset += 2;
v[offset] = 0; offset++; // flags
}
} }
m_AckRequests.clear (); // msg
} if (msg && m_Destination)
// next keys offset += CreateGarlicClove (msg, v.data () + offset, payloadLen - offset);
if (m_SendReverseKey) // ack
{ if (m_AckRequests.size () > 0)
v[offset] = eECIESx25519BlkNextKey; offset++;
htobe16buf (v.data () + offset, m_NextReceiveRatchet->newKey ? 35 : 3); offset += 2;
v[offset] = ECIESX25519_NEXT_KEY_REVERSE_KEY_FLAG;
int keyID = m_NextReceiveRatchet->keyID - 1;
if (m_NextReceiveRatchet->newKey)
{ {
v[offset] |= ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG; v[offset] = eECIESx25519BlkAck; offset++;
keyID++; htobe16buf (v.data () + offset, m_AckRequests.size () * 4); offset += 2;
for (auto& it: m_AckRequests)
{
htobe16buf (v.data () + offset, it.first); offset += 2;
htobe16buf (v.data () + offset, it.second); offset += 2;
}
m_AckRequests.clear ();
} }
offset++; // flag // next keys
htobe16buf (v.data () + offset, keyID); offset += 2; // keyid if (m_SendReverseKey)
if (m_NextReceiveRatchet->newKey)
{ {
memcpy (v.data () + offset, m_NextReceiveRatchet->key.GetPublicKey (), 32); v[offset] = eECIESx25519BlkNextKey; offset++;
offset += 32; // public key htobe16buf (v.data () + offset, m_NextReceiveRatchet->newKey ? 35 : 3); offset += 2;
v[offset] = ECIESX25519_NEXT_KEY_REVERSE_KEY_FLAG;
int keyID = m_NextReceiveRatchet->keyID - 1;
if (m_NextReceiveRatchet->newKey)
{
v[offset] |= ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG;
keyID++;
}
offset++; // flag
htobe16buf (v.data () + offset, keyID); offset += 2; // keyid
if (m_NextReceiveRatchet->newKey)
{
memcpy (v.data () + offset, m_NextReceiveRatchet->key->GetPublicKey (), 32);
offset += 32; // public key
}
m_SendReverseKey = false;
} }
m_SendReverseKey = false; if (m_SendForwardKey)
}
if (m_SendForwardKey)
{
v[offset] = eECIESx25519BlkNextKey; offset++;
htobe16buf (v.data () + offset, m_NextSendRatchet->newKey ? 35 : 3); offset += 2;
v[offset] = m_NextSendRatchet->newKey ? ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG : ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG;
if (!m_NextSendRatchet->keyID) v[offset] |= ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG; // for first key only
offset++; // flag
htobe16buf (v.data () + offset, m_NextSendRatchet->keyID); offset += 2; // keyid
if (m_NextSendRatchet->newKey)
{ {
memcpy (v.data () + offset, m_NextSendRatchet->key.GetPublicKey (), 32); v[offset] = eECIESx25519BlkNextKey; offset++;
offset += 32; // public key htobe16buf (v.data () + offset, m_NextSendRatchet->newKey ? 35 : 3); offset += 2;
v[offset] = m_NextSendRatchet->newKey ? ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG : ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG;
if (!m_NextSendRatchet->keyID) v[offset] |= ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG; // for first key only
offset++; // flag
htobe16buf (v.data () + offset, m_NextSendRatchet->keyID); offset += 2; // keyid
if (m_NextSendRatchet->newKey)
{
memcpy (v.data () + offset, m_NextSendRatchet->key->GetPublicKey (), 32);
offset += 32; // public key
}
}
// padding
if (paddingSize)
{
v[offset] = eECIESx25519BlkPadding; offset++;
htobe16buf (v.data () + offset, paddingSize); offset += 2;
memset (v.data () + offset, 0, paddingSize); offset += paddingSize;
} }
}
// padding
if (paddingSize)
{
v[offset] = eECIESx25519BlkPadding; offset++;
htobe16buf (v.data () + offset, paddingSize); offset += 2;
memset (v.data () + offset, 0, paddingSize); offset += paddingSize;
} }
return v; return v;
} }
size_t ECIESX25519AEADRatchetSession::CreateGarlicClove (std::shared_ptr<const I2NPMessage> msg, uint8_t * buf, size_t len, bool isDestination) size_t ECIESX25519AEADRatchetSession::CreateGarlicClove (std::shared_ptr<const I2NPMessage> msg, uint8_t * buf, size_t len)
{ {
if (!msg) return 0; if (!msg) return 0;
uint16_t cloveSize = msg->GetPayloadLength () + 9 + 1; uint16_t cloveSize = msg->GetPayloadLength () + 9 + 1;
if (isDestination) cloveSize += 32; if (m_Destination) cloveSize += 32;
if ((int)len < cloveSize + 3) return 0; if ((int)len < cloveSize + 3) return 0;
buf[0] = eECIESx25519BlkGalicClove; // clove type buf[0] = eECIESx25519BlkGalicClove; // clove type
htobe16buf (buf + 1, cloveSize); // size htobe16buf (buf + 1, cloveSize); // size
buf += 3; buf += 3;
if (isDestination) if (m_Destination)
{ {
*buf = (eGarlicDeliveryTypeDestination << 5); *buf = (eGarlicDeliveryTypeDestination << 5);
memcpy (buf + 1, *m_Destination, 32); buf += 32; memcpy (buf + 1, *m_Destination, 32); buf += 32;

View File

@ -27,7 +27,8 @@ namespace garlic
{ {
const int ECIESX25519_RESTART_TIMEOUT = 120; // number of second since session creation we can restart session after const int ECIESX25519_RESTART_TIMEOUT = 120; // number of second since session creation we can restart session after
const int ECIESX25519_EXPIRATION_TIMEOUT = 480; // in seconds const int ECIESX25519_EXPIRATION_TIMEOUT = 480; // in seconds
const int ECIESX25519_INACTIVITY_TIMEOUT = 90; // number of second we receive nothing and should restart if we can const int ECIESX25519_INACTIVITY_TIMEOUT = 90; // number of seconds we receive nothing and should restart if we can
const int ECIESX25519_SEND_INACTIVITY_TIMEOUT = 5000; // number of milliseconds we can send empty(pyaload only) packet after
const int ECIESX25519_INCOMING_TAGS_EXPIRATION_TIMEOUT = 600; // in seconds const int ECIESX25519_INCOMING_TAGS_EXPIRATION_TIMEOUT = 600; // in seconds
const int ECIESX25519_PREVIOUS_TAGSET_EXPIRATION_TIMEOUT = 180; // 180 const int ECIESX25519_PREVIOUS_TAGSET_EXPIRATION_TIMEOUT = 180; // 180
const int ECIESX25519_TAGSET_MAX_NUM_TAGS = 4096; // number of tags we request new tagset after const int ECIESX25519_TAGSET_MAX_NUM_TAGS = 4096; // number of tags we request new tagset after
@ -79,6 +80,18 @@ namespace garlic
uint64_t m_ExpirationTimestamp = 0; uint64_t m_ExpirationTimestamp = 0;
}; };
class NSRatchetTagSet: public RatchetTagSet
{
public:
NSRatchetTagSet (std::shared_ptr<ECIESX25519AEADRatchetSession> session):
RatchetTagSet (session), m_DummySession (session) {};
private:
std::shared_ptr<ECIESX25519AEADRatchetSession> m_DummySession; // we need a strong pointer for NS
};
enum ECIESx25519BlockType enum ECIESx25519BlockType
{ {
eECIESx25519BlkDateTime = 0, eECIESx25519BlkDateTime = 0,
@ -110,7 +123,7 @@ namespace garlic
struct DHRatchet struct DHRatchet
{ {
int keyID = 0; int keyID = 0;
i2p::crypto::X25519Keys key; std::shared_ptr<i2p::crypto::X25519Keys> key;
uint8_t remote[32]; // last remote public key uint8_t remote[32]; // last remote public key
bool newKey = true; bool newKey = true;
}; };
@ -136,6 +149,7 @@ namespace garlic
bool IsInactive (uint64_t ts) const { return ts > m_LastActivityTimestamp + ECIESX25519_INACTIVITY_TIMEOUT && CanBeRestarted (ts); } bool IsInactive (uint64_t ts) const { return ts > m_LastActivityTimestamp + ECIESX25519_INACTIVITY_TIMEOUT && CanBeRestarted (ts); }
bool IsRatchets () const { return true; }; bool IsRatchets () const { return true; };
uint64_t GetLastActivityTimestamp () const { return m_LastActivityTimestamp; };
private: private:
@ -157,7 +171,7 @@ namespace garlic
bool NewExistingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen); bool NewExistingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen);
std::vector<uint8_t> CreatePayload (std::shared_ptr<const I2NPMessage> msg, bool first); std::vector<uint8_t> CreatePayload (std::shared_ptr<const I2NPMessage> msg, bool first);
size_t CreateGarlicClove (std::shared_ptr<const I2NPMessage> msg, uint8_t * buf, size_t len, bool isDestination = false); size_t CreateGarlicClove (std::shared_ptr<const I2NPMessage> msg, uint8_t * buf, size_t len);
size_t CreateLeaseSetClove (std::shared_ptr<const i2p::data::LocalLeaseSet> ls, uint64_t ts, uint8_t * buf, size_t len); size_t CreateLeaseSetClove (std::shared_ptr<const i2p::data::LocalLeaseSet> ls, uint64_t ts, uint8_t * buf, size_t len);
void GenerateMoreReceiveTags (std::shared_ptr<RatchetTagSet> receiveTagset, int numTags); void GenerateMoreReceiveTags (std::shared_ptr<RatchetTagSet> receiveTagset, int numTags);
@ -168,14 +182,16 @@ namespace garlic
uint8_t m_H[32], m_CK[64] /* [chainkey, key] */, m_RemoteStaticKey[32]; uint8_t m_H[32], m_CK[64] /* [chainkey, key] */, m_RemoteStaticKey[32];
uint8_t m_Aepk[32]; // Alice's ephemeral keys, for incoming only uint8_t m_Aepk[32]; // Alice's ephemeral keys, for incoming only
uint8_t m_NSREncodedKey[32], m_NSRH[32], m_NSRKey[32]; // new session reply, for incoming only uint8_t m_NSREncodedKey[32], m_NSRH[32], m_NSRKey[32]; // new session reply, for incoming only
i2p::crypto::X25519Keys m_EphemeralKeys; std::shared_ptr<i2p::crypto::X25519Keys> m_EphemeralKeys;
SessionState m_State = eSessionStateNew; SessionState m_State = eSessionStateNew;
uint64_t m_SessionCreatedTimestamp = 0, m_LastActivityTimestamp = 0; // incoming uint64_t m_SessionCreatedTimestamp = 0, m_LastActivityTimestamp = 0, // incoming
std::shared_ptr<RatchetTagSet> m_SendTagset, m_NSRTagset; m_LastSentTimestamp = 0; // in milliseconds
std::shared_ptr<RatchetTagSet> m_SendTagset, m_NSRSendTagset;
std::unique_ptr<i2p::data::IdentHash> m_Destination;// TODO: might not need it std::unique_ptr<i2p::data::IdentHash> m_Destination;// TODO: might not need it
std::list<std::pair<uint16_t, int> > m_AckRequests; // (tagsetid, index) std::list<std::pair<uint16_t, int> > m_AckRequests; // (tagsetid, index)
bool m_SendReverseKey = false, m_SendForwardKey = false; bool m_SendReverseKey = false, m_SendForwardKey = false;
std::unique_ptr<DHRatchet> m_NextReceiveRatchet, m_NextSendRatchet; std::unique_ptr<DHRatchet> m_NextReceiveRatchet, m_NextSendRatchet;
uint8_t m_PaddingSizes[32], m_NextPaddingSize;
public: public:

View File

@ -432,7 +432,8 @@ namespace garlic
return ret; return ret;
} }
GarlicDestination::GarlicDestination (): m_NumTags (32) // 32 tags by default GarlicDestination::GarlicDestination (): m_NumTags (32), // 32 tags by default
m_NumRatchetInboundTags (0) // 0 means standard
{ {
m_Ctx = BN_CTX_new (); m_Ctx = BN_CTX_new ();
} }

View File

@ -114,6 +114,7 @@ namespace garlic
virtual bool CleanupUnconfirmedTags () { return false; }; // for I2CP, override in ElGamalAESSession virtual bool CleanupUnconfirmedTags () { return false; }; // for I2CP, override in ElGamalAESSession
virtual bool MessageConfirmed (uint32_t msgID); virtual bool MessageConfirmed (uint32_t msgID);
virtual bool IsRatchets () const { return false; }; virtual bool IsRatchets () const { return false; };
virtual uint64_t GetLastActivityTimestamp () const { return 0; }; // non-zero for rathets only
void SetLeaseSetUpdated () void SetLeaseSetUpdated ()
{ {
@ -232,6 +233,8 @@ namespace garlic
void CleanUp (); void CleanUp ();
void SetNumTags (int numTags) { m_NumTags = numTags; }; void SetNumTags (int numTags) { m_NumTags = numTags; };
int GetNumTags () const { return m_NumTags; }; int GetNumTags () const { return m_NumTags; };
void SetNumRatchetInboundTags (int numTags) { m_NumRatchetInboundTags = numTags; };
int GetNumRatchetInboundTags () const { return m_NumRatchetInboundTags; };
std::shared_ptr<GarlicRoutingSession> GetRoutingSession (std::shared_ptr<const i2p::data::RoutingDestination> destination, bool attachLeaseSet); std::shared_ptr<GarlicRoutingSession> GetRoutingSession (std::shared_ptr<const i2p::data::RoutingDestination> destination, bool attachLeaseSet);
void CleanupExpiredTags (); void CleanupExpiredTags ();
void RemoveDeliveryStatusSession (uint32_t msgID); void RemoveDeliveryStatusSession (uint32_t msgID);
@ -278,6 +281,7 @@ namespace garlic
std::unordered_map<i2p::data::IdentHash, ElGamalAESSessionPtr> m_Sessions; std::unordered_map<i2p::data::IdentHash, ElGamalAESSessionPtr> m_Sessions;
std::unordered_map<i2p::data::Tag<32>, ECIESX25519AEADRatchetSessionPtr> m_ECIESx25519Sessions; // static key -> session std::unordered_map<i2p::data::Tag<32>, ECIESX25519AEADRatchetSessionPtr> m_ECIESx25519Sessions; // static key -> session
// incoming // incoming
int m_NumRatchetInboundTags;
std::unordered_map<SessionTag, std::shared_ptr<AESDecryption>, std::hash<i2p::data::Tag<32> > > m_Tags; std::unordered_map<SessionTag, std::shared_ptr<AESDecryption>, std::hash<i2p::data::Tag<32> > > m_Tags;
std::unordered_map<uint64_t, ECIESX25519AEADRatchetIndexTagset> m_ECIESx25519Tags; // session tag -> session std::unordered_map<uint64_t, ECIESX25519AEADRatchetIndexTagset> m_ECIESx25519Tags; // session tag -> session
// DeliveryStatus // DeliveryStatus

View File

@ -83,7 +83,7 @@ namespace transport
void NTCP2Establisher::KDF1Alice () void NTCP2Establisher::KDF1Alice ()
{ {
KeyDerivationFunction1 (m_RemoteStaticKey, m_EphemeralKeys, m_RemoteStaticKey, GetPub ()); KeyDerivationFunction1 (m_RemoteStaticKey, *m_EphemeralKeys, m_RemoteStaticKey, GetPub ());
} }
void NTCP2Establisher::KDF1Bob () void NTCP2Establisher::KDF1Bob ()
@ -102,7 +102,7 @@ namespace transport
// x25519 between remote pub and ephemaral priv // x25519 between remote pub and ephemaral priv
uint8_t inputKeyMaterial[32]; uint8_t inputKeyMaterial[32];
m_EphemeralKeys.Agree (GetRemotePub (), inputKeyMaterial); m_EphemeralKeys->Agree (GetRemotePub (), inputKeyMaterial);
MixKey (inputKeyMaterial); MixKey (inputKeyMaterial);
} }
@ -127,13 +127,13 @@ namespace transport
void NTCP2Establisher::KDF3Bob () void NTCP2Establisher::KDF3Bob ()
{ {
uint8_t inputKeyMaterial[32]; uint8_t inputKeyMaterial[32];
m_EphemeralKeys.Agree (m_RemoteStaticKey, inputKeyMaterial); m_EphemeralKeys->Agree (m_RemoteStaticKey, inputKeyMaterial);
MixKey (inputKeyMaterial); MixKey (inputKeyMaterial);
} }
void NTCP2Establisher::CreateEphemeralKey () void NTCP2Establisher::CreateEphemeralKey ()
{ {
m_EphemeralKeys.GenerateKeys (); m_EphemeralKeys = i2p::transport::transports.GetNextX25519KeysPair ();
} }
void NTCP2Establisher::CreateSessionRequestMessage () void NTCP2Establisher::CreateSessionRequestMessage ()
@ -338,11 +338,8 @@ namespace transport
KDF3Bob (); KDF3Bob ();
if (i2p::crypto::AEADChaCha20Poly1305 (m_SessionConfirmedBuffer + 48, m3p2Len - 16, GetH (), 32, GetK (), nonce, m3p2Buf, m3p2Len - 16, false)) // decrypt if (i2p::crypto::AEADChaCha20Poly1305 (m_SessionConfirmedBuffer + 48, m3p2Len - 16, GetH (), 32, GetK (), nonce, m3p2Buf, m3p2Len - 16, false)) // decrypt
{
// caclulate new h again for KDF data // caclulate new h again for KDF data
memcpy (m_SessionConfirmedBuffer + 16, m_H, 32); // h || ciphertext MixHash (m_SessionConfirmedBuffer + 48, m3p2Len); // h = SHA256(h || ciphertext)
SHA256 (m_SessionConfirmedBuffer + 16, m3p2Len + 32, m_H); //h = SHA256(h || ciphertext);
}
else else
{ {
LogPrint (eLogWarning, "NTCP2: SessionConfirmed Part2 AEAD verification failed "); LogPrint (eLogWarning, "NTCP2: SessionConfirmed Part2 AEAD verification failed ");
@ -1305,7 +1302,8 @@ namespace transport
if (ecode != boost::asio::error::operation_aborted) if (ecode != boost::asio::error::operation_aborted)
{ {
LogPrint (eLogInfo, "NTCP2: Not connected in ", timeout, " seconds"); LogPrint (eLogInfo, "NTCP2: Not connected in ", timeout, " seconds");
//i2p::data::netdb.SetUnreachable (conn->GetRemoteIdentity ()->GetIdentHash (), true); if (conn->GetRemoteIdentity ())
i2p::data::netdb.SetUnreachable (conn->GetRemoteIdentity ()->GetIdentHash (), true);
conn->Terminate (); conn->Terminate ();
} }
}); });

View File

@ -79,7 +79,7 @@ namespace transport
NTCP2Establisher (); NTCP2Establisher ();
~NTCP2Establisher (); ~NTCP2Establisher ();
const uint8_t * GetPub () const { return m_EphemeralKeys.GetPublicKey (); }; const uint8_t * GetPub () const { return m_EphemeralKeys->GetPublicKey (); };
const uint8_t * GetRemotePub () const { return m_RemoteEphemeralPublicKey; }; // Y for Alice and X for Bob const uint8_t * GetRemotePub () const { return m_RemoteEphemeralPublicKey; }; // Y for Alice and X for Bob
uint8_t * GetRemotePub () { return m_RemoteEphemeralPublicKey; }; // to set uint8_t * GetRemotePub () { return m_RemoteEphemeralPublicKey; }; // to set
@ -110,7 +110,7 @@ namespace transport
bool ProcessSessionConfirmedMessagePart1 (const uint8_t * nonce); bool ProcessSessionConfirmedMessagePart1 (const uint8_t * nonce);
bool ProcessSessionConfirmedMessagePart2 (const uint8_t * nonce, uint8_t * m3p2Buf); bool ProcessSessionConfirmedMessagePart2 (const uint8_t * nonce, uint8_t * m3p2Buf);
i2p::crypto::X25519Keys m_EphemeralKeys; std::shared_ptr<i2p::crypto::X25519Keys> m_EphemeralKeys;
uint8_t m_RemoteEphemeralPublicKey[32]; // x25519 uint8_t m_RemoteEphemeralPublicKey[32]; // x25519
uint8_t m_RemoteStaticKey[32], m_IV[16], m_H[32] /*h*/, m_CK[64] /* [ck, k]*/; uint8_t m_RemoteStaticKey[32], m_IV[16], m_H[32] /*h*/, m_CK[64] /* [ck, k]*/;
i2p::data::IdentHash m_RemoteIdentHash; i2p::data::IdentHash m_RemoteIdentHash;

View File

@ -11,7 +11,7 @@
// this file is called NetDb.hpp to resolve conflict with libc's netdb.h on case insensitive fs // this file is called NetDb.hpp to resolve conflict with libc's netdb.h on case insensitive fs
#include <inttypes.h> #include <inttypes.h>
#include <set> #include <set>
#include <map> #include <unordered_map>
#include <list> #include <list>
#include <string> #include <string>
#include <thread> #include <thread>
@ -138,9 +138,9 @@ namespace data
private: private:
mutable std::mutex m_LeaseSetsMutex; mutable std::mutex m_LeaseSetsMutex;
std::map<IdentHash, std::shared_ptr<LeaseSet> > m_LeaseSets; std::unordered_map<IdentHash, std::shared_ptr<LeaseSet> > m_LeaseSets;
mutable std::mutex m_RouterInfosMutex; mutable std::mutex m_RouterInfosMutex;
std::map<IdentHash, std::shared_ptr<RouterInfo> > m_RouterInfos; std::unordered_map<IdentHash, std::shared_ptr<RouterInfo> > m_RouterInfos;
mutable std::mutex m_FloodfillsMutex; mutable std::mutex m_FloodfillsMutex;
std::list<std::shared_ptr<RouterInfo> > m_Floodfills; std::list<std::shared_ptr<RouterInfo> > m_Floodfills;

View File

@ -301,7 +301,7 @@ namespace transport
void SSUData::Send (std::shared_ptr<i2p::I2NPMessage> msg) void SSUData::Send (std::shared_ptr<i2p::I2NPMessage> msg)
{ {
uint32_t msgID = msg->ToSSU (); uint32_t msgID = msg->ToSSU ();
if (m_SentMessages.count (msgID) > 0) if (m_SentMessages.find (msgID) != m_SentMessages.end())
{ {
LogPrint (eLogWarning, "SSU: message ", msgID, " already sent"); LogPrint (eLogWarning, "SSU: message ", msgID, " already sent");
return; return;
@ -326,8 +326,7 @@ namespace transport
{ {
Fragment * fragment = new Fragment; Fragment * fragment = new Fragment;
fragment->fragmentNum = fragmentNum; fragment->fragmentNum = fragmentNum;
uint8_t * buf = fragment->buf; uint8_t * payload = fragment->buf + sizeof (SSUHeader);
uint8_t * payload = buf + sizeof (SSUHeader);
*payload = DATA_FLAG_WANT_REPLY; // for compatibility *payload = DATA_FLAG_WANT_REPLY; // for compatibility
payload++; payload++;
*payload = 1; // always 1 message fragment per message *payload = 1; // always 1 message fragment per message
@ -346,14 +345,20 @@ namespace transport
payload += 3; payload += 3;
memcpy (payload, msgBuf, size); memcpy (payload, msgBuf, size);
size += payload - buf; size += payload - fragment->buf;
if (size & 0x0F) // make sure 16 bytes boundary uint8_t rem = size & 0x0F;
size = ((size >> 4) + 1) << 4; // (/16 + 1)*16 if (rem) // make sure 16 bytes boundary
{
auto padding = 16 - rem;
memset (fragment->buf + size, 0, padding);
size += padding;
}
fragment->len = size; fragment->len = size;
fragments.push_back (std::unique_ptr<Fragment> (fragment)); fragments.push_back (std::unique_ptr<Fragment> (fragment));
// encrypt message with session key // encrypt message with session key
m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, size); uint8_t buf[SSU_V4_MAX_PACKET_SIZE + 18];
m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, fragment->buf, size, buf);
try try
{ {
m_Session.Send (buf, size); m_Session.Send (buf, size);
@ -432,6 +437,7 @@ namespace transport
{ {
if (ecode != boost::asio::error::operation_aborted) if (ecode != boost::asio::error::operation_aborted)
{ {
uint8_t buf[SSU_V4_MAX_PACKET_SIZE + 18];
uint32_t ts = i2p::util::GetSecondsSinceEpoch (); uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
int numResent = 0; int numResent = 0;
for (auto it = m_SentMessages.begin (); it != m_SentMessages.end ();) for (auto it = m_SentMessages.begin (); it != m_SentMessages.end ();)
@ -445,7 +451,8 @@ namespace transport
{ {
try try
{ {
m_Session.Send (f->buf, f->len); // resend m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, f->buf, f->len, buf);
m_Session.Send (buf, f->len); // resend
numResent++; numResent++;
} }
catch (boost::system::system_error& ec) catch (boost::system::system_error& ec)

View File

@ -11,7 +11,7 @@
#include <inttypes.h> #include <inttypes.h>
#include <string.h> #include <string.h>
#include <map> #include <unordered_map>
#include <vector> #include <vector>
#include <unordered_set> #include <unordered_set>
#include <memory> #include <memory>
@ -124,8 +124,8 @@ namespace transport
private: private:
SSUSession& m_Session; SSUSession& m_Session;
std::map<uint32_t, std::unique_ptr<IncompleteMessage> > m_IncompleteMessages; std::unordered_map<uint32_t, std::unique_ptr<IncompleteMessage> > m_IncompleteMessages;
std::map<uint32_t, std::unique_ptr<SentMessage> > m_SentMessages; std::unordered_map<uint32_t, std::unique_ptr<SentMessage> > m_SentMessages;
std::unordered_set<uint32_t> m_ReceivedMessages; std::unordered_set<uint32_t> m_ReceivedMessages;
boost::asio::deadline_timer m_ResendTimer, m_IncompleteMessagesCleanupTimer; boost::asio::deadline_timer m_ResendTimer, m_IncompleteMessagesCleanupTimer;
int m_MaxPacketSize, m_PacketSize; int m_MaxPacketSize, m_PacketSize;

View File

@ -744,24 +744,30 @@ namespace transport
} }
void SSUSession::FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len) void SSUSession::FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len)
{
FillHeaderAndEncrypt (payloadType, buf, len, buf);
}
void SSUSession::FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * in, size_t len, uint8_t * out)
{ {
if (len < sizeof (SSUHeader)) if (len < sizeof (SSUHeader))
{ {
LogPrint (eLogError, "SSU: Unexpected packet length ", len); LogPrint (eLogError, "SSU: Unexpected packet length ", len);
return; return;
} }
SSUHeader * header = (SSUHeader *)buf; SSUHeader * header = (SSUHeader *)out;
RAND_bytes (header->iv, 16); // random iv RAND_bytes (header->iv, 16); // random iv
m_SessionKeyEncryption.SetIV (header->iv); m_SessionKeyEncryption.SetIV (header->iv);
header->flag = payloadType << 4; // MSB is 0 SSUHeader * inHeader = (SSUHeader *)in;
htobe32buf (header->time, i2p::util::GetSecondsSinceEpoch ()); inHeader->flag = payloadType << 4; // MSB is 0
uint8_t * encrypted = &header->flag; htobe32buf (inHeader->time, i2p::util::GetSecondsSinceEpoch ());
uint16_t encryptedLen = len - (encrypted - buf); uint8_t * encrypted = &header->flag, * clear = &inHeader->flag;
m_SessionKeyEncryption.Encrypt (encrypted, encryptedLen, encrypted); uint16_t encryptedLen = len - (encrypted - out);
// assume actual buffer size is 18 (16 + 2) bytes more m_SessionKeyEncryption.Encrypt (clear, encryptedLen, encrypted);
memcpy (buf + len, header->iv, 16); // assume actual out buffer size is 18 (16 + 2) bytes more
memcpy (out + len, header->iv, 16);
uint16_t netid = i2p::context.GetNetID (); uint16_t netid = i2p::context.GetNetID ();
htobe16buf (buf + len + 16, (netid == I2PD_NET_ID) ? encryptedLen : encryptedLen ^ ((netid - 2) << 8)); htobe16buf (out + len + 16, (netid == I2PD_NET_ID) ? encryptedLen : encryptedLen ^ ((netid - 2) << 8));
i2p::crypto::HMACMD5Digest (encrypted, encryptedLen + 18, m_MacKey, header->mac); i2p::crypto::HMACMD5Digest (encrypted, encryptedLen + 18, m_MacKey, header->mac);
} }

View File

@ -138,6 +138,7 @@ namespace transport
void FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len, const i2p::crypto::AESKey& aesKey, void FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len, const i2p::crypto::AESKey& aesKey,
const uint8_t * iv, const i2p::crypto::MACKey& macKey, uint8_t flag = 0); const uint8_t * iv, const i2p::crypto::MACKey& macKey, uint8_t flag = 0);
void FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len); // with session key void FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len); // with session key
void FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * in, size_t len, uint8_t * out); // with session key
void Decrypt (uint8_t * buf, size_t len, const i2p::crypto::AESKey& aesKey); void Decrypt (uint8_t * buf, size_t len, const i2p::crypto::AESKey& aesKey);
void DecryptSessionKey (uint8_t * buf, size_t len); void DecryptSessionKey (uint8_t * buf, size_t len);
bool Validate (uint8_t * buf, size_t len, const i2p::crypto::MACKey& macKey); bool Validate (uint8_t * buf, size_t len, const i2p::crypto::MACKey& macKey);

View File

@ -21,23 +21,27 @@ namespace i2p
{ {
namespace transport namespace transport
{ {
DHKeysPairSupplier::DHKeysPairSupplier (int size): template<typename Keys>
EphemeralKeysSupplier<Keys>::EphemeralKeysSupplier (int size):
m_QueueSize (size), m_IsRunning (false), m_Thread (nullptr) m_QueueSize (size), m_IsRunning (false), m_Thread (nullptr)
{ {
} }
DHKeysPairSupplier::~DHKeysPairSupplier () template<typename Keys>
EphemeralKeysSupplier<Keys>::~EphemeralKeysSupplier ()
{ {
Stop (); Stop ();
} }
void DHKeysPairSupplier::Start () template<typename Keys>
void EphemeralKeysSupplier<Keys>::Start ()
{ {
m_IsRunning = true; m_IsRunning = true;
m_Thread = new std::thread (std::bind (&DHKeysPairSupplier::Run, this)); m_Thread = new std::thread (std::bind (&EphemeralKeysSupplier<Keys>::Run, this));
} }
void DHKeysPairSupplier::Stop () template<typename Keys>
void EphemeralKeysSupplier<Keys>::Stop ()
{ {
{ {
std::unique_lock<std::mutex> l(m_AcquiredMutex); std::unique_lock<std::mutex> l(m_AcquiredMutex);
@ -52,19 +56,20 @@ namespace transport
} }
} }
void DHKeysPairSupplier::Run () template<typename Keys>
void EphemeralKeysSupplier<Keys>::Run ()
{ {
while (m_IsRunning) while (m_IsRunning)
{ {
int num, total = 0; int num, total = 0;
while ((num = m_QueueSize - (int)m_Queue.size ()) > 0 && total < 10) while ((num = m_QueueSize - (int)m_Queue.size ()) > 0 && total < 10)
{ {
CreateDHKeysPairs (num); CreateEphemeralKeys (num);
total += num; total += num;
} }
if (total >= 10) if (total >= 10)
{ {
LogPrint (eLogWarning, "Transports: ", total, " DH keys generated at the time"); LogPrint (eLogWarning, "Transports: ", total, " ephemeral keys generated at the time");
std::this_thread::sleep_for (std::chrono::seconds(1)); // take a break std::this_thread::sleep_for (std::chrono::seconds(1)); // take a break
} }
else else
@ -76,24 +81,26 @@ namespace transport
} }
} }
void DHKeysPairSupplier::CreateDHKeysPairs (int num) template<typename Keys>
void EphemeralKeysSupplier<Keys>::CreateEphemeralKeys (int num)
{ {
if (num > 0) if (num > 0)
{ {
for (int i = 0; i < num; i++) for (int i = 0; i < num; i++)
{ {
auto pair = std::make_shared<i2p::crypto::DHKeys> (); auto pair = std::make_shared<Keys> ();
pair->GenerateKeys (); pair->GenerateKeys ();
std::unique_lock<std::mutex> l(m_AcquiredMutex); std::unique_lock<std::mutex> l(m_AcquiredMutex);
m_Queue.push (pair); m_Queue.push (pair);
} }
} }
} }
std::shared_ptr<i2p::crypto::DHKeys> DHKeysPairSupplier::Acquire () template<typename Keys>
std::shared_ptr<Keys> EphemeralKeysSupplier<Keys>::Acquire ()
{ {
{ {
std::unique_lock<std::mutex> l(m_AcquiredMutex); std::unique_lock<std::mutex> l(m_AcquiredMutex);
if (!m_Queue.empty ()) if (!m_Queue.empty ())
{ {
auto pair = m_Queue.front (); auto pair = m_Queue.front ();
@ -103,12 +110,13 @@ namespace transport
} }
} }
// queue is empty, create new // queue is empty, create new
auto pair = std::make_shared<i2p::crypto::DHKeys> (); auto pair = std::make_shared<Keys> ();
pair->GenerateKeys (); pair->GenerateKeys ();
return pair; return pair;
} }
void DHKeysPairSupplier::Return (std::shared_ptr<i2p::crypto::DHKeys> pair) template<typename Keys>
void EphemeralKeysSupplier<Keys>::Return (std::shared_ptr<Keys> pair)
{ {
if (pair) if (pair)
{ {
@ -126,7 +134,7 @@ namespace transport
m_IsOnline (true), m_IsRunning (false), m_IsNAT (true), m_Thread (nullptr), m_Service (nullptr), m_IsOnline (true), m_IsRunning (false), m_IsNAT (true), m_Thread (nullptr), m_Service (nullptr),
m_Work (nullptr), m_PeerCleanupTimer (nullptr), m_PeerTestTimer (nullptr), m_Work (nullptr), m_PeerCleanupTimer (nullptr), m_PeerTestTimer (nullptr),
m_NTCPServer (nullptr), m_SSUServer (nullptr), m_NTCP2Server (nullptr), m_NTCPServer (nullptr), m_SSUServer (nullptr), m_NTCP2Server (nullptr),
m_DHKeysPairSupplier (5), // 5 pre-generated keys m_DHKeysPairSupplier (5), m_X25519KeysPairSupplier (5), // 5 pre-generated keys
m_TotalSentBytes(0), m_TotalReceivedBytes(0), m_TotalTransitTransmittedBytes (0), m_TotalSentBytes(0), m_TotalReceivedBytes(0), m_TotalTransitTransmittedBytes (0),
m_InBandwidth (0), m_OutBandwidth (0), m_TransitBandwidth(0), m_InBandwidth (0), m_OutBandwidth (0), m_TransitBandwidth(0),
m_LastInBandwidthUpdateBytes (0), m_LastOutBandwidthUpdateBytes (0), m_LastInBandwidthUpdateBytes (0), m_LastOutBandwidthUpdateBytes (0),
@ -158,6 +166,7 @@ namespace transport
i2p::config::GetOption("nat", m_IsNAT); i2p::config::GetOption("nat", m_IsNAT);
m_DHKeysPairSupplier.Start (); m_DHKeysPairSupplier.Start ();
m_X25519KeysPairSupplier.Start ();
m_IsRunning = true; m_IsRunning = true;
m_Thread = new std::thread (std::bind (&Transports::Run, this)); m_Thread = new std::thread (std::bind (&Transports::Run, this));
std::string ntcpproxy; i2p::config::GetOption("ntcpproxy", ntcpproxy); std::string ntcpproxy; i2p::config::GetOption("ntcpproxy", ntcpproxy);
@ -312,6 +321,7 @@ namespace transport
} }
m_DHKeysPairSupplier.Stop (); m_DHKeysPairSupplier.Stop ();
m_X25519KeysPairSupplier.Stop ();
m_IsRunning = false; m_IsRunning = false;
if (m_Service) m_Service->stop (); if (m_Service) m_Service->stop ();
if (m_Thread) if (m_Thread)
@ -431,6 +441,8 @@ namespace transport
bool Transports::ConnectToPeer (const i2p::data::IdentHash& ident, Peer& peer) bool Transports::ConnectToPeer (const i2p::data::IdentHash& ident, Peer& peer)
{ {
if (!peer.router) // reconnect
peer.router = netdb.FindRouter (ident); // try to get new one from netdb
if (peer.router) // we have RI already if (peer.router) // we have RI already
{ {
if (!peer.numAttempts) // NTCP2 if (!peer.numAttempts) // NTCP2
@ -440,7 +452,7 @@ namespace transport
{ {
// NTCP2 have priority over NTCP // NTCP2 have priority over NTCP
auto address = peer.router->GetNTCP2Address (true, !context.SupportsV6 ()); // published only auto address = peer.router->GetNTCP2Address (true, !context.SupportsV6 ()); // published only
if (address) if (address && !peer.router->IsUnreachable ())
{ {
auto s = std::make_shared<NTCP2Session> (*m_NTCP2Server, peer.router); auto s = std::make_shared<NTCP2Session> (*m_NTCP2Server, peer.router);
@ -628,6 +640,16 @@ namespace transport
m_DHKeysPairSupplier.Return (pair); m_DHKeysPairSupplier.Return (pair);
} }
std::shared_ptr<i2p::crypto::X25519Keys> Transports::GetNextX25519KeysPair ()
{
return m_X25519KeysPairSupplier.Acquire ();
}
void Transports::ReuseX25519KeysPair (std::shared_ptr<i2p::crypto::X25519Keys> pair)
{
m_X25519KeysPairSupplier.Return (pair);
}
void Transports::PeerConnected (std::shared_ptr<TransportSession> session) void Transports::PeerConnected (std::shared_ptr<TransportSession> session)
{ {
m_Service->post([session, this]() m_Service->post([session, this]()
@ -638,6 +660,7 @@ namespace transport
auto it = m_Peers.find (ident); auto it = m_Peers.find (ident);
if (it != m_Peers.end ()) if (it != m_Peers.end ())
{ {
it->second.router = nullptr; // we don't need RouterInfo after successive connect
bool sendDatabaseStore = true; bool sendDatabaseStore = true;
if (it->second.delayedMessages.size () > 0) if (it->second.delayedMessages.size () > 0)
{ {

View File

@ -13,7 +13,7 @@
#include <mutex> #include <mutex>
#include <condition_variable> #include <condition_variable>
#include <functional> #include <functional>
#include <map> #include <unordered_map>
#include <vector> #include <vector>
#include <queue> #include <queue>
#include <string> #include <string>
@ -32,32 +32,36 @@ namespace i2p
{ {
namespace transport namespace transport
{ {
class DHKeysPairSupplier template<typename Keys>
class EphemeralKeysSupplier
{ {
// called from this file only, so implementation is in Transports.cpp
public: public:
DHKeysPairSupplier (int size); EphemeralKeysSupplier (int size);
~DHKeysPairSupplier (); ~EphemeralKeysSupplier ();
void Start (); void Start ();
void Stop (); void Stop ();
std::shared_ptr<i2p::crypto::DHKeys> Acquire (); std::shared_ptr<Keys> Acquire ();
void Return (std::shared_ptr<i2p::crypto::DHKeys> pair); void Return (std::shared_ptr<Keys> pair);
private: private:
void Run (); void Run ();
void CreateDHKeysPairs (int num); void CreateEphemeralKeys (int num);
private: private:
const int m_QueueSize; const int m_QueueSize;
std::queue<std::shared_ptr<i2p::crypto::DHKeys> > m_Queue; std::queue<std::shared_ptr<Keys> > m_Queue;
bool m_IsRunning; bool m_IsRunning;
std::thread * m_Thread; std::thread * m_Thread;
std::condition_variable m_Acquired; std::condition_variable m_Acquired;
std::mutex m_AcquiredMutex; std::mutex m_AcquiredMutex;
}; };
typedef EphemeralKeysSupplier<i2p::crypto::DHKeys> DHKeysPairSupplier;
typedef EphemeralKeysSupplier<i2p::crypto::X25519Keys> X25519KeysPairSupplier;
struct Peer struct Peer
{ {
@ -97,6 +101,8 @@ namespace transport
boost::asio::io_service& GetService () { return *m_Service; }; boost::asio::io_service& GetService () { return *m_Service; };
std::shared_ptr<i2p::crypto::DHKeys> GetNextDHKeysPair (); std::shared_ptr<i2p::crypto::DHKeys> GetNextDHKeysPair ();
void ReuseDHKeysPair (std::shared_ptr<i2p::crypto::DHKeys> pair); void ReuseDHKeysPair (std::shared_ptr<i2p::crypto::DHKeys> pair);
std::shared_ptr<i2p::crypto::X25519Keys> GetNextX25519KeysPair ();
void ReuseX25519KeysPair (std::shared_ptr<i2p::crypto::X25519Keys> pair);
void SendMessage (const i2p::data::IdentHash& ident, std::shared_ptr<i2p::I2NPMessage> msg); void SendMessage (const i2p::data::IdentHash& ident, std::shared_ptr<i2p::I2NPMessage> msg);
void SendMessages (const i2p::data::IdentHash& ident, const std::vector<std::shared_ptr<i2p::I2NPMessage> >& msgs); void SendMessages (const i2p::data::IdentHash& ident, const std::vector<std::shared_ptr<i2p::I2NPMessage> >& msgs);
@ -157,9 +163,10 @@ namespace transport
SSUServer * m_SSUServer; SSUServer * m_SSUServer;
NTCP2Server * m_NTCP2Server; NTCP2Server * m_NTCP2Server;
mutable std::mutex m_PeersMutex; mutable std::mutex m_PeersMutex;
std::map<i2p::data::IdentHash, Peer> m_Peers; std::unordered_map<i2p::data::IdentHash, Peer> m_Peers;
DHKeysPairSupplier m_DHKeysPairSupplier; DHKeysPairSupplier m_DHKeysPairSupplier;
X25519KeysPairSupplier m_X25519KeysPairSupplier;
std::atomic<uint64_t> m_TotalSentBytes, m_TotalReceivedBytes, m_TotalTransitTransmittedBytes; std::atomic<uint64_t> m_TotalSentBytes, m_TotalReceivedBytes, m_TotalTransitTransmittedBytes;
uint32_t m_InBandwidth, m_OutBandwidth, m_TransitBandwidth; // bytes per second uint32_t m_InBandwidth, m_OutBandwidth, m_TransitBandwidth; // bytes per second

View File

@ -66,7 +66,7 @@ namespace tunnel
// length of bytes doesn't fit full tunnel message // length of bytes doesn't fit full tunnel message
// every follow-on fragment adds 7 bytes // every follow-on fragment adds 7 bytes
size_t nonFit = (fullMsgLen + numFollowOnFragments*7) % TUNNEL_DATA_MAX_PAYLOAD_SIZE; size_t nonFit = (fullMsgLen + numFollowOnFragments*7) % TUNNEL_DATA_MAX_PAYLOAD_SIZE;
if (!nonFit || nonFit > m_RemainingSize) if (!nonFit || nonFit > m_RemainingSize || m_RemainingSize < fullMsgLen/5)
{ {
CompleteCurrentTunnelDataMessage (); CompleteCurrentTunnelDataMessage ();
CreateCurrentTunnelDataMessage (); CreateCurrentTunnelDataMessage ();

View File

@ -16,8 +16,8 @@
#define MAKE_VERSION_NUMBER(a,b,c) ((a*100+b)*100+c) #define MAKE_VERSION_NUMBER(a,b,c) ((a*100+b)*100+c)
#define I2PD_VERSION_MAJOR 2 #define I2PD_VERSION_MAJOR 2
#define I2PD_VERSION_MINOR 32 #define I2PD_VERSION_MINOR 33
#define I2PD_VERSION_MICRO 1 #define I2PD_VERSION_MICRO 0
#define I2PD_VERSION_PATCH 0 #define I2PD_VERSION_PATCH 0
#define I2PD_VERSION MAKE_VERSION(I2PD_VERSION_MAJOR, I2PD_VERSION_MINOR, I2PD_VERSION_MICRO) #define I2PD_VERSION MAKE_VERSION(I2PD_VERSION_MAJOR, I2PD_VERSION_MINOR, I2PD_VERSION_MICRO)
#define VERSION I2PD_VERSION #define VERSION I2PD_VERSION
@ -30,7 +30,7 @@
#define I2P_VERSION_MAJOR 0 #define I2P_VERSION_MAJOR 0
#define I2P_VERSION_MINOR 9 #define I2P_VERSION_MINOR 9
#define I2P_VERSION_MICRO 46 #define I2P_VERSION_MICRO 47
#define I2P_VERSION_PATCH 0 #define I2P_VERSION_PATCH 0
#define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO) #define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO)
#define I2P_VERSION_NUMBER MAKE_VERSION_NUMBER(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO) #define I2P_VERSION_NUMBER MAKE_VERSION_NUMBER(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO)

View File

@ -198,13 +198,18 @@ namespace client
for (const auto& it: addresses) for (const auto& it: addresses)
{ {
f << it.first << ","; if (it.second->IsValid ())
if (it.second->IsIdentHash ()) {
f << it.second->identHash.ToBase32 (); f << it.first << ",";
if (it.second->IsIdentHash ())
f << it.second->identHash.ToBase32 ();
else
f << it.second->blindedPublicKey->ToB33 ();
f << std::endl;
num++;
}
else else
f << it.second->blindedPublicKey->ToB33 (); LogPrint (eLogWarning, "Addressbook: invalid address ", it.first);
f << std::endl;
num++;
} }
LogPrint (eLogInfo, "Addressbook: ", num, " addresses saved"); LogPrint (eLogInfo, "Addressbook: ", num, " addresses saved");
return num; return num;

View File

@ -253,7 +253,8 @@ namespace client
bool ClientContext::LoadPrivateKeys (i2p::data::PrivateKeys& keys, const std::string& filename, bool ClientContext::LoadPrivateKeys (i2p::data::PrivateKeys& keys, const std::string& filename,
i2p::data::SigningKeyType sigType, i2p::data::CryptoKeyType cryptoType) i2p::data::SigningKeyType sigType, i2p::data::CryptoKeyType cryptoType)
{ {
if (filename == "transient") static const std::string transient("transient");
if (!filename.compare (0, transient.length (), transient)) // starts with transient
{ {
keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType, cryptoType); keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType, cryptoType);
LogPrint (eLogInfo, "Clients: New transient keys address ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " created"); LogPrint (eLogInfo, "Clients: New transient keys address ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " created");
@ -461,6 +462,8 @@ namespace client
} }
std::string explicitPeers = GetI2CPStringOption(section, I2CP_PARAM_EXPLICIT_PEERS, ""); std::string explicitPeers = GetI2CPStringOption(section, I2CP_PARAM_EXPLICIT_PEERS, "");
if (explicitPeers.length () > 0) options[I2CP_PARAM_EXPLICIT_PEERS] = explicitPeers; if (explicitPeers.length () > 0) options[I2CP_PARAM_EXPLICIT_PEERS] = explicitPeers;
std::string ratchetInboundTags = GetI2CPStringOption(section, I2CP_PARAM_RATCHET_INBOUND_TAGS, "");
if (ratchetInboundTags.length () > 0) options[I2CP_PARAM_RATCHET_INBOUND_TAGS] = ratchetInboundTags;
} }
void ClientContext::ReadI2CPOptionsFromConfig (const std::string& prefix, std::map<std::string, std::string>& options) const void ClientContext::ReadI2CPOptionsFromConfig (const std::string& prefix, std::map<std::string, std::string>& options) const
@ -531,6 +534,7 @@ namespace client
return; return;
} }
std::map<std::string, std::shared_ptr<ClientDestination> > destinations; // keys -> destination
for (auto& section: pt) for (auto& section: pt)
{ {
std::string name = section.first; std::string name = section.first;
@ -562,16 +566,23 @@ namespace client
std::shared_ptr<ClientDestination> localDestination = nullptr; std::shared_ptr<ClientDestination> localDestination = nullptr;
if (keys.length () > 0) if (keys.length () > 0)
{ {
i2p::data::PrivateKeys k; auto it = destinations.find (keys);
if(LoadPrivateKeys (k, keys, sigType, cryptoType)) if (it != destinations.end ())
localDestination = it->second;
else
{ {
localDestination = FindLocalDestination (k.GetPublic ()->GetIdentHash ()); i2p::data::PrivateKeys k;
if (!localDestination) if(LoadPrivateKeys (k, keys, sigType, cryptoType))
{ {
if(matchTunnels) localDestination = FindLocalDestination (k.GetPublic ()->GetIdentHash ());
localDestination = CreateNewMatchedTunnelDestination(k, dest, &options); if (!localDestination)
else {
localDestination = CreateNewLocalDestination (k, type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT, &options); if(matchTunnels)
localDestination = CreateNewMatchedTunnelDestination(k, dest, &options);
else
localDestination = CreateNewLocalDestination (k, type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT, &options);
destinations[keys] = localDestination;
}
} }
} }
} }
@ -661,6 +672,8 @@ namespace client
// optional params // optional params
int inPort = section.second.get (I2P_SERVER_TUNNEL_INPORT, 0); int inPort = section.second.get (I2P_SERVER_TUNNEL_INPORT, 0);
std::string accessList = section.second.get (I2P_SERVER_TUNNEL_ACCESS_LIST, ""); std::string accessList = section.second.get (I2P_SERVER_TUNNEL_ACCESS_LIST, "");
if(accessList == "")
accessList=section.second.get (I2P_SERVER_TUNNEL_WHITE_LIST, "");
std::string hostOverride = section.second.get (I2P_SERVER_TUNNEL_HOST_OVERRIDE, ""); std::string hostOverride = section.second.get (I2P_SERVER_TUNNEL_HOST_OVERRIDE, "");
std::string webircpass = section.second.get<std::string> (I2P_SERVER_TUNNEL_WEBIRC_PASSWORD, ""); std::string webircpass = section.second.get<std::string> (I2P_SERVER_TUNNEL_WEBIRC_PASSWORD, "");
bool gzip = section.second.get (I2P_SERVER_TUNNEL_GZIP, true); bool gzip = section.second.get (I2P_SERVER_TUNNEL_GZIP, true);
@ -675,12 +688,21 @@ namespace client
ReadI2CPOptions (section, options); ReadI2CPOptions (section, options);
std::shared_ptr<ClientDestination> localDestination = nullptr; std::shared_ptr<ClientDestination> localDestination = nullptr;
i2p::data::PrivateKeys k; auto it = destinations.find (keys);
if(!LoadPrivateKeys (k, keys, sigType, cryptoType)) if (it != destinations.end ())
continue; localDestination = it->second;
localDestination = FindLocalDestination (k.GetPublic ()->GetIdentHash ()); else
if (!localDestination) {
localDestination = CreateNewLocalDestination (k, true, &options); i2p::data::PrivateKeys k;
if(!LoadPrivateKeys (k, keys, sigType, cryptoType))
continue;
localDestination = FindLocalDestination (k.GetPublic ()->GetIdentHash ());
if (!localDestination)
{
localDestination = CreateNewLocalDestination (k, true, &options);
destinations[keys] = localDestination;
}
}
if (type == I2P_TUNNELS_SECTION_TYPE_UDPSERVER) if (type == I2P_TUNNELS_SECTION_TYPE_UDPSERVER)
{ {
// udp server tunnel // udp server tunnel

View File

@ -54,6 +54,7 @@ namespace client
const char I2P_SERVER_TUNNEL_SIGNATURE_TYPE[] = "signaturetype"; const char I2P_SERVER_TUNNEL_SIGNATURE_TYPE[] = "signaturetype";
const char I2P_SERVER_TUNNEL_INPORT[] = "inport"; const char I2P_SERVER_TUNNEL_INPORT[] = "inport";
const char I2P_SERVER_TUNNEL_ACCESS_LIST[] = "accesslist"; const char I2P_SERVER_TUNNEL_ACCESS_LIST[] = "accesslist";
const char I2P_SERVER_TUNNEL_WHITE_LIST[] = "whitelist";
const char I2P_SERVER_TUNNEL_GZIP[] = "gzip"; const char I2P_SERVER_TUNNEL_GZIP[] = "gzip";
const char I2P_SERVER_TUNNEL_WEBIRC_PASSWORD[] = "webircpassword"; const char I2P_SERVER_TUNNEL_WEBIRC_PASSWORD[] = "webircpassword";
const char I2P_SERVER_TUNNEL_ADDRESS[] = "address"; const char I2P_SERVER_TUNNEL_ADDRESS[] = "address";

View File

@ -55,12 +55,22 @@ namespace client
void I2CPDestination::SetEncryptionPrivateKey (const uint8_t * key) void I2CPDestination::SetEncryptionPrivateKey (const uint8_t * key)
{ {
memcpy (m_EncryptionPrivateKey, key, 256); m_Decryptor = i2p::data::PrivateKeys::CreateDecryptor (m_Identity->GetCryptoKeyType (), key);
m_Decryptor = i2p::data::PrivateKeys::CreateDecryptor (m_Identity->GetCryptoKeyType (), m_EncryptionPrivateKey); }
void I2CPDestination::SetECIESx25519EncryptionPrivateKey (const uint8_t * key)
{
if (!m_ECIESx25519Decryptor || memcmp (m_ECIESx25519PrivateKey, key, 32)) // new key?
{
m_ECIESx25519Decryptor = std::make_shared<i2p::crypto::ECIESX25519AEADRatchetDecryptor>(key, true); // calculate public
memcpy (m_ECIESx25519PrivateKey, key, 32);
}
} }
bool I2CPDestination::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, i2p::data::CryptoKeyType preferredCrypto) const bool I2CPDestination::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, i2p::data::CryptoKeyType preferredCrypto) const
{ {
if (preferredCrypto == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET && m_ECIESx25519Decryptor)
return m_ECIESx25519Decryptor->Decrypt (encrypted, data, ctx, true);
if (m_Decryptor) if (m_Decryptor)
return m_Decryptor->Decrypt (encrypted, data, ctx, true); return m_Decryptor->Decrypt (encrypted, data, ctx, true);
else else
@ -68,6 +78,19 @@ namespace client
return false; return false;
} }
const uint8_t * I2CPDestination::GetEncryptionPublicKey (i2p::data::CryptoKeyType keyType) const
{
if (keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET && m_ECIESx25519Decryptor)
return m_ECIESx25519Decryptor->GetPubicKey ();
return nullptr;
}
bool I2CPDestination::SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const
{
return keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET ? (bool)m_ECIESx25519Decryptor : m_EncryptionKeyType == keyType;
}
void I2CPDestination::HandleDataMessage (const uint8_t * buf, size_t len) void I2CPDestination::HandleDataMessage (const uint8_t * buf, size_t len)
{ {
uint32_t length = bufbe32toh (buf); uint32_t length = bufbe32toh (buf);
@ -77,7 +100,8 @@ namespace client
void I2CPDestination::CreateNewLeaseSet (std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels) void I2CPDestination::CreateNewLeaseSet (std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels)
{ {
i2p::data::LocalLeaseSet ls (m_Identity, m_EncryptionPrivateKey, tunnels); // we don't care about encryption key uint8_t priv[256] = {0};
i2p::data::LocalLeaseSet ls (m_Identity, priv, tunnels); // we don't care about encryption key, we need leases only
m_LeaseSetExpirationTime = ls.GetExpirationTime (); m_LeaseSetExpirationTime = ls.GetExpirationTime ();
uint8_t * leases = ls.GetLeases (); uint8_t * leases = ls.GetLeases ();
leases[-1] = tunnels.size (); leases[-1] = tunnels.size ();
@ -572,27 +596,21 @@ namespace client
offset += ls.GetBufferLen (); offset += ls.GetBufferLen ();
// private keys // private keys
int numPrivateKeys = buf[offset]; offset++; int numPrivateKeys = buf[offset]; offset++;
uint16_t currentKeyType = 0;
const uint8_t * currentKey = nullptr;
for (int i = 0; i < numPrivateKeys; i++) for (int i = 0; i < numPrivateKeys; i++)
{ {
if (offset + 4 > len) return; if (offset + 4 > len) return;
uint16_t keyType = bufbe16toh (buf + offset); offset += 2; // encryption type uint16_t keyType = bufbe16toh (buf + offset); offset += 2; // encryption type
uint16_t keyLen = bufbe16toh (buf + offset); offset += 2; // private key length uint16_t keyLen = bufbe16toh (buf + offset); offset += 2; // private key length
if (offset + keyLen > len) return; if (offset + keyLen > len) return;
if (!currentKey || keyType > currentKeyType) if (keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET)
m_Destination->SetECIESx25519EncryptionPrivateKey (buf + offset);
else
{ {
currentKeyType = keyType; m_Destination->SetEncryptionType (keyType);
currentKey = buf + offset; m_Destination->SetEncryptionPrivateKey (buf + offset);
} }
offset += keyLen; offset += keyLen;
} }
// TODO: support multiple keys
if (currentKey)
{
m_Destination->SetEncryptionPrivateKey (currentKey);
m_Destination->SetEncryptionType (currentKeyType);
}
m_Destination->LeaseSet2Created (storeType, ls.GetBuffer (), ls.GetBufferLen ()); m_Destination->LeaseSet2Created (storeType, ls.GetBuffer (), ls.GetBufferLen ());
} }

View File

@ -75,14 +75,15 @@ namespace client
void SetEncryptionPrivateKey (const uint8_t * key); void SetEncryptionPrivateKey (const uint8_t * key);
void SetEncryptionType (i2p::data::CryptoKeyType keyType) { m_EncryptionKeyType = keyType; }; void SetEncryptionType (i2p::data::CryptoKeyType keyType) { m_EncryptionKeyType = keyType; };
void SetECIESx25519EncryptionPrivateKey (const uint8_t * key);
void LeaseSetCreated (const uint8_t * buf, size_t len); // called from I2CPSession void LeaseSetCreated (const uint8_t * buf, size_t len); // called from I2CPSession
void LeaseSet2Created (uint8_t storeType, const uint8_t * buf, size_t len); // called from I2CPSession void LeaseSet2Created (uint8_t storeType, const uint8_t * buf, size_t len); // called from I2CPSession
void SendMsgTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint32_t nonce); // called from I2CPSession void SendMsgTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint32_t nonce); // called from I2CPSession
// implements LocalDestination // implements LocalDestination
bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, i2p::data::CryptoKeyType preferredCrypto) const; bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, i2p::data::CryptoKeyType preferredCrypto) const;
bool SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const { return m_EncryptionKeyType == keyType; }; bool SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const;
// TODO: implement GetEncryptionPublicKey const uint8_t * GetEncryptionPublicKey (i2p::data::CryptoKeyType keyType) const; // for 4 only
std::shared_ptr<const i2p::data::IdentityEx> GetIdentity () const { return m_Identity; }; std::shared_ptr<const i2p::data::IdentityEx> GetIdentity () const { return m_Identity; };
protected: protected:
@ -101,9 +102,10 @@ namespace client
std::shared_ptr<I2CPSession> m_Owner; std::shared_ptr<I2CPSession> m_Owner;
std::shared_ptr<const i2p::data::IdentityEx> m_Identity; std::shared_ptr<const i2p::data::IdentityEx> m_Identity;
uint8_t m_EncryptionPrivateKey[256];
i2p::data::CryptoKeyType m_EncryptionKeyType; i2p::data::CryptoKeyType m_EncryptionKeyType;
std::shared_ptr<i2p::crypto::CryptoKeyDecryptor> m_Decryptor; std::shared_ptr<i2p::crypto::CryptoKeyDecryptor> m_Decryptor; // standard
std::shared_ptr<i2p::crypto::ECIESX25519AEADRatchetDecryptor> m_ECIESx25519Decryptor;
uint8_t m_ECIESx25519PrivateKey[32];
uint64_t m_LeaseSetExpirationTime; uint64_t m_LeaseSetExpirationTime;
}; };

View File

@ -611,10 +611,22 @@ namespace client
void I2PUDPServerTunnel::HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len) void I2PUDPServerTunnel::HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
{ {
std::lock_guard<std::mutex> lock(m_SessionsMutex); if (!m_LastSession || m_LastSession->Identity.GetLL()[0] != from.GetIdentHash ().GetLL()[0] || fromPort != m_LastSession->RemotePort)
auto session = ObtainUDPSession(from, toPort, fromPort); {
session->IPSocket.send_to(boost::asio::buffer(buf, len), m_RemoteEndpoint); std::lock_guard<std::mutex> lock(m_SessionsMutex);
session->LastActivity = i2p::util::GetMillisecondsSinceEpoch(); m_LastSession = ObtainUDPSession(from, toPort, fromPort);
}
m_LastSession->IPSocket.send_to(boost::asio::buffer(buf, len), m_RemoteEndpoint);
m_LastSession->LastActivity = i2p::util::GetMillisecondsSinceEpoch();
}
void I2PUDPServerTunnel::HandleRecvFromI2PRaw (uint16_t, uint16_t, const uint8_t * buf, size_t len)
{
if (m_LastSession)
{
m_LastSession->IPSocket.send_to(boost::asio::buffer(buf, len), m_RemoteEndpoint);
m_LastSession->LastActivity = i2p::util::GetMillisecondsSinceEpoch();
}
} }
void I2PUDPServerTunnel::ExpireStale(const uint64_t delta) { void I2PUDPServerTunnel::ExpireStale(const uint64_t delta) {
@ -634,7 +646,7 @@ namespace client
uint64_t now = i2p::util::GetMillisecondsSinceEpoch(); uint64_t now = i2p::util::GetMillisecondsSinceEpoch();
std::vector<uint16_t> removePorts; std::vector<uint16_t> removePorts;
for (const auto & s : m_Sessions) { for (const auto & s : m_Sessions) {
if (now - s.second.second >= delta) if (now - s.second->second >= delta)
removePorts.push_back(s.first); removePorts.push_back(s.first);
} }
for(auto port : removePorts) { for(auto port : removePorts) {
@ -647,7 +659,7 @@ namespace client
auto ih = from.GetIdentHash(); auto ih = from.GetIdentHash();
for (auto & s : m_Sessions ) for (auto & s : m_Sessions )
{ {
if ( s->Identity == ih) if (s->Identity.GetLL()[0] == ih.GetLL()[0] && remotePort == s->RemotePort)
{ {
/** found existing session */ /** found existing session */
LogPrint(eLogDebug, "UDPServer: found session ", s->IPSocket.local_endpoint(), " ", ih.ToBase32()); LogPrint(eLogDebug, "UDPServer: found session ", s->IPSocket.local_endpoint(), " ", ih.ToBase32());
@ -680,6 +692,7 @@ namespace client
LocalPort(ourPort), LocalPort(ourPort),
RemotePort(theirPort) RemotePort(theirPort)
{ {
IPSocket.set_option (boost::asio::socket_base::receive_buffer_size (I2P_UDP_MAX_MTU ));
memcpy(Identity, to->data(), 32); memcpy(Identity, to->data(), 32);
Receive(); Receive();
} }
@ -695,12 +708,30 @@ namespace client
if(!ecode) if(!ecode)
{ {
LogPrint(eLogDebug, "UDPSession: forward ", len, "B from ", FromEndpoint); LogPrint(eLogDebug, "UDPSession: forward ", len, "B from ", FromEndpoint);
LastActivity = i2p::util::GetMillisecondsSinceEpoch(); auto ts = i2p::util::GetMillisecondsSinceEpoch();
m_Destination->SendDatagramTo(m_Buffer, len, Identity, LocalPort, RemotePort); auto session = m_Destination->GetSession (Identity);
if (ts > LastActivity + I2P_UDP_REPLIABLE_DATAGRAM_INTERVAL)
m_Destination->SendDatagram(session, m_Buffer, len, LocalPort, RemotePort);
else
m_Destination->SendRawDatagram(session, m_Buffer, len, LocalPort, RemotePort);
size_t numPackets = 0;
while (numPackets < i2p::datagram::DATAGRAM_SEND_QUEUE_MAX_SIZE)
{
boost::system::error_code ec;
size_t moreBytes = IPSocket.available(ec);
if (ec || !moreBytes) break;
len = IPSocket.receive_from (boost::asio::buffer (m_Buffer, I2P_UDP_MAX_MTU), FromEndpoint, 0, ec);
m_Destination->SendRawDatagram (session, m_Buffer, len, LocalPort, RemotePort);
numPackets++;
}
if (numPackets > 0)
LogPrint(eLogDebug, "UDPSession: forward more ", numPackets, "packets B from ", FromEndpoint);
m_Destination->FlushSendQueue (session);
LastActivity = ts;
Receive(); Receive();
} else {
LogPrint(eLogError, "UDPSession: ", ecode.message());
} }
else
LogPrint(eLogError, "UDPSession: ", ecode.message());
} }
I2PUDPServerTunnel::I2PUDPServerTunnel(const std::string & name, std::shared_ptr<i2p::client::ClientDestination> localDestination, I2PUDPServerTunnel::I2PUDPServerTunnel(const std::string & name, std::shared_ptr<i2p::client::ClientDestination> localDestination,
@ -714,6 +745,7 @@ namespace client
m_LocalDest->Start(); m_LocalDest->Start();
auto dgram = m_LocalDest->CreateDatagramDestination(gzip); auto dgram = m_LocalDest->CreateDatagramDestination(gzip);
dgram->SetReceiver(std::bind(&I2PUDPServerTunnel::HandleRecvFromI2P, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5)); dgram->SetReceiver(std::bind(&I2PUDPServerTunnel::HandleRecvFromI2P, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5));
dgram->SetRawReceiver(std::bind(&I2PUDPServerTunnel::HandleRecvFromI2PRaw, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
} }
I2PUDPServerTunnel::~I2PUDPServerTunnel() I2PUDPServerTunnel::~I2PUDPServerTunnel()
@ -761,14 +793,18 @@ namespace client
m_RemoteIdent(nullptr), m_RemoteIdent(nullptr),
m_ResolveThread(nullptr), m_ResolveThread(nullptr),
m_LocalSocket(localDestination->GetService(), localEndpoint), m_LocalSocket(localDestination->GetService(), localEndpoint),
RemotePort(remotePort), RemotePort(remotePort), m_LastPort (0),
m_cancel_resolve(false) m_cancel_resolve(false)
{ {
m_LocalSocket.set_option (boost::asio::socket_base::receive_buffer_size (I2P_UDP_MAX_MTU ));
auto dgram = m_LocalDest->CreateDatagramDestination(gzip); auto dgram = m_LocalDest->CreateDatagramDestination(gzip);
dgram->SetReceiver(std::bind(&I2PUDPClientTunnel::HandleRecvFromI2P, this, dgram->SetReceiver(std::bind(&I2PUDPClientTunnel::HandleRecvFromI2P, this,
std::placeholders::_1, std::placeholders::_2, std::placeholders::_1, std::placeholders::_2,
std::placeholders::_3, std::placeholders::_4, std::placeholders::_3, std::placeholders::_4,
std::placeholders::_5)); std::placeholders::_5));
dgram->SetRawReceiver(std::bind(&I2PUDPClientTunnel::HandleRecvFromI2PRaw, this,
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
} }
void I2PUDPClientTunnel::Start() { void I2PUDPClientTunnel::Start() {
@ -796,16 +832,45 @@ namespace client
return; // drop, remote not resolved return; // drop, remote not resolved
} }
auto remotePort = m_RecvEndpoint.port(); auto remotePort = m_RecvEndpoint.port();
auto itr = m_Sessions.find(remotePort); if (!m_LastPort || m_LastPort != remotePort)
if (itr == m_Sessions.end()) { {
// track new udp convo auto itr = m_Sessions.find(remotePort);
m_Sessions[remotePort] = {boost::asio::ip::udp::endpoint(m_RecvEndpoint), 0}; if (itr != m_Sessions.end())
m_LastSession = itr->second;
else
{
m_LastSession = std::make_shared<UDPConvo>(boost::asio::ip::udp::endpoint(m_RecvEndpoint), 0);
m_Sessions.emplace (remotePort, m_LastSession);
}
m_LastPort = remotePort;
} }
// send off to remote i2p destination // send off to remote i2p destination
auto ts = i2p::util::GetMillisecondsSinceEpoch();
LogPrint(eLogDebug, "UDP Client: send ", transferred, " to ", m_RemoteIdent->ToBase32(), ":", RemotePort); LogPrint(eLogDebug, "UDP Client: send ", transferred, " to ", m_RemoteIdent->ToBase32(), ":", RemotePort);
m_LocalDest->GetDatagramDestination()->SendDatagramTo(m_RecvBuff, transferred, *m_RemoteIdent, remotePort, RemotePort); auto session = m_LocalDest->GetDatagramDestination()->GetSession (*m_RemoteIdent);
if (ts > m_LastSession->second + I2P_UDP_REPLIABLE_DATAGRAM_INTERVAL)
m_LocalDest->GetDatagramDestination()->SendDatagram (session, m_RecvBuff, transferred, remotePort, RemotePort);
else
m_LocalDest->GetDatagramDestination()->SendRawDatagram (session, m_RecvBuff, transferred, remotePort, RemotePort);
size_t numPackets = 0;
while (numPackets < i2p::datagram::DATAGRAM_SEND_QUEUE_MAX_SIZE)
{
boost::system::error_code ec;
size_t moreBytes = m_LocalSocket.available(ec);
if (ec || !moreBytes) break;
transferred = m_LocalSocket.receive_from (boost::asio::buffer (m_RecvBuff, I2P_UDP_MAX_MTU), m_RecvEndpoint, 0, ec);
remotePort = m_RecvEndpoint.port();
// TODO: check remotePort
m_LocalDest->GetDatagramDestination()->SendRawDatagram (session, m_RecvBuff, transferred, remotePort, RemotePort);
numPackets++;
}
if (numPackets)
LogPrint(eLogDebug, "UDP Client: sent ", numPackets, " more packets to ", m_RemoteIdent->ToBase32());
m_LocalDest->GetDatagramDestination()->FlushSendQueue (session);
// mark convo as active // mark convo as active
m_Sessions[remotePort].second = i2p::util::GetMillisecondsSinceEpoch(); if (m_LastSession)
m_LastSession->second = ts;
RecvFromLocal(); RecvFromLocal();
} }
@ -843,26 +908,30 @@ namespace client
void I2PUDPClientTunnel::HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len) void I2PUDPClientTunnel::HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
{ {
if(m_RemoteIdent && from.GetIdentHash() == *m_RemoteIdent) if(m_RemoteIdent && from.GetIdentHash() == *m_RemoteIdent)
{ HandleRecvFromI2PRaw (fromPort, toPort, buf, len);
auto itr = m_Sessions.find(toPort);
// found convo ?
if(itr != m_Sessions.end())
{
// found convo
if (len > 0) {
LogPrint(eLogDebug, "UDP Client: got ", len, "B from ", from.GetIdentHash().ToBase32());
m_LocalSocket.send_to(boost::asio::buffer(buf, len), itr->second.first);
// mark convo as active
itr->second.second = i2p::util::GetMillisecondsSinceEpoch();
}
}
else
LogPrint(eLogWarning, "UDP Client: not tracking udp session using port ", (int) toPort);
}
else else
LogPrint(eLogWarning, "UDP Client: unwarranted traffic from ", from.GetIdentHash().ToBase32()); LogPrint(eLogWarning, "UDP Client: unwarranted traffic from ", from.GetIdentHash().ToBase32());
} }
void I2PUDPClientTunnel::HandleRecvFromI2PRaw(uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
{
auto itr = m_Sessions.find(toPort);
// found convo ?
if(itr != m_Sessions.end())
{
// found convo
if (len > 0)
{
LogPrint(eLogDebug, "UDP Client: got ", len, "B from ", m_RemoteIdent ? m_RemoteIdent->ToBase32() : "");
m_LocalSocket.send_to(boost::asio::buffer(buf, len), itr->second->first);
// mark convo as active
itr->second->second = i2p::util::GetMillisecondsSinceEpoch();
}
}
else
LogPrint(eLogWarning, "UDP Client: not tracking udp session using port ", (int) toPort);
}
I2PUDPClientTunnel::~I2PUDPClientTunnel() { I2PUDPClientTunnel::~I2PUDPClientTunnel() {
auto dgram = m_LocalDest->GetDatagramDestination(); auto dgram = m_LocalDest->GetDatagramDestination();
if (dgram) dgram->ResetReceiver(); if (dgram) dgram->ResetReceiver();

View File

@ -165,9 +165,10 @@ namespace client
/** 2 minute timeout for udp sessions */ /** 2 minute timeout for udp sessions */
const uint64_t I2P_UDP_SESSION_TIMEOUT = 1000 * 60 * 2; const uint64_t I2P_UDP_SESSION_TIMEOUT = 1000 * 60 * 2;
const uint64_t I2P_UDP_REPLIABLE_DATAGRAM_INTERVAL = 100; // in milliseconds
/** max size for i2p udp */ /** max size for i2p udp */
const size_t I2P_UDP_MAX_MTU = i2p::datagram::MAX_DATAGRAM_SIZE; const size_t I2P_UDP_MAX_MTU = 64*1024;
struct UDPSession struct UDPSession
{ {
@ -237,6 +238,7 @@ namespace client
private: private:
void HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); void HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
void HandleRecvFromI2PRaw (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
UDPSessionPtr ObtainUDPSession(const i2p::data::IdentityEx& from, uint16_t localPort, uint16_t remotePort); UDPSessionPtr ObtainUDPSession(const i2p::data::IdentityEx& from, uint16_t localPort, uint16_t remotePort);
private: private:
@ -248,6 +250,7 @@ namespace client
std::mutex m_SessionsMutex; std::mutex m_SessionsMutex;
std::vector<UDPSessionPtr> m_Sessions; std::vector<UDPSessionPtr> m_Sessions;
std::shared_ptr<i2p::client::ClientDestination> m_LocalDest; std::shared_ptr<i2p::client::ClientDestination> m_LocalDest;
UDPSessionPtr m_LastSession;
}; };
class I2PUDPClientTunnel class I2PUDPClientTunnel
@ -273,10 +276,14 @@ namespace client
void RecvFromLocal(); void RecvFromLocal();
void HandleRecvFromLocal(const boost::system::error_code & e, std::size_t transferred); void HandleRecvFromLocal(const boost::system::error_code & e, std::size_t transferred);
void HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); void HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
void HandleRecvFromI2PRaw(uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
void TryResolving(); void TryResolving();
private:
const std::string m_Name; const std::string m_Name;
std::mutex m_SessionsMutex; std::mutex m_SessionsMutex;
std::map<uint16_t, UDPConvo > m_Sessions; // maps i2p port -> local udp convo std::unordered_map<uint16_t, std::shared_ptr<UDPConvo> > m_Sessions; // maps i2p port -> local udp convo
const std::string m_RemoteDest; const std::string m_RemoteDest;
std::shared_ptr<i2p::client::ClientDestination> m_LocalDest; std::shared_ptr<i2p::client::ClientDestination> m_LocalDest;
const boost::asio::ip::udp::endpoint m_LocalEndpoint; const boost::asio::ip::udp::endpoint m_LocalEndpoint;
@ -285,8 +292,9 @@ namespace client
boost::asio::ip::udp::socket m_LocalSocket; boost::asio::ip::udp::socket m_LocalSocket;
boost::asio::ip::udp::endpoint m_RecvEndpoint; boost::asio::ip::udp::endpoint m_RecvEndpoint;
uint8_t m_RecvBuff[I2P_UDP_MAX_MTU]; uint8_t m_RecvBuff[I2P_UDP_MAX_MTU];
uint16_t RemotePort; uint16_t RemotePort, m_LastPort;
bool m_cancel_resolve; bool m_cancel_resolve;
std::shared_ptr<UDPConvo> m_LastSession;
}; };
class I2PServerTunnel: public I2PService class I2PServerTunnel: public I2PService

View File

@ -494,7 +494,7 @@ namespace client
context.GetAddressBook().InsertFullAddress(dest); context.GetAddressBook().InsertFullAddress(dest);
auto leaseSet = session->localDestination->FindLeaseSet(dest->GetIdentHash()); auto leaseSet = session->localDestination->FindLeaseSet(dest->GetIdentHash());
if (leaseSet) if (leaseSet)
Connect(leaseSet); Connect(leaseSet, session);
else else
{ {
session->localDestination->RequestDestination(dest->GetIdentHash(), session->localDestination->RequestDestination(dest->GetIdentHash(),
@ -509,18 +509,25 @@ namespace client
SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true); SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true);
} }
void SAMSocket::Connect (std::shared_ptr<const i2p::data::LeaseSet> remote) void SAMSocket::Connect (std::shared_ptr<const i2p::data::LeaseSet> remote, std::shared_ptr<SAMSession> session)
{ {
auto session = m_Owner.FindSession(m_ID); if (!session) session = m_Owner.FindSession(m_ID);
if(session) if (session)
{ {
m_SocketType = eSAMSocketTypeStream; m_SocketType = eSAMSocketTypeStream;
m_Stream = session->localDestination->CreateStream (remote); m_Stream = session->localDestination->CreateStream (remote);
m_Stream->Send ((uint8_t *)m_Buffer, m_BufferOffset); // connect and send if (m_Stream)
m_BufferOffset = 0; {
I2PReceive (); m_Stream->Send ((uint8_t *)m_Buffer, m_BufferOffset); // connect and send
SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false); m_BufferOffset = 0;
I2PReceive ();
SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false);
}
else
SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true);
} }
else
SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true);
} }
void SAMSocket::HandleConnectLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet) void SAMSocket::HandleConnectLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet)
@ -1015,8 +1022,8 @@ namespace client
{ {
{"DSA_SHA1", i2p::data::SIGNING_KEY_TYPE_DSA_SHA1}, {"DSA_SHA1", i2p::data::SIGNING_KEY_TYPE_DSA_SHA1},
{"ECDSA_SHA256_P256", i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256}, {"ECDSA_SHA256_P256", i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256},
{"ECDSA_SHA256_P384", i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA384_P384}, {"ECDSA_SHA384_P384", i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA384_P384},
{"ECDSA_SHA256_P521", i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA512_P521}, {"ECDSA_SHA512_P521", i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA512_P521},
{"EdDSA_SHA512_Ed25519", i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519}, {"EdDSA_SHA512_Ed25519", i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519},
{"GOST_GOSTR3411256_GOSTR3410CRYPTOPROA", i2p::data::SIGNING_KEY_TYPE_GOSTR3410_CRYPTO_PRO_A_GOSTR3411_256}, {"GOST_GOSTR3411256_GOSTR3410CRYPTOPROA", i2p::data::SIGNING_KEY_TYPE_GOSTR3410_CRYPTO_PRO_A_GOSTR3411_256},
{"GOST_GOSTR3411512_GOSTR3410TC26A512", i2p::data::SIGNING_KEY_TYPE_GOSTR3410_TC26_A_512_GOSTR3411_512}, {"GOST_GOSTR3411512_GOSTR3410TC26A512", i2p::data::SIGNING_KEY_TYPE_GOSTR3410_TC26_A_512_GOSTR3411_512},

View File

@ -134,7 +134,7 @@ namespace client
size_t ProcessDatagramSend (char * buf, size_t len, const char * data); // from SAM 1.0 size_t ProcessDatagramSend (char * buf, size_t len, const char * data); // from SAM 1.0
void ExtractParams (char * buf, std::map<std::string, std::string>& params); void ExtractParams (char * buf, std::map<std::string, std::string>& params);
void Connect (std::shared_ptr<const i2p::data::LeaseSet> remote); void Connect (std::shared_ptr<const i2p::data::LeaseSet> remote, std::shared_ptr<SAMSession> session = nullptr);
void HandleConnectLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet); void HandleConnectLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet);
void SendNamingLookupReply (std::shared_ptr<const i2p::data::IdentityEx> identity); void SendNamingLookupReply (std::shared_ptr<const i2p::data::IdentityEx> identity);
void HandleNamingLookupLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet, std::string name); void HandleNamingLookupLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet, std::string name);

View File

@ -35,6 +35,7 @@
<translation type="qt" /> <translation type="qt" />
<releases> <releases>
<release version="2.33.0" date="2020-08-24" />
<release version="2.32.1" date="2020-06-02" /> <release version="2.32.1" date="2020-06-02" />
<release version="2.32.0" date="2020-05-25" /> <release version="2.32.0" date="2020-05-25" />
<release version="2.31.0" date="2020-04-10" /> <release version="2.31.0" date="2020-04-10" />