Compare commits

..

114 Commits

Author SHA1 Message Date
orignal
3b8baa85a3 2.25.0 2019-05-09 10:21:11 -04:00
R4SAS
73921b1024 fix ipv6 fallback address 2019-05-08 00:45:52 +03:00
R4SAS
ece140f18c [httpproxy] make addresshelper support configurable for every httpproxy 2019-04-25 23:06:14 +03:00
orignal
5e42947fbd always lookup SSU session if peer's endpoint doesn't match 2019-04-25 12:54:44 -04:00
orignal
1bfb9b02f5 make sure remote endpoint matches stored with 2019-04-24 11:40:58 -04:00
r4sas
16a14c2b76 [android] set datadir path from system environment 2019-04-20 19:47:06 +00:00
orignal
f6199c6c17 print store hash for encrypted LeaseSet 2019-04-20 09:44:16 -04:00
orignal
d7e7f06e88 re-request encrypted LeaseSet 2019-04-17 15:53:07 -04:00
orignal
4c4e856a1a ntcp2.addressv6 parameter 2019-04-17 14:40:00 -04:00
orignal
07bbbbaf61 fixed gcc 4.7 build 2019-04-17 12:42:43 -04:00
orignal
3236827781 add/removed NTCP addresses 2019-04-16 21:04:04 -04:00
orignal
0be664cc3d publish NTCP2 address instead NTCP if NTCP is disabled 2019-04-15 16:32:16 -04:00
orignal
6cc6849ccc use published timestamp for blinding 2019-04-12 14:05:07 -04:00
orignal
5d5cd71714 limit expiration by next midnight for encrypted LS2 2019-04-12 11:13:46 -04:00
orignal
d248343517 Handle CreateLeaseSet2 I2CP message for encrypted leasesets 2019-04-11 14:06:53 -04:00
orignal
64d800427f allow HTTP headers without value 2019-04-10 15:25:09 -04:00
orignal
c4c896a833 publish encrypted LS2 2019-04-10 12:04:19 -04:00
orignal
b6b5bb3f75 publish LeaseSet with store hash 2019-04-09 15:36:10 -04:00
orignal
5d69bb7383 correct ecrypted LS2 layout 2019-04-09 10:34:05 -04:00
orignal
76e222079a Merge branch 'openssl' of https://github.com/PurpleI2P/i2pd into openssl 2019-04-09 09:21:47 -04:00
orignal
73abb9278d correct ecrypted LS2 size 2019-04-09 09:21:38 -04:00
orignal
8fd843e7ce Merge pull request #1334 from rex4539/fix-typos
Fix typos
2019-04-08 17:36:55 -04:00
Dimitris Apostolou
6a497a23d9 Fix typos 2019-04-08 22:22:42 +03:00
orignal
3ac74e1091 create encrypted LS2 2019-04-08 13:27:21 -04:00
orignal
ef0fb48f1f blind private keys 2019-04-05 16:03:58 -04:00
orignal
414ef2bc3d fixed gcc 4.7 build 2019-04-04 16:18:52 -04:00
orignal
ea791309ad moved credential and blinding to BlindedPublicKey 2019-04-02 16:32:18 -04:00
orignal
706da6e431 allow .b32.i2p in jump links 2019-04-02 13:11:49 -04:00
R4SAS
ed116e7cea add gcc 9 support 2019-03-30 13:05:32 +03:00
orignal
5b56f4007b counter is always in Little Endian 2019-03-29 18:45:31 -04:00
orignal
e2071542bf use 16 bytes iv for chacha20 2019-03-29 16:18:51 -04:00
orignal
cdb217b774 always initialize m_Pkey 2019-03-29 13:15:32 -04:00
orignal
079798940b lookup for b33 address 2019-03-29 11:59:59 -04:00
orignal
f1c24689bf fixed #1319. send correct response 2019-03-29 09:29:28 -04:00
orignal
1f9cf6ed7c show lookup keys 2019-03-28 19:00:56 -04:00
orignal
43f218410f correct b33 address length threshold 2019-03-28 18:48:38 -04:00
orignal
3fd9d5f641 save b33 addresses 2019-03-28 16:06:53 -04:00
orignal
f5ab8f2062 replaced GetIdentHash by GetAddress 2019-03-28 12:19:19 -04:00
orignal
8774a8fbc2 handle b33 addresses in I2P tunnels 2019-03-28 10:17:03 -04:00
orignal
6f4f0f03d2 handle b33 addresses in I2P tunnels 2019-03-28 09:57:34 -04:00
orignal
00b5fdce03 create stream to blinded dest 2019-03-27 16:04:46 -04:00
orignal
baee6a0d91 generic address for AdressBook 2019-03-27 15:19:10 -04:00
orignal
ff44bcc489 complete implementation of RedDSA 2019-03-24 18:42:52 -04:00
R4SAS
c797ac4268 Update README.md 2019-03-23 03:25:10 +03:00
r4sas
d22a76d4d1 remove incorrect line 2019-03-22 23:40:59 +00:00
r4sas
a6642e0ebc add script for packaging archive with android binaries 2019-03-22 23:37:20 +00:00
orignal
3d4d260a34 extract b33 address 2019-03-22 16:04:47 -04:00
orignal
8e4b9da97d pass blinded key instead identity for encrypted LS2 2019-03-22 15:32:13 -04:00
l-n-s
2be80ba30f Fix Docker build 2019-03-22 13:14:02 -04:00
R4SAS
2e44c88d6c [2.24.0][android] update addressbook 2019-03-21 18:34:39 +03:00
orignal
21eb1ce6c9 2.24.0 2019-03-21 11:30:12 -04:00
orignal
cdfd411df7 2.24.0 2019-03-21 10:58:59 -04:00
R4SAS
a6149ca90c [android] upload gradlew script 2019-03-21 00:09:49 +03:00
R4SAS
642435486c [android] add gradle wrapper, update buildtools version, fixes in code. 2019-03-20 23:49:54 +03:00
orignal
fc84d6c4b7 remove unused timer 2019-03-17 21:37:42 -04:00
orignal
aa4bddd6ec common HKDF 2019-03-15 12:25:20 -04:00
orignal
8ec12a1b65 fixed race condition for publishing 2019-03-10 09:22:42 -04:00
orignal
0fbf552e95 lookup and handle encrypted LeaseSet2 2019-03-07 14:52:59 -05:00
orignal
09b1b120d7 update LeaseSet2 if store type changed 2019-03-07 14:51:05 -05:00
orignal
557244bc3f verify blinding key for encrypted LS2 2019-03-07 11:55:47 -05:00
orignal
24c5ed1cff calculate store hash for encrypted LeaseSet2 2019-03-06 16:08:04 -05:00
orignal
32e55ebd0c blind public key for encrypted LeaseSet2 2019-03-05 15:51:24 -05:00
orignal
ea3070d02b derivation of subcredentials for LeaseSet2 2019-03-05 12:41:01 -05:00
orignal
9aaba49a9f decrypt and handle Layer 2 of encrypted LeaseSet 2 2019-03-04 15:47:35 -05:00
orignal
9b64be07a9 set chacha20 counter to 1 2019-03-04 15:08:03 -05:00
r4sas
42c3c28ea7 [addressbook] reset eTags if addressbook can't be loaded 2019-03-04 18:29:29 +00:00
orignal
9e9236badb don't check TRANSIENT destination 2019-03-04 07:35:48 -05:00
orignal
560ebcec8d persist.addressbook parameter added 2019-03-01 14:42:20 -05:00
orignal
9b1fe4338b reuse_address for ipv6 acceptor 2019-02-28 16:00:26 -05:00
orignal
9188e3ad3f ChaCha20 decrypt 2019-02-28 13:31:51 -05:00
orignal
af65af5be9 H and HKDF for encrypted LeaseSet2 2019-02-27 15:52:47 -05:00
orignal
2f0115c300 handle RedDSA as EdDSA 2019-02-27 13:18:09 -05:00
orignal
0646461342 check published timestamp for LeaseSet2 2019-02-26 16:20:24 -05:00
orignal
ec30ec0996 Merge pull request #1304 from lifecoder-phoenix/openssl
Fix #1257
2019-02-25 07:01:33 -05:00
Life Coder
cdecb7a43c Fix #1257 2019-02-25 10:10:09 +01:00
Life Coder
aa9c1b66a0 Fix #1257 2019-02-25 09:57:18 +01:00
orignal
846eac29dc filter out unspecified addresses. Check floodfill status change 2019-02-24 18:26:58 -05:00
orignal
0f9e3c5b33 fix crash if public key is null 2019-02-22 13:17:43 -05:00
orignal
aa27746982 remove address string 2019-02-22 11:03:31 -05:00
R4SAS
d8a4954bf1 [NetDb] check PersistProfiles on load
* tabulation fixes
2019-02-22 18:37:32 +03:00
orignal
d40a029dae eliminate extra copy 2019-02-20 12:36:05 -05:00
orignal
96d961c393 correct public key for EdDSA trasient key 2019-02-15 15:03:58 -05:00
orignal
7b6814e32d correct flags 2019-02-14 21:22:49 -05:00
orignal
6fee2d3536 correct options szie 2019-02-14 17:49:23 -05:00
orignal
636fc633d4 send offline signature in streaming 2019-02-14 12:11:25 -05:00
orignal
72a239838e publish offline signature 2019-02-12 14:56:39 -05:00
orignal
a463dbc5fb Merge pull request #1295 from l-n-s/websocket_support
Support websocket connections over HTTP proxy
2019-02-12 12:30:44 -05:00
l-n-s
016ae3b9e9 rewrite for efficiency 2019-02-12 11:20:54 -05:00
R4SAS
7d0d421724 [windows] handle unexpected conditions (#1185) 2019-02-12 04:27:09 +03:00
R4SAS
83b5856a19 fix overflow warning, fix little typos 2019-02-12 03:09:29 +03:00
l-n-s
f617b27110 Support websocket connections over HTTP proxy 2019-02-11 17:18:01 -05:00
R4SAS
a91a0263cf update outproxy user-agent header rewrite 2019-02-12 00:51:47 +03:00
orignal
80ffe13f3e correct offline signature layout 2019-02-08 15:12:51 -05:00
orignal
1eb726c9bb create offline keys 2019-02-08 12:19:51 -05:00
orignal
1fa3ba8b42 read offline info 2019-02-07 16:04:31 -05:00
orignal
b6bfd66a49 use identity from LeaseSet 2019-02-06 21:19:44 -05:00
R4SAS
1be0e7ddaa [windows] add functional
* check tunnels count on graceful shutdown
* add tray menu item for accept/decline transit tunnels
2019-02-07 02:02:28 +03:00
orignal
2cac9b03ff common code for offline signatures 2019-02-06 13:36:03 -05:00
R4SAS
f5f4190803 catch error 10045 on stopping SAM acceptor (#1233), fix warning in util 2019-02-06 03:03:37 +03:00
R4SAS
a14d554947 fix tray icon disappearing, var type warning, code tabulation 2019-02-06 00:24:01 +03:00
orignal
6d9e5147b5 handle offline signature 2019-02-05 15:32:18 -05:00
R4SAS
841452cb9e Merge pull request #1292 from PurpleI2P/inet_pton_xp
inet_pton for winxp
2019-02-05 17:36:36 +03:00
R4SAS
9c76368dbc inet_pton for winxp 2019-02-05 14:13:23 +03:00
orignal
bd5122c6ea fixed build error 2019-02-01 17:41:12 -05:00
orignal
6643258618 implement Update for LeaseSet2 2019-02-01 12:55:13 -05:00
orignal
bc3f02cb6b fix #1290. copy correct size if message didn't fit previous 2019-01-31 16:03:10 -05:00
orignal
d848ae332a encryption keys priority 2019-01-30 14:10:40 -05:00
orignal
08ddc98303 initial LeaseSet2 support in I2CP 2019-01-29 11:30:31 -05:00
orignal
a3344c4290 resolve SIGNATURE_TYPE string values 2019-01-23 10:52:17 -05:00
orignal
22c1ce3ea5 don't pick port 9150 (Tor browser) 2019-01-23 09:53:30 -05:00
R4SAS
afb14e6782 [fedora] fix build in release on fc30+
fixes #1284
2019-01-22 04:57:53 +03:00
R4SAS
e177363377 [fedora] specify srcdir only if building at 30+ 2019-01-22 04:08:52 +03:00
R4SAS
ce213934c9 try fix build in fedora rawhide 2019-01-22 02:33:44 +03:00
R4SAS
af286ec52e try fix build in fedora rawhide 2019-01-22 02:32:51 +03:00
92 changed files with 3549 additions and 1898 deletions

View File

@@ -1,6 +1,42 @@
# for this file format description,
# see https://github.com/olivierlacan/keep-a-changelog
## [2.25.0] - 2019-05-09
### Added
- Create, publish and handle encrypted LeaseSet2
- Support of b33 addresses
- RedDSA key blinding
- .b32.i2p addresses in jump links
- ntcp2.addressv6 parameter
### Changed
- Allow HTTP headers without value
- Set data directory from external storage path for Android
- addresshelper support is configurable per tunnel
- gradlew script for android build
### Fixed
- Deletion of expired encrypted LeaseSet2 on floodfills
- ipv6 fallback address
- SSU incoming packets routing
## [2.24.0] - 2019-03-21
### Added
- Support of transient keys for LeaseSet2
- Support of encrypted LeaseSet2
- Recognize signature type 11 (RedDSA)
- Support websocket connections over HTTP proxy
- Ability to disable full addressbook persist
### Changed
- Don't load peer profiles if non-persistant
- REUSE_ADDR for ipv6 acceptors
- Reset eTags if addressbook can't be loaded
### Fixed
- Build with boost 1.70
- Filter out unspecified addresses from RouterInfo
- Check floodfill status change
- Correct SAM response for invalid key
- SAM crash on termination for Windows
- Race condition for publishing
## [2.23.0] - 2019-01-21
### Added
- Standard LeaseSet2 support

View File

@@ -19,10 +19,7 @@ else ifeq ($(shell expr match ${CXXVER} "4\.[7-9]"),3) # >= 4.7
NEEDED_CXXFLAGS += -std=c++11 -D_GLIBCXX_USE_NANOSLEEP=1
else ifeq ($(shell expr match ${CXXVER} "4\.6"),3) # = 4.6
NEEDED_CXXFLAGS += -std=c++0x
else ifeq ($(shell expr match ${CXXVER} "[5-7]\.[0-9]"),3) # gcc >= 5.0
NEEDED_CXXFLAGS += -std=c++11
LDLIBS = -latomic
else ifeq ($(shell expr match ${CXXVER} "[7-8]"),1) # gcc 7 ubuntu or gcc 8 arch
else ifeq ($(shell expr match ${CXXVER} "[5-9]"),1) # gcc >= 5
NEEDED_CXXFLAGS += -std=c++11
LDLIBS = -latomic
else # not supported

View File

@@ -1,3 +1,6 @@
![GitHub release](https://img.shields.io/github/release/PurpleI2P/i2pd.svg?label=latest%20release)
![GitHub](https://img.shields.io/github/license/PurpleI2P/i2pd.svg)
i2pd
====
@@ -38,8 +41,12 @@ Resources
Installing
----------
The easiest way to install i2pd is by using
[precompiled binaries](https://github.com/PurpleI2P/i2pd/releases/latest).
The easiest way to install i2pd is by using precompiled packages and binaries.
You can fetch most of them on [release](https://github.com/PurpleI2P/i2pd/releases/latest) page.
Please see [documentation](https://i2pd.readthedocs.io/en/latest/user-guide/install/) for more info.
Building
--------
See [documentation](https://i2pd.readthedocs.io/en/latest/) for how to build
i2pd from source on your OS.
@@ -54,11 +61,11 @@ Build instructions:
**Supported systems:**
* GNU/Linux x86/x64 - [![Build Status](https://travis-ci.org/PurpleI2P/i2pd.svg?branch=openssl)](https://travis-ci.org/PurpleI2P/i2pd)
* Windows - [![Build status](https://ci.appveyor.com/api/projects/status/1908qe4p48ff1x23?svg=true)](https://ci.appveyor.com/project/PurpleI2P/i2pd)
* Mac OS X - [![Build Status](https://travis-ci.org/PurpleI2P/i2pd.svg?branch=openssl)](https://travis-ci.org/PurpleI2P/i2pd)
* CentOS / Fedora - [![Build Status](https://copr.fedorainfracloud.org/coprs/supervillain/i2pd/package/i2pd-git/status_image/last_build.png)](https://copr.fedorainfracloud.org/coprs/supervillain/i2pd/package/i2pd-git/)
* Docker image - [![Build Status](https://dockerbuildbadges.quelltext.eu/status.svg?organization=meeh&repository=i2pd)](https://hub.docker.com/r/meeh/i2pd/builds/)
* GNU/Linux - [![Build Status](https://travis-ci.org/PurpleI2P/i2pd.svg?branch=openssl)](https://travis-ci.org/PurpleI2P/i2pd)
* Windows - [![Build status](https://ci.appveyor.com/api/projects/status/1908qe4p48ff1x23?svg=true)](https://ci.appveyor.com/project/PurpleI2P/i2pd)
* Mac OS X - [![Build Status](https://travis-ci.org/PurpleI2P/i2pd.svg?branch=openssl)](https://travis-ci.org/PurpleI2P/i2pd)
* CentOS / Fedora / Mageia - [![Build Status](https://copr.fedorainfracloud.org/coprs/supervillain/i2pd/package/i2pd-git/status_image/last_build.png)](https://copr.fedorainfracloud.org/coprs/supervillain/i2pd/package/i2pd-git/)
* Docker image - [![Build Status](https://dockerbuildbadges.quelltext.eu/status.svg?organization=meeh&repository=i2pd)](https://hub.docker.com/r/meeh/i2pd/builds/)
* FreeBSD
* Android
* iOS

View File

@@ -50,7 +50,7 @@ namespace util
if (isDaemon)
{
LogPrint(eLogDebug, "Daemon: running as service");
I2PService service(SERVICE_NAME);
I2PService service((PSTR)SERVICE_NAME);
if (!I2PService::Run(service))
{
LogPrint(eLogError, "Daemon: Service failed to run w/err 0x%08lx\n", GetLastError());

View File

@@ -24,17 +24,22 @@
#define ID_GRACEFUL_SHUTDOWN 2004
#define ID_STOP_GRACEFUL_SHUTDOWN 2005
#define ID_RELOAD 2006
#define ID_ACCEPT_TRANSIT 2007
#define ID_DECLINE_TRANSIT 2008
#define ID_TRAY_ICON 2050
#define WM_TRAYICON (WM_USER + 1)
#define IDT_GRACEFUL_SHUTDOWN_TIMER 2100
#define FRAME_UPDATE_TIMER 2101
#define IDT_GRACEFUL_TUNNELCHECK_TIMER 2102
namespace i2p
{
namespace win32
{
static DWORD GracefulShutdownEndtime = 0;
static void ShowPopupMenu (HWND hWnd, POINT *curpos, int wDefaultItem)
{
HMENU hPopup = CreatePopupMenu();
@@ -42,11 +47,17 @@ namespace win32
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_APP, "Show app");
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_ABOUT, "&About...");
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_SEPARATOR, 0, NULL);
if(!i2p::context.AcceptsTunnels())
InsertMenu (hPopup, -1,
i2p::util::DaemonWin32::Instance ().isGraceful ? MF_BYPOSITION | MF_STRING | MF_GRAYED : MF_BYPOSITION | MF_STRING,
ID_ACCEPT_TRANSIT, "Accept &transit");
else
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_DECLINE_TRANSIT, "Decline &transit");
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_RELOAD, "&Reload configs");
if (!i2p::util::DaemonWin32::Instance ().isGraceful)
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_GRACEFUL_SHUTDOWN, "&Graceful shutdown");
else
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_STOP_GRACEFUL_SHUTDOWN, "&Stop graceful shutdown");
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_STOP_GRACEFUL_SHUTDOWN, "Stop &graceful shutdown");
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_EXIT, "E&xit");
SetMenuDefaultItem (hPopup, ID_CONSOLE, FALSE);
SendMessage (hWnd, WM_INITMENUPOPUP, (WPARAM)hPopup, 0);
@@ -148,7 +159,13 @@ namespace win32
s << "; ";
s << "Success Rate: " << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate() << "%\n";
s << "Uptime: "; ShowUptime(s, i2p::context.GetUptime ());
s << "\n";
if (GracefulShutdownEndtime != 0)
{
DWORD GracefulTimeLeft = (GracefulShutdownEndtime - GetTickCount()) / 1000;
s << "Graceful shutdown, time left: "; ShowUptime(s, GracefulTimeLeft);
}
else
s << "\n";
s << "Inbound: " << i2p::transport::transports.GetInBandwidth() / 1024 << " KiB/s; ";
s << "Outbound: " << i2p::transport::transports.GetOutBandwidth() / 1024 << " KiB/s\n";
s << "Received: "; ShowTransfered (s, i2p::transport::transports.GetTotalReceivedBytes());
@@ -166,10 +183,13 @@ namespace win32
static LRESULT CALLBACK WndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static UINT s_uTaskbarRestart;
switch (uMsg)
{
case WM_CREATE:
{
s_uTaskbarRestart = RegisterWindowMessage(TEXT("TaskbarCreated"));
AddTrayIcon (hWnd);
break;
}
@@ -178,6 +198,7 @@ namespace win32
RemoveTrayIcon (hWnd);
KillTimer (hWnd, FRAME_UPDATE_TIMER);
KillTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER);
KillTimer (hWnd, IDT_GRACEFUL_TUNNELCHECK_TIMER);
PostQuitMessage (0);
break;
}
@@ -197,10 +218,28 @@ namespace win32
PostMessage (hWnd, WM_CLOSE, 0, 0);
return 0;
}
case ID_ACCEPT_TRANSIT:
{
i2p::context.SetAcceptsTunnels (true);
std::stringstream text;
text << "I2Pd now accept transit tunnels";
MessageBox( hWnd, TEXT(text.str ().c_str ()), TEXT("i2pd"), MB_ICONINFORMATION | MB_OK );
return 0;
}
case ID_DECLINE_TRANSIT:
{
i2p::context.SetAcceptsTunnels (false);
std::stringstream text;
text << "I2Pd now decline new transit tunnels";
MessageBox( hWnd, TEXT(text.str ().c_str ()), TEXT("i2pd"), MB_ICONINFORMATION | MB_OK );
return 0;
}
case ID_GRACEFUL_SHUTDOWN:
{
i2p::context.SetAcceptsTunnels (false);
SetTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER, 10*60*1000, nullptr); // 10 minutes
SetTimer (hWnd, IDT_GRACEFUL_TUNNELCHECK_TIMER, 1000, nullptr); // check tunnels every second
GracefulShutdownEndtime = GetTickCount() + 10*60*1000;
i2p::util::DaemonWin32::Instance ().isGraceful = true;
return 0;
}
@@ -208,6 +247,8 @@ namespace win32
{
i2p::context.SetAcceptsTunnels (true);
KillTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER);
KillTimer (hWnd, IDT_GRACEFUL_TUNNELCHECK_TIMER);
GracefulShutdownEndtime = 0;
i2p::util::DaemonWin32::Instance ().isGraceful = false;
return 0;
}
@@ -223,7 +264,7 @@ namespace win32
{
char buf[30];
std::string httpAddr; i2p::config::GetOption("http.address", httpAddr);
uint16_t httpPort; i2p::config::GetOption("http.port", httpPort);
uint16_t httpPort; i2p::config::GetOption("http.port", httpPort);
snprintf(buf, 30, "http://%s:%d", httpAddr.c_str(), httpPort);
ShellExecute(NULL, "open", buf, NULL, NULL, SW_SHOWNORMAL);
return 0;
@@ -290,14 +331,27 @@ namespace win32
}
case WM_TIMER:
{
if (wParam == IDT_GRACEFUL_SHUTDOWN_TIMER)
switch(wParam)
{
PostMessage (hWnd, WM_CLOSE, 0, 0); // exit
return 0;
}
if (wParam == FRAME_UPDATE_TIMER)
{
InvalidateRect(hWnd, NULL, TRUE);
case IDT_GRACEFUL_SHUTDOWN_TIMER:
{
GracefulShutdownEndtime = 0;
PostMessage (hWnd, WM_CLOSE, 0, 0); // exit
return 0;
}
case FRAME_UPDATE_TIMER:
{
InvalidateRect(hWnd, NULL, TRUE);
return 0;
}
case IDT_GRACEFUL_TUNNELCHECK_TIMER:
{
if (i2p::tunnel::tunnels.CountTransitTunnels() == 0)
PostMessage (hWnd, WM_CLOSE, 0, 0);
else
SetTimer (hWnd, IDT_GRACEFUL_TUNNELCHECK_TIMER, 1000, nullptr);
return 0;
}
}
break;
}
@@ -318,6 +372,12 @@ namespace win32
EndPaint(hWnd, &ps);
break;
}
default:
{
if (uMsg == s_uTaskbarRestart)
AddTrayIcon (hWnd);
break;
}
}
return DefWindowProc( hWnd, uMsg, wParam, lParam);
}

View File

@@ -297,7 +297,8 @@ void InstallService(PCSTR pszServiceName, PCSTR pszDisplayName, DWORD dwStartTyp
FreeHandles(schSCManager, schService);
return;
}
strncat(szPath, " --daemon", MAX_PATH);
char SvcOpt[] = " --daemon";
strncat(szPath, SvcOpt, strlen(SvcOpt));
// Open the local default service control manager database
schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE);

View File

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

4
android/.gitignore vendored
View File

@@ -12,7 +12,5 @@ local.properties
build.sh
android.iml
build
gradle
gradlew
gradlew.bat

View File

@@ -4,11 +4,11 @@
android:installLocation="auto">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" /> <!-- normal perm, per https://developer.android.com/guide/topics/permissions/normal-permissions.html -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <!-- normal perm -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <!-- required in API 26+ -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<application
android:allowBackup="true"
@@ -48,5 +48,4 @@
android:value="org.purplei2p.i2pd.I2PDPermsAskerActivity" />
</activity>
</application>
</manifest>

File diff suppressed because it is too large Load Diff

View File

@@ -2,9 +2,10 @@ buildscript {
repositories {
mavenCentral()
jcenter()
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.3'
classpath 'com.android.tools.build:gradle:3.3.2'
}
}
@@ -18,18 +19,18 @@ repositories {
}
dependencies {
compile 'com.android.support:support-compat:28.0.0'
implementation 'com.android.support:support-compat:28.0.0'
}
android {
compileSdkVersion 28
buildToolsVersion "28.0.1"
buildToolsVersion "28.0.3"
defaultConfig {
applicationId "org.purplei2p.i2pd"
targetSdkVersion 28
minSdkVersion 14
versionCode 2230
versionName "2.23.0"
versionCode 2250
versionName "2.25.0"
ndk {
abiFilters 'armeabi-v7a'
abiFilters 'x86'

Binary file not shown.

View File

@@ -0,0 +1,6 @@
#Thu Mar 14 18:21:08 MSK 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip

172
android/gradlew vendored Executable file
View File

@@ -0,0 +1,172 @@
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

84
android/gradlew.bat vendored Normal file
View File

@@ -0,0 +1,84 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@@ -65,6 +65,8 @@ namespace android
}
}
*/
std::string dataDir = "";
DaemonAndroidImpl::DaemonAndroidImpl ()
//:
/*mutex(nullptr), */
@@ -85,7 +87,7 @@ namespace android
//m_IsRunning=false;
// make sure assets are ready before proceed
i2p::fs::DetectDataDir("", false);
i2p::fs::DetectDataDir(dataDir, false);
int numAttempts = 0;
do
{
@@ -203,5 +205,10 @@ namespace android
{
daemon.stop();
}
void SetDataDir(std::string jdataDir)
{
dataDir = jdataDir;
}
}
}

View File

@@ -42,6 +42,8 @@ namespace android
// stops the daemon
void stop();
// set datadir received from jni
void SetDataDir(std::string jdataDir);
/*
class Worker : public QObject
{

View File

@@ -5,7 +5,7 @@
#include "Transports.h"
JNIEXPORT jstring JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_getABICompiledWith
(JNIEnv * env, jclass clazz) {
(JNIEnv *env, jclass clazz) {
#if defined(__arm__)
#if defined(__ARM_ARCH_7A__)
#if defined(__ARM_NEON__)
@@ -42,27 +42,53 @@ JNIEXPORT jstring JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_getABICompiledWith
}
JNIEXPORT jstring JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_startDaemon
(JNIEnv * env, jclass clazz) {
(JNIEnv *env, jclass clazz) {
return env->NewStringUTF(i2p::android::start().c_str());
}
JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_stopDaemon
(JNIEnv * env, jclass clazz) {
(JNIEnv *env, jclass clazz) {
i2p::android::stop();
}
JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_stopAcceptingTunnels
(JNIEnv * env, jclass clazz) {
(JNIEnv *env, jclass clazz) {
i2p::context.SetAcceptsTunnels (false);
}
JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_startAcceptingTunnels
(JNIEnv * env, jclass clazz) {
(JNIEnv *env, jclass clazz) {
i2p::context.SetAcceptsTunnels (true);
}
JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_onNetworkStateChanged
(JNIEnv * env, jclass clazz, jboolean isConnected) {
(JNIEnv *env, jclass clazz, jboolean isConnected) {
bool isConnectedBool = (bool) isConnected;
i2p::transport::transports.SetOnline (isConnectedBool);
}
JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_setDataDir
(JNIEnv *env, jclass clazz, jstring jdataDir) {
/*
// Method 1: convert UTF-16 jstring to std::string (https://stackoverflow.com/a/41820336)
const jclass stringClass = env->GetObjectClass(jdataDir);
const jmethodID getBytes = env->GetMethodID(stringClass, "getBytes", "(Ljava/lang/String;)[B");
const jbyteArray stringJbytes = (jbyteArray) env->CallObjectMethod(jdataDir, getBytes, env->NewStringUTF("UTF-8"));
size_t length = (size_t) env->GetArrayLength(stringJbytes);
jbyte* pBytes = env->GetByteArrayElements(stringJbytes, NULL);
std::string dataDir = std::string((char *)pBytes, length);
env->ReleaseByteArrayElements(stringJbytes, pBytes, JNI_ABORT);
env->DeleteLocalRef(stringJbytes);
env->DeleteLocalRef(stringClass); */
// Method 2: get string chars and make char array.
auto dataDir = env->GetStringUTFChars(jdataDir, NULL);
env->ReleaseStringUTFChars(jdataDir, dataDir);
// Set DataDir
i2p::android::SetDataDir(dataDir);
}

View File

@@ -30,6 +30,9 @@ JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_startAcceptingTunnels
JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_onNetworkStateChanged
(JNIEnv * env, jclass clazz, jboolean isConnected);
JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_setDataDir
(JNIEnv *env, jclass clazz, jstring jdataDir);
#ifdef __cplusplus
}
#endif

View File

@@ -16,4 +16,5 @@
<string name="stopped">Приложение было остановлено</string>
<string name="remaining">осталось</string>
<string name="title_activity_i2_pdperms_asker_prompt">Запрос</string>
<string name="permDenied">Права для записи на SD карту отклонены, вам необходимо предоставить их для продолжения</string>
</resources>

View File

@@ -16,4 +16,5 @@
<string name="stopped">Application stopped</string>
<string name="remaining">remaining</string>
<string name="title_activity_i2_pdperms_asker_prompt">Prompt</string>
<string name="permDenied">SD card write permission denied, you need to allow this to continue</string>
</resources>

View File

@@ -2,18 +2,18 @@ package org.purplei2p.i2pd;
import java.util.HashSet;
import java.util.Set;
import android.os.Environment;
import android.util.Log;
import org.purplei2p.i2pd.R;
public class DaemonSingleton {
private static final String TAG="i2pd";
private static final String TAG = "i2pd";
private static final DaemonSingleton instance = new DaemonSingleton();
public interface StateUpdateListener { void daemonStateUpdate(); }
private final Set<StateUpdateListener> stateUpdateListeners = new HashSet<>();
public static DaemonSingleton getInstance() {
return instance;
}
public static DaemonSingleton getInstance() { return instance; }
public synchronized void addStateChangeListener(StateUpdateListener listener) { stateUpdateListeners.add(listener); }
public synchronized void removeStateChangeListener(StateUpdateListener listener) { stateUpdateListeners.remove(listener); }
@@ -82,6 +82,7 @@ public class DaemonSingleton {
}
try {
synchronized (DaemonSingleton.this) {
I2PD_JNI.setDataDir(Environment.getExternalStorageDirectory().getAbsolutePath() + "/i2pd");
daemonStartResult = I2PD_JNI.startDaemon();
if("ok".equals(daemonStartResult)){
setState(State.startedOkay);
@@ -91,7 +92,6 @@ public class DaemonSingleton {
} catch (Throwable tr) {
lastThrowable=tr;
setState(State.startFailed);
return;
}
}

View File

@@ -1,5 +1,6 @@
package org.purplei2p.i2pd;
import android.annotation.TargetApi;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;

View File

@@ -119,11 +119,10 @@ public class I2PDPermsAskerActivity extends Activity {
// permission denied, boo! Disable the
// functionality that depends on this permission.
textview_retry.setText("SD card write permission denied, you need to allow this to continue");
textview_retry.setText(R.string.permDenied);
textview_retry.setVisibility(TextView.VISIBLE);
button_request_write_ext_storage_perms.setVisibility(Button.VISIBLE);
}
return;
}
// other 'case' lines to check for other

View File

@@ -18,6 +18,8 @@ public class I2PD_JNI {
public static native void onNetworkStateChanged(boolean isConnected);
public static native void setDataDir(String jdataDir);
public static void loadLibraries() {
//System.loadLibrary("c++_shared");
System.loadLibrary("i2pd");

View File

@@ -1,8 +1,8 @@
#APP_ABI := all
#APP_ABI := armeabi-v7a x86
#APP_ABI := x86
#APP_ABI := x86_64
APP_ABI := armeabi-v7a
APP_ABI := all
#APP_ABI += x86
#APP_ABI += x86_64
#APP_ABI += armeabi-v7a
#APP_ABI += arm64-v8a
#can be android-3 but will fail for x86 since arch-x86 is not present at ndkroot/platforms/android-3/ . libz is taken from there.
APP_PLATFORM := android-14

View File

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

View File

@@ -0,0 +1,2 @@
archive
i2pd_*_android_binary.zip

View File

@@ -0,0 +1,45 @@
#!/bin/bash
# Copyright (c) 2013-2017, The PurpleI2P Project
#
# This file is part of Purple i2pd project and licensed under BSD3
#
# See full license text in LICENSE file at top of project tree
GITDESC=$(git describe --tags)
declare -A ABILIST=(
["armeabi-v7a"]="armv7l"
["arm64-v8a"]="aarch64"
["x86"]="x86"
["x86_64"]="x86_64"
)
# Remove old files and archives
if [ -d archive ]; then
rm -r archive
fi
if [ -f i2pd_*_android_binary.zip ]; then
rm i2pd_*_android_binary.zip
fi
# Prepare files for package
mkdir archive
for ABI in "${!ABILIST[@]}"; do
if [ -f ../../android_binary_only/libs/${ABI}/i2pd ]; then
cp ../../android_binary_only/libs/${ABI}/i2pd archive/i2pd-${ABILIST[$ABI]}
fi
done
cp i2pd archive/i2pd
cp -rH ../../android/assets/* archive/
# Compress files
cd archive
zip -r6 ../i2pd_${GITDESC}_android_binary.zip .
# Remove temporary folder
cd ..
rm -r archive

View File

@@ -0,0 +1,33 @@
#!/bin/sh
# Copyright (c) 2013-2019, The PurpleI2P Project
#
# This file is part of Purple i2pd project and licensed under BSD3
#
# See full license text in LICENSE file at top of project tree
#
# That script written for use with Termux.
# https://stackoverflow.com/a/246128
SOURCE="${0}"
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"
SOURCE="$(readlink "$SOURCE")"
[[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
done
DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"
arch=$(uname -m)
screenfind=$(which screen)
if [ -z $screenfind ]; then
echo "Can't find 'screen' installed. That script needs it!";
exit 1;
fi
if [ -z i2pd-$arch ]; then
echo "Can't find i2pd binary for your archtecture.";
exit 1;
fi
screen -AmdS i2pd ./i2pd-$arch --datadir=$DIR

View File

@@ -24,7 +24,7 @@ RUN mkdir -p "$I2PD_HOME" "$DATA_DIR" \
# 1. install deps, clone and build.
# 2. strip binaries.
# 3. Purge all dependencies and other unrelated packages, including build directory.
RUN apk --no-cache --virtual build-dependendencies add make gcc g++ libtool boost-dev build-base openssl-dev openssl git \
RUN apk --no-cache --virtual build-dependendencies add make gcc g++ libtool zlib-dev boost-dev build-base openssl-dev openssl git \
&& mkdir -p /tmp/build \
&& cd /tmp/build && git clone -b ${GIT_BRANCH} ${REPO_URL} \
&& cd i2pd \

View File

@@ -1,7 +1,7 @@
%define git_hash %(git rev-parse HEAD | cut -c -7)
Name: i2pd-git
Version: 2.23.0
Version: 2.25.0
Release: git%{git_hash}%{?dist}
Summary: I2P router written in C++
Conflicts: i2pd
@@ -47,8 +47,13 @@ cd build
-DWITH_LIBRARY=OFF \
-DWITH_UPNP=ON \
-DWITH_HARDENING=ON \
%if 0%{?fedora} > 29
-DBUILD_SHARED_LIBS:BOOL=OFF \
.
%else
-DBUILD_SHARED_LIBS:BOOL=OFF
%endif
%endif
make %{?_smp_mflags}
@@ -105,6 +110,12 @@ getent passwd i2pd >/dev/null || \
%changelog
* Thu May 9 2019 orignal <i2porignal@yandex.ru> - 2.25.0
- update to 2.25.0
* Thu Mar 21 2019 orignal <i2porignal@yandex.ru> - 2.24.0
- update to 2.24.0
* Mon Jan 21 2019 orignal <i2porignal@yandex.ru> - 2.23.0
- update to 2.23.0

View File

@@ -1,5 +1,5 @@
Name: i2pd
Version: 2.23.0
Version: 2.25.0
Release: 1%{?dist}
Summary: I2P router written in C++
Conflicts: i2pd-git
@@ -45,8 +45,13 @@ cd build
-DWITH_LIBRARY=OFF \
-DWITH_UPNP=ON \
-DWITH_HARDENING=ON \
%if 0%{?fedora} > 29
-DBUILD_SHARED_LIBS:BOOL=OFF \
.
%else
-DBUILD_SHARED_LIBS:BOOL=OFF
%endif
%endif
make %{?_smp_mflags}
@@ -103,6 +108,12 @@ getent passwd i2pd >/dev/null || \
%changelog
* Thu May 9 2019 orignal <i2porignal@yandex.ru> - 2.25.0
- update to 2.25.0
* Thu Mar 21 2019 orignal <i2porignal@yandex.ru> - 2.24.0
- update to 2.24.0
* Mon Jan 21 2019 orignal <i2porignal@yandex.ru> - 2.23.0
- update to 2.23.0

View File

@@ -154,14 +154,24 @@ namespace i2p
i2p::context.SetSupportsV6 (ipv6);
i2p::context.SetSupportsV4 (ipv4);
bool ntcp; i2p::config::GetOption("ntcp", ntcp);
i2p::context.PublishNTCPAddress (ntcp, !ipv6);
bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2);
if (ntcp2)
{
bool published; i2p::config::GetOption("ntcp2.published", published);
if (published)
{
uint16_t port; i2p::config::GetOption("ntcp2.port", port);
i2p::context.PublishNTCP2Address (port, true); // publish
uint16_t ntcp2port; i2p::config::GetOption("ntcp2.port", ntcp2port);
if (!ntcp && !ntcp2port) ntcp2port = port; // use standard port
i2p::context.PublishNTCP2Address (ntcp2port, true); // publish
if (ipv6)
{
std::string ipv6Addr; i2p::config::GetOption("ntcp2.addressv6", ipv6Addr);
auto addr = boost::asio::ip::address_v6::from_string (ipv6Addr);
if (!addr.is_unspecified () && addr != boost::asio::ip::address_v6::any ())
i2p::context.UpdateNTCP2V6Address (addr); // set ipv6 address if configured
}
}
else
i2p::context.PublishNTCP2Address (port, false); // unpublish
@@ -256,7 +266,7 @@ namespace i2p
pos = comma + 1;
}
while (comma != std::string::npos);
LogPrint(eLogInfo, "Daemon: setting restricted routes to use ", idents.size(), " trusted routesrs");
LogPrint(eLogInfo, "Daemon: setting restricted routes to use ", idents.size(), " trusted routers");
i2p::transport::transports.RestrictRoutesToRouters(idents);
restricted = idents.size() > 0;
}

View File

@@ -358,7 +358,7 @@ namespace http {
{
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<p class='content'>\r\n";
for(auto& it: dest->GetLeaseSets ())
s << it.second->GetIdentHash ().ToBase32 () << " " << (int)it.second->GetStoreType () << "<br>\r\n";
s << it.first.ToBase32 () << " " << (int)it.second->GetStoreType () << "<br>\r\n";
s << "</p>\r\n</div>\r\n";
} else
s << "<b>LeaseSets:</b> <i>0</i><br>\r\n";
@@ -798,8 +798,7 @@ namespace http {
}
HTTPConnection::HTTPConnection (std::string hostname, std::shared_ptr<boost::asio::ip::tcp::socket> socket):
m_Socket (socket), m_Timer (socket->get_io_service ()), m_BufferLen (0),
expected_host(hostname)
m_Socket (socket), m_BufferLen (0), expected_host(hostname)
{
/* cache options */
i2p::config::GetOption("http.auth", needAuth);

View File

@@ -39,7 +39,6 @@ namespace http
private:
std::shared_ptr<boost::asio::ip::tcp::socket> m_Socket;
boost::asio::deadline_timer m_Timer;
char m_Buffer[HTTP_CONNECTION_BUFFER_SIZE + 1];
size_t m_BufferLen;
std::string m_SendBuffer;

12
debian/changelog vendored
View File

@@ -1,3 +1,15 @@
i2pd (2.25.0-1) unstable; urgency=medium
* updated to version 2.25.0/0.9.40
-- orignal <orignal@i2pmail.org> Thu, 9 May 2019 16:00:00 +0000
i2pd (2.24.0-1) unstable; urgency=medium
* updated to version 2.24.0/0.9.39
-- orignal <orignal@i2pmail.org> Thu, 21 Mar 2019 16:00:00 +0000
i2pd (2.23.0-1) unstable; urgency=medium
* updated to version 2.23.0/0.9.38

View File

@@ -9,6 +9,7 @@
*
*/
#include "I2PEndian.h"
#include "ChaCha20.h"
#if !OPENSSL_AEAD_CHACHA20_POLY1305
@@ -89,14 +90,14 @@ void Chacha20Init (Chacha20State& state, const uint8_t * nonce, const uint8_t *
for (size_t i = 0; i < 8; i++)
state.data[4 + i] = chacha::u8t32le(key + i * 4);
state.data[12] = counter;
state.data[12] = htole32 (counter);
for (size_t i = 0; i < 3; i++)
state.data[13 + i] = chacha::u8t32le(nonce + i * 4);
}
void Chacha20SetCounter (Chacha20State& state, uint32_t counter)
{
state.data[12] = counter;
state.data[12] = htole32 (counter);
state.offset = 0;
}

View File

@@ -73,7 +73,7 @@ namespace config {
("limits.coresize", value<uint32_t>()->default_value(0), "Maximum size of corefile in Kb (0 - use system limit)")
("limits.openfiles", value<uint16_t>()->default_value(0), "Maximum number of open files (0 - use system default)")
("limits.transittunnels", value<uint16_t>()->default_value(2500), "Maximum active transit sessions (default:2500)")
("limits.ntcpsoft", value<uint16_t>()->default_value(0), "Threshold to start probabalistic backoff with ntcp sessions (default: use system limit)")
("limits.ntcpsoft", value<uint16_t>()->default_value(0), "Threshold to start probabilistic backoff with ntcp sessions (default: use system limit)")
("limits.ntcphard", value<uint16_t>()->default_value(0), "Maximum number of ntcp sessions (default: use system limit)")
("limits.ntcpthreads", value<uint16_t>()->default_value(1), "Maximum number of threads used by NTCP DH worker (default: 1)")
;
@@ -153,8 +153,8 @@ namespace config {
("i2pcontrol.address", value<std::string>()->default_value("127.0.0.1"), "I2PCP listen address")
("i2pcontrol.port", value<uint16_t>()->default_value(7650), "I2PCP listen port")
("i2pcontrol.password", value<std::string>()->default_value("itoopie"), "I2PCP access password")
("i2pcontrol.cert", value<std::string>()->default_value("i2pcontrol.crt.pem"), "I2PCP connection cerificate")
("i2pcontrol.key", value<std::string>()->default_value("i2pcontrol.key.pem"), "I2PCP connection cerificate key")
("i2pcontrol.cert", value<std::string>()->default_value("i2pcontrol.crt.pem"), "I2PCP connection certificate")
("i2pcontrol.key", value<std::string>()->default_value("i2pcontrol.key.pem"), "I2PCP connection certificate key")
;
bool upnp_default = false;
@@ -164,7 +164,7 @@ namespace config {
options_description upnp("UPnP options");
upnp.add_options()
("upnp.enabled", value<bool>()->default_value(upnp_default), "Enable or disable UPnP: automatic port forwarding")
("upnp.name", value<std::string>()->default_value("I2Pd"), "Name i2pd appears in UPnP forwardings list")
("upnp.name", value<std::string>()->default_value("I2Pd"), "Name i2pd appears in UPnP forwarding list")
;
options_description precomputation("Precomputation options");
@@ -239,6 +239,7 @@ namespace config {
("ntcp2.enabled", value<bool>()->default_value(true), "Enable NTCP2 (default: enabled)")
("ntcp2.published", value<bool>()->default_value(false), "Publish NTCP2 (default: disabled)")
("ntcp2.port", value<uint16_t>()->default_value(0), "Port to listen for incoming NTCP2 connections (default: auto)")
("ntcp2.addressv6", value<std::string>()->default_value("::"), "Address to bind NTCP2 on")
;
options_description nettime("Time sync options");
@@ -256,6 +257,7 @@ namespace config {
options_description persist("Network information persisting options");
persist.add_options()
("persist.profiles", value<bool>()->default_value(true), "Persist peer profiles (default: true)")
("persist.addressbook", value<bool>()->default_value(true), "Persist full addresses (default: true)")
;
m_OptionsDesc

View File

@@ -32,12 +32,12 @@ namespace config {
* @param argc Cmdline arguments count, should be passed from main().
* @param argv Cmdline parameters array, should be passed from main()
*
* If --help is given in parameters, shows it's list with description
* terminates the program with exitcode 0.
* If --help is given in parameters, shows its list with description
* and terminates the program with exitcode 0.
*
* In case of parameter misuse boost throws an exception.
* We internally handle type boost::program_options::unknown_option,
* and then terminate program with exitcode 1.
* and then terminate the program with exitcode 1.
*
* Other exceptions will be passed to higher level.
*/
@@ -107,7 +107,7 @@ namespace config {
/**
* @brief Check is value explicitly given or default
* @param name Name of checked parameter
* @return true if value set to default, false othervise
* @return true if value set to default, false otherwise
*/
bool IsDefault(const char *name);
}

View File

@@ -8,11 +8,14 @@
#include <openssl/crypto.h>
#include "TunnelBase.h"
#include <openssl/ssl.h>
#include "Crypto.h"
#if OPENSSL_HKDF
#include <openssl/kdf.h>
#endif
#if !OPENSSL_AEAD_CHACHA20_POLY1305
#include "ChaCha20.h"
#include "Poly1305.h"
#endif
#include "Crypto.h"
#include "Ed25519.h"
#include "I2PEndian.h"
#include "Log.h"
@@ -1228,6 +1231,49 @@ namespace crypto
#endif
}
void ChaCha20 (const uint8_t * msg, size_t msgLen, const uint8_t * key, const uint8_t * nonce, uint8_t * out)
{
#if OPENSSL_AEAD_CHACHA20_POLY1305
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new ();
uint32_t iv[4];
iv[0] = htole32 (1); memcpy (iv + 1, nonce, 12); // counter | nonce
EVP_EncryptInit_ex(ctx, EVP_chacha20 (), NULL, key, (const uint8_t *)iv);
int outlen = 0;
EVP_EncryptUpdate(ctx, out, &outlen, msg, msgLen);
EVP_EncryptFinal_ex(ctx, NULL, &outlen);
EVP_CIPHER_CTX_free (ctx);
#else
chacha::Chacha20State state;
chacha::Chacha20Init (state, nonce, key, 1);
if (out != msg) memcpy (out, msg, msgLen);
chacha::Chacha20Encrypt (state, out, msgLen);
#endif
}
void HKDF (const uint8_t * salt, const uint8_t * key, size_t keyLen, const std::string& info, uint8_t * out)
{
#if OPENSSL_HKDF
EVP_PKEY_CTX * pctx = EVP_PKEY_CTX_new_id (EVP_PKEY_HKDF, NULL);
EVP_PKEY_derive_init (pctx);
EVP_PKEY_CTX_set_hkdf_md (pctx, EVP_sha256());
EVP_PKEY_CTX_set1_hkdf_salt (pctx, salt, 32);
EVP_PKEY_CTX_set1_hkdf_key (pctx, key, keyLen);
if (info.length () > 0)
EVP_PKEY_CTX_add1_hkdf_info (pctx, info.c_str (), info.length ());
size_t outlen = 64;
EVP_PKEY_derive (pctx, out, &outlen);
EVP_PKEY_CTX_free (pctx);
#else
uint8_t prk[32]; unsigned int len;
HMAC(EVP_sha256(), salt, 32, key, keyLen, prk, &len);
auto l = info.length ();
memcpy (out, info.c_str (), l); out[l] = 0x01;
HMAC(EVP_sha256(), prk, 32, out, l + 1, out, &len);
memcpy (out + 32, info.c_str (), l); out[l + 32] = 0x02;
HMAC(EVP_sha256(), prk, 32, out, l + 33, out + 32, &len);
#endif
}
// init and terminate
/* std::vector <std::unique_ptr<std::mutex> > m_OpenSSLMutexes;

View File

@@ -27,6 +27,7 @@
# define X509_getm_notAfter X509_get_notAfter
#else
# define LEGACY_OPENSSL 0
# define OPENSSL_HKDF 1
# if (OPENSSL_VERSION_NUMBER >= 0x010101000) // 1.1.1
# define OPENSSL_EDDSA 1
# define OPENSSL_X25519 1
@@ -290,6 +291,13 @@ namespace crypto
void AEADChaCha20Poly1305Encrypt (const std::vector<std::pair<uint8_t *, size_t> >& bufs, const uint8_t * key, const uint8_t * nonce, uint8_t * mac); // encrypt multiple buffers with zero ad
// ChaCha20
void ChaCha20 (const uint8_t * msg, size_t msgLen, const uint8_t * key, const uint8_t * nonce, uint8_t * out);
// HKDF
void HKDF (const uint8_t * salt, const uint8_t * key, size_t keyLen, const std::string& info, uint8_t * out); // salt - 32, out - 64, info <= 32
// init and terminate
void InitCrypto (bool precomputation);
void TerminateCrypto ();

View File

@@ -213,7 +213,7 @@ namespace client
return pool->Reconfigure(inLen, outLen, inQuant, outQuant);
}
std::shared_ptr<const i2p::data::LeaseSet> LeaseSetDestination::FindLeaseSet (const i2p::data::IdentHash& ident)
std::shared_ptr<i2p::data::LeaseSet> LeaseSetDestination::FindLeaseSet (const i2p::data::IdentHash& ident)
{
std::shared_ptr<i2p::data::LeaseSet> remoteLS;
{
@@ -272,15 +272,21 @@ namespace client
if (!m_Pool) return nullptr;
if (!m_LeaseSet)
UpdateLeaseSet ();
auto ls = GetLeaseSetMt ();
return (ls && ls->GetInnerLeaseSet ()) ? ls->GetInnerLeaseSet () : ls; // always non-encrypted
}
std::shared_ptr<const i2p::data::LocalLeaseSet> LeaseSetDestination::GetLeaseSetMt ()
{
std::lock_guard<std::mutex> l(m_LeaseSetMutex);
return m_LeaseSet;
}
void LeaseSetDestination::SetLeaseSet (i2p::data::LocalLeaseSet * newLeaseSet)
void LeaseSetDestination::SetLeaseSet (std::shared_ptr<const i2p::data::LocalLeaseSet> newLeaseSet)
{
{
std::lock_guard<std::mutex> l(m_LeaseSetMutex);
m_LeaseSet.reset (newLeaseSet);
m_LeaseSet = newLeaseSet;
}
i2p::garlic::GarlicDestination::SetLeaseSetUpdated ();
if (m_IsPublic)
@@ -361,55 +367,76 @@ namespace client
}
i2p::data::IdentHash key (buf + DATABASE_STORE_KEY_OFFSET);
std::shared_ptr<i2p::data::LeaseSet> leaseSet;
if (buf[DATABASE_STORE_TYPE_OFFSET] == i2p::data::NETDB_STORE_TYPE_LEASESET || // 1
buf[DATABASE_STORE_TYPE_OFFSET] == i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2) // 3
switch (buf[DATABASE_STORE_TYPE_OFFSET])
{
LogPrint (eLogDebug, "Destination: Remote LeaseSet");
std::lock_guard<std::mutex> lock(m_RemoteLeaseSetsMutex);
auto it = m_RemoteLeaseSets.find (key);
if (it != m_RemoteLeaseSets.end ())
case i2p::data::NETDB_STORE_TYPE_LEASESET: // 1
case i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2: // 3
{
leaseSet = it->second;
if (leaseSet->IsNewer (buf + offset, len - offset))
LogPrint (eLogDebug, "Destination: Remote LeaseSet");
std::lock_guard<std::mutex> lock(m_RemoteLeaseSetsMutex);
auto it = m_RemoteLeaseSets.find (key);
if (it != m_RemoteLeaseSets.end ())
{
leaseSet->Update (buf + offset, len - offset);
leaseSet = it->second;
if (leaseSet->IsNewer (buf + offset, len - offset))
{
leaseSet->Update (buf + offset, len - offset);
if (leaseSet->IsValid () && leaseSet->GetIdentHash () == key)
LogPrint (eLogDebug, "Destination: Remote LeaseSet updated");
else
{
LogPrint (eLogDebug, "Destination: Remote LeaseSet update failed");
m_RemoteLeaseSets.erase (it);
leaseSet = nullptr;
}
}
else
LogPrint (eLogDebug, "Destination: Remote LeaseSet is older. Not updated");
}
else
{
if (buf[DATABASE_STORE_TYPE_OFFSET] == i2p::data::NETDB_STORE_TYPE_LEASESET)
leaseSet = std::make_shared<i2p::data::LeaseSet> (buf + offset, len - offset); // LeaseSet
else
leaseSet = std::make_shared<i2p::data::LeaseSet2> (buf[DATABASE_STORE_TYPE_OFFSET], buf + offset, len - offset); // LeaseSet2
if (leaseSet->IsValid () && leaseSet->GetIdentHash () == key)
LogPrint (eLogDebug, "Destination: Remote LeaseSet updated");
{
if (leaseSet->GetIdentHash () != GetIdentHash ())
{
LogPrint (eLogDebug, "Destination: New remote LeaseSet added");
m_RemoteLeaseSets[key] = leaseSet;
}
else
LogPrint (eLogDebug, "Destination: Own remote LeaseSet dropped");
}
else
{
LogPrint (eLogDebug, "Destination: Remote LeaseSet update failed");
m_RemoteLeaseSets.erase (it);
LogPrint (eLogError, "Destination: New remote LeaseSet failed");
leaseSet = nullptr;
}
}
else
LogPrint (eLogDebug, "Destination: Remote LeaseSet is older. Not updated");
break;
}
else
case i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2: // 5
{
if (buf[DATABASE_STORE_TYPE_OFFSET] == i2p::data::NETDB_STORE_TYPE_LEASESET)
leaseSet = std::make_shared<i2p::data::LeaseSet> (buf + offset, len - offset); // LeaseSet
else
leaseSet = std::make_shared<i2p::data::LeaseSet2> (buf[DATABASE_STORE_TYPE_OFFSET], buf + offset, len - offset); // LeaseSet2
if (leaseSet->IsValid () && leaseSet->GetIdentHash () == key)
auto it2 = m_LeaseSetRequests.find (key);
if (it2 != m_LeaseSetRequests.end () && it2->second->requestedBlindedKey)
{
if (leaseSet->GetIdentHash () != GetIdentHash ())
auto ls2 = std::make_shared<i2p::data::LeaseSet2> (buf + offset, len - offset, it2->second->requestedBlindedKey);
if (ls2->IsValid ())
{
LogPrint (eLogDebug, "Destination: New remote LeaseSet added");
m_RemoteLeaseSets[key] = leaseSet;
m_RemoteLeaseSets[ls2->GetIdentHash ()] = ls2; // ident is not key
m_RemoteLeaseSets[key] = ls2; // also store as key for next lookup
leaseSet = ls2;
}
else
LogPrint (eLogDebug, "Destination: Own remote LeaseSet dropped");
}
else
{
LogPrint (eLogError, "Destination: New remote LeaseSet failed");
leaseSet = nullptr;
}
LogPrint (eLogInfo, "Destination: Couldn't find request for encrypted LeaseSet2");
break;
}
default:
LogPrint (eLogError, "Destination: Unexpected client's DatabaseStore type ", buf[DATABASE_STORE_TYPE_OFFSET], ", dropped");
}
else
LogPrint (eLogError, "Destination: Unexpected client's DatabaseStore type ", buf[DATABASE_STORE_TYPE_OFFSET], ", dropped");
auto it1 = m_LeaseSetRequests.find (key);
if (it1 != m_LeaseSetRequests.end ())
@@ -485,7 +512,8 @@ namespace client
void LeaseSetDestination::Publish ()
{
if (!m_LeaseSet || !m_Pool)
auto leaseSet = GetLeaseSetMt ();
if (!leaseSet || !m_Pool)
{
LogPrint (eLogError, "Destination: Can't publish non-existing LeaseSet");
return;
@@ -517,7 +545,7 @@ namespace client
LogPrint (eLogError, "Destination: Can't publish LeaseSet. No inbound tunnels");
return;
}
auto floodfill = i2p::data::netdb.GetClosestFloodfill (m_LeaseSet->GetIdentHash (), m_ExcludedFloodfills);
auto floodfill = i2p::data::netdb.GetClosestFloodfill (leaseSet->GetIdentHash (), m_ExcludedFloodfills);
if (!floodfill)
{
LogPrint (eLogError, "Destination: Can't publish LeaseSet, no more floodfills found");
@@ -527,7 +555,7 @@ namespace client
m_ExcludedFloodfills.insert (floodfill->GetIdentHash ());
LogPrint (eLogDebug, "Destination: Publish LeaseSet of ", GetIdentHash ().ToBase32 ());
RAND_bytes ((uint8_t *)&m_PublishReplyToken, 4);
auto msg = WrapMessage (floodfill, i2p::CreateDatabaseStoreMsg (m_LeaseSet, m_PublishReplyToken, inbound));
auto msg = WrapMessage (floodfill, i2p::CreateDatabaseStoreMsg (leaseSet, m_PublishReplyToken, inbound));
m_PublishConfirmationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_CONFIRMATION_TIMEOUT));
m_PublishConfirmationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishConfirmationTimer,
shared_from_this (), std::placeholders::_1));
@@ -550,7 +578,7 @@ namespace client
else
{
LogPrint (eLogWarning, "Destination: Publish confirmation was not received in ", PUBLISH_CONFIRMATION_TIMEOUT, " seconds from Java floodfill for crypto type ", (int)GetIdentity ()->GetCryptoKeyType ());
// Java floodfill never sends confirmantion back for unknown crypto type
// Java floodfill never sends confirmation back for unknown crypto type
// assume it successive and try to verify
m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_VERIFICATION_TIMEOUT));
m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer,
@@ -565,26 +593,32 @@ namespace client
{
if (ecode != boost::asio::error::operation_aborted)
{
auto ls = GetLeaseSetMt ();
if (!ls)
{
LogPrint (eLogWarning, "Destination: couldn't verify LeaseSet for ", GetIdentHash().ToBase32());
return;
}
auto s = shared_from_this ();
RequestLeaseSet (GetIdentHash (),
// "this" added due to bug in gcc 4.7-4.8
[s,this](std::shared_ptr<i2p::data::LeaseSet> leaseSet)
// we must capture this for gcc 4.7 due the bug
RequestLeaseSet (ls->GetStoreHash (),
[s, ls, this](std::shared_ptr<const i2p::data::LeaseSet> leaseSet)
{
if (leaseSet)
{
if (s->m_LeaseSet && *s->m_LeaseSet == *leaseSet)
if (*ls == *leaseSet)
{
// we got latest LeasetSet
LogPrint (eLogDebug, "Destination: published LeaseSet verified for ", GetIdentHash().ToBase32());
LogPrint (eLogDebug, "Destination: published LeaseSet verified for ", s->GetIdentHash().ToBase32());
s->m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_REGULAR_VERIFICATION_INTERNAL));
s->m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer, s, std::placeholders::_1));
return;
}
else
LogPrint (eLogDebug, "Destination: LeaseSet is different than just published for ", GetIdentHash().ToBase32());
LogPrint (eLogDebug, "Destination: LeaseSet is different than just published for ", s->GetIdentHash().ToBase32());
}
else
LogPrint (eLogWarning, "Destination: couldn't find published LeaseSet for ", GetIdentHash().ToBase32());
LogPrint (eLogWarning, "Destination: couldn't find published LeaseSet for ", s->GetIdentHash().ToBase32());
// we have to publish again
s->Publish ();
});
@@ -605,7 +639,27 @@ namespace client
m_Service.post ([requestComplete](void){requestComplete (nullptr);});
return false;
}
m_Service.post (std::bind (&LeaseSetDestination::RequestLeaseSet, shared_from_this (), dest, requestComplete));
m_Service.post (std::bind (&LeaseSetDestination::RequestLeaseSet, shared_from_this (), dest, requestComplete, nullptr));
return true;
}
bool LeaseSetDestination::RequestDestinationWithEncryptedLeaseSet (std::shared_ptr<const i2p::data::BlindedPublicKey> dest, RequestComplete requestComplete)
{
if (!dest || !m_Pool || !IsReady ())
{
if (requestComplete)
m_Service.post ([requestComplete](void){requestComplete (nullptr);});
return false;
}
auto storeHash = dest->GetStoreHash ();
auto leaseSet = FindLeaseSet (storeHash);
if (leaseSet)
{
if (requestComplete)
m_Service.post ([requestComplete, leaseSet](void){requestComplete (leaseSet);});
return true;
}
m_Service.post (std::bind (&LeaseSetDestination::RequestLeaseSet, shared_from_this (), storeHash, requestComplete, dest));
return true;
}
@@ -624,13 +678,20 @@ namespace client
});
}
void LeaseSetDestination::RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete)
void LeaseSetDestination::CancelDestinationRequestWithEncryptedLeaseSet (std::shared_ptr<const i2p::data::BlindedPublicKey> dest, bool notify)
{
if (dest)
CancelDestinationRequest (dest->GetStoreHash (), notify);
}
void LeaseSetDestination::RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete, std::shared_ptr<const i2p::data::BlindedPublicKey> requestedBlindedKey)
{
std::set<i2p::data::IdentHash> excluded;
auto floodfill = i2p::data::netdb.GetClosestFloodfill (dest, excluded);
if (floodfill)
{
auto request = std::make_shared<LeaseSetRequest> (m_Service);
request->requestedBlindedKey = requestedBlindedKey; // for encrypted LeaseSet2
if (requestComplete)
request->requestComplete.push_back (requestComplete);
auto ts = i2p::util::GetSecondsSinceEpoch ();
@@ -777,6 +838,9 @@ namespace client
m_DatagramDestination (nullptr), m_RefCounter (0),
m_ReadyChecker(GetService())
{
if (keys.IsOfflineSignature () && GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_LEASESET)
SetLeaseSetType (i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2); // offline keys can be published with LS2 only
m_EncryptionKeyType = GetIdentity ()->GetCryptoKeyType ();
// extract encryption type params for LS2
if (GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2 && params)
@@ -922,7 +986,7 @@ namespace client
{
auto s = GetSharedFromThis ();
RequestDestination (dest,
[s, streamRequestComplete, port](std::shared_ptr<i2p::data::LeaseSet> ls)
[s, streamRequestComplete, port](std::shared_ptr<const i2p::data::LeaseSet> ls)
{
if (ls)
streamRequestComplete(s->CreateStream (ls, port));
@@ -932,6 +996,24 @@ namespace client
}
}
void ClientDestination::CreateStream (StreamRequestComplete streamRequestComplete, std::shared_ptr<const i2p::data::BlindedPublicKey> dest, int port)
{
if (!streamRequestComplete)
{
LogPrint (eLogError, "Destination: request callback is not specified in CreateStream");
return;
}
auto s = GetSharedFromThis ();
RequestDestinationWithEncryptedLeaseSet (dest,
[s, streamRequestComplete, port](std::shared_ptr<i2p::data::LeaseSet> ls)
{
if (ls)
streamRequestComplete(s->CreateStream (ls, port));
else
streamRequestComplete (nullptr);
});
}
std::shared_ptr<i2p::stream::Stream> ClientDestination::CreateStream (std::shared_ptr<const i2p::data::LeaseSet> remote, int port)
{
if (m_StreamingDestination)
@@ -1036,21 +1118,22 @@ namespace client
void ClientDestination::CreateNewLeaseSet (std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels)
{
i2p::data::LocalLeaseSet * leaseSet = nullptr;
std::shared_ptr<i2p::data::LocalLeaseSet> leaseSet;
if (GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_LEASESET)
{
leaseSet = new i2p::data::LocalLeaseSet (GetIdentity (), m_EncryptionPublicKey, tunnels);
leaseSet = std::make_shared<i2p::data::LocalLeaseSet> (GetIdentity (), m_EncryptionPublicKey, tunnels);
// sign
Sign (leaseSet->GetBuffer (), leaseSet->GetBufferLen () - leaseSet->GetSignatureLen (), leaseSet->GetSignature ());
}
else
{
// standard LS2 (type 3) assumed for now. TODO: implement others
// standard LS2 (type 3) first
auto keyLen = m_Decryptor ? m_Decryptor->GetPublicKeyLen () : 256;
leaseSet = new i2p::data::LocalLeaseSet2 (i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2,
GetIdentity (), m_EncryptionKeyType, keyLen, m_EncryptionPublicKey, tunnels);
// sign
Sign (leaseSet->GetBuffer () - 1, leaseSet->GetBufferLen () - leaseSet->GetSignatureLen () + 1, leaseSet->GetSignature ()); // + leading store type
auto ls2 = std::make_shared<i2p::data::LocalLeaseSet2> (i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2,
m_Keys, m_EncryptionKeyType, keyLen, m_EncryptionPublicKey, tunnels);
if (GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2) // encrypt if type 5
ls2 = std::make_shared<i2p::data::LocalEncryptedLeaseSet2> (ls2, m_Keys);
leaseSet = ls2;
}
SetLeaseSet (leaseSet);
}

View File

@@ -82,6 +82,7 @@ namespace client
std::list<RequestComplete> requestComplete;
std::shared_ptr<i2p::tunnel::OutboundTunnel> outboundTunnel;
std::shared_ptr<i2p::tunnel::InboundTunnel> replyTunnel;
std::shared_ptr<const i2p::data::BlindedPublicKey> requestedBlindedKey; // for encrypted LeaseSet2 only
void Complete (std::shared_ptr<i2p::data::LeaseSet> ls)
{
@@ -107,9 +108,11 @@ namespace client
boost::asio::io_service& GetService () { return m_Service; };
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () { return m_Pool; };
bool IsReady () const { return m_LeaseSet && !m_LeaseSet->IsExpired () && m_Pool->GetOutboundTunnels ().size () > 0; };
std::shared_ptr<const i2p::data::LeaseSet> FindLeaseSet (const i2p::data::IdentHash& ident);
std::shared_ptr<i2p::data::LeaseSet> FindLeaseSet (const i2p::data::IdentHash& ident);
bool RequestDestination (const i2p::data::IdentHash& dest, RequestComplete requestComplete = nullptr);
bool RequestDestinationWithEncryptedLeaseSet (std::shared_ptr<const i2p::data::BlindedPublicKey> dest, RequestComplete requestComplete = nullptr);
void CancelDestinationRequest (const i2p::data::IdentHash& dest, bool notify = true);
void CancelDestinationRequestWithEncryptedLeaseSet (std::shared_ptr<const i2p::data::BlindedPublicKey> dest, bool notify = true);
// implements GarlicDestination
std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet ();
@@ -124,8 +127,9 @@ namespace client
protected:
void SetLeaseSet (i2p::data::LocalLeaseSet * newLeaseSet);
void SetLeaseSet (std::shared_ptr<const i2p::data::LocalLeaseSet> newLeaseSet);
int GetLeaseSetType () const { return m_LeaseSetType; };
void SetLeaseSetType (int leaseSetType) { m_LeaseSetType = leaseSetType; };
virtual void CleanupDestination () {}; // additional clean up in derived classes
// I2CP
virtual void HandleDataMessage (const uint8_t * buf, size_t len) = 0;
@@ -135,6 +139,7 @@ namespace client
void Run ();
void UpdateLeaseSet ();
std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSetMt ();
void Publish ();
void HandlePublishConfirmationTimer (const boost::system::error_code& ecode);
void HandlePublishVerificationTimer (const boost::system::error_code& ecode);
@@ -143,7 +148,7 @@ namespace client
void HandleDatabaseSearchReplyMessage (const uint8_t * buf, size_t len);
void HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
void RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete);
void RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete, std::shared_ptr<const i2p::data::BlindedPublicKey> requestedBlindedKey = nullptr);
bool SendLeaseSetRequest (const i2p::data::IdentHash& dest, std::shared_ptr<const i2p::data::RouterInfo> nextFloodfill, std::shared_ptr<LeaseSetRequest> request);
void HandleRequestTimoutTimer (const boost::system::error_code& ecode, const i2p::data::IdentHash& dest);
void HandleCleanupTimer (const boost::system::error_code& ecode);
@@ -160,7 +165,7 @@ namespace client
std::shared_ptr<i2p::tunnel::TunnelPool> m_Pool;
std::mutex m_LeaseSetMutex;
std::shared_ptr<i2p::data::LocalLeaseSet> m_LeaseSet;
std::shared_ptr<const i2p::data::LocalLeaseSet> m_LeaseSet;
bool m_IsPublic;
uint32_t m_PublishReplyToken;
uint64_t m_LastSubmissionTime; // in seconds
@@ -208,6 +213,7 @@ namespace client
std::shared_ptr<i2p::stream::StreamingDestination> GetStreamingDestination (int port = 0) const;
// following methods operate with default streaming destination
void CreateStream (StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash& dest, int port = 0);
void CreateStream (StreamRequestComplete streamRequestComplete, std::shared_ptr<const i2p::data::BlindedPublicKey> dest, int port = 0);
std::shared_ptr<i2p::stream::Stream> CreateStream (std::shared_ptr<const i2p::data::LeaseSet> remote, int port = 0);
void AcceptStreams (const i2p::stream::StreamingDestination::Acceptor& acceptor);
void StopAcceptingStreams ();

View File

@@ -121,8 +121,8 @@ namespace crypto
return passed;
}
void Ed25519::Sign (const uint8_t * expandedPrivateKey, const uint8_t * publicKeyEncoded, const uint8_t * buf, size_t len,
uint8_t * signature) const
void Ed25519::Sign (const uint8_t * expandedPrivateKey, const uint8_t * publicKeyEncoded,
const uint8_t * buf, size_t len, uint8_t * signature) const
{
BN_CTX * bnCtx = BN_CTX_new ();
// calculate r
@@ -153,6 +153,44 @@ namespace crypto
BN_CTX_free (bnCtx);
}
void Ed25519::SignRedDSA (const uint8_t * privateKey, const uint8_t * publicKeyEncoded,
const uint8_t * buf, size_t len, uint8_t * signature) const
{
BN_CTX * bnCtx = BN_CTX_new ();
// T = 80 random bytes
uint8_t T[80];
RAND_bytes (T, 80);
// calculate r = H*(T || publickey || data)
SHA512_CTX ctx;
SHA512_Init (&ctx);
SHA512_Update (&ctx, T, 80);
SHA512_Update (&ctx, publicKeyEncoded, 32);
SHA512_Update (&ctx, buf, len); // data
uint8_t digest[64];
SHA512_Final (digest, &ctx);
BIGNUM * r = DecodeBN<64> (digest);
BN_mod (r, r, l, bnCtx); // % l
EncodeBN (r, digest, 32);
// calculate R
uint8_t R[EDDSA25519_SIGNATURE_LENGTH/2]; // we must use separate buffer because signature might be inside buf
EncodePoint (Normalize (MulB (digest, bnCtx), bnCtx), R);
// calculate S
SHA512_Init (&ctx);
SHA512_Update (&ctx, R, EDDSA25519_SIGNATURE_LENGTH/2); // R
SHA512_Update (&ctx, publicKeyEncoded, EDDSA25519_PUBLIC_KEY_LENGTH); // public key
SHA512_Update (&ctx, buf, len); // data
SHA512_Final (digest, &ctx);
BIGNUM * h = DecodeBN<64> (digest);
// S = (r + h*a) % l
BIGNUM * a = DecodeBN<EDDSA25519_PRIVATE_KEY_LENGTH> (privateKey);
BN_mod_mul (h, h, a, l, bnCtx); // %l
BN_mod_add (h, h, r, l, bnCtx); // %l
memcpy (signature, R, EDDSA25519_SIGNATURE_LENGTH/2);
EncodeBN (h, signature + EDDSA25519_SIGNATURE_LENGTH/2, EDDSA25519_SIGNATURE_LENGTH/2); // S
BN_free (r); BN_free (h); BN_free (a);
BN_CTX_free (bnCtx);
}
EDDSAPoint Ed25519::Sum (const EDDSAPoint& p1, const EDDSAPoint& p2, BN_CTX * ctx) const
{
// x3 = (x1*y2+y1*x2)*(z1*z2-d*t1*t2)
@@ -491,6 +529,39 @@ namespace crypto
}
#endif
void Ed25519::BlindPublicKey (const uint8_t * pub, const uint8_t * seed, uint8_t * blinded)
{
BN_CTX * ctx = BN_CTX_new ();
// calculate alpha = seed mod l
BIGNUM * alpha = DecodeBN<64> (seed); // seed is in Little Endian
BN_mod (alpha, alpha, l, ctx); // % l
uint8_t priv[32];
EncodeBN (alpha, priv, 32); // back to Little Endian
BN_free (alpha);
// A' = BLIND_PUBKEY(A, alpha) = A + DERIVE_PUBLIC(alpha)
auto A1 = Sum (DecodePublicKey (pub, ctx), MulB (priv, ctx), ctx); // pub + B*alpha
EncodePublicKey (A1, blinded, ctx);
BN_CTX_free (ctx);
}
void Ed25519::BlindPrivateKey (const uint8_t * priv, const uint8_t * seed, uint8_t * blindedPriv, uint8_t * blindedPub)
{
BN_CTX * ctx = BN_CTX_new ();
// calculate alpha = seed mod l
BIGNUM * alpha = DecodeBN<64> (seed); // seed is in Little Endian
BN_mod (alpha, alpha, l, ctx); // % l
BIGNUM * p = DecodeBN<32> (priv); // priv is in Little Endian
BN_add (alpha, alpha, p); // alpha = alpha + priv
// a' = BLIND_PRIVKEY(a, alpha) = (a + alpha) mod L
BN_mod (alpha, alpha, l, ctx); // % l
EncodeBN (alpha, blindedPriv, 32);
// A' = DERIVE_PUBLIC(a')
auto A1 = MulB (blindedPriv, ctx);
EncodePublicKey (A1, blindedPub, ctx);
BN_free (alpha); BN_free (p);
BN_CTX_free (ctx);
}
void Ed25519::ExpandPrivateKey (const uint8_t * key, uint8_t * expandedKey)
{
SHA512 (key, EDDSA25519_PRIVATE_KEY_LENGTH, expandedKey);
@@ -499,6 +570,18 @@ namespace crypto
expandedKey[EDDSA25519_PRIVATE_KEY_LENGTH - 1] |= 0x40; // set second bit
}
void Ed25519::CreateRedDSAPrivateKey (uint8_t * priv)
{
uint8_t seed[32];
RAND_bytes (seed, 32);
BIGNUM * p = DecodeBN<32> (seed);
BN_CTX * ctx = BN_CTX_new ();
BN_mod (p, p, l, ctx); // % l
EncodeBN (p, priv, 32);
BN_CTX_free (ctx);
BN_free (p);
}
static std::unique_ptr<Ed25519> g_Ed25519;
std::unique_ptr<Ed25519>& GetEd25519 ()
{

View File

@@ -80,12 +80,16 @@ namespace crypto
void ScalarMul (const uint8_t * p, const uint8_t * e, uint8_t * buf, BN_CTX * ctx) const; // p is point, e is number for x25519
void ScalarMulB (const uint8_t * e, uint8_t * buf, BN_CTX * ctx) const;
#endif
void BlindPublicKey (const uint8_t * pub, const uint8_t * seed, uint8_t * blinded); // for encrypted LeaseSet2, pub - 32, seed - 64, blinded - 32
void BlindPrivateKey (const uint8_t * priv, const uint8_t * seed, uint8_t * blindedPriv, uint8_t * blindedPub); // for encrypted LeaseSet2, pub - 32, seed - 64, blinded - 32
bool Verify (const EDDSAPoint& publicKey, const uint8_t * digest, const uint8_t * signature) const;
void Sign (const uint8_t * expandedPrivateKey, const uint8_t * publicKeyEncoded, const uint8_t * buf, size_t len, uint8_t * signature) const;
void SignRedDSA (const uint8_t * privateKey, const uint8_t * publicKeyEncoded, const uint8_t * buf, size_t len, uint8_t * signature) const;
static void ExpandPrivateKey (const uint8_t * key, uint8_t * expandedKey); // key - 32 bytes, expandedKey - 64 bytes
void CreateRedDSAPrivateKey (uint8_t * priv); // priv is 32 bytes
private:
EDDSAPoint Sum (const EDDSAPoint& p1, const EDDSAPoint& p2, BN_CTX * ctx) const;

View File

@@ -11,6 +11,7 @@
#ifdef _WIN32
#include <shlobj.h>
#include <windows.h>
#endif
#include "Base.h"
@@ -20,187 +21,211 @@
namespace i2p {
namespace fs {
std::string appName = "i2pd";
std::string dataDir = "";
std::string appName = "i2pd";
std::string dataDir = "";
#ifdef _WIN32
std::string dirSep = "\\";
std::string dirSep = "\\";
#else
std::string dirSep = "/";
std::string dirSep = "/";
#endif
const std::string & GetAppName () {
return appName;
}
const std::string & GetAppName () {
return appName;
}
void SetAppName (const std::string& name) {
appName = name;
}
void SetAppName (const std::string& name) {
appName = name;
}
const std::string & GetDataDir () {
return dataDir;
}
const std::string & GetDataDir () {
return dataDir;
}
void DetectDataDir(const std::string & cmdline_param, bool isService) {
if (cmdline_param != "") {
dataDir = cmdline_param;
return;
}
void DetectDataDir(const std::string & cmdline_param, bool isService) {
if (cmdline_param != "") {
dataDir = cmdline_param;
return;
}
#if defined(WIN32) || defined(_WIN32)
char localAppData[MAX_PATH];
// check executable directory first
GetModuleFileName (NULL, localAppData, MAX_PATH);
auto execPath = boost::filesystem::path(localAppData).parent_path();
// if config file exists in .exe's folder use it
if(boost::filesystem::exists(execPath/"i2pd.conf")) // TODO: magic string
dataDir = execPath.string ();
else
{
// otherwise %appdata%
SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, localAppData);
dataDir = std::string(localAppData) + "\\" + appName;
}
return;
char localAppData[MAX_PATH];
// check executable directory first
if(!GetModuleFileName(NULL, localAppData, MAX_PATH))
{
#if defined(WIN32_APP)
MessageBox(NULL, TEXT("Unable to get application path!"), TEXT("I2Pd: error"), MB_ICONERROR | MB_OK);
#else
fprintf(stderr, "Error: Unable to get application path!");
#endif
exit(1);
}
else
{
auto execPath = boost::filesystem::path(localAppData).parent_path();
// if config file exists in .exe's folder use it
if(boost::filesystem::exists(execPath/"i2pd.conf")) // TODO: magic string
dataDir = execPath.string ();
else // otherwise %appdata%
{
if(SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, localAppData) != S_OK)
{
#if defined(WIN32_APP)
MessageBox(NULL, TEXT("Unable to get AppData path!"), TEXT("I2Pd: error"), MB_ICONERROR | MB_OK);
#else
fprintf(stderr, "Error: Unable to get AppData path!");
#endif
exit(1);
}
else
dataDir = std::string(localAppData) + "\\" + appName;
}
}
return;
#elif defined(MAC_OSX)
char *home = getenv("HOME");
dataDir = (home != NULL && strlen(home) > 0) ? home : "";
dataDir += "/Library/Application Support/" + appName;
return;
char *home = getenv("HOME");
dataDir = (home != NULL && strlen(home) > 0) ? home : "";
dataDir += "/Library/Application Support/" + appName;
return;
#else /* other unix */
#if defined(ANDROID)
const char * ext = getenv("EXTERNAL_STORAGE");
if (!ext) ext = "/sdcard";
if (boost::filesystem::exists(ext))
{
dataDir = std::string (ext) + "/" + appName;
const char * ext = getenv("EXTERNAL_STORAGE");
if (!ext) ext = "/sdcard";
if (boost::filesystem::exists(ext))
{
dataDir = std::string (ext) + "/" + appName;
return;
}
#endif
// otherwise use /data/files
char *home = getenv("HOME");
if (isService) {
dataDir = "/var/lib/" + appName;
} else if (home != NULL && strlen(home) > 0) {
dataDir = std::string(home) + "/." + appName;
} else {
dataDir = "/tmp/" + appName;
}
return;
#endif
}
// otherwise use /data/files
#endif
char *home = getenv("HOME");
if (isService) {
dataDir = "/var/lib/" + appName;
} else if (home != NULL && strlen(home) > 0) {
dataDir = std::string(home) + "/." + appName;
} else {
dataDir = "/tmp/" + appName;
}
return;
#endif
}
bool Init() {
if (!boost::filesystem::exists(dataDir))
boost::filesystem::create_directory(dataDir);
std::string destinations = DataDirPath("destinations");
if (!boost::filesystem::exists(destinations))
boost::filesystem::create_directory(destinations);
std::string tags = DataDirPath("tags");
if (!boost::filesystem::exists(tags))
boost::filesystem::create_directory(tags);
else
i2p::garlic::CleanUpTagsFiles ();
bool Init() {
if (!boost::filesystem::exists(dataDir))
boost::filesystem::create_directory(dataDir);
return true;
}
std::string destinations = DataDirPath("destinations");
if (!boost::filesystem::exists(destinations))
boost::filesystem::create_directory(destinations);
bool ReadDir(const std::string & path, std::vector<std::string> & files) {
if (!boost::filesystem::exists(path))
return false;
boost::filesystem::directory_iterator it(path);
boost::filesystem::directory_iterator end;
std::string tags = DataDirPath("tags");
if (!boost::filesystem::exists(tags))
boost::filesystem::create_directory(tags);
else
i2p::garlic::CleanUpTagsFiles ();
for ( ; it != end; it++) {
if (!boost::filesystem::is_regular_file(it->status()))
continue;
files.push_back(it->path().string());
}
return true;
}
return true;
}
bool ReadDir(const std::string & path, std::vector<std::string> & files) {
if (!boost::filesystem::exists(path))
return false;
boost::filesystem::directory_iterator it(path);
boost::filesystem::directory_iterator end;
bool Exists(const std::string & path) {
return boost::filesystem::exists(path);
}
for ( ; it != end; it++) {
if (!boost::filesystem::is_regular_file(it->status()))
continue;
files.push_back(it->path().string());
}
uint32_t GetLastUpdateTime (const std::string & path)
{
if (!boost::filesystem::exists(path)) return 0;
boost::system::error_code ec;
auto t = boost::filesystem::last_write_time (path, ec);
return ec ? 0 : t;
}
return true;
}
bool Remove(const std::string & path) {
if (!boost::filesystem::exists(path))
return false;
return boost::filesystem::remove(path);
}
bool Exists(const std::string & path) {
return boost::filesystem::exists(path);
}
uint32_t GetLastUpdateTime (const std::string & path)
{
if (!boost::filesystem::exists(path))
return 0;
boost::system::error_code ec;
auto t = boost::filesystem::last_write_time (path, ec);
return ec ? 0 : t;
}
bool Remove(const std::string & path) {
if (!boost::filesystem::exists(path))
return false;
return boost::filesystem::remove(path);
}
bool CreateDirectory (const std::string& path)
{
if (boost::filesystem::exists(path) &&
boost::filesystem::is_directory (boost::filesystem::status (path))) return true;
if (boost::filesystem::exists(path) && boost::filesystem::is_directory (boost::filesystem::status (path)))
return true;
return boost::filesystem::create_directory(path);
}
void HashedStorage::SetPlace(const std::string &path) {
root = path + i2p::fs::dirSep + name;
}
void HashedStorage::SetPlace(const std::string &path) {
root = path + i2p::fs::dirSep + name;
}
bool HashedStorage::Init(const char * chars, size_t count) {
if (!boost::filesystem::exists(root)) {
boost::filesystem::create_directories(root);
}
bool HashedStorage::Init(const char * chars, size_t count) {
if (!boost::filesystem::exists(root)) {
boost::filesystem::create_directories(root);
}
for (size_t i = 0; i < count; i++) {
auto p = root + i2p::fs::dirSep + prefix1 + chars[i];
if (boost::filesystem::exists(p))
continue;
if (boost::filesystem::create_directory(p))
continue; /* ^ throws exception on failure */
return false;
}
return true;
}
for (size_t i = 0; i < count; i++) {
auto p = root + i2p::fs::dirSep + prefix1 + chars[i];
if (boost::filesystem::exists(p))
continue;
if (boost::filesystem::create_directory(p))
continue; /* ^ throws exception on failure */
return false;
}
return true;
}
std::string HashedStorage::Path(const std::string & ident) const {
std::string safe_ident = ident;
std::replace(safe_ident.begin(), safe_ident.end(), '/', '-');
std::replace(safe_ident.begin(), safe_ident.end(), '\\', '-');
std::string HashedStorage::Path(const std::string & ident) const {
std::string safe_ident = ident;
std::replace(safe_ident.begin(), safe_ident.end(), '/', '-');
std::replace(safe_ident.begin(), safe_ident.end(), '\\', '-');
std::stringstream t("");
t << this->root << i2p::fs::dirSep;
t << prefix1 << safe_ident[0] << i2p::fs::dirSep;
t << prefix2 << safe_ident << "." << suffix;
std::stringstream t("");
t << this->root << i2p::fs::dirSep;
t << prefix1 << safe_ident[0] << i2p::fs::dirSep;
t << prefix2 << safe_ident << "." << suffix;
return t.str();
}
return t.str();
}
void HashedStorage::Remove(const std::string & ident) {
std::string path = Path(ident);
if (!boost::filesystem::exists(path))
return;
boost::filesystem::remove(path);
}
void HashedStorage::Remove(const std::string & ident) {
std::string path = Path(ident);
if (!boost::filesystem::exists(path))
return;
boost::filesystem::remove(path);
}
void HashedStorage::Traverse(std::vector<std::string> & files) {
Iterate([&files] (const std::string & fname) {
files.push_back(fname);
});
}
void HashedStorage::Traverse(std::vector<std::string> & files) {
Iterate([&files] (const std::string & fname) {
files.push_back(fname);
});
}
void HashedStorage::Iterate(FilenameVisitor v)
{
boost::filesystem::path p(root);
boost::filesystem::recursive_directory_iterator it(p);
boost::filesystem::recursive_directory_iterator end;
void HashedStorage::Iterate(FilenameVisitor v)
{
boost::filesystem::path p(root);
boost::filesystem::recursive_directory_iterator it(p);
boost::filesystem::recursive_directory_iterator end;
for ( ; it != end; it++) {
if (!boost::filesystem::is_regular_file( it->status() ))
continue;
const std::string & t = it->path().string();
v(t);
}
}
for ( ; it != end; it++) {
if (!boost::filesystem::is_regular_file( it->status() ))
continue;
const std::string & t = it->path().string();
v(t);
}
}
} // fs
} // i2p

View File

@@ -66,7 +66,7 @@ namespace fs {
/** @brief Returns current application name, default 'i2pd' */
const std::string & GetAppName ();
/** @brief Set applicaton name, affects autodetection of datadir */
/** @brief Set application name, affects autodetection of datadir */
void SetAppName (const std::string& name);
/** @brief Returns datadir path */

View File

@@ -578,7 +578,7 @@ namespace garlic
tunnel = from->GetTunnelPool ()->GetNextOutboundTunnel ();
else
LogPrint (eLogError, "Garlic: Tunnel pool is not set for inbound tunnel");
if (tunnel) // we have send it through an outbound tunnel
if (tunnel) // we have sent it through an outbound tunnel
tunnel->SendTunnelDataMsg (gwHash, gwTunnel, msg);
else
LogPrint (eLogWarning, "Garlic: No outbound tunnels available for garlic clove");

View File

@@ -55,12 +55,16 @@ namespace http {
static std::pair<std::string, std::string> parse_header_line(const std::string& line)
{
std::size_t pos = 0;
std::size_t len = 2; /* strlen(": ") */
std::size_t len = 1; /*: */
std::size_t max = line.length();
if ((pos = line.find(": ", pos)) == std::string::npos)
return std::make_pair("", "");
while ((pos + len) < max && isspace(line.at(pos + len)))
len++;
if ((pos = line.find(':', pos)) == std::string::npos)
return std::make_pair("", ""); // no ':' found
if (pos + 1 < max) // ':' at the end of header is valid
{
while ((pos + len) < max && isspace(line.at(pos + len)))
len++;
if (len == 1) return std::make_pair("", ""); // no following space, but something else
}
return std::make_pair(line.substr(0, pos), line.substr(pos + len));
}

View File

@@ -279,7 +279,7 @@ namespace i2p
if (!leaseSet) return nullptr;
auto m = NewI2NPShortMessage ();
uint8_t * payload = m->GetPayload ();
memcpy (payload + DATABASE_STORE_KEY_OFFSET, leaseSet->GetIdentHash (), 32);
memcpy (payload + DATABASE_STORE_KEY_OFFSET, leaseSet->GetStoreHash (), 32);
payload[DATABASE_STORE_TYPE_OFFSET] = leaseSet->GetStoreType (); // LeaseSet or LeaseSet2
htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, replyToken);
size_t size = DATABASE_STORE_HEADER_SIZE;

View File

@@ -1,8 +1,7 @@
#include <time.h>
#include <stdio.h>
#include "Crypto.h"
#include "I2PEndian.h"
#include "Log.h"
#include "Timestamp.h"
#include "Identity.h"
namespace i2p
@@ -77,6 +76,7 @@ namespace data
LogPrint (eLogError, "Identity: RSA signing key type ", (int)type, " is not supported");
break;
case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519:
case SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519:
{
size_t padding = 128 - i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH; // 96 = 128 - 32
RAND_bytes (m_StandardIdentity.signingKey, padding);
@@ -275,6 +275,13 @@ namespace data
return 128;
}
const uint8_t * IdentityEx::GetSigningPublicKeyBuffer () const
{
auto keyLen = GetSigningPublicKeyLen ();
if (keyLen > 128) return nullptr; // P521
return m_StandardIdentity.signingKey + 128 - keyLen;
}
size_t IdentityEx::GetSigningPrivateKeyLen () const
{
if (!m_Verifier) CreateVerifier ();
@@ -336,6 +343,8 @@ namespace data
return new i2p::crypto::GOSTR3410_256_Verifier (i2p::crypto::eGOSTR3410CryptoProA);
case SIGNING_KEY_TYPE_GOSTR3410_TC26_A_512_GOSTR3411_512:
return new i2p::crypto::GOSTR3410_512_Verifier (i2p::crypto::eGOSTR3410TC26A512);
case SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519:
return new i2p::crypto::RedDSA25519Verifier ();
case SIGNING_KEY_TYPE_RSA_SHA256_2048:
case SIGNING_KEY_TYPE_RSA_SHA384_3072:
case SIGNING_KEY_TYPE_RSA_SHA512_4096:
@@ -432,6 +441,9 @@ namespace data
m_Public = std::make_shared<IdentityEx>(Identity (keys));
memcpy (m_PrivateKey, keys.privateKey, 256); // 256
memcpy (m_SigningPrivateKey, keys.signingPrivateKey, m_Public->GetSigningPrivateKeyLen ());
m_OfflineSignature.resize (0);
m_TransientSignatureLen = 0;
m_TransientSigningPrivateKeyLen = 0;
m_Signer = nullptr;
CreateSigner ();
return *this;
@@ -441,12 +453,23 @@ namespace data
{
m_Public = std::make_shared<IdentityEx>(*other.m_Public);
memcpy (m_PrivateKey, other.m_PrivateKey, 256); // 256
memcpy (m_SigningPrivateKey, other.m_SigningPrivateKey, m_Public->GetSigningPrivateKeyLen ());
m_OfflineSignature = other.m_OfflineSignature;
m_TransientSignatureLen = other.m_TransientSignatureLen;
m_TransientSigningPrivateKeyLen = other.m_TransientSigningPrivateKeyLen;
memcpy (m_SigningPrivateKey, other.m_SigningPrivateKey, m_TransientSigningPrivateKeyLen > 0 ? m_TransientSigningPrivateKeyLen : m_Public->GetSigningPrivateKeyLen ());
m_Signer = nullptr;
CreateSigner ();
return *this;
}
size_t PrivateKeys::GetFullLen () const
{
size_t ret = m_Public->GetFullLen () + 256 + m_Public->GetSigningPrivateKeyLen ();
if (IsOfflineSignature ())
ret += m_OfflineSignature.size () + m_TransientSigningPrivateKeyLen;
return ret;
}
size_t PrivateKeys::FromBuffer (const uint8_t * buf, size_t len)
{
m_Public = std::make_shared<IdentityEx>();
@@ -455,11 +478,50 @@ namespace data
memcpy (m_PrivateKey, buf + ret, 256); // private key always 256
ret += 256;
size_t signingPrivateKeySize = m_Public->GetSigningPrivateKeyLen ();
if(signingPrivateKeySize + ret > len) return 0; // overflow
if(signingPrivateKeySize + ret > len || signingPrivateKeySize > 128) return 0; // overflow
memcpy (m_SigningPrivateKey, buf + ret, signingPrivateKeySize);
ret += signingPrivateKeySize;
m_Signer = nullptr;
CreateSigner ();
// check if signing private key is all zeros
bool allzeros = true;
for (size_t i = 0; i < signingPrivateKeySize; i++)
if (m_SigningPrivateKey[i])
{
allzeros = false;
break;
}
if (allzeros)
{
// offline information
const uint8_t * offlineInfo = buf + ret;
ret += 4; // expires timestamp
SigningKeyType keyType = bufbe16toh (buf + ret); ret += 2; // key type
std::unique_ptr<i2p::crypto::Verifier> transientVerifier (IdentityEx::CreateVerifier (keyType));
if (!transientVerifier) return 0;
auto keyLen = transientVerifier->GetPublicKeyLen ();
if (keyLen + ret > len) return 0;
transientVerifier->SetPublicKey (buf + ret); ret += keyLen;
if (m_Public->GetSignatureLen () + ret > len) return 0;
if (!m_Public->Verify (offlineInfo, keyLen + 6, buf + ret))
{
LogPrint (eLogError, "Identity: offline signature verification failed");
return 0;
}
ret += m_Public->GetSignatureLen ();
m_TransientSignatureLen = transientVerifier->GetSignatureLen ();
// copy offline signature
size_t offlineInfoLen = buf + ret - offlineInfo;
m_OfflineSignature.resize (offlineInfoLen);
memcpy (m_OfflineSignature.data (), offlineInfo, offlineInfoLen);
// override signing private key
m_TransientSigningPrivateKeyLen = transientVerifier->GetPrivateKeyLen ();
if (m_TransientSigningPrivateKeyLen + ret > len || m_TransientSigningPrivateKeyLen > 128) return 0;
memcpy (m_SigningPrivateKey, buf + ret, m_TransientSigningPrivateKeyLen);
ret += m_TransientSigningPrivateKeyLen;
CreateSigner (keyType);
}
else
CreateSigner (m_Public->GetSigningKeyType ());
return ret;
}
@@ -470,8 +532,23 @@ namespace data
ret += 256;
size_t signingPrivateKeySize = m_Public->GetSigningPrivateKeyLen ();
if(ret + signingPrivateKeySize > len) return 0; // overflow
memcpy (buf + ret, m_SigningPrivateKey, signingPrivateKeySize);
if (IsOfflineSignature ())
memset (buf + ret, 0, signingPrivateKeySize);
else
memcpy (buf + ret, m_SigningPrivateKey, signingPrivateKeySize);
ret += signingPrivateKeySize;
if (IsOfflineSignature ())
{
// offline signature
auto offlineSignatureLen = m_OfflineSignature.size ();
if (ret + offlineSignatureLen > len) return 0;
memcpy (buf + ret, m_OfflineSignature.data (), offlineSignatureLen);
ret += offlineSignatureLen;
// transient private key
if (ret + m_TransientSigningPrivateKeyLen > len) return 0;
memcpy (buf + ret, m_SigningPrivateKey, m_TransientSigningPrivateKeyLen);
ret += m_TransientSigningPrivateKeyLen;
}
return ret;
}
@@ -506,38 +583,66 @@ namespace data
void PrivateKeys::CreateSigner () const
{
if (m_Signer) return;
switch (m_Public->GetSigningKeyType ())
if (IsOfflineSignature ())
CreateSigner (bufbe16toh (m_OfflineSignature.data () + 4)); // key type
else
CreateSigner (m_Public->GetSigningKeyType ());
}
void PrivateKeys::CreateSigner (SigningKeyType keyType) const
{
if (m_Signer) return;
if (keyType == SIGNING_KEY_TYPE_DSA_SHA1)
m_Signer.reset (new i2p::crypto::DSASigner (m_SigningPrivateKey, m_Public->GetStandardIdentity ().signingKey));
else if (keyType == SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519 && !IsOfflineSignature ())
m_Signer.reset (new i2p::crypto::EDDSA25519Signer (m_SigningPrivateKey, m_Public->GetStandardIdentity ().certificate - i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH)); // TODO: remove public key check
else
{
// public key is not required
auto signer = CreateSigner (keyType, m_SigningPrivateKey);
if (signer) m_Signer.reset (signer);
}
}
i2p::crypto::Signer * PrivateKeys::CreateSigner (SigningKeyType keyType, const uint8_t * priv)
{
switch (keyType)
{
case SIGNING_KEY_TYPE_DSA_SHA1:
m_Signer.reset (new i2p::crypto::DSASigner (m_SigningPrivateKey, m_Public->GetStandardIdentity ().signingKey));
break;
case SIGNING_KEY_TYPE_ECDSA_SHA256_P256:
m_Signer.reset (new i2p::crypto::ECDSAP256Signer (m_SigningPrivateKey));
return new i2p::crypto::ECDSAP256Signer (priv);
break;
case SIGNING_KEY_TYPE_ECDSA_SHA384_P384:
m_Signer.reset (new i2p::crypto::ECDSAP384Signer (m_SigningPrivateKey));
return new i2p::crypto::ECDSAP384Signer (priv);
break;
case SIGNING_KEY_TYPE_ECDSA_SHA512_P521:
m_Signer.reset (new i2p::crypto::ECDSAP521Signer (m_SigningPrivateKey));
return new i2p::crypto::ECDSAP521Signer (priv);
break;
case SIGNING_KEY_TYPE_RSA_SHA256_2048:
case SIGNING_KEY_TYPE_RSA_SHA384_3072:
case SIGNING_KEY_TYPE_RSA_SHA512_4096:
LogPrint (eLogError, "Identity: RSA signing key type ", (int)m_Public->GetSigningKeyType (), " is not supported");
LogPrint (eLogError, "Identity: RSA signing key type ", (int)keyType, " is not supported");
break;
case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519:
m_Signer.reset (new i2p::crypto::EDDSA25519Signer (m_SigningPrivateKey, m_Public->GetStandardIdentity ().certificate - i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH));
return new i2p::crypto::EDDSA25519Signer (priv, nullptr);
break;
case SIGNING_KEY_TYPE_GOSTR3410_CRYPTO_PRO_A_GOSTR3411_256:
m_Signer.reset (new i2p::crypto::GOSTR3410_256_Signer (i2p::crypto::eGOSTR3410CryptoProA, m_SigningPrivateKey));
return new i2p::crypto::GOSTR3410_256_Signer (i2p::crypto::eGOSTR3410CryptoProA, priv);
break;
case SIGNING_KEY_TYPE_GOSTR3410_TC26_A_512_GOSTR3411_512:
m_Signer.reset (new i2p::crypto::GOSTR3410_512_Signer (i2p::crypto::eGOSTR3410TC26A512, m_SigningPrivateKey));
return new i2p::crypto::GOSTR3410_512_Signer (i2p::crypto::eGOSTR3410TC26A512, priv);
break;
case SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519:
return new i2p::crypto::RedDSA25519Signer (priv);
break;
default:
LogPrint (eLogError, "Identity: Signing key type ", (int)m_Public->GetSigningKeyType (), " is not supported");
LogPrint (eLogError, "Identity: Signing key type ", (int)keyType, " is not supported");
}
return nullptr;
}
size_t PrivateKeys::GetSignatureLen () const
{
return IsOfflineSignature () ? m_TransientSignatureLen : m_Public->GetSignatureLen ();
}
uint8_t * PrivateKeys::GetPadding()
@@ -582,35 +687,7 @@ namespace data
PrivateKeys keys;
// signature
uint8_t signingPublicKey[512]; // signing public key is 512 bytes max
switch (type)
{
case SIGNING_KEY_TYPE_ECDSA_SHA256_P256:
i2p::crypto::CreateECDSAP256RandomKeys (keys.m_SigningPrivateKey, signingPublicKey);
break;
case SIGNING_KEY_TYPE_ECDSA_SHA384_P384:
i2p::crypto::CreateECDSAP384RandomKeys (keys.m_SigningPrivateKey, signingPublicKey);
break;
case SIGNING_KEY_TYPE_ECDSA_SHA512_P521:
i2p::crypto::CreateECDSAP521RandomKeys (keys.m_SigningPrivateKey, signingPublicKey);
break;
case SIGNING_KEY_TYPE_RSA_SHA256_2048:
case SIGNING_KEY_TYPE_RSA_SHA384_3072:
case SIGNING_KEY_TYPE_RSA_SHA512_4096:
LogPrint (eLogWarning, "Identity: RSA signature type is not supported. Creating EdDSA");
// no break here
case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519:
i2p::crypto::CreateEDDSA25519RandomKeys (keys.m_SigningPrivateKey, signingPublicKey);
break;
case SIGNING_KEY_TYPE_GOSTR3410_CRYPTO_PRO_A_GOSTR3411_256:
i2p::crypto::CreateGOSTR3410RandomKeys (i2p::crypto::eGOSTR3410CryptoProA, keys.m_SigningPrivateKey, signingPublicKey);
break;
case SIGNING_KEY_TYPE_GOSTR3410_TC26_A_512_GOSTR3411_512:
i2p::crypto::CreateGOSTR3410RandomKeys (i2p::crypto::eGOSTR3410TC26A512, keys.m_SigningPrivateKey, signingPublicKey);
break;
default:
LogPrint (eLogWarning, "Identity: Signing key type ", (int)type, " is not supported. Create DSA-SHA1");
return PrivateKeys (i2p::data::CreateRandomKeys ()); // DSA-SHA1
}
GenerateSigningKeyPair (type, keys.m_SigningPrivateKey, signingPublicKey);
// encryption
uint8_t publicKey[256];
GenerateCryptoKeyPair (cryptoType, keys.m_PrivateKey, publicKey);
@@ -623,6 +700,42 @@ namespace data
return PrivateKeys (i2p::data::CreateRandomKeys ()); // DSA-SHA1
}
void PrivateKeys::GenerateSigningKeyPair (SigningKeyType type, uint8_t * priv, uint8_t * pub)
{
switch (type)
{
case SIGNING_KEY_TYPE_ECDSA_SHA256_P256:
i2p::crypto::CreateECDSAP256RandomKeys (priv, pub);
break;
case SIGNING_KEY_TYPE_ECDSA_SHA384_P384:
i2p::crypto::CreateECDSAP384RandomKeys (priv, pub);
break;
case SIGNING_KEY_TYPE_ECDSA_SHA512_P521:
i2p::crypto::CreateECDSAP521RandomKeys (priv, pub);
break;
case SIGNING_KEY_TYPE_RSA_SHA256_2048:
case SIGNING_KEY_TYPE_RSA_SHA384_3072:
case SIGNING_KEY_TYPE_RSA_SHA512_4096:
LogPrint (eLogWarning, "Identity: RSA signature type is not supported. Creating EdDSA");
// no break here
case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519:
i2p::crypto::CreateEDDSA25519RandomKeys (priv, pub);
break;
case SIGNING_KEY_TYPE_GOSTR3410_CRYPTO_PRO_A_GOSTR3411_256:
i2p::crypto::CreateGOSTR3410RandomKeys (i2p::crypto::eGOSTR3410CryptoProA, priv, pub);
break;
case SIGNING_KEY_TYPE_GOSTR3410_TC26_A_512_GOSTR3411_512:
i2p::crypto::CreateGOSTR3410RandomKeys (i2p::crypto::eGOSTR3410TC26A512, priv, pub);
break;
case SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519:
i2p::crypto::CreateRedDSA25519RandomKeys (priv, pub);
break;
default:
LogPrint (eLogWarning, "Identity: Signing key type ", (int)type, " is not supported. Create DSA-SHA1");
i2p::crypto::CreateDSARandomKeys (priv, pub); // DSA-SHA1
}
}
void PrivateKeys::GenerateCryptoKeyPair (CryptoKeyType type, uint8_t * priv, uint8_t * pub)
{
switch (type)
@@ -642,6 +755,27 @@ namespace data
}
}
PrivateKeys PrivateKeys::CreateOfflineKeys (SigningKeyType type, uint32_t expires) const
{
PrivateKeys keys (*this);
std::unique_ptr<i2p::crypto::Verifier> verifier (IdentityEx::CreateVerifier (type));
if (verifier)
{
size_t pubKeyLen = verifier->GetPublicKeyLen ();
keys.m_TransientSigningPrivateKeyLen = verifier->GetPrivateKeyLen ();
keys.m_TransientSignatureLen = verifier->GetSignatureLen ();
keys.m_OfflineSignature.resize (pubKeyLen + m_Public->GetSignatureLen () + 6);
htobe32buf (keys.m_OfflineSignature.data (), expires); // expires
htobe16buf (keys.m_OfflineSignature.data () + 4, type); // type
GenerateSigningKeyPair (type, keys.m_SigningPrivateKey, keys.m_OfflineSignature.data () + 6); // public key
Sign (keys.m_OfflineSignature.data (), pubKeyLen + 6, keys.m_OfflineSignature.data () + 6 + pubKeyLen); // signature
// recreate signer
keys.m_Signer = nullptr;
keys.CreateSigner (type);
}
return keys;
}
Keys CreateRandomKeys ()
{
Keys keys;
@@ -656,15 +790,7 @@ namespace data
{
uint8_t buf[41]; // ident + yyyymmdd
memcpy (buf, (const uint8_t *)ident, 32);
time_t t = time (nullptr);
struct tm tm;
#ifdef _WIN32
gmtime_s(&tm, &t);
sprintf_s((char *)(buf + 32), 9, "%04i%02i%02i", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
#else
gmtime_r(&t, &tm);
sprintf((char *)(buf + 32), "%04i%02i%02i", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
#endif
i2p::util::GetCurrentDate ((char *)(buf + 32));
IdentHash key;
SHA256(buf, 40, key);
return key;

View File

@@ -6,6 +6,7 @@
#include <string>
#include <memory>
#include <atomic>
#include <vector>
#include "Base.h"
#include "Signature.h"
#include "CryptoKey.h"
@@ -66,9 +67,9 @@ namespace data
const uint16_t SIGNING_KEY_TYPE_RSA_SHA512_4096 = 6;
const uint16_t SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519 = 7;
const uint16_t SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519ph = 8; // not implemented
// following signature type should never appear in netid=2
const uint16_t SIGNING_KEY_TYPE_GOSTR3410_CRYPTO_PRO_A_GOSTR3411_256 = 9;
const uint16_t SIGNING_KEY_TYPE_GOSTR3410_TC26_A_512_GOSTR3411_512 = 10; // approved by FSB
const uint16_t SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519 = 11; // for LeaseSet2 only
typedef uint16_t SigningKeyType;
typedef uint16_t CryptoKeyType;
@@ -99,6 +100,7 @@ namespace data
std::shared_ptr<i2p::crypto::CryptoKeyEncryptor> CreateEncryptor (const uint8_t * key) const;
size_t GetFullLen () const { return m_ExtendedLen + DEFAULT_IDENTITY_SIZE; };
size_t GetSigningPublicKeyLen () const;
const uint8_t * GetSigningPublicKeyBuffer () const; // returns NULL for P521
size_t GetSigningPrivateKeyLen () const;
size_t GetSignatureLen () const;
bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const;
@@ -142,11 +144,13 @@ namespace data
std::shared_ptr<const IdentityEx> GetPublic () const { return m_Public; };
const uint8_t * GetPrivateKey () const { return m_PrivateKey; };
const uint8_t * GetSigningPrivateKey () const { return m_SigningPrivateKey; };
uint8_t * GetPadding();
void RecalculateIdentHash(uint8_t * buf=nullptr) { m_Public->RecalculateIdentHash(buf); }
size_t GetSignatureLen () const; // might not match identity
bool IsOfflineSignature () const { return m_TransientSignatureLen > 0; };
uint8_t * GetPadding();
void RecalculateIdentHash(uint8_t * buf=nullptr) { m_Public->RecalculateIdentHash(buf); }
void Sign (const uint8_t * buf, int len, uint8_t * signature) const;
size_t GetFullLen () const { return m_Public->GetFullLen () + 256 + m_Public->GetSigningPrivateKeyLen (); };
size_t GetFullLen () const;
size_t FromBuffer (const uint8_t * buf, size_t len);
size_t ToBuffer (uint8_t * buf, size_t len) const;
@@ -157,18 +161,28 @@ namespace data
static std::shared_ptr<i2p::crypto::CryptoKeyDecryptor> CreateDecryptor (CryptoKeyType cryptoType, const uint8_t * key);
static PrivateKeys CreateRandomKeys (SigningKeyType type = SIGNING_KEY_TYPE_DSA_SHA1, CryptoKeyType cryptoType = CRYPTO_KEY_TYPE_ELGAMAL);
static void GenerateSigningKeyPair (SigningKeyType type, uint8_t * priv, uint8_t * pub);
static void GenerateCryptoKeyPair (CryptoKeyType type, uint8_t * priv, uint8_t * pub); // priv and pub are 256 bytes long
static i2p::crypto::Signer * CreateSigner (SigningKeyType keyType, const uint8_t * priv);
// offline keys
PrivateKeys CreateOfflineKeys (SigningKeyType type, uint32_t expires) const;
const std::vector<uint8_t>& GetOfflineSignature () const { return m_OfflineSignature; };
private:
void CreateSigner () const;
void CreateSigner (SigningKeyType keyType) const;
private:
std::shared_ptr<IdentityEx> m_Public;
uint8_t m_PrivateKey[256];
uint8_t m_SigningPrivateKey[1024]; // assume private key doesn't exceed 1024 bytes
uint8_t m_SigningPrivateKey[128]; // assume private key doesn't exceed 128 bytes
mutable std::unique_ptr<i2p::crypto::Signer> m_Signer;
std::vector<uint8_t> m_OfflineSignature; // non zero length, if applicable
size_t m_TransientSignatureLen = 0;
size_t m_TransientSigningPrivateKeyLen = 0;
};
// kademlia

View File

@@ -1,6 +1,10 @@
#include <string.h>
#include <openssl/sha.h>
#include <openssl/hmac.h>
#include <zlib.h> // for crc32
#include "I2PEndian.h"
#include "Crypto.h"
#include "Ed25519.h"
#include "Log.h"
#include "Timestamp.h"
#include "NetDb.hpp"
@@ -250,55 +254,195 @@ namespace data
memcpy (m_Buffer, buf, len);
}
BlindedPublicKey::BlindedPublicKey (std::shared_ptr<const IdentityEx> identity, SigningKeyType blindedKeyType):
m_BlindedSigType (blindedKeyType)
{
if (!identity) return;
auto len = identity->GetSigningPublicKeyLen ();
m_PublicKey.resize (len);
memcpy (m_PublicKey.data (), identity->GetSigningPublicKeyBuffer (), len);
m_SigType = identity->GetSigningKeyType ();
}
BlindedPublicKey::BlindedPublicKey (const std::string& b33)
{
uint8_t addr[40]; // TODO: define length from b33
size_t l = i2p::data::Base32ToByteStream (b33.c_str (), b33.length (), addr, 40);
uint32_t checksum = crc32 (0, addr + 3, l - 3);
// checksum is Little Endian
addr[0] ^= checksum; addr[1] ^= (checksum >> 8); addr[2] ^= (checksum >> 16);
uint8_t flag = addr[0];
size_t offset = 1;
if (flag & 0x01) // two bytes signatures
{
m_SigType = bufbe16toh (addr + offset); offset += 2;
m_BlindedSigType = bufbe16toh (addr + offset); offset += 2;
}
else // one byte sig
{
m_SigType = addr[offset]; offset++;
m_BlindedSigType = addr[offset]; offset++;
}
std::unique_ptr<i2p::crypto::Verifier> blindedVerifier (i2p::data::IdentityEx::CreateVerifier (m_SigType));
if (blindedVerifier)
{
auto len = blindedVerifier->GetPublicKeyLen ();
if (offset + len <= l)
{
m_PublicKey.resize (len);
memcpy (m_PublicKey.data (), addr + offset, len);
}
else
LogPrint (eLogError, "LeaseSet2: public key in b33 address is too short for signature type ", (int)m_SigType);
}
else
LogPrint (eLogError, "LeaseSet2: unknown signature type ", (int)m_SigType, " in b33");
}
std::string BlindedPublicKey::ToB33 () const
{
if (m_PublicKey.size () > 32) return ""; // assume 25519
uint8_t addr[35]; char str[60]; // TODO: define actual length
addr[0] = 0; // flags
addr[1] = m_SigType; // sig type
addr[2] = m_BlindedSigType; // blinded sig type
memcpy (addr + 3, m_PublicKey.data (), m_PublicKey.size ());
uint32_t checksum = crc32 (0, addr + 3, m_PublicKey.size ());
// checksum is Little Endian
addr[0] ^= checksum; addr[1] ^= (checksum >> 8); addr[2] ^= (checksum >> 16);
auto l = ByteStreamToBase32 (addr, m_PublicKey.size () + 3, str, 60);
return std::string (str, str + l);
}
void BlindedPublicKey::GetCredential (uint8_t * credential) const
{
// A = destination's signing public key
// stA = signature type of A, 2 bytes big endian
uint16_t stA = htobe16 (GetSigType ());
// stA1 = signature type of blinded A, 2 bytes big endian
uint16_t stA1 = htobe16 (GetBlindedSigType ());
// credential = H("credential", A || stA || stA1)
H ("credential", { {GetPublicKey (), GetPublicKeyLen ()}, {(const uint8_t *)&stA, 2}, {(const uint8_t *)&stA1, 2} }, credential);
}
void BlindedPublicKey::GetSubcredential (const uint8_t * blinded, size_t len, uint8_t * subcredential) const
{
uint8_t credential[32];
GetCredential (credential);
// subcredential = H("subcredential", credential || blindedPublicKey)
H ("subcredential", { {credential, 32}, {blinded, len} }, subcredential);
}
void BlindedPublicKey::GenerateAlpha (const char * date, uint8_t * seed) const
{
uint16_t stA = htobe16 (GetSigType ()), stA1 = htobe16 (GetBlindedSigType ());
uint8_t salt[32];
//seed = HKDF(H("I2PGenerateAlpha", keydata), datestring || secret, "i2pblinding1", 64)
H ("I2PGenerateAlpha", { {GetPublicKey (), GetPublicKeyLen ()}, {(const uint8_t *)&stA, 2}, {(const uint8_t *)&stA1, 2} }, salt);
i2p::crypto::HKDF (salt, (const uint8_t *)date, 8, "i2pblinding1", seed);
}
void BlindedPublicKey::GetBlindedKey (const char * date, uint8_t * blindedKey) const
{
uint8_t seed[64];
GenerateAlpha (date, seed);
i2p::crypto::GetEd25519 ()->BlindPublicKey (GetPublicKey (), seed, blindedKey);
}
void BlindedPublicKey::BlindPrivateKey (const uint8_t * priv, const char * date, uint8_t * blindedPriv, uint8_t * blindedPub) const
{
uint8_t seed[64];
GenerateAlpha (date, seed);
i2p::crypto::GetEd25519 ()->BlindPrivateKey (priv, seed, blindedPriv, blindedPub);
}
void BlindedPublicKey::H (const std::string& p, const std::vector<std::pair<const uint8_t *, size_t> >& bufs, uint8_t * hash) const
{
SHA256_CTX ctx;
SHA256_Init (&ctx);
SHA256_Update (&ctx, p.c_str (), p.length ());
for (const auto& it: bufs)
SHA256_Update (&ctx, it.first, it.second);
SHA256_Final (hash, &ctx);
}
i2p::data::IdentHash BlindedPublicKey::GetStoreHash (const char * date) const
{
i2p::data::IdentHash hash;
if (m_BlindedSigType == i2p::data::SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519 ||
m_BlindedSigType == SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519)
{
uint8_t blinded[32];
if (date)
GetBlindedKey (date, blinded);
else
{
char currentDate[9];
i2p::util::GetCurrentDate (currentDate);
GetBlindedKey (currentDate, blinded);
}
auto stA1 = htobe16 (m_BlindedSigType);
SHA256_CTX ctx;
SHA256_Init (&ctx);
SHA256_Update (&ctx, (const uint8_t *)&stA1, 2);
SHA256_Update (&ctx, blinded, 32);
SHA256_Final ((uint8_t *)hash, &ctx);
}
else
LogPrint (eLogError, "LeaseSet2: blinded key type ", (int)m_BlindedSigType, " is not supported");
return hash;
}
LeaseSet2::LeaseSet2 (uint8_t storeType, const uint8_t * buf, size_t len, bool storeLeases):
LeaseSet (storeLeases), m_StoreType (storeType)
LeaseSet (storeLeases), m_StoreType (storeType), m_OrigStoreType (storeType)
{
SetBuffer (buf, len);
if (storeType == NETDB_STORE_TYPE_ENCRYPTED_LEASESET2)
ReadFromBufferEncrypted (buf, len);
ReadFromBufferEncrypted (buf, len, nullptr);
else
ReadFromBuffer (buf, len);
}
LeaseSet2::LeaseSet2 (const uint8_t * buf, size_t len, std::shared_ptr<const BlindedPublicKey> key):
LeaseSet (true), m_StoreType (NETDB_STORE_TYPE_ENCRYPTED_LEASESET2), m_OrigStoreType (NETDB_STORE_TYPE_ENCRYPTED_LEASESET2)
{
ReadFromBufferEncrypted (buf, len, key);
}
void LeaseSet2::Update (const uint8_t * buf, size_t len, bool verifySignature)
{
// shouldn't be called for now. Must be called from NetDb::AddLeaseSet later
SetBuffer (buf, len);
// TODO:verify signature if requested
if (GetStoreType () != NETDB_STORE_TYPE_ENCRYPTED_LEASESET2)
ReadFromBuffer (buf, len, false, verifySignature);
// TODO: implement encrypted
}
void LeaseSet2::ReadFromBuffer (const uint8_t * buf, size_t len)
void LeaseSet2::ReadFromBuffer (const uint8_t * buf, size_t len, bool readIdentity, bool verifySignature)
{
// standard LS2 header
auto identity = std::make_shared<IdentityEx>(buf, len);
SetIdentity (identity);
std::shared_ptr<const IdentityEx> identity;
if (readIdentity)
{
identity = std::make_shared<IdentityEx>(buf, len);
SetIdentity (identity);
}
else
identity = GetIdentity ();
size_t offset = identity->GetFullLen ();
if (offset + 8 >= len) return;
uint32_t timestamp = bufbe32toh (buf + offset); offset += 4; // published timestamp (seconds)
m_PublishedTimestamp = bufbe32toh (buf + offset); offset += 4; // published timestamp (seconds)
uint16_t expires = bufbe16toh (buf + offset); offset += 2; // expires (seconds)
SetExpirationTime ((timestamp + expires)*1000LL); // in milliseconds
SetExpirationTime ((m_PublishedTimestamp + expires)*1000LL); // in milliseconds
uint16_t flags = bufbe16toh (buf + offset); offset += 2; // flags
std::unique_ptr<i2p::crypto::Verifier> transientVerifier;
if (flags & 0x0001)
if (flags & LEASESET2_FLAG_OFFLINE_KEYS)
{
// transient key
if (offset + 6 >= len) return;
const uint8_t * signedData = buf + offset;
uint32_t expiresTimestamp = bufbe32toh (buf + offset); offset += 4; // expires timestamp
if (expiresTimestamp < i2p::util::GetSecondsSinceEpoch ())
{
LogPrint (eLogWarning, "LeaseSet2: transient key expired");
m_TransientVerifier = ProcessOfflineSignature (identity, buf, len, offset);
if (!m_TransientVerifier)
{
LogPrint (eLogError, "LeaseSet2: offline signature failed");
return;
}
uint16_t keyType = bufbe16toh (buf + offset); offset += 2;
transientVerifier.reset (i2p::data::IdentityEx::CreateVerifier (keyType));
if (!transientVerifier) return;
auto keyLen = transientVerifier->GetPublicKeyLen ();
if (offset + keyLen >= len) return;
transientVerifier->SetPublicKey (buf + offset); offset += keyLen;
if (offset + identity->GetSignatureLen () >= len) return;
if (!identity->Verify (signedData, keyLen + 6, buf + offset)) return;
offset += identity->GetSignatureLen ();
}
}
// type specific part
size_t s = 0;
@@ -315,10 +459,13 @@ namespace data
}
if (!s) return;
offset += s;
// verify signature
bool verified = transientVerifier ? VerifySignature (transientVerifier, buf, len, offset) :
VerifySignature (identity, buf, len, offset);
SetIsValid (verified);
if (verifySignature || m_TransientVerifier)
{
// verify signature
bool verified = m_TransientVerifier ? VerifySignature (m_TransientVerifier, buf, len, offset) :
VerifySignature (identity, buf, len, offset);
SetIsValid (verified);
}
}
template<typename Verifier>
@@ -344,6 +491,7 @@ namespace data
offset += propertiesLen; // skip for now. TODO: implement properties
if (offset + 1 >= len) return 0;
// key sections
uint16_t currentKeyType = 0;
int numKeySections = buf[offset]; offset++;
for (int i = 0; i < numKeySections; i++)
{
@@ -351,10 +499,16 @@ namespace data
if (offset + 2 >= len) return 0;
uint16_t encryptionKeyLen = bufbe16toh (buf + offset); offset += 2;
if (offset + encryptionKeyLen >= len) return 0;
if (!m_Encryptor && IsStoreLeases ()) // create encryptor with leases only, first key
if (IsStoreLeases ()) // create encryptor with leases only
{
// we pick first valid key, higher key type has higher priority 4-1-0
// if two keys with of the same type, pick first
auto encryptor = i2p::data::IdentityEx::CreateEncryptor (keyType, buf + offset);
m_Encryptor = encryptor; // TODO: atomic
if (encryptor && (!m_Encryptor || keyType > currentKeyType))
{
m_Encryptor = encryptor; // TODO: atomic
currentKeyType = keyType;
}
}
offset += encryptionKeyLen;
}
@@ -409,52 +563,100 @@ namespace data
return offset;
}
void LeaseSet2::ReadFromBufferEncrypted (const uint8_t * buf, size_t len)
void LeaseSet2::ReadFromBufferEncrypted (const uint8_t * buf, size_t len, std::shared_ptr<const BlindedPublicKey> key)
{
size_t offset = 0;
// blinded key
if (len < 2) return;
uint16_t blindedKeyType = bufbe16toh (buf + offset); offset += 2;
const uint8_t * stA1 = buf + offset; // stA1 = blinded signature type, 2 bytes big endian
uint16_t blindedKeyType = bufbe16toh (stA1); offset += 2;
std::unique_ptr<i2p::crypto::Verifier> blindedVerifier (i2p::data::IdentityEx::CreateVerifier (blindedKeyType));
if (!blindedVerifier) return;
auto blindedKeyLen = blindedVerifier->GetPublicKeyLen ();
if (offset + blindedKeyLen >= len) return;
blindedVerifier->SetPublicKey (buf + offset); offset += blindedKeyLen;
const uint8_t * blindedPublicKey = buf + offset;
blindedVerifier->SetPublicKey (blindedPublicKey); offset += blindedKeyLen;
// expiration
if (offset + 8 >= len) return;
uint32_t timestamp = bufbe32toh (buf + offset); offset += 4; // published timestamp (seconds)
const uint8_t * publishedTimestamp = buf + offset;
m_PublishedTimestamp = bufbe32toh (publishedTimestamp); offset += 4; // published timestamp (seconds)
uint16_t expires = bufbe16toh (buf + offset); offset += 2; // expires (seconds)
SetExpirationTime ((timestamp + expires)*1000LL); // in milliseconds
SetExpirationTime ((m_PublishedTimestamp + expires)*1000LL); // in milliseconds
uint16_t flags = bufbe16toh (buf + offset); offset += 2; // flags
std::unique_ptr<i2p::crypto::Verifier> transientVerifier;
if (flags & 0x0001)
if (flags & LEASESET2_FLAG_OFFLINE_KEYS)
{
// transient key
if (offset + 6 >= len) return;
const uint8_t * signedData = buf + offset;
uint32_t expiresTimestamp = bufbe32toh (buf + offset); offset += 4; // expires timestamp
if (expiresTimestamp < i2p::util::GetSecondsSinceEpoch ())
m_TransientVerifier = ProcessOfflineSignature (blindedVerifier, buf, len, offset);
if (!m_TransientVerifier)
{
LogPrint (eLogWarning, "LeaseSet2: transient key expired");
LogPrint (eLogError, "LeaseSet2: offline signature failed");
return;
}
uint16_t keyType = bufbe16toh (buf + offset); offset += 2;
transientVerifier.reset (i2p::data::IdentityEx::CreateVerifier (keyType));
if (!transientVerifier) return;
auto keyLen = transientVerifier->GetPublicKeyLen ();
if (offset + keyLen >= len) return;
transientVerifier->SetPublicKey (buf + offset); offset += keyLen;
if (offset + blindedVerifier->GetSignatureLen () >= len) return;
if (!blindedVerifier->Verify (signedData, keyLen + 6, buf + offset)) return;
offset += blindedVerifier->GetSignatureLen ();
}
}
// outer ciphertext
if (offset + 2 > len) return;
uint16_t lenOuterCiphertext = bufbe16toh (buf + offset); offset += 2 + lenOuterCiphertext;
uint16_t lenOuterCiphertext = bufbe16toh (buf + offset); offset += 2;
const uint8_t * outerCiphertext = buf + offset;
offset += lenOuterCiphertext;
// verify signature
bool verified = transientVerifier ? VerifySignature (transientVerifier, buf, len, offset) :
bool verified = m_TransientVerifier ? VerifySignature (m_TransientVerifier, buf, len, offset) :
VerifySignature (blindedVerifier, buf, len, offset);
SetIsValid (verified);
SetIsValid (verified);
// handle ciphertext
if (verified && key && lenOuterCiphertext >= 32)
{
SetIsValid (false); // we must verify it again in Layer 2
if (blindedKeyType == i2p::data::SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519)
{
// verify blinding
char date[9];
i2p::util::GetDateString (m_PublishedTimestamp, date);
uint8_t blinded[32];
key->GetBlindedKey (date, blinded);
if (memcmp (blindedPublicKey, blinded, 32))
{
LogPrint (eLogError, "LeaseSet2: blinded public key doesn't match");
return;
}
}
// outer key
// outerInput = subcredential || publishedTimestamp
uint8_t subcredential[36];
key->GetSubcredential (blindedPublicKey, blindedKeyLen, subcredential);
memcpy (subcredential + 32, publishedTimestamp, 4);
// outerSalt = outerCiphertext[0:32]
// keys = HKDF(outerSalt, outerInput, "ELS2_L1K", 44)
uint8_t keys[64]; // 44 bytes actual data
i2p::crypto::HKDF (outerCiphertext, subcredential, 36, "ELS2_L1K", keys);
// decrypt Layer 1
// outerKey = keys[0:31]
// outerIV = keys[32:43]
size_t lenOuterPlaintext = lenOuterCiphertext - 32;
std::vector<uint8_t> outerPlainText (lenOuterPlaintext);
i2p::crypto::ChaCha20 (outerCiphertext + 32, lenOuterPlaintext, keys, keys + 32, outerPlainText.data ());
// inner key
// innerInput = authCookie || subcredential || publishedTimestamp, TODO: non-empty authCookie
// innerSalt = innerCiphertext[0:32]
// keys = HKDF(innerSalt, innerInput, "ELS2_L2K", 44)
// skip 1 byte flags
i2p::crypto::HKDF (outerPlainText.data () + 1, subcredential, 36, "ELS2_L2K", keys); // no authCookie
// decrypt Layer 2
// innerKey = keys[0:31]
// innerIV = keys[32:43]
size_t lenInnerPlaintext = lenOuterPlaintext - 32 - 1;
std::vector<uint8_t> innerPlainText (lenInnerPlaintext);
i2p::crypto::ChaCha20 (outerPlainText.data () + 32 + 1, lenInnerPlaintext, keys, keys + 32, innerPlainText.data ());
if (innerPlainText[0] == NETDB_STORE_TYPE_STANDARD_LEASESET2 || innerPlainText[0] == NETDB_STORE_TYPE_META_LEASESET2)
{
// override store type and buffer
m_StoreType = innerPlainText[0];
SetBuffer (innerPlainText.data () + 1, lenInnerPlaintext - 1);
// parse and verify Layer 2
ReadFromBuffer (innerPlainText.data () + 1, lenInnerPlaintext - 1);
}
else
LogPrint (eLogError, "LeaseSet2: unexpected LeaseSet type ", (int)innerPlainText[0], " inside encrypted LeaseSet");
}
}
void LeaseSet2::Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx) const
@@ -586,16 +788,24 @@ namespace data
return ident.Verify(ptr, leases - ptr, leases);
}
LocalLeaseSet2::LocalLeaseSet2 (uint8_t storeType, std::shared_ptr<const IdentityEx> identity,
LocalLeaseSet2::LocalLeaseSet2 (uint8_t storeType, const i2p::data::PrivateKeys& keys,
uint16_t keyType, uint16_t keyLen, const uint8_t * encryptionPublicKey,
std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels):
LocalLeaseSet (identity, nullptr, 0)
LocalLeaseSet (keys.GetPublic (), nullptr, 0)
{
auto identity = keys.GetPublic ();
// assume standard LS2
int num = tunnels.size ();
if (num > MAX_NUM_LEASES) num = MAX_NUM_LEASES;
m_BufferLen = identity->GetFullLen () + 4/*published*/ + 2/*expires*/ + 2/*flag*/ + 2/*properties len*/ +
1/*num keys*/ + 2/*key type*/ + 2/*key len*/ + keyLen/*key*/ + 1/*num leases*/ + num*LEASE2_SIZE + identity->GetSignatureLen ();
1/*num keys*/ + 2/*key type*/ + 2/*key len*/ + keyLen/*key*/ + 1/*num leases*/ + num*LEASE2_SIZE + keys.GetSignatureLen ();
uint16_t flags = 0;
if (keys.IsOfflineSignature ())
{
flags |= LEASESET2_FLAG_OFFLINE_KEYS;
m_BufferLen += keys.GetOfflineSignature ().size ();
}
m_Buffer = new uint8_t[m_BufferLen + 1];
m_Buffer[0] = storeType;
// LS2 header
@@ -603,7 +813,14 @@ namespace data
auto timestamp = i2p::util::GetSecondsSinceEpoch ();
htobe32buf (m_Buffer + offset, timestamp); offset += 4; // published timestamp (seconds)
uint8_t * expiresBuf = m_Buffer + offset; offset += 2; // expires, fill later
htobe16buf (m_Buffer + offset, 0); offset += 2; // flags
htobe16buf (m_Buffer + offset, flags); offset += 2; // flags
if (keys.IsOfflineSignature ())
{
// offline signature
const auto& offlineSignature = keys.GetOfflineSignature ();
memcpy (m_Buffer + offset, offlineSignature.data (), offlineSignature.size ());
offset += offlineSignature.size ();
}
htobe16buf (m_Buffer + offset, 0); offset += 2; // properties len
// keys
m_Buffer[offset] = 1; offset++; // 1 key
@@ -628,7 +845,89 @@ namespace data
SetExpirationTime (expirationTime*1000LL);
auto expires = expirationTime - timestamp;
htobe16buf (expiresBuf, expires > 0 ? expires : 0);
// we don't sign it yet. must be signed later on
// sign
keys.Sign (m_Buffer, offset, m_Buffer + offset); // LS + leading store type
}
LocalLeaseSet2::LocalLeaseSet2 (uint8_t storeType, std::shared_ptr<const IdentityEx> identity, const uint8_t * buf, size_t len):
LocalLeaseSet (identity, nullptr, 0)
{
m_BufferLen = len;
m_Buffer = new uint8_t[m_BufferLen + 1];
memcpy (m_Buffer + 1, buf, len);
m_Buffer[0] = storeType;
}
LocalEncryptedLeaseSet2::LocalEncryptedLeaseSet2 (std::shared_ptr<const LocalLeaseSet2> ls, const i2p::data::PrivateKeys& keys, i2p::data::SigningKeyType blindedKeyType):
LocalLeaseSet2 (ls->GetIdentity ()), m_InnerLeaseSet (ls)
{
size_t lenInnerPlaintext = ls->GetBufferLen () + 1, lenOuterPlaintext = lenInnerPlaintext + 32 + 1,
lenOuterCiphertext = lenOuterPlaintext + 32;
m_BufferLen = 2/*blinded sig type*/ + 32/*blinded pub key*/ + 4/*published*/ + 2/*expires*/ + 2/*flags*/ + 2/*lenOuterCiphertext*/ + lenOuterCiphertext + 64/*signature*/;
m_Buffer = new uint8_t[m_BufferLen + 1];
m_Buffer[0] = NETDB_STORE_TYPE_ENCRYPTED_LEASESET2;
BlindedPublicKey blindedKey (ls->GetIdentity ());
auto timestamp = i2p::util::GetSecondsSinceEpoch ();
char date[9];
i2p::util::GetDateString (timestamp, date);
uint8_t blindedPriv[32], blindedPub[32];
blindedKey.BlindPrivateKey (keys.GetSigningPrivateKey (), date, blindedPriv, blindedPub);
std::unique_ptr<i2p::crypto::Signer> blindedSigner (i2p::data::PrivateKeys::CreateSigner (blindedKeyType, blindedPriv));
auto offset = 1;
htobe16buf (m_Buffer + offset, blindedKeyType); offset += 2; // Blinded Public Key Sig Type
memcpy (m_Buffer + offset, blindedPub, 32); offset += 32; // Blinded Public Key
htobe32buf (m_Buffer + offset, timestamp); offset += 4; // published timestamp (seconds)
auto nextMidnight = (timestamp/86400LL + 1)*86400LL; // 86400 = 24*3600 seconds
auto expirationTime = ls->GetExpirationTime ()/1000LL;
if (expirationTime > nextMidnight) expirationTime = nextMidnight;
SetExpirationTime (expirationTime*1000LL);
htobe16buf (m_Buffer + offset, expirationTime > timestamp ? expirationTime - timestamp : 0); offset += 2; // expires
uint16_t flags = 0;
htobe16buf (m_Buffer + offset, flags); offset += 2; // flags
htobe16buf (m_Buffer + offset, lenOuterCiphertext); offset += 2; // lenOuterCiphertext
// outerChipherText
// Layer 1
uint8_t subcredential[36];
blindedKey.GetSubcredential (blindedPub, 32, subcredential);
htobe32buf (subcredential + 32, timestamp); // outerInput = subcredential || publishedTimestamp
// keys = HKDF(outerSalt, outerInput, "ELS2_L1K", 44)
uint8_t keys1[64]; // 44 bytes actual data
RAND_bytes (m_Buffer + offset, 32); // outerSalt = CSRNG(32)
i2p::crypto::HKDF (m_Buffer + offset, subcredential, 36, "ELS2_L1K", keys1);
offset += 32; // outerSalt
uint8_t * outerPlainText = m_Buffer + offset;
m_Buffer[offset] = 0; offset++; // flag
// Layer 2
// keys = HKDF(outerSalt, outerInput, "ELS2_L2K", 44)
uint8_t keys2[64]; // 44 bytes actual data
RAND_bytes (m_Buffer + offset, 32); // innerSalt = CSRNG(32)
i2p::crypto::HKDF (m_Buffer + offset, subcredential, 36, "ELS2_L2K", keys2);
offset += 32; // innerSalt
m_Buffer[offset] = ls->GetStoreType ();
memcpy (m_Buffer + offset + 1, ls->GetBuffer (), ls->GetBufferLen ());
i2p::crypto::ChaCha20 (m_Buffer + offset, lenInnerPlaintext, keys2, keys2 + 32, m_Buffer + offset); // encrypt Layer 2
offset += lenInnerPlaintext;
i2p::crypto::ChaCha20 (outerPlainText, lenOuterPlaintext, keys1, keys1 + 32, outerPlainText); // encrypt Layer 1
// signature
blindedSigner->Sign (m_Buffer, offset, m_Buffer + offset);
// store hash
m_StoreHash = blindedKey.GetStoreHash (date);
}
LocalEncryptedLeaseSet2::LocalEncryptedLeaseSet2 (std::shared_ptr<const IdentityEx> identity, const uint8_t * buf, size_t len):
LocalLeaseSet2 (NETDB_STORE_TYPE_ENCRYPTED_LEASESET2, identity, buf, len)
{
// fill inner LeaseSet2
auto blindedKey = std::make_shared<BlindedPublicKey>(identity);
i2p::data::LeaseSet2 ls (buf, len, blindedKey); // inner layer
if (ls.IsValid ())
{
m_InnerLeaseSet = std::make_shared<LocalLeaseSet2>(ls.GetStoreType (), identity, ls.GetBuffer (), ls.GetBufferLen ());
m_StoreHash = blindedKey->GetStoreHash ();
}
else
LogPrint (eLogError, "LeaseSet2: couldn't extract inner layer");
}
}
}

View File

@@ -8,6 +8,7 @@
#include <memory>
#include "Identity.h"
#include "Timestamp.h"
#include "I2PEndian.h"
namespace i2p
{
@@ -25,7 +26,7 @@ namespace data
IdentHash tunnelGateway;
uint32_t tunnelID;
uint64_t endDate; // 0 means invalid
bool isUpdated; // trasient
bool isUpdated; // transient
/* return true if this lease expires within t millisecond + fudge factor */
bool ExpiresWithin( const uint64_t t, const uint64_t fudge = 1000 ) const {
auto expire = i2p::util::GetMillisecondsSinceEpoch ();
@@ -77,7 +78,10 @@ namespace data
bool operator== (const LeaseSet& other) const
{ return m_BufferLen == other.m_BufferLen && !memcmp (m_Buffer, other.m_Buffer, m_BufferLen); };
virtual uint8_t GetStoreType () const { return NETDB_STORE_TYPE_LEASESET; };
virtual uint8_t GetOrigStoreType () const { return NETDB_STORE_TYPE_LEASESET; };
virtual uint32_t GetPublishedTimestamp () const { return 0; }; // should be set for LeaseSet2 only
virtual std::shared_ptr<const i2p::crypto::Verifier> GetTransientVerifier () const { return nullptr; };
// implements RoutingDestination
std::shared_ptr<const IdentityEx> GetIdentity () const { return m_Identity; };
void Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx) const;
@@ -122,21 +126,58 @@ namespace data
const uint8_t NETDB_STORE_TYPE_STANDARD_LEASESET2 = 3;
const uint8_t NETDB_STORE_TYPE_ENCRYPTED_LEASESET2 = 5;
const uint8_t NETDB_STORE_TYPE_META_LEASESET2 = 7;
const uint16_t LEASESET2_FLAG_OFFLINE_KEYS = 0x0001;
class BlindedPublicKey // for encrypted LS2
{
public:
BlindedPublicKey (std::shared_ptr<const IdentityEx> identity, SigningKeyType blindedKeyType = i2p::data::SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519);
BlindedPublicKey (const std::string& b33); // from b33 without .b32.i2p
std::string ToB33 () const;
const uint8_t * GetPublicKey () const { return m_PublicKey.data (); };
size_t GetPublicKeyLen () const { return m_PublicKey.size (); };
SigningKeyType GetSigType () const { return m_SigType; };
SigningKeyType GetBlindedSigType () const { return m_BlindedSigType; };
void GetSubcredential (const uint8_t * blinded, size_t len, uint8_t * subcredential) const; // 32 bytes
void GetBlindedKey (const char * date, uint8_t * blindedKey) const; // blinded key 32 bytes, date is 8 chars "YYYYMMDD"
void BlindPrivateKey (const uint8_t * priv, const char * date, uint8_t * blindedPriv, uint8_t * blindedPub) const; // blinded key 32 bytes, date is 8 chars "YYYYMMDD"
i2p::data::IdentHash GetStoreHash (const char * date = nullptr) const; // date is 8 chars "YYYYMMDD", use current if null
private:
void GetCredential (uint8_t * credential) const; // 32 bytes
void GenerateAlpha (const char * date, uint8_t * seed) const; // 64 bytes, date is 8 chars "YYYYMMDD"
void H (const std::string& p, const std::vector<std::pair<const uint8_t *, size_t> >& bufs, uint8_t * hash) const;
private:
std::vector<uint8_t> m_PublicKey;
i2p::data::SigningKeyType m_SigType, m_BlindedSigType;
};
class LeaseSet2: public LeaseSet
{
public:
LeaseSet2 (uint8_t storeType, const uint8_t * buf, size_t len, bool storeLeases = true);
LeaseSet2 (const uint8_t * buf, size_t len, std::shared_ptr<const BlindedPublicKey> key); // store type 5, called from local netdb only
uint8_t GetStoreType () const { return m_StoreType; };
uint8_t GetOrigStoreType () const { return m_OrigStoreType; };
uint32_t GetPublishedTimestamp () const { return m_PublishedTimestamp; };
std::shared_ptr<const i2p::crypto::Verifier> GetTransientVerifier () const { return m_TransientVerifier; };
void Update (const uint8_t * buf, size_t len, bool verifySignature);
// implements RoutingDestination
void Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx) const;
private:
void ReadFromBuffer (const uint8_t * buf, size_t len);
void ReadFromBufferEncrypted (const uint8_t * buf, size_t len);
void ReadFromBuffer (const uint8_t * buf, size_t len, bool readIdentity = true, bool verifySignature = true);
void ReadFromBufferEncrypted (const uint8_t * buf, size_t len, std::shared_ptr<const BlindedPublicKey> key);
size_t ReadStandardLS2TypeSpecificPart (const uint8_t * buf, size_t len);
size_t ReadMetaLS2TypeSpecificPart (const uint8_t * buf, size_t len);
@@ -147,10 +188,33 @@ namespace data
private:
uint8_t m_StoreType;
uint8_t m_StoreType, m_OrigStoreType;
uint32_t m_PublishedTimestamp = 0;
std::shared_ptr<i2p::crypto::Verifier> m_TransientVerifier;
std::shared_ptr<i2p::crypto::CryptoKeyEncryptor> m_Encryptor; // for standardLS2
};
// also called from Streaming.cpp
template<typename Verifier>
std::shared_ptr<i2p::crypto::Verifier> ProcessOfflineSignature (const Verifier& verifier, const uint8_t * buf, size_t len, size_t& offset)
{
if (offset + 6 >= len) return nullptr;
const uint8_t * signedData = buf + offset;
uint32_t expiresTimestamp = bufbe32toh (buf + offset); offset += 4; // expires timestamp
if (expiresTimestamp < i2p::util::GetSecondsSinceEpoch ()) return nullptr;
uint16_t keyType = bufbe16toh (buf + offset); offset += 2;
std::shared_ptr<i2p::crypto::Verifier> transientVerifier (i2p::data::IdentityEx::CreateVerifier (keyType));
if (!transientVerifier) return nullptr;
auto keyLen = transientVerifier->GetPublicKeyLen ();
if (offset + keyLen >= len) return nullptr;
transientVerifier->SetPublicKey (buf + offset); offset += keyLen;
if (offset + verifier->GetSignatureLen () >= len) return nullptr;
if (!verifier->Verify (signedData, keyLen + 6, buf + offset)) return nullptr;
offset += verifier->GetSignatureLen ();
return transientVerifier;
}
//------------------------------------------------------------------------------------
class LocalLeaseSet
{
public:
@@ -166,6 +230,7 @@ namespace data
uint8_t * GetLeases () { return m_Leases; };
const IdentHash& GetIdentHash () const { return m_Identity->GetIdentHash (); };
std::shared_ptr<const IdentityEx> GetIdentity () const { return m_Identity; };
bool IsExpired () const;
uint64_t GetExpirationTime () const { return m_ExpirationTime; };
void SetExpirationTime (uint64_t expirationTime) { m_ExpirationTime = expirationTime; };
@@ -173,6 +238,8 @@ namespace data
{ return GetBufferLen () == other.GetBufferLen () && !memcmp (GetBuffer (), other.GetBuffer (), GetBufferLen ()); };
virtual uint8_t GetStoreType () const { return NETDB_STORE_TYPE_LEASESET; };
virtual const IdentHash& GetStoreHash () const { return GetIdentHash (); }; // differ from ident hash for encrypted LeaseSet2
virtual std::shared_ptr<const LocalLeaseSet> GetInnerLeaseSet () const { return nullptr; }; // non-null for encrypted LeaseSet2
private:
@@ -186,9 +253,11 @@ namespace data
{
public:
LocalLeaseSet2 (uint8_t storeType, std::shared_ptr<const IdentityEx> identity,
LocalLeaseSet2 (uint8_t storeType, const i2p::data::PrivateKeys& keys,
uint16_t keyType, uint16_t keyLen, const uint8_t * encryptionPublicKey,
std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels);
LocalLeaseSet2 (uint8_t storeType, std::shared_ptr<const IdentityEx> identity, const uint8_t * buf, size_t len); // from I2CP
virtual ~LocalLeaseSet2 () { delete[] m_Buffer; };
uint8_t * GetBuffer () const { return m_Buffer + 1; };
@@ -196,11 +265,32 @@ namespace data
uint8_t GetStoreType () const { return m_Buffer[0]; };
private:
protected:
LocalLeaseSet2 (std::shared_ptr<const IdentityEx> identity): LocalLeaseSet (identity, nullptr, 0), m_Buffer (nullptr), m_BufferLen(0) {}; // called from LocalEncryptedLeaseSet2
protected:
uint8_t * m_Buffer; // 1 byte store type + actual buffer
size_t m_BufferLen;
};
class LocalEncryptedLeaseSet2: public LocalLeaseSet2
{
public:
LocalEncryptedLeaseSet2 (std::shared_ptr<const LocalLeaseSet2> ls, const i2p::data::PrivateKeys& keys, i2p::data::SigningKeyType blindedKeyType = i2p::data::SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519);
LocalEncryptedLeaseSet2 (std::shared_ptr<const IdentityEx> identity, const uint8_t * buf, size_t len); // from I2CP
const IdentHash& GetStoreHash () const { return m_StoreHash; };
std::shared_ptr<const LocalLeaseSet> GetInnerLeaseSet () const { return m_InnerLeaseSet; };
private:
IdentHash m_StoreHash;
std::shared_ptr<const LocalLeaseSet2> m_InnerLeaseSet;
};
}
}

View File

@@ -690,7 +690,7 @@ namespace transport
}
if (memcmp (addr->ntcp2->staticKey, m_Establisher->m_RemoteStaticKey, 32))
{
LogPrint (eLogError, "NTCP2: Static key mistmatch in SessionConfirmed");
LogPrint (eLogError, "NTCP2: Static key mismatch in SessionConfirmed");
SendTerminationAndTerminate (eNTCP2IncorrectSParameter);
return;
}
@@ -783,7 +783,7 @@ namespace transport
size_t moreBytes = m_Socket.available(ec);
if (!ec && moreBytes >= m_NextReceivedLen)
{
// read and process messsage immediately if avaliable
// read and process message immediately if available
moreBytes = boost::asio::read (m_Socket, boost::asio::buffer(m_NextReceivedBuffer, m_NextReceivedLen), boost::asio::transfer_all (), ec);
HandleReceived (ec, moreBytes);
}
@@ -887,7 +887,7 @@ namespace transport
Terminate ();
}
else
LogPrint (eLogWarning, "NTCP2: Unexpected temination block size ", size);
LogPrint (eLogWarning, "NTCP2: Unexpected termination block size ", size);
break;
case eNTCP2BlkPadding:
LogPrint (eLogDebug, "NTCP2: padding");
@@ -935,7 +935,7 @@ namespace transport
htobe16buf (buf + 1, len); // size
len += 3;
totalLen += len;
encryptBufs.push_back (std::make_pair (buf, len));
encryptBufs.push_back ( {buf, len} );
if (&it == &msgs.front ()) // first message
{
// allocate two bytes for length
@@ -949,7 +949,7 @@ namespace transport
auto paddingLen = CreatePaddingBlock (totalLen, buf + len, it->maxLen - it->len - 16);
if (paddingLen)
{
encryptBufs.push_back (std::make_pair (buf + len, paddingLen));
encryptBufs.push_back ( {buf + len, paddingLen} );
len += paddingLen;
totalLen += paddingLen;
}
@@ -969,7 +969,7 @@ namespace transport
auto paddingLen = CreatePaddingBlock (totalLen, m_NextSendBuffer, 287 - 16);
// and padding block to encrypt and send
if (paddingLen)
encryptBufs.push_back (std::make_pair (m_NextSendBuffer, paddingLen));
encryptBufs.push_back ( {m_NextSendBuffer, paddingLen} );
bufs.push_back (boost::asio::buffer (m_NextSendBuffer, paddingLen + 16));
macBuf = m_NextSendBuffer + paddingLen;
totalLen += paddingLen;
@@ -1001,7 +1001,7 @@ namespace transport
// encrypt
uint8_t nonce[12];
CreateNonce (m_SendSequenceNumber, nonce); m_SendSequenceNumber++;
i2p::crypto::AEADChaCha20Poly1305Encrypt ({std::make_pair (m_NextSendBuffer + 2, payloadLen)}, m_SendKey, nonce, m_NextSendBuffer + payloadLen + 2);
i2p::crypto::AEADChaCha20Poly1305Encrypt ({ {m_NextSendBuffer + 2, payloadLen} }, m_SendKey, nonce, m_NextSendBuffer + payloadLen + 2);
SetNextSentFrameLength (payloadLen + 16, m_NextSendBuffer);
// send
m_IsSending = true;
@@ -1180,6 +1180,7 @@ namespace transport
{
m_NTCP2V6Acceptor->open (boost::asio::ip::tcp::v6());
m_NTCP2V6Acceptor->set_option (boost::asio::ip::v6_only (true));
m_NTCP2V6Acceptor->set_option (boost::asio::socket_base::reuse_address (true));
m_NTCP2V6Acceptor->bind (boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v6(), address->port));
m_NTCP2V6Acceptor->listen ();

View File

@@ -843,6 +843,7 @@ namespace transport
{
m_NTCPV6Acceptor->open (boost::asio::ip::tcp::v6());
m_NTCPV6Acceptor->set_option (boost::asio::ip::v6_only (true));
m_NTCPV6Acceptor->set_option (boost::asio::socket_base::reuse_address (true));
m_NTCPV6Acceptor->bind (boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v6(), address->port));
m_NTCPV6Acceptor->listen ();
@@ -1286,7 +1287,7 @@ namespace transport
if (it.second->IsTerminationTimeoutExpired (ts))
{
auto session = it.second;
// Termniate modifies m_NTCPSession, so we postpone it
// Terminate modifies m_NTCPSession, so we postpone it
m_Service.post ([session] {
LogPrint (eLogDebug, "NTCP: No activity for ", session->GetTerminationTimeout (), " seconds");
session->Terminate ();

View File

@@ -105,7 +105,7 @@ namespace data
case eI2NPDummyMsg:
// plain RouterInfo from NTCP2 with flags for now
HandleNTCP2RouterInfoMsg (msg);
break;
break;
default: // WTF?
LogPrint (eLogError, "NetDb: unexpected message type ", (int) msg->GetTypeID ());
//i2p::HandleI2NPMessage (msg);
@@ -138,7 +138,7 @@ namespace data
lastDestinationCleanup = ts;
}
if (ts - lastPublish >= NETDB_PUBLISH_INTERVAL) // update timestamp and publish
if (ts - lastPublish >= NETDB_PUBLISH_INTERVAL) // update timestamp and publish
{
i2p::context.UpdateTimestamp (ts);
if (!m_HiddenMode) Publish ();
@@ -148,7 +148,7 @@ namespace data
{
auto numRouters = m_RouterInfos.size ();
if (!numRouters)
throw std::runtime_error("No known routers, reseed seems to be totally failed");
throw std::runtime_error("No known routers, reseed seems to be totally failed");
else // we have peers now
m_FloodfillBootstrap = nullptr;
if (numRouters < 2500 || ts - lastExploratory >= 90)
@@ -170,17 +170,17 @@ namespace data
}
}
void NetDb::SetHidden(bool hide)
void NetDb::SetHidden(bool hide)
{
// TODO: remove reachable addresses from router info
m_HiddenMode = hide;
}
// TODO: remove reachable addresses from router info
m_HiddenMode = hide;
}
bool NetDb::AddRouterInfo (const uint8_t * buf, int len)
{
bool updated;
AddRouterInfo (buf, len, updated);
return updated;
return updated;
}
std::shared_ptr<const RouterInfo> NetDb::AddRouterInfo (const uint8_t * buf, int len, bool& updated)
@@ -196,7 +196,7 @@ namespace data
{
bool updated;
AddRouterInfo (ident, buf, len, updated);
return updated;
return updated;
}
std::shared_ptr<const RouterInfo> NetDb::AddRouterInfo (const IdentHash& ident, const uint8_t * buf, int len, bool& updated)
@@ -207,9 +207,18 @@ namespace data
{
if (r->IsNewer (buf, len))
{
bool wasFloodfill = r->IsFloodfill ();
r->Update (buf, len);
LogPrint (eLogInfo, "NetDb: RouterInfo updated: ", ident.ToBase64());
// TODO: check if floodfill has been changed
if (wasFloodfill != r->IsFloodfill ()) // if floodfill status updated
{
LogPrint (eLogDebug, "NetDb: RouterInfo floodfill status updated: ", ident.ToBase64());
std::unique_lock<std::mutex> l(m_FloodfillsMutex);
if (wasFloodfill)
m_Floodfills.remove (r);
else
m_Floodfills.push_back (r);
}
}
else
{
@@ -220,7 +229,7 @@ namespace data
else
{
r = std::make_shared<RouterInfo> (buf, len);
if (!r->IsUnreachable ())
if (!r->IsUnreachable () && r->HasValidAddresses ())
{
bool inserted = false;
{
@@ -291,10 +300,22 @@ namespace data
bool NetDb::AddLeaseSet2 (const IdentHash& ident, const uint8_t * buf, int len, uint8_t storeType)
{
std::unique_lock<std::mutex> lock(m_LeaseSetsMutex);
// always new LS2 for now. TODO: implement update
auto leaseSet = std::make_shared<LeaseSet2> (storeType, buf, len, false); // we don't need leases in netdb
m_LeaseSets[ident] = leaseSet;
return true;
if (leaseSet->IsValid ())
{
auto it = m_LeaseSets.find(ident);
if (it == m_LeaseSets.end () || it->second->GetStoreType () != storeType ||
leaseSet->GetPublishedTimestamp () > it->second->GetPublishedTimestamp ())
{
// TODO: implement actual update
LogPrint (eLogInfo, "NetDb: LeaseSet2 updated: ", ident.ToBase32());
m_LeaseSets[ident] = leaseSet;
return true;
}
}
else
LogPrint (eLogError, "NetDb: new LeaseSet2 validation failed: ", ident.ToBase32());
return false;
}
std::shared_ptr<RouterInfo> NetDb::FindRouter (const IdentHash& ident) const
@@ -319,6 +340,9 @@ namespace data
std::shared_ptr<RouterProfile> NetDb::FindRouterProfile (const IdentHash& ident) const
{
if (!m_PersistProfiles)
return nullptr;
auto router = FindRouter (ident);
return router ? router->GetProfile () : nullptr;
}
@@ -352,7 +376,7 @@ namespace data
}
m_FloodfillBootstrap = ri;
ReseedFromFloodfill(*ri);
// don't try reseed servers if trying to boostrap from floodfill
// don't try reseed servers if trying to bootstrap from floodfill
return;
}
}
@@ -418,8 +442,9 @@ namespace data
void NetDb::VisitStoredRouterInfos(RouterInfoVisitor v)
{
m_Storage.Iterate([v] (const std::string & filename) {
auto ri = std::make_shared<i2p::data::RouterInfo>(filename);
m_Storage.Iterate([v] (const std::string & filename)
{
auto ri = std::make_shared<i2p::data::RouterInfo>(filename);
v(ri);
});
}
@@ -555,11 +580,11 @@ namespace data
++it;
}
}
// clean up expired floodfiils
// clean up expired floodfills or not floodfills anymore
{
std::unique_lock<std::mutex> l(m_FloodfillsMutex);
for (auto it = m_Floodfills.begin (); it != m_Floodfills.end ();)
if ((*it)->IsUnreachable ())
if ((*it)->IsUnreachable () || !(*it)->IsFloodfill ())
it = m_Floodfills.erase (it);
else
++it;
@@ -651,22 +676,22 @@ namespace data
size_t payloadOffset = offset;
bool updated = false;
uint8_t storeType = buf[DATABASE_STORE_TYPE_OFFSET];
uint8_t storeType = buf[DATABASE_STORE_TYPE_OFFSET];
if (storeType) // LeaseSet or LeaseSet2
{
if (!m->from) // unsolicited LS must be received directly
{
{
if (storeType == NETDB_STORE_TYPE_LEASESET) // 1
{
LogPrint (eLogDebug, "NetDb: store request: LeaseSet for ", ident.ToBase32());
updated = AddLeaseSet (ident, buf + offset, len - offset);
}
else // all others are considered as LeaseSet2
else // all others are considered as LeaseSet2
{
LogPrint (eLogDebug, "NetDb: store request: LeaseSet2 of type ", storeType, " for ", ident.ToBase32());
updated = AddLeaseSet2 (ident, buf + offset, len - offset, storeType);
}
}
}
}
else // RouterInfo
{
@@ -873,7 +898,7 @@ namespace data
}
if (!replyMsg && (lookupType == DATABASE_LOOKUP_TYPE_LEASESET_LOOKUP ||
lookupType == DATABASE_LOOKUP_TYPE_NORMAL_LOOKUP))
lookupType == DATABASE_LOOKUP_TYPE_NORMAL_LOOKUP))
{
auto leaseSet = FindLeaseSet (ident);
if (!leaseSet)
@@ -1180,13 +1205,13 @@ namespace data
return res;
}
std::shared_ptr<const RouterInfo> NetDb::GetRandomRouterInFamily(const std::string & fam) const {
return GetRandomRouter(
[fam](std::shared_ptr<const RouterInfo> router)->bool
{
return router->IsFamily(fam);
});
}
std::shared_ptr<const RouterInfo> NetDb::GetRandomRouterInFamily(const std::string & fam) const {
return GetRandomRouter(
[fam](std::shared_ptr<const RouterInfo> router)->bool
{
return router->IsFamily(fam);
});
}
std::shared_ptr<const RouterInfo> NetDb::GetClosestNonFloodfill (const IdentHash& destination,
const std::set<IdentHash>& excluded) const
@@ -1218,7 +1243,7 @@ namespace data
{
if (!it->second->IsValid () || ts > it->second->GetExpirationTime () - LEASE_ENDDATE_THRESHOLD)
{
LogPrint (eLogInfo, "NetDb: LeaseSet ", it->second->GetIdentHash ().ToBase64 (), " expired or invalid");
LogPrint (eLogInfo, "NetDb: LeaseSet ", it->first.ToBase64 (), " expired or invalid");
it = m_LeaseSets.erase (it);
}
else

View File

@@ -375,7 +375,7 @@ namespace data
if (end - contentPos >= contentLength)
break; // we are beyond contentLength
}
if (numFiles) // check if routers are not outdated
if (numFiles) // check if routers are not outdated
{
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
int numOutdated = 0;

View File

@@ -37,7 +37,7 @@ namespace i2p
void RouterContext::CreateNewRouter ()
{
m_Keys = i2p::data::PrivateKeys::CreateRandomKeys (i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519);
SaveKeys ();
SaveKeys ();
NewRouterInfo ();
}
@@ -47,14 +47,17 @@ namespace i2p
routerInfo.SetRouterIdentity (GetIdentity ());
uint16_t port; i2p::config::GetOption("port", port);
if (!port)
{
port = rand () % (30777 - 9111) + 9111; // I2P network ports range
bool ipv4; i2p::config::GetOption("ipv4", ipv4);
bool ipv6; i2p::config::GetOption("ipv6", ipv6);
bool ssu; i2p::config::GetOption("ssu", ssu);
bool ntcp; i2p::config::GetOption("ntcp", ntcp);
bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2);
bool nat; i2p::config::GetOption("nat", nat);
std::string ifname; i2p::config::GetOption("ifname", ifname);
if (port == 9150) port = 9151; // Tor browser
}
bool ipv4; i2p::config::GetOption("ipv4", ipv4);
bool ipv6; i2p::config::GetOption("ipv6", ipv6);
bool ssu; i2p::config::GetOption("ssu", ssu);
bool ntcp; i2p::config::GetOption("ntcp", ntcp);
bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2);
bool nat; i2p::config::GetOption("nat", nat);
std::string ifname; i2p::config::GetOption("ifname", ifname);
std::string ifname4; i2p::config::GetOption("ifname4", ifname4);
std::string ifname6; i2p::config::GetOption("ifname6", ifname6);
if (ipv4)
@@ -76,7 +79,7 @@ namespace i2p
}
if (ipv6)
{
std::string host = "::";
std::string host = "::1";
if (!i2p::config::IsDefault("host") && !ipv4) // override if v6 only
i2p::config::GetOption("host", host);
else if (!ifname.empty())
@@ -100,11 +103,16 @@ namespace i2p
m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ());
if (ntcp2) // we don't store iv in the address if non published so we must update it from keys
{
{
if (!m_NTCP2Keys) NewNTCP2Keys ();
UpdateNTCP2Address (true);
UpdateNTCP2Address (true);
if (!ntcp) // NTCP2 should replace NTCP
{
bool published; i2p::config::GetOption("ntcp2.published", published);
if (published)
PublishNTCP2Address (port, true);
}
}
}
void RouterContext::UpdateRouterInfo ()
@@ -117,14 +125,14 @@ namespace i2p
void RouterContext::NewNTCP2Keys ()
{
m_StaticKeys.reset (new i2p::crypto::X25519Keys ());
m_StaticKeys->GenerateKeys ();
m_StaticKeys->GenerateKeys ();
m_NTCP2Keys.reset (new NTCP2PrivateKeys ());
m_StaticKeys->GetPrivateKey (m_NTCP2Keys->staticPrivateKey);
memcpy (m_NTCP2Keys->staticPublicKey, m_StaticKeys->GetPublicKey (), 32);
RAND_bytes (m_NTCP2Keys->iv, 16);
// save
std::ofstream fk (i2p::fs::DataDirPath (NTCP2_KEYS), std::ofstream::binary | std::ofstream::out);
fk.write ((char *)m_NTCP2Keys.get (), sizeof (NTCP2PrivateKeys));
fk.write ((char *)m_NTCP2Keys.get (), sizeof (NTCP2PrivateKeys));
}
void RouterContext::SetStatus (RouterStatus status)
@@ -166,7 +174,10 @@ namespace i2p
{
if (!m_NTCP2Keys) return;
if (!port)
{
port = rand () % (30777 - 9111) + 9111; // I2P network ports range
if (port == 9150) port = 9151; // Tor browser
}
bool updated = false;
for (auto& address : m_RouterInfo.GetAddresses ())
{
@@ -180,7 +191,7 @@ namespace i2p
}
}
if (updated)
UpdateRouterInfo ();
UpdateRouterInfo ();
}
void RouterContext::UpdateNTCP2Address (bool enable)
@@ -193,7 +204,7 @@ namespace i2p
{
found = true;
if (!enable)
{
{
addresses.erase (it);
updated= true;
}
@@ -201,14 +212,14 @@ namespace i2p
}
}
if (enable && !found)
{
m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv);
{
m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv);
updated = true;
}
if (updated)
UpdateRouterInfo ();
}
void RouterContext::UpdateAddress (const boost::asio::ip::address& host)
{
bool updated = false;
@@ -332,6 +343,51 @@ namespace i2p
return m_RouterInfo.GetCaps () & i2p::data::RouterInfo::eUnreachable;
}
void RouterContext::PublishNTCPAddress (bool publish, bool v4only)
{
auto& addresses = m_RouterInfo.GetAddresses ();
if (publish)
{
for (const auto& addr : addresses) // v4
{
if (addr->transportStyle == i2p::data::RouterInfo::eTransportSSU &&
addr->host.is_v4 ())
{
// insert NTCP address with host/port from SSU
m_RouterInfo.AddNTCPAddress (addr->host.to_string ().c_str (), addr->port);
break;
}
}
if (!v4only)
{
for (const auto& addr : addresses) // v6
{
if (addr->transportStyle == i2p::data::RouterInfo::eTransportSSU &&
addr->host.is_v6 ())
{
// insert NTCP address with host/port from SSU
m_RouterInfo.AddNTCPAddress (addr->host.to_string ().c_str (), addr->port);
break;
}
}
}
}
else
{
for (auto it = addresses.begin (); it != addresses.end ();)
{
if ((*it)->transportStyle == i2p::data::RouterInfo::eTransportNTCP && !(*it)->IsNTCP2 () &&
(!v4only || (*it)->host.is_v4 ()))
{
it = addresses.erase (it);
if (v4only) break; // otherwise might be more than one address
}
else
++it;
}
}
}
void RouterContext::SetUnreachable ()
{
// set caps
@@ -341,22 +397,13 @@ namespace i2p
caps &= ~i2p::data::RouterInfo::eFloodfill; // can't be floodfill
caps &= ~i2p::data::RouterInfo::eSSUIntroducer; // can't be introducer
m_RouterInfo.SetCaps (caps);
// remove NTCP address
auto& addresses = m_RouterInfo.GetAddresses ();
for (auto it = addresses.begin (); it != addresses.end (); ++it)
{
if ((*it)->transportStyle == i2p::data::RouterInfo::eTransportNTCP && !(*it)->IsNTCP2 () &&
(*it)->host.is_v4 ())
{
addresses.erase (it);
break;
}
}
// remove NTCP v4 address
PublishNTCPAddress (false);
// delete previous introducers
auto& addresses = m_RouterInfo.GetAddresses ();
for (auto& addr : addresses)
if (addr->ssu)
addr->ssu->introducers.clear ();
// update
UpdateRouterInfo ();
}
@@ -371,27 +418,15 @@ namespace i2p
if (m_IsFloodfill)
caps |= i2p::data::RouterInfo::eFloodfill;
m_RouterInfo.SetCaps (caps);
auto& addresses = m_RouterInfo.GetAddresses ();
// insert NTCP back
bool ntcp; i2p::config::GetOption("ntcp", ntcp);
if (ntcp) {
for (const auto& addr : addresses)
{
if (addr->transportStyle == i2p::data::RouterInfo::eTransportSSU &&
addr->host.is_v4 ())
{
// insert NTCP address with host/port from SSU
m_RouterInfo.AddNTCPAddress (addr->host.to_string ().c_str (), addr->port);
break;
}
}
}
if (ntcp)
PublishNTCPAddress (true);
// delete previous introducers
auto& addresses = m_RouterInfo.GetAddresses ();
for (auto& addr : addresses)
if (addr->ssu)
addr->ssu->introducers.clear ();
// update
UpdateRouterInfo ();
}
@@ -480,11 +515,11 @@ namespace i2p
if (!found && port) // we have found NTCP2 v4 but not v6
{
m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, host, port);
m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, host, port);
updated = true;
}
if (updated)
UpdateRouterInfo ();
UpdateRouterInfo ();
}
void RouterContext::UpdateStats ()
@@ -527,7 +562,7 @@ namespace i2p
}
// read NTCP2 keys if available
std::ifstream n2k (i2p::fs::DataDirPath (NTCP2_KEYS), std::ifstream::in | std::ifstream::binary);
if (n2k)
if (n2k)
{
n2k.seekg (0, std::ios::end);
len = n2k.tellg();
@@ -535,8 +570,8 @@ namespace i2p
if (len == sizeof (NTCP2PrivateKeys))
{
m_NTCP2Keys.reset (new NTCP2PrivateKeys ());
n2k.read ((char *)m_NTCP2Keys.get (), sizeof (NTCP2PrivateKeys));
}
n2k.read ((char *)m_NTCP2Keys.get (), sizeof (NTCP2PrivateKeys));
}
n2k.close ();
}
// read RouterInfo
@@ -631,7 +666,7 @@ namespace i2p
i2p::crypto::X25519Keys& RouterContext::GetStaticKeys ()
{
if (!m_StaticKeys)
{
{
if (!m_NTCP2Keys) NewNTCP2Keys ();
auto x = new i2p::crypto::X25519Keys (m_NTCP2Keys->staticPrivateKey, m_NTCP2Keys->staticPublicKey);
if (!m_StaticKeys)
@@ -639,6 +674,6 @@ namespace i2p
else
delete x;
}
return *m_StaticKeys;
}
return *m_StaticKeys;
}
}

View File

@@ -81,6 +81,7 @@ namespace i2p
void UpdateAddress (const boost::asio::ip::address& host); // called from SSU or Daemon
void PublishNTCP2Address (int port, bool publish = true);
void UpdateNTCP2Address (bool enable);
void PublishNTCPAddress (bool publish, bool v4only = true);
bool AddIntroducer (const i2p::data::RouterInfo::Introducer& introducer);
void RemoveIntroducer (const boost::asio::ip::udp::endpoint& e);
bool IsUnreachable () const;

View File

@@ -51,7 +51,7 @@ namespace data
void RouterInfo::Update (const uint8_t * buf, int len)
{
// verify signature since we have indentity already
// verify signature since we have identity already
int l = len - m_RouterIdentity->GetSignatureLen ();
if (m_RouterIdentity->Verify (buf, l, buf + l))
{
@@ -208,26 +208,21 @@ namespace data
{
boost::system::error_code ecode;
address->host = boost::asio::ip::address::from_string (value, ecode);
if (ecode)
if (!ecode)
{
if (address->transportStyle == eTransportNTCP)
{
supportedTransports |= eNTCPV4; // TODO:
address->addressString = value;
}
else
{
supportedTransports |= eSSUV4; // TODO:
address->addressString = value;
}
}
else
{
// add supported protocol
if (address->host.is_v4 ())
supportedTransports |= (address->transportStyle == eTransportNTCP) ? eNTCPV4 : eSSUV4;
else
supportedTransports |= (address->transportStyle == eTransportNTCP) ? eNTCPV6 : eSSUV6;
#if BOOST_VERSION >= 104900
if (!address->host.is_unspecified ()) // check if address is valid
#else
address->host.to_string (ecode);
if (!ecode)
#endif
{
// add supported protocol
if (address->host.is_v4 ())
supportedTransports |= (address->transportStyle == eTransportNTCP) ? eNTCPV4 : eSSUV4;
else
supportedTransports |= (address->transportStyle == eTransportNTCP) ? eNTCPV6 : eSSUV6;
}
}
}
else if (!strcmp (key, "port"))
@@ -888,7 +883,7 @@ namespace data
template<typename Filter>
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetAddress (Filter filter) const
{
// TODO: make it more gereric using comparator
// TODO: make it more generic using comparator
#if (BOOST_VERSION >= 105300)
auto addresses = boost::atomic_load (&m_Addresses);
#else

View File

@@ -102,7 +102,6 @@ namespace data
{
TransportStyle transportStyle;
boost::asio::ip::address host;
std::string addressString;
int port;
uint64_t date;
uint8_t cost;
@@ -170,6 +169,7 @@ namespace data
void EnableV4 ();
void DisableV4 ();
bool IsCompatible (const RouterInfo& other) const { return m_SupportedTransports & other.m_SupportedTransports; };
bool HasValidAddresses () const { return m_SupportedTransports; };
bool UsesIntroducer () const;
bool IsIntroducer () const { return m_Caps & eSSUIntroducer; };
bool IsPeerTesting () const { return m_Caps & eSSUTesting; };

View File

@@ -328,7 +328,11 @@ namespace transport
{
if (!session || session->GetRemoteEndpoint () != packet->from) // we received packet for other session than previous
{
if (session) session->FlushData ();
if (session)
{
session->FlushData ();
session = nullptr;
}
auto it = sessions->find (packet->from);
if (it != sessions->end ())
session = it->second;
@@ -750,6 +754,8 @@ namespace transport
if (it.second->IsTerminationTimeoutExpired (ts))
{
auto session = it.second;
if (it.first != session->GetRemoteEndpoint ())
LogPrint (eLogWarning, "SSU: remote endpoint ", session->GetRemoteEndpoint (), " doesn't match key ", it.first, " adjusted");
m_Service.post ([session]
{
LogPrint (eLogWarning, "SSU: no activity with ", session->GetRemoteEndpoint (), " for ", session->GetTerminationTimeout (), " seconds");
@@ -776,6 +782,8 @@ namespace transport
if (it.second->IsTerminationTimeoutExpired (ts))
{
auto session = it.second;
if (it.first != session->GetRemoteEndpoint ())
LogPrint (eLogWarning, "SSU: remote endpoint ", session->GetRemoteEndpoint (), " doesn't match key ", it.first);
m_ServiceV6.post ([session]
{
LogPrint (eLogWarning, "SSU: no activity with ", session->GetRemoteEndpoint (), " for ", session->GetTerminationTimeout (), " seconds");

View File

@@ -158,7 +158,7 @@ namespace transport
ProcessData (buf + headerSize, len - headerSize);
break;
case PAYLOAD_TYPE_SESSION_REQUEST:
ProcessSessionRequest (buf, len, senderEndpoint); // buf with header
ProcessSessionRequest (buf, len); // buf with header
break;
case PAYLOAD_TYPE_SESSION_CREATED:
ProcessSessionCreated (buf, len); // buf with header
@@ -194,7 +194,7 @@ namespace transport
}
}
void SSUSession::ProcessSessionRequest (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint)
void SSUSession::ProcessSessionRequest (const uint8_t * buf, size_t len)
{
LogPrint (eLogDebug, "SSU message: session request");
bool sendRelayTag = true;
@@ -212,10 +212,9 @@ namespace transport
}
if (headerSize >= len)
{
LogPrint (eLogError, "Session reaquest header size ", headerSize, " exceeds packet length ", len);
LogPrint (eLogError, "Session request header size ", headerSize, " exceeds packet length ", len);
return;
}
m_RemoteEndpoint = senderEndpoint;
if (!m_DHKeysPair)
m_DHKeysPair = transports.GetNextDHKeysPair ();
CreateAESandMacKey (buf + headerSize);
@@ -1097,7 +1096,7 @@ namespace transport
// intro key
if (toAddress)
{
// send our intro key to address instead it's own
// send our intro key to address instead of its own
auto addr = i2p::context.GetRouterInfo ().GetSSUAddress ();
if (addr)
memcpy (payload, addr->ssu->key, 32); // intro key

View File

@@ -80,7 +80,8 @@ namespace transport
void Close ();
void Done ();
void Failed ();
boost::asio::ip::udp::endpoint& GetRemoteEndpoint () { return m_RemoteEndpoint; };
const boost::asio::ip::udp::endpoint& GetRemoteEndpoint () { return m_RemoteEndpoint; };
bool IsV6 () const { return m_RemoteEndpoint.address ().is_v6 (); };
void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs);
void SendPeerTest (); // Alice
@@ -103,7 +104,7 @@ namespace transport
size_t GetSSUHeaderSize (const uint8_t * buf) const;
void PostI2NPMessages (std::vector<std::shared_ptr<I2NPMessage> > msgs);
void ProcessMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); // call for established session
void ProcessSessionRequest (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint);
void ProcessSessionRequest (const uint8_t * buf, size_t len);
void SendSessionRequest ();
void SendRelayRequest (const i2p::data::RouterInfo::Introducer& introducer, uint32_t nonce);
void ProcessSessionCreated (uint8_t * buf, size_t len);
@@ -139,7 +140,7 @@ namespace transport
friend class SSUData; // TODO: change in later
SSUServer& m_Server;
boost::asio::ip::udp::endpoint m_RemoteEndpoint;
const boost::asio::ip::udp::endpoint m_RemoteEndpoint;
boost::asio::deadline_timer m_ConnectTimer;
bool m_IsPeerTest;
SessionState m_State;

View File

@@ -7,7 +7,8 @@ namespace i2p
namespace crypto
{
#if OPENSSL_EDDSA
EDDSA25519Verifier::EDDSA25519Verifier ()
EDDSA25519Verifier::EDDSA25519Verifier ():
m_Pkey (nullptr)
{
m_MDCtx = EVP_MD_CTX_create ();
}
@@ -97,7 +98,7 @@ namespace crypto
uint8_t publicKey[EDDSA25519_PUBLIC_KEY_LENGTH];
size_t len = EDDSA25519_PUBLIC_KEY_LENGTH;
EVP_PKEY_get_raw_public_key (m_Pkey, publicKey, &len);
if (memcmp (publicKey, signingPublicKey, EDDSA25519_PUBLIC_KEY_LENGTH))
if (signingPublicKey && memcmp (publicKey, signingPublicKey, EDDSA25519_PUBLIC_KEY_LENGTH))
{
LogPrint (eLogWarning, "EdDSA public key mismatch. Fallback");
EVP_PKEY_free (m_Pkey);

View File

@@ -487,6 +487,42 @@ namespace crypto
typedef GOSTR3410Signer<GOSTR3411_256_Hash> GOSTR3410_256_Signer;
typedef GOSTR3410Verifier<GOSTR3411_512_Hash> GOSTR3410_512_Verifier;
typedef GOSTR3410Signer<GOSTR3411_512_Hash> GOSTR3410_512_Signer;
// RedDSA
typedef EDDSA25519Verifier RedDSA25519Verifier;
class RedDSA25519Signer: public Signer
{
public:
RedDSA25519Signer (const uint8_t * signingPrivateKey)
{
memcpy (m_PrivateKey, signingPrivateKey, EDDSA25519_PRIVATE_KEY_LENGTH);
BN_CTX * ctx = BN_CTX_new ();
auto publicKey = GetEd25519 ()->GeneratePublicKey (m_PrivateKey, ctx);
GetEd25519 ()->EncodePublicKey (publicKey, m_PublicKeyEncoded, ctx);
BN_CTX_free (ctx);
}
~RedDSA25519Signer () {};
void Sign (const uint8_t * buf, int len, uint8_t * signature) const
{
GetEd25519 ()->SignRedDSA (m_PrivateKey, m_PublicKeyEncoded, buf, len, signature);
}
const uint8_t * GetPublicKey () const { return m_PublicKeyEncoded; }; // for keys creation
private:
uint8_t m_PrivateKey[EDDSA25519_PRIVATE_KEY_LENGTH];
uint8_t m_PublicKeyEncoded[EDDSA25519_PUBLIC_KEY_LENGTH];
};
inline void CreateRedDSA25519RandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
{
GetEd25519 ()->CreateRedDSAPrivateKey (signingPrivateKey);
RedDSA25519Signer signer (signingPrivateKey);
memcpy (signingPublicKey, signer.GetPublicKey (), EDDSA25519_PUBLIC_KEY_LENGTH);
}
}
}

View File

@@ -217,61 +217,17 @@ namespace stream
void Stream::ProcessPacket (Packet * packet)
{
// process flags
uint32_t receivedSeqn = packet->GetSeqn ();
uint16_t flags = packet->GetFlags ();
LogPrint (eLogDebug, "Streaming: Process seqn=", receivedSeqn, ", flags=", flags);
const uint8_t * optionData = packet->GetOptionData ();
if (flags & PACKET_FLAG_DELAY_REQUESTED)
optionData += 2;
if (flags & PACKET_FLAG_FROM_INCLUDED)
if (!ProcessOptions (flags, packet))
{
m_RemoteIdentity = std::make_shared<i2p::data::IdentityEx>(optionData, packet->GetOptionSize ());
if (m_RemoteIdentity->IsRSA ())
{
LogPrint (eLogInfo, "Streaming: Incoming stream from RSA destination ", m_RemoteIdentity->GetIdentHash ().ToBase64 (), " Discarded");
m_LocalDestination.DeletePacket (packet);
Terminate ();
return;
}
optionData += m_RemoteIdentity->GetFullLen ();
if (!m_RemoteLeaseSet)
LogPrint (eLogDebug, "Streaming: Incoming stream from ", m_RemoteIdentity->GetIdentHash ().ToBase64 (), ", sSID=", m_SendStreamID, ", rSID=", m_RecvStreamID);
m_LocalDestination.DeletePacket (packet);
Terminate ();
return;
}
if (flags & PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED)
{
uint16_t maxPacketSize = bufbe16toh (optionData);
LogPrint (eLogDebug, "Streaming: Max packet size ", maxPacketSize);
optionData += 2;
}
if (flags & PACKET_FLAG_SIGNATURE_INCLUDED)
{
uint8_t signature[256];
auto signatureLen = m_RemoteIdentity->GetSignatureLen ();
if(signatureLen <= sizeof(signature))
{
memcpy (signature, optionData, signatureLen);
memset (const_cast<uint8_t *>(optionData), 0, signatureLen);
if (!m_RemoteIdentity->Verify (packet->GetBuffer (), packet->GetLength (), signature))
{
LogPrint (eLogError, "Streaming: Signature verification failed, sSID=", m_SendStreamID, ", rSID=", m_RecvStreamID);
Close ();
flags |= PACKET_FLAG_CLOSE;
}
memcpy (const_cast<uint8_t *>(optionData), signature, signatureLen);
optionData += signatureLen;
}
else
{
LogPrint(eLogError, "Streaming: Signature too big, ", signatureLen, " bytes");
}
}
packet->offset = packet->GetPayload () - packet->buf;
if (packet->GetLength () > 0)
{
@@ -298,6 +254,94 @@ namespace stream
}
}
bool Stream::ProcessOptions (uint16_t flags, Packet * packet)
{
const uint8_t * optionData = packet->GetOptionData ();
size_t optionSize = packet->GetOptionSize ();
if (flags & PACKET_FLAG_DELAY_REQUESTED)
optionData += 2;
if (flags & PACKET_FLAG_FROM_INCLUDED)
{
if (m_RemoteLeaseSet) m_RemoteIdentity = m_RemoteLeaseSet->GetIdentity ();
if (!m_RemoteIdentity)
m_RemoteIdentity = std::make_shared<i2p::data::IdentityEx>(optionData, optionSize);
if (m_RemoteIdentity->IsRSA ())
{
LogPrint (eLogInfo, "Streaming: Incoming stream from RSA destination ", m_RemoteIdentity->GetIdentHash ().ToBase64 (), " Discarded");
return false;
}
optionData += m_RemoteIdentity->GetFullLen ();
if (!m_RemoteLeaseSet)
LogPrint (eLogDebug, "Streaming: Incoming stream from ", m_RemoteIdentity->GetIdentHash ().ToBase64 (), ", sSID=", m_SendStreamID, ", rSID=", m_RecvStreamID);
}
if (flags & PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED)
{
uint16_t maxPacketSize = bufbe16toh (optionData);
LogPrint (eLogDebug, "Streaming: Max packet size ", maxPacketSize);
optionData += 2;
}
if (flags & PACKET_FLAG_OFFLINE_SIGNATURE)
{
if (!m_RemoteIdentity)
{
LogPrint (eLogInfo, "Streaming: offline signature without identity");
return false;
}
// if we have it in LeaseSet already we don't need to parse it again
if (m_RemoteLeaseSet) m_TransientVerifier = m_RemoteLeaseSet->GetTransientVerifier ();
if (m_TransientVerifier)
{
// skip option data
optionData += 6; // timestamp and key type
optionData += m_TransientVerifier->GetPublicKeyLen (); // public key
optionData += m_RemoteIdentity->GetSignatureLen (); // signature
}
else
{
// transient key
size_t offset = 0;
m_TransientVerifier = i2p::data::ProcessOfflineSignature (m_RemoteIdentity, optionData, optionSize - (optionData - packet->GetOptionData ()), offset);
optionData += offset;
if (!m_TransientVerifier)
{
LogPrint (eLogError, "Streaming: offline signature failed");
return false;
}
}
}
if (flags & PACKET_FLAG_SIGNATURE_INCLUDED)
{
uint8_t signature[256];
auto signatureLen = m_RemoteIdentity->GetSignatureLen ();
if(signatureLen <= sizeof(signature))
{
memcpy (signature, optionData, signatureLen);
memset (const_cast<uint8_t *>(optionData), 0, signatureLen);
bool verified = m_TransientVerifier ?
m_TransientVerifier->Verify (packet->GetBuffer (), packet->GetLength (), signature) :
m_RemoteIdentity->Verify (packet->GetBuffer (), packet->GetLength (), signature);
if (!verified)
{
LogPrint (eLogError, "Streaming: Signature verification failed, sSID=", m_SendStreamID, ", rSID=", m_RecvStreamID);
Close ();
flags |= PACKET_FLAG_CLOSE;
}
memcpy (const_cast<uint8_t *>(optionData), signature, signatureLen);
optionData += signatureLen;
}
else
{
LogPrint (eLogError, "Streaming: Signature too big, ", signatureLen, " bytes");
return false;
}
}
return true;
}
void Stream::ProcessAck (Packet * packet)
{
bool acknowledged = false;
@@ -438,19 +482,28 @@ namespace stream
uint16_t flags = PACKET_FLAG_SYNCHRONIZE | PACKET_FLAG_FROM_INCLUDED |
PACKET_FLAG_SIGNATURE_INCLUDED | PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED;
if (isNoAck) flags |= PACKET_FLAG_NO_ACK;
bool isOfflineSignature = m_LocalDestination.GetOwner ()->GetPrivateKeys ().IsOfflineSignature ();
if (isOfflineSignature) flags |= PACKET_FLAG_OFFLINE_SIGNATURE;
htobe16buf (packet + size, flags);
size += 2; // flags
size_t identityLen = m_LocalDestination.GetOwner ()->GetIdentity ()->GetFullLen ();
size_t signatureLen = m_LocalDestination.GetOwner ()->GetIdentity ()->GetSignatureLen ();
htobe16buf (packet + size, identityLen + signatureLen + 2); // identity + signature + packet size
size_t signatureLen = m_LocalDestination.GetOwner ()->GetPrivateKeys ().GetSignatureLen ();
uint8_t * optionsSize = packet + size; // set options size later
size += 2; // options size
m_LocalDestination.GetOwner ()->GetIdentity ()->ToBuffer (packet + size, identityLen);
size += identityLen; // from
htobe16buf (packet + size, STREAMING_MTU);
size += 2; // max packet size
if (isOfflineSignature)
{
const auto& offlineSignature = m_LocalDestination.GetOwner ()->GetPrivateKeys ().GetOfflineSignature ();
memcpy (packet + size, offlineSignature.data (), offlineSignature.size ());
size += offlineSignature.size (); // offline signature
}
uint8_t * signature = packet + size; // set it later
memset (signature, 0, signatureLen); // zeroes for now
size += signatureLen; // signature
htobe16buf (optionsSize, packet + size - 2 - optionsSize); // actual options size
size += m_SendBuffer.Get (packet + size, STREAMING_MTU - size); // payload
m_LocalDestination.GetOwner ()->Sign (packet, size, signature);
}
@@ -549,7 +602,7 @@ namespace stream
size++; // NACK count
}
size++; // resend delay
htobuf16 (packet + size, 0); // nof flags set
htobuf16 (packet + size, 0); // no flags set
size += 2; // flags
htobuf16 (packet + size, 0); // no options
size += 2; // options size
@@ -849,6 +902,12 @@ namespace stream
LogPrint (eLogWarning, "Streaming: LeaseSet ", m_RemoteIdentity->GetIdentHash ().ToBase64 (), " not found");
m_LocalDestination.GetOwner ()->RequestDestination (m_RemoteIdentity->GetIdentHash ()); // try to request for a next attempt
}
else
{
// LeaseSet updated
m_RemoteIdentity = m_RemoteLeaseSet->GetIdentity ();
m_TransientVerifier = m_RemoteLeaseSet->GetTransientVerifier ();
}
}
if (m_RemoteLeaseSet)
{
@@ -858,7 +917,12 @@ namespace stream
if (leases.empty ())
{
expired = false;
m_LocalDestination.GetOwner ()->RequestDestination (m_RemoteIdentity->GetIdentHash ()); // time to request
// time to request
if (m_RemoteLeaseSet->GetOrigStoreType () == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2)
m_LocalDestination.GetOwner ()->RequestDestinationWithEncryptedLeaseSet (
std::make_shared<i2p::data::BlindedPublicKey>(m_RemoteIdentity));
else
m_LocalDestination.GetOwner ()->RequestDestination (m_RemoteIdentity->GetIdentHash ());
leases = m_RemoteLeaseSet->GetNonExpiredLeases (true); // then with threshold
}
if (!leases.empty ())

View File

@@ -38,6 +38,7 @@ namespace stream
const uint16_t PACKET_FLAG_PROFILE_INTERACTIVE = 0x0100;
const uint16_t PACKET_FLAG_ECHO = 0x0200;
const uint16_t PACKET_FLAG_NO_ACK = 0x0400;
const uint16_t PACKET_FLAG_OFFLINE_SIGNATURE = 0x0800;
const size_t STREAMING_MTU = 1730;
const size_t MAX_PACKET_SIZE = 4096;
@@ -195,6 +196,7 @@ namespace stream
void SavePacket (Packet * packet);
void ProcessPacket (Packet * packet);
bool ProcessOptions (uint16_t flags, Packet * packet);
void ProcessAck (Packet * packet);
size_t ConcatenatePackets (uint8_t * buf, size_t len);
@@ -216,6 +218,7 @@ namespace stream
bool m_IsAckSendScheduled;
StreamingDestination& m_LocalDestination;
std::shared_ptr<const i2p::data::IdentityEx> m_RemoteIdentity;
std::shared_ptr<const i2p::crypto::Verifier> m_TransientVerifier; // in case of offline key
std::shared_ptr<const i2p::data::LeaseSet> m_RemoteLeaseSet;
std::shared_ptr<i2p::garlic::GarlicRoutingSession> m_RoutingSession;
std::shared_ptr<const i2p::data::Lease> m_CurrentRemoteLease;

View File

@@ -1,3 +1,5 @@
#include <time.h>
#include <stdio.h>
#include <inttypes.h>
#include <string.h>
#include <chrono>
@@ -37,7 +39,6 @@ namespace util
std::chrono::system_clock::now().time_since_epoch()).count ();
}
static int64_t g_TimeOffset = 0; // in seconds
static void SyncTimeWithNTP (const std::string& address)
@@ -178,6 +179,25 @@ namespace util
{
return GetLocalSecondsSinceEpoch () + g_TimeOffset;
}
void GetCurrentDate (char * date)
{
GetDateString (GetSecondsSinceEpoch (), date);
}
void GetDateString (uint64_t timestamp, char * date)
{
using clock = std::chrono::system_clock;
auto t = clock::to_time_t (clock::time_point (std::chrono::seconds(timestamp)));
struct tm tm;
#ifdef _WIN32
gmtime_s(&tm, &t);
sprintf_s(date, 9, "%04i%02i%02i", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
#else
gmtime_r(&t, &tm);
sprintf(date, "%04i%02i%02i", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
#endif
}
}
}

View File

@@ -15,6 +15,9 @@ namespace util
uint32_t GetHoursSinceEpoch ();
uint64_t GetSecondsSinceEpoch ();
void GetCurrentDate (char * date); // returns date as YYYYMMDD string, 9 bytes
void GetDateString (uint64_t timestamp, char * date); // timestap is seconds since epoch, returns date as YYYYMMDD string, 9 bytes
class NTPTimeSync
{
public:

View File

@@ -426,37 +426,28 @@ namespace transport
auto address = peer.router->GetNTCPAddress (!context.SupportsV6 ());
if (address && m_NTCPServer)
{
#if BOOST_VERSION >= 104900
if (!address->host.is_unspecified ()) // we have address now
#else
boost::system::error_code ecode;
address->host.to_string (ecode);
if (!ecode)
#endif
if (!peer.router->UsesIntroducer () && !peer.router->IsUnreachable ())
{
if (!peer.router->UsesIntroducer () && !peer.router->IsUnreachable ())
if(!m_NTCPServer->ShouldLimit())
{
if(!m_NTCPServer->ShouldLimit())
auto s = std::make_shared<NTCPSession> (*m_NTCPServer, peer.router);
if(m_NTCPServer->UsingProxy())
{
auto s = std::make_shared<NTCPSession> (*m_NTCPServer, peer.router);
if(m_NTCPServer->UsingProxy())
{
NTCPServer::RemoteAddressType remote = NTCPServer::eIP4Address;
std::string addr = address->host.to_string();
NTCPServer::RemoteAddressType remote = NTCPServer::eIP4Address;
std::string addr = address->host.to_string();
if(address->host.is_v6())
remote = NTCPServer::eIP6Address;
if(address->host.is_v6())
remote = NTCPServer::eIP6Address;
m_NTCPServer->ConnectWithProxy(addr, address->port, remote, s);
}
else
m_NTCPServer->Connect (address->host, address->port, s);
return true;
m_NTCPServer->ConnectWithProxy(addr, address->port, remote, s);
}
else
{
LogPrint(eLogWarning, "Transports: NTCP Limit hit falling back to SSU");
}
m_NTCPServer->Connect (address->host, address->port, s);
return true;
}
else
{
LogPrint(eLogWarning, "Transports: NTCP Limit hit falling back to SSU");
}
}
}
@@ -469,17 +460,8 @@ namespace transport
if (m_SSUServer && peer.router->IsSSU (!context.SupportsV6 ()))
{
auto address = peer.router->GetSSUAddress (!context.SupportsV6 ());
#if BOOST_VERSION >= 104900
if (!address->host.is_unspecified ()) // we have address now
#else
boost::system::error_code ecode;
address->host.to_string (ecode);
if (!ecode)
#endif
{
m_SSUServer->CreateSession (peer.router, address->host, address->port);
return true;
}
m_SSUServer->CreateSession (peer.router, address->host, address->port);
return true;
}
}
LogPrint (eLogInfo, "Transports: No NTCP or SSU addresses available");

View File

@@ -51,6 +51,19 @@ namespace tunnel
// create fragments
const std::shared_ptr<I2NPMessage> & msg = block.data;
size_t fullMsgLen = diLen + msg->GetLength () + 2; // delivery instructions + payload + 2 bytes length
if (!messageCreated && fullMsgLen > m_RemainingSize) // check if we should complete previous message
{
size_t numFollowOnFragments = fullMsgLen / TUNNEL_DATA_MAX_PAYLOAD_SIZE;
// length of bytes doesn't fit full tunnel message
// every follow-on fragment adds 7 bytes
size_t nonFit = (fullMsgLen + numFollowOnFragments*7) % TUNNEL_DATA_MAX_PAYLOAD_SIZE;
if (!nonFit || nonFit > m_RemainingSize)
{
CompleteCurrentTunnelDataMessage ();
CreateCurrentTunnelDataMessage ();
}
}
if (fullMsgLen <= m_RemainingSize)
{
// message fits. First and last fragment
@@ -65,18 +78,6 @@ namespace tunnel
}
else
{
if (!messageCreated) // check if we should complete previous message
{
size_t numFollowOnFragments = fullMsgLen / TUNNEL_DATA_MAX_PAYLOAD_SIZE;
// length of bytes don't fit full tunnel message
// every follow-on fragment adds 7 bytes
size_t nonFit = (fullMsgLen + numFollowOnFragments*7) % TUNNEL_DATA_MAX_PAYLOAD_SIZE;
if (!nonFit || nonFit > m_RemainingSize)
{
CompleteCurrentTunnelDataMessage ();
CreateCurrentTunnelDataMessage ();
}
}
if (diLen + 6 <= m_RemainingSize)
{
// delivery instructions fit

View File

@@ -9,6 +9,7 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sysinfoapi.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iphlpapi.h>
@@ -21,9 +22,10 @@
#define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x))
#define FREE(x) HeapFree(GetProcessHeap(), 0, (x))
/* // No more needed. Exists in MinGW.
int inet_pton(int af, const char *src, void *dst)
{ // This function was written by Petar Korponai?. See http://stackoverflow.com/questions/15660203/inet-pton-identifier-not-found
// inet_pton exists Windows since Vista, but XP haven't that function!
// This function was written by Petar Korponai?. See http://stackoverflow.com/questions/15660203/inet-pton-identifier-not-found
int inet_pton_xp(int af, const char *src, void *dst)
{
struct sockaddr_storage ss;
int size = sizeof (ss);
char src_copy[INET6_ADDRSTRLEN + 1];
@@ -45,7 +47,7 @@ int inet_pton(int af, const char *src, void *dst)
}
}
return 0;
}*/
}
#else /* !WIN32 => UNIX */
#include <sys/types.h>
#include <ifaddrs.h>
@@ -58,210 +60,245 @@ namespace util
namespace net
{
#ifdef WIN32
int GetMTUWindowsIpv4(sockaddr_in inputAddress, int fallback)
{
ULONG outBufLen = 0;
PIP_ADAPTER_ADDRESSES pAddresses = nullptr;
PIP_ADAPTER_ADDRESSES pCurrAddresses = nullptr;
PIP_ADAPTER_UNICAST_ADDRESS pUnicast = nullptr;
bool IsWindowsXPorLater()
{
OSVERSIONINFO osvi;
ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&osvi);
if(GetAdaptersAddresses(AF_INET, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen)
== ERROR_BUFFER_OVERFLOW) {
FREE(pAddresses);
pAddresses = (IP_ADAPTER_ADDRESSES*) MALLOC(outBufLen);
}
if (osvi.dwMajorVersion <= 5)
return true;
else
return false;
}
DWORD dwRetVal = GetAdaptersAddresses(
AF_INET, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen
);
int GetMTUWindowsIpv4(sockaddr_in inputAddress, int fallback)
{
ULONG outBufLen = 0;
PIP_ADAPTER_ADDRESSES pAddresses = nullptr;
PIP_ADAPTER_ADDRESSES pCurrAddresses = nullptr;
PIP_ADAPTER_UNICAST_ADDRESS pUnicast = nullptr;
if(dwRetVal != NO_ERROR) {
LogPrint(eLogError, "NetIface: GetMTU(): enclosed GetAdaptersAddresses() call has failed");
FREE(pAddresses);
return fallback;
}
if(GetAdaptersAddresses(AF_INET, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen)
== ERROR_BUFFER_OVERFLOW)
{
FREE(pAddresses);
pAddresses = (IP_ADAPTER_ADDRESSES*) MALLOC(outBufLen);
}
pCurrAddresses = pAddresses;
while(pCurrAddresses) {
PIP_ADAPTER_UNICAST_ADDRESS firstUnicastAddress = pCurrAddresses->FirstUnicastAddress;
DWORD dwRetVal = GetAdaptersAddresses(
AF_INET, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen
);
pUnicast = pCurrAddresses->FirstUnicastAddress;
if(pUnicast == nullptr) {
LogPrint(eLogError, "NetIface: GetMTU(): not a unicast ipv4 address, this is not supported");
}
for(int i = 0; pUnicast != nullptr; ++i) {
LPSOCKADDR lpAddr = pUnicast->Address.lpSockaddr;
sockaddr_in* localInterfaceAddress = (sockaddr_in*) lpAddr;
if(localInterfaceAddress->sin_addr.S_un.S_addr == inputAddress.sin_addr.S_un.S_addr) {
auto result = pAddresses->Mtu;
FREE(pAddresses);
return result;
}
pUnicast = pUnicast->Next;
}
pCurrAddresses = pCurrAddresses->Next;
}
if(dwRetVal != NO_ERROR)
{
LogPrint(eLogError, "NetIface: GetMTU(): enclosed GetAdaptersAddresses() call has failed");
FREE(pAddresses);
return fallback;
}
LogPrint(eLogError, "NetIface: GetMTU(): no usable unicast ipv4 addresses found");
FREE(pAddresses);
return fallback;
}
pCurrAddresses = pAddresses;
while(pCurrAddresses)
{
PIP_ADAPTER_UNICAST_ADDRESS firstUnicastAddress = pCurrAddresses->FirstUnicastAddress;
int GetMTUWindowsIpv6(sockaddr_in6 inputAddress, int fallback)
{
ULONG outBufLen = 0;
PIP_ADAPTER_ADDRESSES pAddresses = nullptr;
PIP_ADAPTER_ADDRESSES pCurrAddresses = nullptr;
PIP_ADAPTER_UNICAST_ADDRESS pUnicast = nullptr;
pUnicast = pCurrAddresses->FirstUnicastAddress;
if(pUnicast == nullptr)
LogPrint(eLogError, "NetIface: GetMTU(): not a unicast ipv4 address, this is not supported");
if(GetAdaptersAddresses(AF_INET6, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen)
== ERROR_BUFFER_OVERFLOW) {
FREE(pAddresses);
pAddresses = (IP_ADAPTER_ADDRESSES*) MALLOC(outBufLen);
}
for(int i = 0; pUnicast != nullptr; ++i)
{
LPSOCKADDR lpAddr = pUnicast->Address.lpSockaddr;
sockaddr_in* localInterfaceAddress = (sockaddr_in*) lpAddr;
if(localInterfaceAddress->sin_addr.S_un.S_addr == inputAddress.sin_addr.S_un.S_addr)
{
auto result = pAddresses->Mtu;
FREE(pAddresses);
return result;
}
pUnicast = pUnicast->Next;
}
pCurrAddresses = pCurrAddresses->Next;
}
DWORD dwRetVal = GetAdaptersAddresses(
AF_INET6, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen
);
LogPrint(eLogError, "NetIface: GetMTU(): no usable unicast ipv4 addresses found");
FREE(pAddresses);
return fallback;
}
if(dwRetVal != NO_ERROR) {
LogPrint(eLogError, "NetIface: GetMTU(): enclosed GetAdaptersAddresses() call has failed");
FREE(pAddresses);
return fallback;
}
int GetMTUWindowsIpv6(sockaddr_in6 inputAddress, int fallback)
{
ULONG outBufLen = 0;
PIP_ADAPTER_ADDRESSES pAddresses = nullptr;
PIP_ADAPTER_ADDRESSES pCurrAddresses = nullptr;
PIP_ADAPTER_UNICAST_ADDRESS pUnicast = nullptr;
bool found_address = false;
pCurrAddresses = pAddresses;
while(pCurrAddresses) {
PIP_ADAPTER_UNICAST_ADDRESS firstUnicastAddress = pCurrAddresses->FirstUnicastAddress;
pUnicast = pCurrAddresses->FirstUnicastAddress;
if(pUnicast == nullptr) {
LogPrint(eLogError, "NetIface: GetMTU(): not a unicast ipv6 address, this is not supported");
}
for(int i = 0; pUnicast != nullptr; ++i) {
LPSOCKADDR lpAddr = pUnicast->Address.lpSockaddr;
sockaddr_in6 *localInterfaceAddress = (sockaddr_in6*) lpAddr;
if(GetAdaptersAddresses(AF_INET6, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen)
== ERROR_BUFFER_OVERFLOW)
{
FREE(pAddresses);
pAddresses = (IP_ADAPTER_ADDRESSES*) MALLOC(outBufLen);
}
for (int j = 0; j != 8; ++j) {
if (localInterfaceAddress->sin6_addr.u.Word[j] != inputAddress.sin6_addr.u.Word[j]) {
break;
} else {
found_address = true;
}
} if (found_address) {
auto result = pAddresses->Mtu;
FREE(pAddresses);
pAddresses = nullptr;
return result;
}
pUnicast = pUnicast->Next;
}
DWORD dwRetVal = GetAdaptersAddresses(
AF_INET6, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen
);
pCurrAddresses = pCurrAddresses->Next;
}
if(dwRetVal != NO_ERROR)
{
LogPrint(eLogError, "NetIface: GetMTU(): enclosed GetAdaptersAddresses() call has failed");
FREE(pAddresses);
return fallback;
}
LogPrint(eLogError, "NetIface: GetMTU(): no usable unicast ipv6 addresses found");
FREE(pAddresses);
return fallback;
}
bool found_address = false;
pCurrAddresses = pAddresses;
while(pCurrAddresses)
{
PIP_ADAPTER_UNICAST_ADDRESS firstUnicastAddress = pCurrAddresses->FirstUnicastAddress;
pUnicast = pCurrAddresses->FirstUnicastAddress;
if(pUnicast == nullptr)
LogPrint(eLogError, "NetIface: GetMTU(): not a unicast ipv6 address, this is not supported");
int GetMTUWindows(const boost::asio::ip::address& localAddress, int fallback)
{
for(int i = 0; pUnicast != nullptr; ++i)
{
LPSOCKADDR lpAddr = pUnicast->Address.lpSockaddr;
sockaddr_in6 *localInterfaceAddress = (sockaddr_in6*) lpAddr;
for (int j = 0; j != 8; ++j)
{
if (localInterfaceAddress->sin6_addr.u.Word[j] != inputAddress.sin6_addr.u.Word[j])
break;
else
found_address = true;
}
if (found_address)
{
auto result = pAddresses->Mtu;
FREE(pAddresses);
pAddresses = nullptr;
return result;
}
pUnicast = pUnicast->Next;
}
pCurrAddresses = pCurrAddresses->Next;
}
LogPrint(eLogError, "NetIface: GetMTU(): no usable unicast ipv6 addresses found");
FREE(pAddresses);
return fallback;
}
int GetMTUWindows(const boost::asio::ip::address& localAddress, int fallback)
{
#ifdef UNICODE
string localAddress_temporary = localAddress.to_string();
wstring localAddressUniversal(localAddress_temporary.begin(), localAddress_temporary.end());
string localAddress_temporary = localAddress.to_string();
wstring localAddressUniversal(localAddress_temporary.begin(), localAddress_temporary.end());
#else
std::string localAddressUniversal = localAddress.to_string();
std::string localAddressUniversal = localAddress.to_string();
#endif
if(localAddress.is_v4()) {
sockaddr_in inputAddress;
inet_pton(AF_INET, localAddressUniversal.c_str(), &(inputAddress.sin_addr));
return GetMTUWindowsIpv4(inputAddress, fallback);
} else if(localAddress.is_v6()) {
sockaddr_in6 inputAddress;
inet_pton(AF_INET6, localAddressUniversal.c_str(), &(inputAddress.sin6_addr));
return GetMTUWindowsIpv6(inputAddress, fallback);
} else {
LogPrint(eLogError, "NetIface: GetMTU(): address family is not supported");
return fallback;
}
if (IsWindowsXPorLater())
{
#define inet_pton inet_pton_xp
}
}
if(localAddress.is_v4())
{
sockaddr_in inputAddress;
inet_pton(AF_INET, localAddressUniversal.c_str(), &(inputAddress.sin_addr));
return GetMTUWindowsIpv4(inputAddress, fallback);
}
else if(localAddress.is_v6())
{
sockaddr_in6 inputAddress;
inet_pton(AF_INET6, localAddressUniversal.c_str(), &(inputAddress.sin6_addr));
return GetMTUWindowsIpv6(inputAddress, fallback);
} else {
LogPrint(eLogError, "NetIface: GetMTU(): address family is not supported");
return fallback;
}
}
#else // assume unix
int GetMTUUnix(const boost::asio::ip::address& localAddress, int fallback)
{
ifaddrs* ifaddr, *ifa = nullptr;
if(getifaddrs(&ifaddr) == -1)
{
ifaddrs* ifaddr, *ifa = nullptr;
if(getifaddrs(&ifaddr) == -1)
{
LogPrint(eLogError, "NetIface: Can't call getifaddrs(): ", strerror(errno));
return fallback;
}
LogPrint(eLogError, "NetIface: Can't call getifaddrs(): ", strerror(errno));
return fallback;
}
int family = 0;
// look for interface matching local address
for(ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next)
int family = 0;
// look for interface matching local address
for(ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next)
{
if(!ifa->ifa_addr)
continue;
if(!ifa->ifa_addr)
continue;
family = ifa->ifa_addr->sa_family;
if(family == AF_INET && localAddress.is_v4())
family = ifa->ifa_addr->sa_family;
if(family == AF_INET && localAddress.is_v4())
{
sockaddr_in* sa = (sockaddr_in*) ifa->ifa_addr;
if(!memcmp(&sa->sin_addr, localAddress.to_v4().to_bytes().data(), 4))
break; // address matches
}
sockaddr_in* sa = (sockaddr_in*) ifa->ifa_addr;
if(!memcmp(&sa->sin_addr, localAddress.to_v4().to_bytes().data(), 4))
break; // address matches
}
else if(family == AF_INET6 && localAddress.is_v6())
{
sockaddr_in6* sa = (sockaddr_in6*) ifa->ifa_addr;
if(!memcmp(&sa->sin6_addr, localAddress.to_v6().to_bytes().data(), 16))
break; // address matches
}
}
int mtu = fallback;
if(ifa && family)
sockaddr_in6* sa = (sockaddr_in6*) ifa->ifa_addr;
if(!memcmp(&sa->sin6_addr, localAddress.to_v6().to_bytes().data(), 16))
break; // address matches
}
}
int mtu = fallback;
if(ifa && family)
{ // interface found?
int fd = socket(family, SOCK_DGRAM, 0);
if(fd > 0)
int fd = socket(family, SOCK_DGRAM, 0);
if(fd > 0)
{
ifreq ifr;
strncpy(ifr.ifr_name, ifa->ifa_name, IFNAMSIZ); // set interface for query
if(ioctl(fd, SIOCGIFMTU, &ifr) >= 0)
mtu = ifr.ifr_mtu; // MTU
else
LogPrint (eLogError, "NetIface: Failed to run ioctl: ", strerror(errno));
close(fd);
}
ifreq ifr;
strncpy(ifr.ifr_name, ifa->ifa_name, IFNAMSIZ); // set interface for query
if(ioctl(fd, SIOCGIFMTU, &ifr) >= 0)
mtu = ifr.ifr_mtu; // MTU
else
LogPrint (eLogError, "NetIface: Failed to run ioctl: ", strerror(errno));
close(fd);
}
else
LogPrint(eLogError, "NetIface: Failed to create datagram socket");
}
LogPrint(eLogError, "NetIface: Failed to create datagram socket");
}
else
LogPrint(eLogWarning, "NetIface: interface for local address", localAddress.to_string(), " not found");
freeifaddrs(ifaddr);
LogPrint(eLogWarning, "NetIface: interface for local address", localAddress.to_string(), " not found");
freeifaddrs(ifaddr);
return mtu;
}
return mtu;
}
#endif // WIN32
int GetMTU(const boost::asio::ip::address& localAddress)
{
const int fallback = 576; // fallback MTU
int GetMTU(const boost::asio::ip::address& localAddress)
{
const int fallback = 576; // fallback MTU
#ifdef WIN32
return GetMTUWindows(localAddress, fallback);
return GetMTUWindows(localAddress, fallback);
#else
return GetMTUUnix(localAddress, fallback);
return GetMTUUnix(localAddress, fallback);
#endif
return fallback;
}
return fallback;
}
const boost::asio::ip::address GetInterfaceAddress(const std::string & ifname, bool ipv6)
{
#ifdef WIN32
LogPrint(eLogError, "NetIface: cannot get address by interface name, not implemented on WIN32");
return boost::asio::ip::address::from_string("127.0.0.1");
if(ipv6)
return boost::asio::ip::address::from_string("::1");
else
return boost::asio::ip::address::from_string("127.0.0.1");
#else
int af = (ipv6 ? AF_INET6 : AF_INET);
ifaddrs * addrs = nullptr;
@@ -291,8 +328,9 @@ namespace net
}
if(addrs) freeifaddrs(addrs);
std::string fallback;
if(ipv6) {
fallback = "::";
if(ipv6)
{
fallback = "::1";
LogPrint(eLogWarning, "NetIface: cannot find ipv6 address for interface ", ifname);
} else {
fallback = "127.0.0.1";

View File

@@ -7,7 +7,7 @@
#define MAKE_VERSION(a,b,c) STRINGIZE(a) "." STRINGIZE(b) "." STRINGIZE(c)
#define I2PD_VERSION_MAJOR 2
#define I2PD_VERSION_MINOR 23
#define I2PD_VERSION_MINOR 25
#define I2PD_VERSION_MICRO 0
#define I2PD_VERSION_PATCH 0
#define I2PD_VERSION MAKE_VERSION(I2PD_VERSION_MAJOR, I2PD_VERSION_MINOR, I2PD_VERSION_MICRO)
@@ -21,7 +21,7 @@
#define I2P_VERSION_MAJOR 0
#define I2P_VERSION_MINOR 9
#define I2P_VERSION_MICRO 38
#define I2P_VERSION_MICRO 40
#define I2P_VERSION_PATCH 0
#define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO)

View File

@@ -7,6 +7,7 @@
#include <condition_variable>
#include <openssl/rand.h>
#include <boost/algorithm/string.hpp>
#include <boost/filesystem.hpp>
#include "Base.h"
#include "util.h"
#include "Identity.h"
@@ -30,23 +31,30 @@ namespace client
std::string etagsPath, indexPath, localPath;
public:
AddressBookFilesystemStorage (): storage("addressbook", "b", "", "b32") {};
AddressBookFilesystemStorage (): storage("addressbook", "b", "", "b32")
{
i2p::config::GetOption("persist.addressbook", m_IsPersist);
}
std::shared_ptr<const i2p::data::IdentityEx> GetAddress (const i2p::data::IdentHash& ident) const;
void AddAddress (std::shared_ptr<const i2p::data::IdentityEx> address);
void RemoveAddress (const i2p::data::IdentHash& ident);
bool Init ();
int Load (std::map<std::string, i2p::data::IdentHash>& addresses);
int LoadLocal (std::map<std::string, i2p::data::IdentHash>& addresses);
int Save (const std::map<std::string, i2p::data::IdentHash>& addresses);
int Load (std::map<std::string, std::shared_ptr<Address> > & addresses);
int LoadLocal (std::map<std::string, std::shared_ptr<Address> >& addresses);
int Save (const std::map<std::string, std::shared_ptr<Address> >& addresses);
void SaveEtag (const i2p::data::IdentHash& subsciption, const std::string& etag, const std::string& lastModified);
bool GetEtag (const i2p::data::IdentHash& subscription, std::string& etag, std::string& lastModified);
void ResetEtags ();
private:
int LoadFromFile (const std::string& filename, std::map<std::string, i2p::data::IdentHash>& addresses); // returns -1 if can't open file, otherwise number of records
int LoadFromFile (const std::string& filename, std::map<std::string, std::shared_ptr<Address> >& addresses); // returns -1 if can't open file, otherwise number of records
private:
bool m_IsPersist;
};
bool AddressBookFilesystemStorage::Init()
@@ -69,6 +77,11 @@ namespace client
std::shared_ptr<const i2p::data::IdentityEx> AddressBookFilesystemStorage::GetAddress (const i2p::data::IdentHash& ident) const
{
if (!m_IsPersist)
{
LogPrint(eLogDebug, "Addressbook: Persistence is disabled");
return nullptr;
}
std::string filename = storage.Path(ident.ToBase32());
std::ifstream f(filename, std::ifstream::binary);
if (!f.is_open ()) {
@@ -92,6 +105,7 @@ namespace client
void AddressBookFilesystemStorage::AddAddress (std::shared_ptr<const i2p::data::IdentityEx> address)
{
if (!m_IsPersist) return;
std::string path = storage.Path( address->GetIdentHash().ToBase32() );
std::ofstream f (path, std::ofstream::binary | std::ofstream::out);
if (!f.is_open ()) {
@@ -107,10 +121,11 @@ namespace client
void AddressBookFilesystemStorage::RemoveAddress (const i2p::data::IdentHash& ident)
{
if (!m_IsPersist) return;
storage.Remove( ident.ToBase32() );
}
int AddressBookFilesystemStorage::LoadFromFile (const std::string& filename, std::map<std::string, i2p::data::IdentHash>& addresses)
int AddressBookFilesystemStorage::LoadFromFile (const std::string& filename, std::map<std::string, std::shared_ptr<Address> >& addresses)
{
int num = 0;
std::ifstream f (filename, std::ifstream::in); // in text mode
@@ -129,16 +144,14 @@ namespace client
std::string name = s.substr(0, pos++);
std::string addr = s.substr(pos);
i2p::data::IdentHash ident;
ident.FromBase32 (addr);
addresses[name] = ident;
addresses[name] = std::make_shared<Address>(addr);
num++;
}
}
return num;
}
int AddressBookFilesystemStorage::Load (std::map<std::string, i2p::data::IdentHash>& addresses)
int AddressBookFilesystemStorage::Load (std::map<std::string, std::shared_ptr<Address> >& addresses)
{
int num = LoadFromFile (indexPath, addresses);
if (num < 0)
@@ -152,7 +165,7 @@ namespace client
return num;
}
int AddressBookFilesystemStorage::LoadLocal (std::map<std::string, i2p::data::IdentHash>& addresses)
int AddressBookFilesystemStorage::LoadLocal (std::map<std::string, std::shared_ptr<Address> >& addresses)
{
int num = LoadFromFile (localPath, addresses);
if (num < 0) return 0;
@@ -160,7 +173,7 @@ namespace client
return num;
}
int AddressBookFilesystemStorage::Save (const std::map<std::string, i2p::data::IdentHash>& addresses)
int AddressBookFilesystemStorage::Save (const std::map<std::string, std::shared_ptr<Address> >& addresses)
{
if (addresses.empty()) {
LogPrint(eLogWarning, "Addressbook: not saving empty addressbook");
@@ -175,8 +188,14 @@ namespace client
return 0;
}
for (const auto& it: addresses) {
f << it.first << "," << it.second.ToBase32 () << std::endl;
for (const auto& it: addresses)
{
f << it.first << ",";
if (it.second->IsIdentHash ())
f << it.second->identHash.ToBase32 ();
else
f << it.second->blindedPublicKey->ToB33 ();
f << std::endl;
num++;
}
LogPrint (eLogInfo, "Addressbook: ", num, " addresses saved");
@@ -205,7 +224,39 @@ namespace client
return true;
}
void AddressBookFilesystemStorage::ResetEtags ()
{
LogPrint (eLogError, "Addressbook: resetting eTags");
for (boost::filesystem::directory_iterator it (etagsPath); it != boost::filesystem::directory_iterator (); ++it)
{
if (!boost::filesystem::is_regular_file (it->status ()))
continue;
boost::filesystem::remove (it->path ());
}
}
//---------------------------------------------------------------------
Address::Address (const std::string& b32)
{
if (b32.length () <= B33_ADDRESS_THRESHOLD)
{
addressType = eAddressIndentHash;
identHash.FromBase32 (b32);
}
else
{
addressType = eAddressBlindedPublicKey;
blindedPublicKey = std::make_shared<i2p::data::BlindedPublicKey>(b32);
}
}
Address::Address (const i2p::data::IdentHash& hash)
{
addressType = eAddressIndentHash;
identHash = hash;
}
AddressBook::AddressBook (): m_Storage(nullptr), m_IsLoaded (false), m_IsDownloading (false),
m_NumRetries (0), m_DefaultSubscription (nullptr), m_SubscriptionsUpdateTimer (nullptr)
{
@@ -242,7 +293,7 @@ namespace client
}
if (m_IsDownloading)
{
LogPrint (eLogInfo, "Addressbook: subscriptions is downloading, abort");
LogPrint (eLogInfo, "Addressbook: subscriptions are downloading, abort");
for (int i = 0; i < 30; i++)
{
if (!m_IsDownloading)
@@ -265,67 +316,70 @@ namespace client
m_Subscriptions.clear ();
}
bool AddressBook::GetIdentHash (const std::string& address, i2p::data::IdentHash& ident)
std::shared_ptr<const Address> AddressBook::GetAddress (const std::string& address)
{
auto pos = address.find(".b32.i2p");
if (pos != std::string::npos)
{
Base32ToByteStream (address.c_str(), pos, ident, 32);
return true;
}
return std::make_shared<const Address>(address.substr (0, pos));
else
{
pos = address.find (".i2p");
if (pos != std::string::npos)
{
auto identHash = FindAddress (address);
if (identHash)
{
ident = *identHash;
return true;
}
else
{
LookupAddress (address); // TODO:
return false;
}
}
}
auto addr = FindAddress (address);
if (!addr)
LookupAddress (address); // TODO:
return addr;
}
}
// if not .b32 we assume full base64 address
i2p::data::IdentityEx dest;
if (!dest.FromBase64 (address))
return false;
ident = dest.GetIdentHash ();
return true;
return nullptr;
return std::make_shared<const Address>(dest.GetIdentHash ());
}
const i2p::data::IdentHash * AddressBook::FindAddress (const std::string& address)
std::shared_ptr<const Address> AddressBook::FindAddress (const std::string& address)
{
auto it = m_Addresses.find (address);
if (it != m_Addresses.end ())
return &it->second;
return it->second;
return nullptr;
}
void AddressBook::InsertAddress (const std::string& address, const std::string& base64)
void AddressBook::InsertAddress (const std::string& address, const std::string& jump)
{
auto ident = std::make_shared<i2p::data::IdentityEx>();
ident->FromBase64 (base64);
m_Storage->AddAddress (ident);
m_Addresses[address] = ident->GetIdentHash ();
LogPrint (eLogInfo, "Addressbook: added ", address," -> ", ToAddress(ident->GetIdentHash ()));
auto pos = jump.find(".b32.i2p");
if (pos != std::string::npos)
{
m_Addresses[address] = std::make_shared<Address>(jump.substr (0, pos));
LogPrint (eLogInfo, "Addressbook: added ", address," -> ", jump);
}
else
{
// assume base64
auto ident = std::make_shared<i2p::data::IdentityEx>();
if (ident->FromBase64 (jump))
{
m_Storage->AddAddress (ident);
m_Addresses[address] = std::make_shared<Address>(ident->GetIdentHash ());
LogPrint (eLogInfo, "Addressbook: added ", address," -> ", ToAddress(ident->GetIdentHash ()));
}
else
LogPrint (eLogError, "Addressbook: malformed address ", jump);
}
}
void AddressBook::InsertAddress (std::shared_ptr<const i2p::data::IdentityEx> address)
void AddressBook::InsertFullAddress (std::shared_ptr<const i2p::data::IdentityEx> address)
{
m_Storage->AddAddress (address);
}
std::shared_ptr<const i2p::data::IdentityEx> AddressBook::GetAddress (const std::string& address)
std::shared_ptr<const i2p::data::IdentityEx> AddressBook::GetFullAddress (const std::string& address)
{
i2p::data::IdentHash ident;
if (!GetIdentHash (address, ident)) return nullptr;
return m_Storage->GetAddress (ident);
auto addr = GetAddress (address);
if (!addr || !addr->IsIdentHash ()) return nullptr;
return m_Storage->GetAddress (addr->identHash);
}
void AddressBook::LoadHosts ()
@@ -343,6 +397,9 @@ namespace client
LoadHostsFromStream (f, false);
m_IsLoaded = true;
}
// reset eTags, because we dont know how old hosts.txt is or can't load addressbook
m_Storage->ResetEtags ();
}
bool AddressBook::LoadHostsFromStream (std::istream& f, bool is_update)
@@ -379,16 +436,17 @@ namespace client
auto it = m_Addresses.find (name);
if (it != m_Addresses.end ()) // already exists ?
{
if (it->second != ident->GetIdentHash ()) // address changed?
if (it->second->IsIdentHash () && it->second->identHash != ident->GetIdentHash ()) // address changed?
{
it->second = ident->GetIdentHash ();
it->second->identHash = ident->GetIdentHash ();
m_Storage->AddAddress (ident);
LogPrint (eLogInfo, "Addressbook: updated host: ", name);
}
}
else
{
m_Addresses.insert (std::make_pair (name, ident->GetIdentHash ()));
//m_Addresses.emplace (name, std::make_shared<Address>(ident->GetIdentHash ()));
m_Addresses[name] = std::make_shared<Address>(ident->GetIdentHash ()); // for gcc 4.7
m_Storage->AddAddress (ident);
if (is_update)
LogPrint (eLogInfo, "Addressbook: added new host: ", name);
@@ -443,32 +501,33 @@ namespace client
void AddressBook::LoadLocal ()
{
std::map<std::string, i2p::data::IdentHash> localAddresses;
std::map<std::string, std::shared_ptr<Address>> localAddresses;
m_Storage->LoadLocal (localAddresses);
for (const auto& it: localAddresses)
{
if (!it.second->IsIdentHash ()) continue; // skip blinded for now
auto dot = it.first.find ('.');
if (dot != std::string::npos)
{
auto domain = it.first.substr (dot + 1);
auto it1 = m_Addresses.find (domain); // find domain in our addressbook
if (it1 != m_Addresses.end ())
if (it1 != m_Addresses.end () && it1->second->IsIdentHash ())
{
auto dest = context.FindLocalDestination (it1->second);
auto dest = context.FindLocalDestination (it1->second->identHash);
if (dest)
{
// address is ours
std::shared_ptr<AddressResolver> resolver;
auto it2 = m_Resolvers.find (it1->second);
auto it2 = m_Resolvers.find (it1->second->identHash);
if (it2 != m_Resolvers.end ())
resolver = it2->second; // resolver exists
else
{
// create new resolver
resolver = std::make_shared<AddressResolver>(dest);
m_Resolvers.insert (std::make_pair(it1->second, resolver));
m_Resolvers.insert (std::make_pair(it1->second->identHash, resolver));
}
resolver->AddAddress (it.first, it.second);
resolver->AddAddress (it.first, it.second->identHash);
}
}
}
@@ -598,11 +657,11 @@ namespace client
void AddressBook::LookupAddress (const std::string& address)
{
const i2p::data::IdentHash * ident = nullptr;
std::shared_ptr<const Address> addr;
auto dot = address.find ('.');
if (dot != std::string::npos)
ident = FindAddress (address.substr (dot + 1));
if (!ident)
addr = FindAddress (address.substr (dot + 1));
if (!addr || !addr->IsIdentHash ()) // TODO:
{
LogPrint (eLogError, "Addressbook: Can't find domain for ", address);
return;
@@ -620,14 +679,14 @@ namespace client
std::unique_lock<std::mutex> l(m_LookupsMutex);
m_Lookups[nonce] = address;
}
LogPrint (eLogDebug, "Addressbook: Lookup of ", address, " to ", ident->ToBase32 (), " nonce=", nonce);
LogPrint (eLogDebug, "Addressbook: Lookup of ", address, " to ", addr->identHash.ToBase32 (), " nonce=", nonce);
size_t len = address.length () + 9;
uint8_t * buf = new uint8_t[len];
memset (buf, 0, 4);
htobe32buf (buf + 4, nonce);
buf[8] = address.length ();
memcpy (buf + 9, address.c_str (), address.length ());
datagram->SendDatagramTo (buf, len, *ident, ADDRESS_RESPONSE_DATAGRAM_PORT, ADDRESS_RESOLVER_DATAGRAM_PORT);
datagram->SendDatagramTo (buf, len, addr->identHash, ADDRESS_RESPONSE_DATAGRAM_PORT, ADDRESS_RESOLVER_DATAGRAM_PORT);
delete[] buf;
}
}
@@ -657,7 +716,7 @@ namespace client
// TODO: verify from
i2p::data::IdentHash hash(buf + 8);
if (!hash.IsZero ())
m_Addresses[address] = hash;
m_Addresses[address] = std::make_shared<Address>(hash);
else
LogPrint (eLogInfo, "AddressBook: Lookup response: ", address, " not found");
}
@@ -679,14 +738,19 @@ namespace client
i2p::http::URL url;
// must be run in separate thread
LogPrint (eLogInfo, "Addressbook: Downloading hosts database from ", m_Link);
if (!url.parse(m_Link)) {
if (!url.parse(m_Link))
{
LogPrint(eLogError, "Addressbook: failed to parse url: ", m_Link);
return false;
}
if (!m_Book.GetIdentHash (url.host, m_Ident)) {
auto addr = m_Book.GetAddress (url.host);
if (!addr || !addr->IsIdentHash ())
{
LogPrint (eLogError, "Addressbook: Can't resolve ", url.host);
return false;
}
else
m_Ident = addr->identHash;
/* this code block still needs some love */
std::condition_variable newDataReceived;
std::mutex newDataReceivedMutex;

View File

@@ -13,6 +13,7 @@
#include "Identity.h"
#include "Log.h"
#include "Destination.h"
#include "LeaseSet.h"
namespace i2p
{
@@ -28,6 +29,19 @@ namespace client
const uint16_t ADDRESS_RESOLVER_DATAGRAM_PORT = 53;
const uint16_t ADDRESS_RESPONSE_DATAGRAM_PORT = 54;
const size_t B33_ADDRESS_THRESHOLD = 52; // characters
struct Address
{
enum { eAddressIndentHash, eAddressBlindedPublicKey } addressType;
i2p::data::IdentHash identHash;
std::shared_ptr<i2p::data::BlindedPublicKey> blindedPublicKey;
Address (const std::string& b32);
Address (const i2p::data::IdentHash& hash);
bool IsIdentHash () const { return addressType == eAddressIndentHash; };
};
inline std::string GetB32Address(const i2p::data::IdentHash& ident) { return ident.ToBase32().append(".b32.i2p"); }
class AddressBookStorage // interface for storage
@@ -40,12 +54,13 @@ namespace client
virtual void RemoveAddress (const i2p::data::IdentHash& ident) = 0;
virtual bool Init () = 0;
virtual int Load (std::map<std::string, i2p::data::IdentHash>& addresses) = 0;
virtual int LoadLocal (std::map<std::string, i2p::data::IdentHash>& addresses) = 0;
virtual int Save (const std::map<std::string, i2p::data::IdentHash>& addresses) = 0;
virtual int Load (std::map<std::string, std::shared_ptr<Address> >& addresses) = 0;
virtual int LoadLocal (std::map<std::string, std::shared_ptr<Address> >& addresses) = 0;
virtual int Save (const std::map<std::string, std::shared_ptr<Address> >& addresses) = 0;
virtual void SaveEtag (const i2p::data::IdentHash& subscription, const std::string& etag, const std::string& lastModified) = 0;
virtual bool GetEtag (const i2p::data::IdentHash& subscription, std::string& etag, std::string& lastModified) = 0;
virtual void ResetEtags () = 0;
};
class AddressBookSubscription;
@@ -59,12 +74,12 @@ namespace client
void Start ();
void StartResolvers ();
void Stop ();
bool GetIdentHash (const std::string& address, i2p::data::IdentHash& ident);
std::shared_ptr<const i2p::data::IdentityEx> GetAddress (const std::string& address);
const i2p::data::IdentHash * FindAddress (const std::string& address);
std::shared_ptr<const Address> GetAddress (const std::string& address);
std::shared_ptr<const i2p::data::IdentityEx> GetFullAddress (const std::string& address);
std::shared_ptr<const Address> FindAddress (const std::string& address);
void LookupAddress (const std::string& address);
void InsertAddress (const std::string& address, const std::string& base64); // for jump service
void InsertAddress (std::shared_ptr<const i2p::data::IdentityEx> address);
void InsertAddress (const std::string& address, const std::string& jump); // for jump links
void InsertFullAddress (std::shared_ptr<const i2p::data::IdentityEx> address);
bool LoadHostsFromStream (std::istream& f, bool is_update);
void DownloadComplete (bool success, const i2p::data::IdentHash& subscription, const std::string& etag, const std::string& lastModified);
@@ -92,7 +107,7 @@ namespace client
private:
std::mutex m_AddressBookMutex;
std::map<std::string, i2p::data::IdentHash> m_Addresses;
std::map<std::string, std::shared_ptr<Address> > m_Addresses;
std::map<i2p::data::IdentHash, std::shared_ptr<AddressResolver> > m_Resolvers; // local destination->resolver
std::mutex m_LookupsMutex;
std::map<uint32_t, std::string> m_Lookups; // nonce -> address

View File

@@ -72,19 +72,26 @@ namespace client
if (eol != receiver->buffer && eol[-1] == '\r') eol[-1] = 0; // workaround for Transmission, it sends '\r\n' terminated address
receiver->data = (uint8_t *)eol + 1;
receiver->dataLen = receiver->bufferOffset - (eol - receiver->buffer + 1);
i2p::data::IdentHash ident;
if (!context.GetAddressBook ().GetIdentHash (receiver->buffer, ident))
auto addr = context.GetAddressBook ().GetAddress (receiver->buffer);
if (!addr)
{
LogPrint (eLogError, "BOB: address ", receiver->buffer, " not found");
return;
}
auto leaseSet = GetLocalDestination ()->FindLeaseSet (ident);
if (leaseSet)
CreateConnection (receiver, leaseSet);
if (addr->IsIdentHash ())
{
auto leaseSet = GetLocalDestination ()->FindLeaseSet (addr->identHash);
if (leaseSet)
CreateConnection (receiver, leaseSet);
else
GetLocalDestination ()->RequestDestination (addr->identHash,
std::bind (&BOBI2PInboundTunnel::HandleDestinationRequestComplete,
this, std::placeholders::_1, receiver));
}
else
GetLocalDestination ()->RequestDestination (ident,
std::bind (&BOBI2PInboundTunnel::HandleDestinationRequestComplete,
this, std::placeholders::_1, receiver));
GetLocalDestination ()->RequestDestinationWithEncryptedLeaseSet (addr->blindedPublicKey,
std::bind (&BOBI2PInboundTunnel::HandleDestinationRequestComplete,
this, std::placeholders::_1, receiver));
}
else
{
@@ -540,29 +547,37 @@ namespace client
void BOBCommandSession::LookupCommandHandler (const char * operand, size_t len)
{
LogPrint (eLogDebug, "BOB: lookup ", operand);
i2p::data::IdentHash ident;
if (!context.GetAddressBook ().GetIdentHash (operand, ident))
auto addr = context.GetAddressBook ().GetAddress (operand);
if (!addr)
{
SendReplyError ("Address Not found");
return;
}
auto localDestination = m_CurrentDestination ? m_CurrentDestination->GetLocalDestination () : i2p::client::context.GetSharedLocalDestination ();
auto leaseSet = localDestination->FindLeaseSet (ident);
if (leaseSet)
SendReplyOK (leaseSet->GetIdentity ()->ToBase64 ().c_str ());
else
{
auto s = shared_from_this ();
localDestination->RequestDestination (ident,
[s](std::shared_ptr<i2p::data::LeaseSet> ls)
if (addr->IsIdentHash ())
{
// we might have leaseset already
auto leaseSet = localDestination->FindLeaseSet (addr->identHash);
if (leaseSet)
{
SendReplyOK (leaseSet->GetIdentity ()->ToBase64 ().c_str ());
return;
}
}
// trying to request
auto s = shared_from_this ();
auto requstCallback =
[s](std::shared_ptr<i2p::data::LeaseSet> ls)
{
if (ls)
s->SendReplyOK (ls->GetIdentity ()->ToBase64 ().c_str ());
else
s->SendReplyError ("LeaseSet Not found");
}
);
}
};
if (addr->IsIdentHash ())
localDestination->RequestDestination (addr->identHash, requstCallback);
else
localDestination->RequestDestinationWithEncryptedLeaseSet (addr->blindedPublicKey, requstCallback);
}
void BOBCommandSession::ClearCommandHandler (const char * operand, size_t len)

View File

@@ -39,7 +39,7 @@ namespace client
if (!m_SharedLocalDestination)
CreateNewSharedLocalDestination ();
// addressbook
// addressbook
m_AddressBook.Start ();
// HTTP proxy
@@ -207,16 +207,16 @@ namespace client
{
m_HttpProxy->Stop ();
m_HttpProxy = nullptr;
}
}
ReadHttpProxy ();
// recreate SOCKS proxy
if (m_SocksProxy)
{
m_SocksProxy->Stop ();
m_SocksProxy = nullptr;
}
ReadSocksProxy ();
}
ReadSocksProxy ();
// delete unused destinations
std::unique_lock<std::mutex> l(m_DestinationsMutex);
@@ -391,10 +391,10 @@ namespace client
options[I2CP_PARAM_TAGS_TO_SEND] = GetI2CPOption (section, I2CP_PARAM_TAGS_TO_SEND, DEFAULT_TAGS_TO_SEND);
options[I2CP_PARAM_MIN_TUNNEL_LATENCY] = GetI2CPOption(section, I2CP_PARAM_MIN_TUNNEL_LATENCY, DEFAULT_MIN_TUNNEL_LATENCY);
options[I2CP_PARAM_MAX_TUNNEL_LATENCY] = GetI2CPOption(section, I2CP_PARAM_MAX_TUNNEL_LATENCY, DEFAULT_MAX_TUNNEL_LATENCY);
options[I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY] = GetI2CPOption(section, I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY, DEFAULT_INITIAL_ACK_DELAY);
options[I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY] = GetI2CPOption(section, I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY, DEFAULT_INITIAL_ACK_DELAY);
options[I2CP_PARAM_LEASESET_TYPE] = GetI2CPOption(section, I2CP_PARAM_LEASESET_TYPE, DEFAULT_LEASESET_TYPE);
std::string encType = GetI2CPStringOption(section, I2CP_PARAM_LEASESET_ENCRYPTION_TYPE, "");
if (encType.length () > 0) options[I2CP_PARAM_LEASESET_ENCRYPTION_TYPE] = encType;
if (encType.length () > 0) options[I2CP_PARAM_LEASESET_ENCRYPTION_TYPE] = encType;
}
void ClientContext::ReadI2CPOptionsFromConfig (const std::string& prefix, std::map<std::string, std::string>& options) const
@@ -418,21 +418,21 @@ namespace client
{
int numClientTunnels = 0, numServerTunnels = 0;
std::string tunConf; i2p::config::GetOption("tunconf", tunConf);
if (tunConf.empty ())
if (tunConf.empty ())
{
// TODO: cleanup this in 2.8.0
tunConf = i2p::fs::DataDirPath ("tunnels.cfg");
if (i2p::fs::Exists(tunConf))
LogPrint(eLogWarning, "Clients: please rename tunnels.cfg -> tunnels.conf here: ", tunConf);
else
else
tunConf = i2p::fs::DataDirPath ("tunnels.conf");
}
LogPrint(eLogDebug, "Clients: tunnels config file: ", tunConf);
ReadTunnels (tunConf, numClientTunnels, numServerTunnels);
std::string tunDir; i2p::config::GetOption("tunnelsdir", tunDir);
if (tunDir.empty ())
tunDir = i2p::fs::DataDirPath ("tunnels.d");
tunDir = i2p::fs::DataDirPath ("tunnels.d");
if (i2p::fs::Exists (tunDir))
{
std::vector<std::string> files;
@@ -450,7 +450,7 @@ namespace client
LogPrint (eLogInfo, "Clients: ", numServerTunnels, " I2P server tunnels created");
}
void ClientContext::ReadTunnels (const std::string& tunConf, int& numClientTunnels, int& numServerTunnels)
{
boost::property_tree::ptree pt;
@@ -540,7 +540,8 @@ namespace client
{
// http proxy
std::string outproxy = section.second.get("outproxy", "");
auto tun = std::make_shared<i2p::proxy::HTTPProxy>(name, address, port, outproxy, localDestination);
bool addresshelper = section.second.get("addresshelper", true);
auto tun = std::make_shared<i2p::proxy::HTTPProxy>(name, address, port, outproxy, addresshelper, localDestination);
clientTunnel = tun;
clientEndpoint = tun->GetLocalEndpoint ();
}
@@ -555,7 +556,7 @@ namespace client
{
// tcp client
auto tun = std::make_shared<I2PClientTunnel> (name, dest, address, port, localDestination, destinationPort);
clientTunnel = tun;
clientTunnel = tun;
clientEndpoint = tun->GetLocalEndpoint ();
}
uint32_t timeout = section.second.get<uint32_t>(I2P_CLIENT_TUNNEL_CONNECT_TIMEOUT, 0);
@@ -674,7 +675,7 @@ namespace client
serverTunnel->SetAccessList (idents);
}
auto ins = m_ServerTunnels.insert (std::make_pair (
std::make_pair (localDestination->GetIdentHash (), inPort),
std::make_pair (localDestination->GetIdentHash (), inPort),
serverTunnel));
if (ins.second)
{
@@ -716,6 +717,7 @@ namespace client
uint16_t httpProxyPort; i2p::config::GetOption("httpproxy.port", httpProxyPort);
i2p::data::SigningKeyType sigType; i2p::config::GetOption("httpproxy.signaturetype", sigType);
std::string httpOutProxyURL; i2p::config::GetOption("httpproxy.outproxy", httpOutProxyURL);
bool httpAddresshelper; i2p::config::GetOption("httpproxy.addresshelper", httpAddresshelper);
LogPrint(eLogInfo, "Clients: starting HTTP Proxy at ", httpProxyAddr, ":", httpProxyPort);
if (httpProxyKeys.length () > 0)
{
@@ -732,7 +734,7 @@ namespace client
}
try
{
m_HttpProxy = new i2p::proxy::HTTPProxy("HTTP Proxy", httpProxyAddr, httpProxyPort, httpOutProxyURL, localDestination);
m_HttpProxy = new i2p::proxy::HTTPProxy("HTTP Proxy", httpProxyAddr, httpProxyPort, httpOutProxyURL, httpAddresshelper, localDestination);
m_HttpProxy->Start();
}
catch (std::exception& e)
@@ -741,7 +743,7 @@ namespace client
}
}
}
void ClientContext::ReadSocksProxy ()
{
std::shared_ptr<ClientDestination> localDestination;

View File

@@ -64,44 +64,47 @@ namespace proxy {
void HostNotFound(std::string & host);
void SendProxyError(std::string & content);
void ForwardToUpstreamProxy();
void HandleUpstreamHTTPProxyConnect(const boost::system::error_code & ec);
void HandleUpstreamSocksProxyConnect(const boost::system::error_code & ec);
void HTTPConnect(const std::string & host, uint16_t port);
void HandleHTTPConnectStreamRequestComplete(std::shared_ptr<i2p::stream::Stream> stream);
void ForwardToUpstreamProxy();
void HandleUpstreamHTTPProxyConnect(const boost::system::error_code & ec);
void HandleUpstreamSocksProxyConnect(const boost::system::error_code & ec);
void HTTPConnect(const std::string & host, uint16_t port);
void HandleHTTPConnectStreamRequestComplete(std::shared_ptr<i2p::stream::Stream> stream);
void HandleSocksProxySendHandshake(const boost::system::error_code & ec, std::size_t bytes_transfered);
void HandleSocksProxyReply(const boost::system::error_code & ec, std::size_t bytes_transfered);
void HandleSocksProxySendHandshake(const boost::system::error_code & ec, std::size_t bytes_transfered);
void HandleSocksProxyReply(const boost::system::error_code & ec, std::size_t bytes_transfered);
typedef std::function<void(boost::asio::ip::tcp::endpoint)> ProxyResolvedHandler;
typedef std::function<void(boost::asio::ip::tcp::endpoint)> ProxyResolvedHandler;
void HandleUpstreamProxyResolved(const boost::system::error_code & ecode, boost::asio::ip::tcp::resolver::iterator itr, ProxyResolvedHandler handler);
void HandleUpstreamProxyResolved(const boost::system::error_code & ecode, boost::asio::ip::tcp::resolver::iterator itr, ProxyResolvedHandler handler);
void SocksProxySuccess();
void HandoverToUpstreamProxy();
void SocksProxySuccess();
void HandoverToUpstreamProxy();
uint8_t m_recv_chunk[8192];
std::string m_recv_buf; // from client
std::string m_send_buf; // to upstream
std::shared_ptr<boost::asio::ip::tcp::socket> m_sock;
std::shared_ptr<boost::asio::ip::tcp::socket> m_proxysock;
boost::asio::ip::tcp::resolver m_proxy_resolver;
std::string m_OutproxyUrl;
i2p::http::URL m_ProxyURL;
i2p::http::URL m_RequestURL;
uint8_t m_socks_buf[255+8]; // for socks request/response
ssize_t m_req_len;
i2p::http::URL m_ClientRequestURL;
i2p::http::HTTPReq m_ClientRequest;
i2p::http::HTTPRes m_ClientResponse;
std::stringstream m_ClientRequestBuffer;
std::shared_ptr<boost::asio::ip::tcp::socket> m_proxysock;
boost::asio::ip::tcp::resolver m_proxy_resolver;
std::string m_OutproxyUrl;
bool m_Addresshelper;
i2p::http::URL m_ProxyURL;
i2p::http::URL m_RequestURL;
uint8_t m_socks_buf[255+8]; // for socks request/response
ssize_t m_req_len;
i2p::http::URL m_ClientRequestURL;
i2p::http::HTTPReq m_ClientRequest;
i2p::http::HTTPRes m_ClientResponse;
std::stringstream m_ClientRequestBuffer;
public:
HTTPReqHandler(HTTPProxy * parent, std::shared_ptr<boost::asio::ip::tcp::socket> sock) :
I2PServiceHandler(parent), m_sock(sock),
m_proxysock(std::make_shared<boost::asio::ip::tcp::socket>(parent->GetService())),
m_proxy_resolver(parent->GetService()),
m_OutproxyUrl(parent->GetOutproxyURL()) {}
m_OutproxyUrl(parent->GetOutproxyURL()),
m_Addresshelper(parent->GetHelperSupport()) {}
~HTTPReqHandler() { Terminate(); }
void Handle () { AsyncSockRead(); } /* overload */
};
@@ -114,8 +117,8 @@ namespace proxy {
return;
}
m_sock->async_read_some(boost::asio::buffer(m_recv_chunk, sizeof(m_recv_chunk)),
std::bind(&HTTPReqHandler::HandleSockRecv, shared_from_this(),
std::placeholders::_1, std::placeholders::_2));
std::bind(&HTTPReqHandler::HandleSockRecv, shared_from_this(),
std::placeholders::_1, std::placeholders::_2));
}
void HTTPReqHandler::Terminate() {
@@ -156,7 +159,7 @@ namespace proxy {
std::stringstream ss;
ss << "<h1>Proxy error: Host not found</h1>\r\n"
<< "<p>Remote host not found in router's addressbook</p>\r\n"
<< "<p>You may try to find this host on jumpservices below:</p>\r\n"
<< "<p>You may try to find this host on jump services below:</p>\r\n"
<< "<ul>\r\n";
for (const auto& js : jumpservices) {
ss << " <li><a href=\"" << js.second << host << "\">" << js.first << "</a></li>\r\n";
@@ -208,7 +211,7 @@ namespace proxy {
void HTTPReqHandler::SanitizeHTTPRequest(i2p::http::HTTPReq & req)
{
/* drop common headers */
req.RemoveHeader("Referer");
req.RemoveHeader("Referrer");
req.RemoveHeader("Via");
req.RemoveHeader("From");
req.RemoveHeader("Forwarded");
@@ -219,7 +222,11 @@ namespace proxy {
/* replace headers */
req.UpdateHeader("User-Agent", "MYOB/6.66 (AN/ON)");
/* add headers */
req.UpdateHeader("Connection", "close"); /* keep-alive conns not supported yet */
/* close connection, if not Connection: (U|u)pgrade (for websocket) */
auto h = req.GetHeader ("Connection");
auto x = h.find("pgrade");
if (!(x != std::string::npos && std::tolower(h[x - 1]) == 'u'))
req.UpdateHeader("Connection", "close");
}
/**
@@ -230,8 +237,6 @@ namespace proxy {
*/
bool HTTPReqHandler::HandleRequest()
{
std::string b64;
m_req_len = m_ClientRequest.parse(m_recv_buf);
if (m_req_len == 0)
@@ -248,10 +253,10 @@ namespace proxy {
m_RequestURL.parse(m_ClientRequest.uri);
bool m_Confirm;
if (ExtractAddressHelper(m_RequestURL, b64, m_Confirm))
std::string jump;
if (ExtractAddressHelper(m_RequestURL, jump, m_Confirm))
{
bool addresshelper; i2p::config::GetOption("httpproxy.addresshelper", addresshelper);
if (!addresshelper)
if (!m_Addresshelper)
{
LogPrint(eLogWarning, "HTTPProxy: addresshelper request rejected");
GenericProxyError("Invalid request", "addresshelper is not supported");
@@ -259,8 +264,8 @@ namespace proxy {
}
if (!i2p::client::context.GetAddressBook ().FindAddress (m_RequestURL.host) || m_Confirm)
{
i2p::client::context.GetAddressBook ().InsertAddress (m_RequestURL.host, b64);
LogPrint (eLogInfo, "HTTPProxy: added b64 from addresshelper for ", m_RequestURL.host);
i2p::client::context.GetAddressBook ().InsertAddress (m_RequestURL.host, jump);
LogPrint (eLogInfo, "HTTPProxy: added address from addresshelper for ", m_RequestURL.host);
std::string full_url = m_RequestURL.to_string();
std::stringstream ss;
ss << "Host " << m_RequestURL.host << " added to router's addressbook from helper. "
@@ -272,7 +277,7 @@ namespace proxy {
{
std::stringstream ss;
ss << "Host " << m_RequestURL.host << " <font color=red>already in router's addressbook</font>. "
<< "Click <a href=\"" << m_RequestURL.query << "?i2paddresshelper=" << b64 << "&update=true\">here</a> to update record.";
<< "Click <a href=\"" << m_RequestURL.query << "?i2paddresshelper=" << jump << "&update=true\">here</a> to update record.";
GenericProxyInfo("Addresshelper found", ss.str().c_str());
return true; /* request processed */
}
@@ -335,9 +340,8 @@ namespace proxy {
}
}
/* check dest_host really exists and inside I2P network */
i2p::data::IdentHash identHash;
if (str_rmatch(dest_host, ".i2p")) {
if (!i2p::client::context.GetAddressBook ().GetIdentHash (dest_host, identHash)) {
if (!i2p::client::context.GetAddressBook ().GetAddress (dest_host)) {
HostNotFound(dest_host);
return true; /* request processed */
}
@@ -349,7 +353,7 @@ namespace proxy {
else
GenericProxyError("Outproxy failure", "bad outproxy settings");
} else {
LogPrint (eLogWarning, "HTTPProxy: outproxy failure for ", dest_host, ": no outprxy enabled");
LogPrint (eLogWarning, "HTTPProxy: outproxy failure for ", dest_host, ": no outproxy enabled");
std::string message = "Host " + dest_host + " not inside I2P network, but outproxy is not enabled";
GenericProxyError("Outproxy failure", message.c_str());
}
@@ -392,13 +396,13 @@ namespace proxy {
// update User-Agent to ESR version of Firefox, same as Tor Browser below version 8, for non-HTTPS connections
if(m_ClientRequest.method != "CONNECT")
m_ClientRequest.UpdateHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; rv:52.0) Gecko/20100101 Firefox/52.0");
m_ClientRequest.UpdateHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; rv:60.0) Gecko/20100101 Firefox/60.0");
m_ClientRequest.write(m_ClientRequestBuffer);
m_ClientRequestBuffer << m_recv_buf.substr(m_req_len);
// assume http if empty schema
if (m_ProxyURL.schema == "" || m_ProxyURL.schema == "http")
if (m_ProxyURL.schema == "" || m_ProxyURL.schema == "http")
{
// handle upstream http proxy
if (!m_ProxyURL.port) m_ProxyURL.port = 80;
@@ -426,8 +430,8 @@ namespace proxy {
m_proxysock->async_connect(ep, std::bind(&HTTPReqHandler::HandleUpstreamHTTPProxyConnect, this, std::placeholders::_1));
}));
}
}
else if (m_ProxyURL.schema == "socks")
}
else if (m_ProxyURL.schema == "socks")
{
// handle upstream socks proxy
if (!m_ProxyURL.port) m_ProxyURL.port = 9050; // default to tor default if not specified
@@ -435,8 +439,8 @@ namespace proxy {
m_proxy_resolver.async_resolve(q, std::bind(&HTTPReqHandler::HandleUpstreamProxyResolved, this, std::placeholders::_1, std::placeholders::_2, [&](boost::asio::ip::tcp::endpoint ep) {
m_proxysock->async_connect(ep, std::bind(&HTTPReqHandler::HandleUpstreamSocksProxyConnect, this, std::placeholders::_1));
}));
}
else
}
else
{
// unknown type, complain
GenericProxyError("unknown outproxy url", m_ProxyURL.to_string().c_str());
@@ -507,7 +511,7 @@ namespace proxy {
std::string hostname(host);
if(str_rmatch(hostname, ".i2p"))
GetOwner()->CreateStream (std::bind (&HTTPReqHandler::HandleHTTPConnectStreamRequestComplete,
shared_from_this(), std::placeholders::_1), host, port);
shared_from_this(), std::placeholders::_1), host, port);
else
ForwardToUpstreamProxy();
}
@@ -619,9 +623,9 @@ namespace proxy {
Done (shared_from_this());
}
HTTPProxy::HTTPProxy(const std::string& name, const std::string& address, int port, const std::string & outproxy, std::shared_ptr<i2p::client::ClientDestination> localDestination):
TCPIPAcceptor(address, port, localDestination ? localDestination : i2p::client::context.GetSharedLocalDestination ()),
m_Name (name), m_OutproxyUrl(outproxy)
HTTPProxy::HTTPProxy(const std::string& name, const std::string& address, int port, const std::string & outproxy, bool addresshelper, std::shared_ptr<i2p::client::ClientDestination> localDestination):
TCPIPAcceptor (address, port, localDestination ? localDestination : i2p::client::context.GetSharedLocalDestination ()),
m_Name (name), m_OutproxyUrl (outproxy), m_Addresshelper (addresshelper)
{
}

View File

@@ -6,12 +6,13 @@ namespace proxy {
class HTTPProxy: public i2p::client::TCPIPAcceptor
{
public:
HTTPProxy(const std::string& name, const std::string& address, int port, const std::string & outproxy, std::shared_ptr<i2p::client::ClientDestination> localDestination);
HTTPProxy(const std::string& name, const std::string& address, int port, const std::string & outproxy, bool addresshelper, std::shared_ptr<i2p::client::ClientDestination> localDestination);
HTTPProxy(const std::string& name, const std::string& address, int port, std::shared_ptr<i2p::client::ClientDestination> localDestination = nullptr) :
HTTPProxy(name, address, port, "", localDestination) {} ;
HTTPProxy(name, address, port, "", true, localDestination) {} ;
~HTTPProxy() {};
std::string GetOutproxyURL() const { return m_OutproxyUrl; }
bool GetHelperSupport() { return m_Addresshelper; }
protected:
// Implements TCPIPAcceptor
@@ -21,6 +22,7 @@ namespace proxy {
private:
std::string m_Name;
std::string m_OutproxyUrl;
bool m_Addresshelper;
};
} // http
} // i2p

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2016, The PurpleI2P Project
* Copyright (c) 2013-2019, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@@ -63,11 +63,20 @@ namespace client
void I2CPDestination::LeaseSetCreated (const uint8_t * buf, size_t len)
{
auto ls = new i2p::data::LocalLeaseSet (m_Identity, buf, len);
auto ls = std::make_shared<i2p::data::LocalLeaseSet> (m_Identity, buf, len);
ls->SetExpirationTime (m_LeaseSetExpirationTime);
SetLeaseSet (ls);
}
void I2CPDestination::LeaseSet2Created (uint8_t storeType, const uint8_t * buf, size_t len)
{
auto ls = (storeType == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2) ?
std::make_shared<i2p::data::LocalEncryptedLeaseSet2> (m_Identity, buf, len):
std::make_shared<i2p::data::LocalLeaseSet2> (storeType, m_Identity, buf, len);
ls->SetExpirationTime (m_LeaseSetExpirationTime);
SetLeaseSet (ls);
}
void I2CPDestination::SendMsgTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint32_t nonce)
{
auto msg = NewI2NPMessage ();
@@ -361,7 +370,7 @@ namespace client
size_t offset = identity->FromBuffer (buf, len);
if (!offset)
{
LogPrint (eLogError, "I2CP: create session maformed identity");
LogPrint (eLogError, "I2CP: create session malformed identity");
SendSessionStatusMessage (3); // invalid
return;
}
@@ -512,6 +521,50 @@ namespace client
LogPrint (eLogError, "I2CP: unexpected sessionID ", sessionID);
}
void I2CPSession::CreateLeaseSet2MessageHandler (const uint8_t * buf, size_t len)
{
uint16_t sessionID = bufbe16toh (buf);
if (sessionID == m_SessionID)
{
size_t offset = 2;
if (m_Destination)
{
uint8_t storeType = buf[offset]; offset++; // store type
i2p::data::LeaseSet2 ls (storeType, buf + offset, len - offset); // outer layer only for encrypted
if (!ls.IsValid ())
{
LogPrint (eLogError, "I2CP: invalid LeaseSet2 of type ", storeType);
return;
}
offset += ls.GetBufferLen ();
// private keys
int numPrivateKeys = buf[offset]; offset++;
uint16_t currentKeyType = 0;
const uint8_t * currentKey = nullptr;
for (int i = 0; i < numPrivateKeys; i++)
{
if (offset + 4 > len) return;
uint16_t keyType = bufbe16toh (buf + offset); offset += 2; // encryption type
uint16_t keyLen = bufbe16toh (buf + offset); offset += 2; // private key length
if (offset + keyLen > len) return;
if (keyType > currentKeyType)
{
currentKeyType = keyType;
currentKey = buf + offset;
}
offset += keyLen;
}
// TODO: support multiple keys
if (currentKey)
m_Destination->SetEncryptionPrivateKey (currentKey);
m_Destination->LeaseSet2Created (storeType, ls.GetBuffer (), ls.GetBufferLen ());
}
}
else
LogPrint (eLogError, "I2CP: unexpected sessionID ", sessionID);
}
void I2CPSession::SendMessageMessageHandler (const uint8_t * buf, size_t len)
{
uint16_t sessionID = bufbe16toh (buf);
@@ -566,12 +619,16 @@ namespace client
case 1: // address
{
auto name = ExtractString (buf + 11, len - 11);
if (!i2p::client::context.GetAddressBook ().GetIdentHash (name, ident))
auto addr = i2p::client::context.GetAddressBook ().GetAddress (name);
if (!addr || !addr->IsIdentHash ())
{
// TODO: handle blinded addresses
LogPrint (eLogError, "I2CP: address ", name, " not found");
SendHostReplyMessage (requestID, nullptr);
return;
}
else
ident = addr->identHash;
break;
}
default:
@@ -704,6 +761,7 @@ namespace client
m_MessagesHandlers[I2CP_DESTROY_SESSION_MESSAGE] = &I2CPSession::DestroySessionMessageHandler;
m_MessagesHandlers[I2CP_RECONFIGURE_SESSION_MESSAGE] = &I2CPSession::ReconfigureSessionMessageHandler;
m_MessagesHandlers[I2CP_CREATE_LEASESET_MESSAGE] = &I2CPSession::CreateLeaseSetMessageHandler;
m_MessagesHandlers[I2CP_CREATE_LEASESET2_MESSAGE] = &I2CPSession::CreateLeaseSet2MessageHandler;
m_MessagesHandlers[I2CP_SEND_MESSAGE_MESSAGE] = &I2CPSession::SendMessageMessageHandler;
m_MessagesHandlers[I2CP_SEND_MESSAGE_EXPIRES_MESSAGE] = &I2CPSession::SendMessageExpiresMessageHandler;
m_MessagesHandlers[I2CP_HOST_LOOKUP_MESSAGE] = &I2CPSession::HostLookupMessageHandler;

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2016, The PurpleI2P Project
* Copyright (c) 2013-2019, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@@ -36,6 +36,7 @@ namespace client
const uint8_t I2CP_DESTROY_SESSION_MESSAGE = 3;
const uint8_t I2CP_REQUEST_VARIABLE_LEASESET_MESSAGE = 37;
const uint8_t I2CP_CREATE_LEASESET_MESSAGE = 4;
const uint8_t I2CP_CREATE_LEASESET2_MESSAGE = 41;
const uint8_t I2CP_SEND_MESSAGE_MESSAGE = 5;
const uint8_t I2CP_SEND_MESSAGE_EXPIRES_MESSAGE = 36;
const uint8_t I2CP_MESSAGE_PAYLOAD_MESSAGE = 31;
@@ -68,6 +69,7 @@ namespace client
void SetEncryptionPrivateKey (const uint8_t * key);
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 SendMsgTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint32_t nonce); // called from I2CPSession
// implements LocalDestination
@@ -126,6 +128,7 @@ namespace client
void DestroySessionMessageHandler (const uint8_t * buf, size_t len);
void ReconfigureSessionMessageHandler (const uint8_t * buf, size_t len);
void CreateLeaseSetMessageHandler (const uint8_t * buf, size_t len);
void CreateLeaseSet2MessageHandler (const uint8_t * buf, size_t len);
void SendMessageMessageHandler (const uint8_t * buf, size_t len);
void SendMessageExpiresMessageHandler (const uint8_t * buf, size_t len);
void HostLookupMessageHandler (const uint8_t * buf, size_t len);

View File

@@ -101,9 +101,9 @@ namespace client
void I2PService::CreateStream (StreamRequestComplete streamRequestComplete, const std::string& dest, int port) {
assert(streamRequestComplete);
i2p::data::IdentHash identHash;
if (i2p::client::context.GetAddressBook ().GetIdentHash (dest, identHash))
CreateStream(streamRequestComplete, identHash, port);
auto address = i2p::client::context.GetAddressBook ().GetAddress (dest);
if (address)
CreateStream(streamRequestComplete, address, port);
else
{
LogPrint (eLogWarning, "I2PService: Remote destination not found: ", dest);
@@ -111,27 +111,31 @@ namespace client
}
}
void I2PService::CreateStream(StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash & identHash, int port)
void I2PService::CreateStream(StreamRequestComplete streamRequestComplete, std::shared_ptr<const Address> address, int port)
{
if(m_ConnectTimeout)
if(m_ConnectTimeout && !m_LocalDestination->IsReady())
{
if(m_LocalDestination->IsReady())
m_LocalDestination->CreateStream (streamRequestComplete, identHash, port);
else
{
AddReadyCallback([this, streamRequestComplete, identHash, port] (const boost::system::error_code & ec) {
if(ec)
{
LogPrint(eLogWarning, "I2PService::CeateStream() ", ec.message());
streamRequestComplete(nullptr);
}
AddReadyCallback([this, streamRequestComplete, address, port] (const boost::system::error_code & ec) {
if(ec)
{
LogPrint(eLogWarning, "I2PService::CeateStream() ", ec.message());
streamRequestComplete(nullptr);
}
else
{ if (address->IsIdentHash ())
this->m_LocalDestination->CreateStream(streamRequestComplete, address->identHash, port);
else
this->m_LocalDestination->CreateStream(streamRequestComplete, identHash, port);
});
}
this->m_LocalDestination->CreateStream (streamRequestComplete, address->blindedPublicKey, port);
}
});
}
else
m_LocalDestination->CreateStream(streamRequestComplete, identHash, port);
{
if (address->IsIdentHash ())
m_LocalDestination->CreateStream (streamRequestComplete, address->identHash, port);
else
m_LocalDestination->CreateStream (streamRequestComplete, address->blindedPublicKey, port);
}
}
TCPIPPipe::TCPIPPipe(I2PService * owner, std::shared_ptr<boost::asio::ip::tcp::socket> upstream, std::shared_ptr<boost::asio::ip::tcp::socket> downstream) : I2PServiceHandler(owner), m_up(upstream), m_down(downstream)

View File

@@ -8,6 +8,7 @@
#include <boost/asio.hpp>
#include "Destination.h"
#include "Identity.h"
#include "AddressBook.h"
namespace i2p
{
@@ -49,7 +50,7 @@ namespace client
m_LocalDestination = dest;
}
void CreateStream (StreamRequestComplete streamRequestComplete, const std::string& dest, int port = 0);
void CreateStream(StreamRequestComplete complete, const i2p::data::IdentHash & ident, int port);
void CreateStream(StreamRequestComplete complete, std::shared_ptr<const Address> address, int port);
inline boost::asio::io_service& GetService () { return m_LocalDestination->GetService (); }
virtual void Start () = 0;

View File

@@ -256,7 +256,13 @@ namespace client
{
if (!m_ConnectionSent && !line.compare(0, 10, "Connection"))
{
m_OutHeader << "Connection: close\r\n";
/* close connection, if not Connection: (U|u)pgrade (for websocket) */
auto x = line.find("pgrade");
if (x != std::string::npos && std::tolower(line[x - 1]) == 'u')
m_OutHeader << line << "\r\n";
else
m_OutHeader << "Connection: close\r\n";
m_ConnectionSent = true;
}
else if (!m_ProxyConnectionSent && !line.compare(0, 16, "Proxy-Connection"))
@@ -387,15 +393,15 @@ namespace client
class I2PClientTunnelHandler: public I2PServiceHandler, public std::enable_shared_from_this<I2PClientTunnelHandler>
{
public:
I2PClientTunnelHandler (I2PClientTunnel * parent, i2p::data::IdentHash destination,
I2PClientTunnelHandler (I2PClientTunnel * parent, std::shared_ptr<const Address> address,
int destinationPort, std::shared_ptr<boost::asio::ip::tcp::socket> socket):
I2PServiceHandler(parent), m_DestinationIdentHash(destination),
I2PServiceHandler(parent), m_Address(address),
m_DestinationPort (destinationPort), m_Socket(socket) {};
void Handle();
void Terminate();
private:
void HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream);
i2p::data::IdentHash m_DestinationIdentHash;
std::shared_ptr<const Address> m_Address;
int m_DestinationPort;
std::shared_ptr<boost::asio::ip::tcp::socket> m_Socket;
};
@@ -404,7 +410,7 @@ namespace client
{
GetOwner()->CreateStream (
std::bind (&I2PClientTunnelHandler::HandleStreamRequestComplete, shared_from_this(), std::placeholders::_1),
m_DestinationIdentHash, m_DestinationPort);
m_Address, m_DestinationPort);
}
void I2PClientTunnelHandler::HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream)
@@ -439,43 +445,39 @@ namespace client
I2PClientTunnel::I2PClientTunnel (const std::string& name, const std::string& destination,
const std::string& address, int port, std::shared_ptr<ClientDestination> localDestination, int destinationPort):
TCPIPAcceptor (address, port, localDestination), m_Name (name), m_Destination (destination),
m_DestinationIdentHash (nullptr), m_DestinationPort (destinationPort)
m_DestinationPort (destinationPort)
{
}
void I2PClientTunnel::Start ()
{
TCPIPAcceptor::Start ();
GetIdentHash();
GetAddress ();
}
void I2PClientTunnel::Stop ()
{
TCPIPAcceptor::Stop();
auto *originalIdentHash = m_DestinationIdentHash;
m_DestinationIdentHash = nullptr;
delete originalIdentHash;
m_Address = nullptr;
}
/* HACK: maybe we should create a caching IdentHash provider in AddressBook */
const i2p::data::IdentHash * I2PClientTunnel::GetIdentHash ()
std::shared_ptr<const Address> I2PClientTunnel::GetAddress ()
{
if (!m_DestinationIdentHash)
if (!m_Address)
{
i2p::data::IdentHash identHash;
if (i2p::client::context.GetAddressBook ().GetIdentHash (m_Destination, identHash))
m_DestinationIdentHash = new i2p::data::IdentHash (identHash);
else
m_Address = i2p::client::context.GetAddressBook ().GetAddress (m_Destination);
if (!m_Address)
LogPrint (eLogWarning, "I2PTunnel: Remote destination ", m_Destination, " not found");
}
return m_DestinationIdentHash;
return m_Address;
}
std::shared_ptr<I2PServiceHandler> I2PClientTunnel::CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket)
{
const i2p::data::IdentHash *identHash = GetIdentHash();
if (identHash)
return std::make_shared<I2PClientTunnelHandler>(this, *identHash, m_DestinationPort, socket);
auto address = GetAddress ();
if (address)
return std::make_shared<I2PClientTunnelHandler>(this, address, m_DestinationPort, socket);
else
return nullptr;
}
@@ -808,9 +810,9 @@ namespace client
void I2PUDPClientTunnel::TryResolving() {
LogPrint(eLogInfo, "UDP Tunnel: Trying to resolve ", m_RemoteDest);
i2p::data::IdentHash * h = new i2p::data::IdentHash;
while(!context.GetAddressBook().GetIdentHash(m_RemoteDest, *h) && !m_cancel_resolve)
std::shared_ptr<const Address> addr;
while(!(addr = context.GetAddressBook().GetAddress(m_RemoteDest)) && !m_cancel_resolve)
{
LogPrint(eLogWarning, "UDP Tunnel: failed to lookup ", m_RemoteDest);
std::this_thread::sleep_for(std::chrono::seconds(1));
@@ -820,7 +822,13 @@ namespace client
LogPrint(eLogError, "UDP Tunnel: lookup of ", m_RemoteDest, " was cancelled");
return;
}
m_RemoteIdent = h;
if (!addr || !addr->IsIdentHash ())
{
LogPrint(eLogError, "UDP Tunnel: ", m_RemoteDest, " not found");
return;
}
m_RemoteIdent = new i2p::data::IdentHash;
*m_RemoteIdent = addr->identHash;
LogPrint(eLogInfo, "UDP Tunnel: resolved ", m_RemoteDest, " to ", m_RemoteIdent->ToBase32());
}

View File

@@ -13,6 +13,7 @@
#include "Datagram.h"
#include "Streaming.h"
#include "I2PService.h"
#include "AddressBook.h"
namespace i2p
{
@@ -129,11 +130,11 @@ namespace client
const char* GetName() { return m_Name.c_str (); }
private:
const i2p::data::IdentHash * GetIdentHash ();
std::shared_ptr<const Address> GetAddress ();
private:
std::string m_Name, m_Destination;
const i2p::data::IdentHash * m_DestinationIdentHash;
std::shared_ptr<const Address> m_Address;
int m_DestinationPort;
};

View File

@@ -14,13 +14,13 @@ namespace client
void MatchedTunnelDestination::ResolveCurrentLeaseSet()
{
if(i2p::client::context.GetAddressBook().GetIdentHash(m_RemoteName, m_RemoteIdent))
auto addr = i2p::client::context.GetAddressBook().GetAddress (m_RemoteName);
if(addr && addr->IsIdentHash ())
{
m_RemoteIdent = addr->identHash;
auto ls = FindLeaseSet(m_RemoteIdent);
if(ls)
{
HandleFoundCurrentLeaseSet(ls);
}
else
RequestDestination(m_RemoteIdent, std::bind(&MatchedTunnelDestination::HandleFoundCurrentLeaseSet, this, std::placeholders::_1));
}

View File

@@ -359,6 +359,24 @@ namespace client
}
forward = std::make_shared<boost::asio::ip::udp::endpoint>(addr, port);
}
//ensure we actually received a destination
if (destination.empty())
{
SendMessageReply (SAM_SESSION_STATUS_INVALID_KEY, strlen(SAM_SESSION_STATUS_INVALID_KEY), true);
return;
}
if (destination != SAM_VALUE_TRANSIENT)
{
//ensure it's a base64 string
i2p::data::PrivateKeys keys;
if (!keys.FromBase64(destination))
{
SendMessageReply(SAM_SESSION_STATUS_INVALID_KEY, strlen(SAM_SESSION_STATUS_INVALID_KEY), true);
return;
}
}
// create destination
auto session = m_Owner.CreateSession (id, destination == SAM_VALUE_TRANSIENT ? "" : destination, &params);
@@ -449,7 +467,7 @@ namespace client
size_t l = dest->FromBase64(destination);
if (l > 0)
{
context.GetAddressBook().InsertAddress(dest);
context.GetAddressBook().InsertFullAddress(dest);
auto leaseSet = session->localDestination->FindLeaseSet(dest->GetIdentHash());
if (leaseSet)
Connect(leaseSet);
@@ -461,7 +479,7 @@ namespace client
}
}
else
SendMessageReply(SAM_SESSION_STATUS_INVALID_KEY, strlen(SAM_SESSION_STATUS_INVALID_KEY), true);
SendMessageReply (SAM_STREAM_STATUS_INVALID_KEY, strlen(SAM_STREAM_STATUS_INVALID_KEY), true);
}
else
SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true);
@@ -558,11 +576,22 @@ namespace client
i2p::data::CryptoKeyType cryptoType = i2p::data::CRYPTO_KEY_TYPE_ELGAMAL;
auto it = params.find (SAM_PARAM_SIGNATURE_TYPE);
if (it != params.end ())
// TODO: extract string values
signatureType = std::stoi(it->second);
{
if (!m_Owner.ResolveSignatureType (it->second, signatureType))
LogPrint (eLogWarning, "SAM: ", SAM_PARAM_SIGNATURE_TYPE, " is invalid ", it->second);
}
it = params.find (SAM_PARAM_CRYPTO_TYPE);
if (it != params.end ())
cryptoType = std::stoi(it->second);
{
try
{
cryptoType = std::stoi(it->second);
}
catch (const std::exception& ex)
{
LogPrint (eLogWarning, "SAM: ", SAM_PARAM_CRYPTO_TYPE, "error: ", ex.what ());
}
}
auto keys = i2p::data::PrivateKeys::CreateRandomKeys (signatureType, cryptoType);
#ifdef _MSC_VER
size_t l = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_DEST_REPLY,
@@ -581,22 +610,29 @@ namespace client
ExtractParams (buf, params);
std::string& name = params[SAM_PARAM_NAME];
std::shared_ptr<const i2p::data::IdentityEx> identity;
i2p::data::IdentHash ident;
std::shared_ptr<const Address> addr;
auto session = m_Owner.FindSession(m_ID);
auto dest = session == nullptr ? context.GetSharedLocalDestination() : session->localDestination;
if (name == "ME")
SendNamingLookupReply (dest->GetIdentity ());
else if ((identity = context.GetAddressBook ().GetAddress (name)) != nullptr)
else if ((identity = context.GetAddressBook ().GetFullAddress (name)) != nullptr)
SendNamingLookupReply (identity);
else if (context.GetAddressBook ().GetIdentHash (name, ident))
else if ((addr = context.GetAddressBook ().GetAddress (name)))
{
auto leaseSet = dest->FindLeaseSet (ident);
if (leaseSet)
SendNamingLookupReply (leaseSet->GetIdentity ());
if (addr->IsIdentHash ())
{
auto leaseSet = dest->FindLeaseSet (addr->identHash);
if (leaseSet)
SendNamingLookupReply (leaseSet->GetIdentity ());
else
dest->RequestDestination (addr->identHash,
std::bind (&SAMSocket::HandleNamingLookupLeaseSetRequestComplete,
shared_from_this (), std::placeholders::_1, name));
}
else
dest->RequestDestination (ident,
dest->RequestDestinationWithEncryptedLeaseSet (addr->blindedPublicKey,
std::bind (&SAMSocket::HandleNamingLookupLeaseSetRequestComplete,
shared_from_this (), std::placeholders::_1, ident));
shared_from_this (), std::placeholders::_1, name));
}
else
{
@@ -621,22 +657,20 @@ namespace client
SendMessageReply (m_Buffer, len, true);
}
void SAMSocket::HandleNamingLookupLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet, i2p::data::IdentHash ident)
void SAMSocket::HandleNamingLookupLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet, std::string name)
{
if (leaseSet)
{
context.GetAddressBook ().InsertAddress (leaseSet->GetIdentity ());
context.GetAddressBook ().InsertFullAddress (leaseSet->GetIdentity ());
SendNamingLookupReply (leaseSet->GetIdentity ());
}
else
{
LogPrint (eLogError, "SAM: naming lookup failed. LeaseSet for ", ident.ToBase32 (), " not found");
LogPrint (eLogError, "SAM: naming lookup failed. LeaseSet for ", name, " not found");
#ifdef _MSC_VER
size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY,
context.GetAddressBook ().ToAddress (ident).c_str());
size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str());
#else
size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY,
context.GetAddressBook ().ToAddress (ident).c_str());
size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str());
#endif
SendMessageReply (m_Buffer, len, false);
}
@@ -815,7 +849,7 @@ namespace client
m_SocketType = eSAMSocketTypeStream;
m_IsAccepting = false;
m_Stream = stream;
context.GetAddressBook ().InsertAddress (stream->GetRemoteIdentity ());
context.GetAddressBook ().InsertFullAddress (stream->GetRemoteIdentity ());
auto session = m_Owner.FindSession (m_ID);
if (session)
{
@@ -921,7 +955,17 @@ namespace client
SAMBridge::SAMBridge (const std::string& address, int port):
m_IsRunning (false), m_Thread (nullptr),
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), port)),
m_DatagramEndpoint (boost::asio::ip::address::from_string(address), port-1), m_DatagramSocket (m_Service, m_DatagramEndpoint)
m_DatagramEndpoint (boost::asio::ip::address::from_string(address), port-1), m_DatagramSocket (m_Service, m_DatagramEndpoint),
m_SignatureTypes
{
{"DSA_SHA1", i2p::data::SIGNING_KEY_TYPE_DSA_SHA1},
{"ECDSA_SHA256_P256", i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256},
{"ECDSA_SHA256_P384", i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA384_P384},
{"ECDSA_SHA256_P521", i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA512_P521},
{"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_GOSTR3411512_GOSTR3410TC26A512", i2p::data::SIGNING_KEY_TYPE_GOSTR3410_TC26_A_512_GOSTR3411_512}
}
{
}
@@ -942,7 +986,16 @@ namespace client
void SAMBridge::Stop ()
{
m_IsRunning = false;
m_Acceptor.cancel ();
try
{
m_Acceptor.cancel ();
}
catch (const std::exception& ex)
{
LogPrint (eLogError, "SAM: runtime exception: ", ex.what ());
}
for (auto& it: m_Sessions)
it.second->CloseStreams ();
m_Sessions.clear ();
@@ -1028,15 +1081,8 @@ namespace client
auto it = params->find (SAM_PARAM_SIGNATURE_TYPE);
if (it != params->end ())
{
// TODO: extract string values
try
{
signatureType = std::stoi(it->second);
}
catch (const std::exception& ex)
{
LogPrint (eLogWarning, "SAM: ", SAM_PARAM_SIGNATURE_TYPE, "error: ", ex.what ());
}
if (!ResolveSignatureType (it->second, signatureType))
LogPrint (eLogWarning, "SAM: ", SAM_PARAM_SIGNATURE_TYPE, " is invalid ", it->second);
}
it = params->find (SAM_PARAM_CRYPTO_TYPE);
if (it != params->end ())
@@ -1166,5 +1212,28 @@ namespace client
else
LogPrint (eLogError, "SAM: datagram receive error: ", ecode.message ());
}
bool SAMBridge::ResolveSignatureType (const std::string& name, i2p::data::SigningKeyType& type) const
{
try
{
type = std::stoi (name);
}
catch (const std::invalid_argument& ex)
{
// name is not numeric, resolving
auto it = m_SignatureTypes.find (name);
if (it != m_SignatureTypes.end ())
type = it->second;
else
return false;
}
catch (const std::exception& ex)
{
return false;
}
// name has been resolved
return true;
}
}
}

View File

@@ -35,6 +35,7 @@ namespace client
const char SAM_STREAM_CONNECT[] = "STREAM CONNECT";
const char SAM_STREAM_STATUS_OK[] = "STREAM STATUS RESULT=OK\n";
const char SAM_STREAM_STATUS_INVALID_ID[] = "STREAM STATUS RESULT=INVALID_ID\n";
const char SAM_STREAM_STATUS_INVALID_KEY[] = "STREAM STATUS RESULT=INVALID_KEY\n";
const char SAM_STREAM_STATUS_CANT_REACH_PEER[] = "STREAM STATUS RESULT=CANT_REACH_PEER\n";
const char SAM_STREAM_STATUS_I2P_ERROR[] = "STREAM STATUS RESULT=I2P_ERROR\n";
const char SAM_STREAM_ACCEPT[] = "STREAM ACCEPT";
@@ -123,7 +124,7 @@ namespace client
void Connect (std::shared_ptr<const i2p::data::LeaseSet> remote);
void HandleConnectLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet);
void SendNamingLookupReply (std::shared_ptr<const i2p::data::IdentityEx> identity);
void HandleNamingLookupLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet, i2p::data::IdentHash ident);
void HandleNamingLookupLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet, std::string name);
void HandleSessionReadinessCheckTimer (const boost::system::error_code& ecode);
void SendSessionCreateReplyOk ();
@@ -184,6 +185,8 @@ namespace client
void RemoveSocket(const std::shared_ptr<SAMSocket> & socket);
bool ResolveSignatureType (const std::string& name, i2p::data::SigningKeyType& type) const;
private:
void Run ();
@@ -207,6 +210,7 @@ namespace client
mutable std::mutex m_OpenSocketsMutex;
std::list<std::shared_ptr<SAMSocket> > m_OpenSockets;
uint8_t m_DatagramReceiveBuffer[i2p::datagram::MAX_DATAGRAM_SIZE+1];
std::map<std::string, i2p::data::SigningKeyType> m_SignatureTypes;
public:

View File

@@ -35,6 +35,8 @@
<translation type="qt" />
<releases>
<release version="2.25.0" date="2019-05-09" />
<release version="2.24.0" date="2019-03-21" />
<release version="2.23.0" date="2019-01-21" />
<release version="2.22.0" date="2018-11-09" />
<release version="2.21.1" date="2018-10-22" />