Compare commits

...

156 Commits

Author SHA1 Message Date
orignal
f22eaa6db5 2.38.0 2021-05-16 14:26:00 -04:00
R4SAS
e37244fa0d remove deprecated options from config file example
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2021-05-15 16:35:38 +03:00
R4SAS
c359c6e634 update config file example, add v6 status to windows daemon window, code cleanup
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2021-05-15 16:31:53 +03:00
orignal
d299cbaabd Add/Remove subsession 2021-05-13 19:30:54 -04:00
orignal
2b22bfadbc show version in hidden content 2021-05-12 11:48:27 -04:00
orignal
baec22610e always set expiration time for NSR tagset 2021-05-12 07:57:37 -04:00
orignal
43b587636b delete tags of termiated session right away 2021-05-11 18:49:17 -04:00
orignal
c6cdb26f47 reduce range for extra tags 2021-05-10 20:20:25 -04:00
orignal
1285e30b3e more pre-calculated x25519 2021-05-10 18:55:39 -04:00
orignal
a8e1cd9a13 don't throw exception if local bind fails 2021-05-10 11:04:08 -04:00
orignal
d6f5640685 attach updated LeaseSet to ECIESx25519 incoming sessions 2021-05-09 07:33:26 -04:00
orignal
79dbf2a43e request encrypted LeaseSet if expired 2021-05-07 22:15:12 -04:00
orignal
5ad4c2a65e run ipv6 peer test again if still testing 2021-05-04 14:59:25 -04:00
orignal
fffa550bb0 SAM subsessions 2021-05-04 14:27:06 -04:00
orignal
0b9cb4e75b check identity and signature length for SessionConfirmed 2021-05-03 19:05:25 -04:00
orignal
7f143a7f23 support EdDSA for blinding 2021-05-02 15:02:52 -04:00
orignal
d8d8a68814 rekey all routers but floodfills 2021-04-30 15:19:31 -04:00
orignal
4018cf9d76 SAM single and master sessions 2021-04-26 21:11:36 -04:00
orignal
bd33ac202f handle hostname for STREAM CREATE 2021-04-26 18:21:00 -04:00
orignal
e091eba831 don't cleanup ipv6 introducers list twice 2021-04-25 17:55:13 -04:00
orignal
4a0dbec4fb recognize non-published NTCP2 address 2021-04-25 16:42:09 -04:00
orignal
90dee900f0 fixed crash 2021-04-25 10:57:31 -04:00
orignal
94555b9c43 don't select next introducers from existing sessions 2021-04-24 14:56:34 -04:00
orignal
db93a7315f find new introducers to connect 2021-04-24 11:11:12 -04:00
orignal
7a19533380 reuse current introducers if no more available 2021-04-23 12:17:48 -04:00
orignal
9d79b26506 check if port if specified 2021-04-22 19:32:47 -04:00
orignal
b43a9cc80d handle master session creation 2021-04-21 19:30:20 -04:00
orignal
b5618af308 find all introducers at the time 2021-04-21 15:41:04 -04:00
orignal
9c8c3b9174 select few introducers at the time 2021-04-21 10:56:39 -04:00
orignal
01e591b261 find ipv6 intrioducer session 2021-04-21 09:55:36 -04:00
orignal
060e30d283 select ipv6 random introducer 2021-04-21 08:16:13 -04:00
orignal
ad019da553 publish ipv6 introducers 2021-04-20 20:02:30 -04:00
orignal
69afd3a1da Merge pull request #1651 from acetoneRu/openssl
Уточнение про логирование в Windows
2021-04-19 07:29:33 -04:00
acetone
7978adc577 Уточнение про логирование в Windows 2021-04-19 06:43:51 -04:00
orignal
ca77ca6ef0 reseed from compatible address 2021-04-18 17:27:50 -04:00
orignal
d5b61ed544 select different routers for peer test 2021-04-17 14:33:53 -04:00
orignal
5edb256990 check if our external IP is valid 2021-04-16 19:31:49 -04:00
orignal
74d0c04314 ipv6 address for relay reponse and relay intro 2021-04-15 16:06:02 -04:00
orignal
39d4464be0 make sure that introducer or peer test router is reachable by SSU 2021-04-15 11:43:43 -04:00
orignal
be48dc6e87 pick correct local address for intro key 2021-04-13 15:11:37 -04:00
orignal
2783337284 require ipv4 for IBGW 2021-04-13 09:16:52 -04:00
orignal
727743979c Merge pull request #1648 from acetoneRu/openssl
SAM section fixed
2021-04-13 07:33:07 -04:00
acetone
4543e14c57 SAM section fixed 2021-04-13 02:43:42 -04:00
orignal
83fc1b0b8e support b32 and b33 addresses in STREAM CONNECT 2021-04-11 17:26:45 -04:00
orignal
df858d9143 publish iexp 2021-04-09 13:29:07 -04:00
orignal
ac47c9c673 don't check U cap 2021-04-09 10:56:46 -04:00
orignal
b9a2d5df02 send HolePunch back based on actual address type 2021-04-08 21:07:14 -04:00
orignal
3e873f88c9 don't drop introducers without iExp 2021-04-07 15:55:38 -04:00
orignal
277cef5ec4 eliminate cost field 2021-04-07 13:05:38 -04:00
orignal
5c9b478e46 published field for SSU addresses 2021-04-05 21:45:48 -04:00
orignal
ff89edf127 pick random introducer 2021-04-05 18:22:48 -04:00
orignal
2cc9791bf2 exclude already expired introducers 2021-04-04 10:36:22 -04:00
orignal
67b32005f6 check if host if unspecified 2021-04-03 22:18:09 -04:00
orignal
0f166973ca check ureachable cap and actual introducers separately 2021-04-03 20:03:19 -04:00
orignal
4f3333c841 don't check range ffor unspecified address 2021-04-03 19:24:07 -04:00
orignal
bea384abea recongnize v4 and v6 SSU addresses without host 2021-04-03 18:56:50 -04:00
orignal
43033695f6 select apropriate address for peer test 2021-04-02 21:31:14 -04:00
orignal
51ef7ef61c don't publish LeaseSet without tunnels 2021-04-01 13:37:21 -04:00
orignal
823b499a02 remove already expired LeaseSets 2021-04-01 11:45:50 -04:00
orignal
bb5ed0b40c assign correct 6 or 4 cap to unpublished address 2021-04-01 11:02:29 -04:00
orignal
94ca2514af set zero expiration timeout if no tunnels 2021-04-01 10:29:03 -04:00
orignal
5412352dec publish ipv6 introducers for ipv6 addresses 2021-03-31 13:42:57 -04:00
orignal
c94e8c7df4 Merge pull request #1646 from acetoneRu/openssl
reg.i2p to subscriptions
2021-03-31 12:28:59 -04:00
acetone
094541caa6 reg.i2p to subscriptions 2021-03-31 12:16:06 -04:00
orignal
8c59977e34 Merge pull request #1645 from acetoneRu/openssl
Configuration file example updated
2021-03-31 12:13:17 -04:00
acetone
881bca6ae3 Depricated "nat" deleted 2021-03-31 12:09:06 -04:00
acetone
22865f8ee4 reseed.yggurl and persist.addressbook added 2021-03-31 12:05:23 -04:00
acetone
f3b728d828 Yggdrasil configuration added
[meshnets] section
2021-03-31 08:45:56 -04:00
orignal
bd7328345f Don't change Yddrasil address if router becomes unreachable through ipv6 2021-03-30 19:27:40 -04:00
orignal
25eae3c116 return relay tag for ipv6 introducer 2021-03-30 11:31:11 -04:00
orignal
5cca5472e6 don't handle unsilicited HolePunch 2021-03-29 15:50:33 -04:00
orignal
8462d382f4 don't create SSU session for HolePunch 2021-03-29 15:16:39 -04:00
orignal
2b0d18a6d7 don't change router status from ipv6 2021-03-29 14:44:50 -04:00
orignal
edf3b7e2fc set X bandiwth for floodfill by default 2021-03-28 13:13:00 -04:00
orignal
167d3a0e3c don't create BN_CTX for ECIES tunnel build record decryption 2021-03-28 12:14:02 -04:00
orignal
86415bc61f publish introducer cap for ipv6 address 2021-03-27 18:49:35 -04:00
orignal
a6ea37a21e set ipv6 address caps depending on peer test 2021-03-27 15:16:56 -04:00
orignal
3695aa924b doesn't send peer test to a reserved address 2021-03-24 10:32:15 -04:00
orignal
9e050d1a23 peer test for ipv6 2021-03-23 15:36:57 -04:00
orignal
34eee2fc26 fixed #1644. check leaseset buffer size 2021-03-22 20:12:58 -04:00
orignal
ac10f3055d pick correct local SSU address for sending peer test 2021-03-19 21:51:45 -04:00
orignal
991b74f036 bind ipv6 or yggdrasil acceptor to specified address 2021-03-19 13:10:24 -04:00
orignal
589049ef0f connect to ipv6 address through introducer 2021-03-19 10:20:02 -04:00
orignal
6b0c7c2313 handle ipv6 address in RelayResponse 2021-03-18 21:29:39 -04:00
orignal
a9c7d0d598 common ExtractIPAddressAndPort 2021-03-18 20:11:24 -04:00
orignal
ef1dfb153c handle ipv6 address for RelayIntro 2021-03-18 18:37:02 -04:00
orignal
ff9ee5873f post LeaseSet creation to I2CP destnation's thread 2021-03-17 15:10:14 -04:00
orignal
a7b56bbbb7 publish Yggdrasil address when enabled 2021-03-17 11:26:52 -04:00
orignal
820a365474 select random peer for first hop for outbound tunnel if number of connections < 100 2021-03-16 18:45:51 -04:00
orignal
1d5d06f731 find actual router for peer 2021-03-16 15:23:00 -04:00
orignal
43d458cf72 publish and upublish NTCP2 and yggdrasil addresses separatly 2021-03-16 13:08:10 -04:00
orignal
436a3e7f54 2.37.0 2021-03-15 09:00:25 -04:00
orignal
7015bad905 2.37.0 2021-03-15 08:30:04 -04:00
orignal
cf8665748b network sattus Mesh added 2021-03-13 10:28:03 -05:00
orignal
1b8da90cbb more precise compatibility check 2021-03-12 20:51:12 -05:00
orignal
6012585067 eliminate false positive symmetric NAT 2021-03-12 17:41:41 -05:00
orignal
f162876600 insert ipv4 address if enabled back 2021-03-12 16:13:01 -05:00
orignal
6555ae5b0a support authorization for reseed proxy 2021-03-11 17:02:56 -05:00
R4SAS
f5af059ef4 [webconsole] add submission of address registration line to reg.i2p
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2021-03-11 15:20:40 +03:00
R4SAS
cb8651ec68 [win32] drop service code, fix start with daemon option. Throw notification when unable to parse config
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2021-03-11 14:40:06 +03:00
orignal
7c0b0a4e3e common HTTP basic authorization string 2021-03-10 22:47:31 -05:00
orignal
880d1a7ccd NTCP2 proxy with authorization 2021-03-10 20:00:21 -05:00
orignal
744b25190a don't set proxy if ntcp2 is disabled 2021-03-10 15:36:10 -05:00
orignal
3792bb4928 delete sig buffer 2021-03-09 22:10:51 -05:00
R4SAS
9049902ced [webconsole] add address registration line generator
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2021-03-10 01:48:54 +03:00
orignal
5f93dc72fd convert ifname* params to address* 2021-03-09 15:28:07 -05:00
orignal
09dadd7e01 Merge pull request #1640 from brain5lug/openssl
logging opimization
2021-03-09 07:46:28 -05:00
orignal
60b92f98db OBEP must be ipv4 compatible 2021-03-08 18:54:17 -05:00
orignal
97f315d488 set correct 4 and 6 caps for unreachable addresses 2021-03-08 15:57:05 -05:00
brain5lug
f3676d7f18 logging opimization 2021-03-08 11:31:00 +03:00
orignal
742dbdb68a rekey low badwidth routers to ECIES 2021-03-07 10:07:51 -05:00
orignal
2d59c968ca don't publish NTCP2 address connected through proxy 2021-03-06 18:43:50 -05:00
orignal
ad22247c9e start other acceptors if connected through a proxy 2021-03-06 15:35:31 -05:00
orignal
f38920c338 Status: Proxy 2021-03-06 08:50:47 -05:00
orignal
8f90b21a5d fixed typo 2021-03-05 22:40:27 -05:00
orignal
ff0e6813c6 fixed typo 2021-03-05 21:53:19 -05:00
orignal
fa5e4d57fd correct caps for SSU address without host 2021-03-05 19:40:37 -05:00
orignal
876973f071 remove coreVersion 2021-03-05 09:29:28 -05:00
orignal
b994af9209 check reachability of floodfill to request from 2021-03-05 08:41:44 -05:00
orignal
1f6cde652e check caps for SSU address 2021-03-04 22:47:56 -05:00
R4SAS
3bf6db1c08 enable yggdrasil address finding for android
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2021-03-05 03:20:12 +03:00
orignal
e70ffc9d7c re-shedule introducers updates if router becomes firewalled 2021-03-04 15:55:51 -05:00
orignal
065cfe3b9d separate ratchet session for ECIES router 2021-03-03 15:30:13 -05:00
orignal
def9873a70 request multiple introducers at the time 2021-03-02 21:10:19 -05:00
orignal
618aa26454 allow some unreachable floodfills 2021-03-02 14:13:28 -05:00
orignal
924a7bc533 use connected peers if others not available 2021-03-02 12:29:51 -05:00
orignal
ef85277a1b select reachable routers for one hop tunnels 2021-03-02 08:46:13 -05:00
orignal
876375f2c3 precise bandwidth limit 2021-03-01 22:13:17 -05:00
orignal
f70ee480ba check connectivity between peers for tunnel 2021-03-01 19:02:27 -05:00
orignal
6d88c3ab05 Symmetric NAT error 2021-03-01 12:20:53 -05:00
orignal
57c969b0ed constants for cost 2021-03-01 11:09:25 -05:00
orignal
ae58a7007b different cost for direct or with introducers SSU address 2021-02-28 19:19:09 -05:00
orignal
11c924bbe7 publish and handle SSU addreses without host 2021-02-28 18:58:25 -05:00
orignal
8bab4f60ef open socket before bing 2021-02-28 09:04:34 -05:00
orignal
bef9a54f4a bind SSU socket to specified address 2021-02-27 16:13:12 -05:00
orignal
288b19c3f7 bind NTCP2 ipv4 acceptor to specified local address 2021-02-27 10:35:50 -05:00
orignal
40f7e9d33e separate decryptor for tunnel builds and floodfill requests 2021-02-26 21:02:51 -05:00
orignal
fab53dda66 fixed typo 2021-02-26 20:38:16 -05:00
orignal
a4e8bf9857 bind NTCP2 connections to specified address 2021-02-26 19:31:38 -05:00
R4SAS
2cdf84cdab [actions] upload windows artifacts 2021-02-26 21:09:01 +00:00
orignal
fbe83f729d don't try to send to unreachable router 2021-02-25 19:55:46 -05:00
R4SAS
4371a084ec check for pubkey in X25519Keys::Agree
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2021-02-26 03:20:06 +03:00
orignal
d13f58088a Merge branch 'openssl' of https://github.com/PurpleI2P/i2pd into openssl 2021-02-24 18:41:31 -05:00
orignal
f75bef7c03 don't set local address if not specified 2021-02-24 18:40:24 -05:00
R4SAS
3d7e93a688 systemd: use SIGTERM instead SIGQUIT, indent UPNP code, make client target
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2021-02-25 00:37:41 +03:00
orignal
a4dda304d2 cancel connect timer upon SessionConfirmed 2021-02-24 10:03:23 -05:00
orignal
124c3ef2d7 always publish SSU port 2021-02-23 21:15:17 -05:00
orignal
c3a2fca76a 4 or 6 caps for non-published addresses 2021-02-23 19:59:35 -05:00
orignal
b60ebfe1c6 parse '4' and '6' address caps 2021-02-22 22:53:25 -05:00
orignal
1d7639b3f4 caps per address 2021-02-22 21:04:26 -05:00
orignal
2d972752ff lookuplocal 2021-02-21 16:20:57 -05:00
orignal
616f0b2a21 address parameter for server tunnels 2021-02-19 15:15:58 -05:00
orignal
94659ba890 create ipv4 and ipv6 NTCP2 addresses separately 2021-02-17 21:51:35 -05:00
orignal
d65bc068de create ipv4 and ipv6 NTCP2 addresses separately 2021-02-17 21:12:17 -05:00
orignal
1ca0354cf2 find NTCP2 address by static key. Don't make router unreachable if can't connect by NTCP2 2021-02-17 18:46:41 -05:00
orignal
b1fcd4d27b show actual IP addresses for proxy connections 2021-02-17 14:26:48 -05:00
64 changed files with 2276 additions and 1582 deletions

View File

@@ -29,3 +29,7 @@ jobs:
run: |
mkdir -p obj/Win32 obj/libi2pd obj/libi2pd_client obj/daemon
make USE_UPNP=yes DEBUG=no -j3
- name: Upload artifacts
uses: actions/upload-artifact@v2
with:
path: i2pd.exe

0
.gitmodules vendored
View File

View File

@@ -1,6 +1,65 @@
# for this file format description,
# see https://github.com/olivierlacan/keep-a-changelog
## [2.38.0] - 2021-03-17
### Added
- Publish ipv6 introducers
- Bind ipv6 or yggdrasil NTCP2 acceptor to specified address
- Support .b32.i2p addresses and hostnames for SAM STREAM CREATE
- ipv6 peer tests
- Publish iexp param for introducers
- Show ipv6 network status on the webconsole
- EdDSA signing keys can also be blinded
- Show router version on the webconsole
### Changed
- Rekey of all routers but floodfills to ECIES
- Increased number of precalculated x25519 keys to 15
- Don't publish LeaseSet without inbound tunnels
- Reseed from compatible address(ipv4 or ipv6)
- Recongnize v4 and v6 SSU addresses without host
- Inbound tunnel gateway must be ipv4 compatible
- Don't select next introducers from existing sessions
- Set X bandwidth for floodfill by default
### Fixed
- Incoming ECIES-x25519 session doesn't send updated LeaseSet
- Unique local address for server tunnels
- Race condition for LeaseSet creation in I2CP
- Relay tag for ipv6 introducer
- Already expired introducers
- Find connected router for first peer in tunnel
- Failed outgoing ECIES-x25519 session's tagset stays forever
- Yggdrasil address disappears if router becomes unreachable through ipv6
- Ignore SSU address/introducers if port is not specified
- Check identity and signature length for SSU SessionConfirmed
## [2.37.0] - 2021-03-15
### Added
- Address registration line for reg.i2p and stats.i2p through the web console
- "4" and "6" caps for addresses without published IP address
- Mesh and Proxy network statuses
- Symmetric NAT network status error
- Bind server tunnel connection to specified address
- lookuplocal BOB extended command
- address4 and address6 parameters to bind outgoing connections to
- Rekey of low-bandwidth routers to ECIES
- Popup notification windows when unable to parse config for Windows
### Changed
- Floodfills with "U" cap are not ignored anymore
- Check transports reachability between tunnel peers and between router and floodfill
- NTCP2 and reseed HTTP proxy support authorization now
- Show actual IP addresses for proxy connections
- Publish and handle SSU addreses without host
- Outbound tunnel endpoint must be ipv4 compatible
- Logging optimization
- Removed Windows service
### Fixed
- Incoming SSU session terminates after 5 seconds
- Outgoing NTCP2 ipv4 session even if ipv4 is disabled
- No incoming Yggdrasil connection if connected through NTCP2 proxy
- Race condition between tunnel build and floodfill requests decryption for ECIES routers
- Numeric bandwidth limitation
- Yggdrasil for Android
## [2.36.0] - 2021-02-15
### Added
- Encrypted lookup and publications to ECIES-x25519 floodfiils

View File

@@ -39,7 +39,7 @@ else ifneq (, $(findstring freebsd, $(SYS))$(findstring openbsd, $(SYS)))
DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp
include Makefile.bsd
else ifneq (, $(findstring mingw, $(SYS))$(findstring cygwin, $(SYS)))
DAEMON_SRC += Win32/DaemonWin32.cpp Win32/Win32Service.cpp Win32/Win32App.cpp Win32/Win32NetState.cpp
DAEMON_SRC += Win32/DaemonWin32.cpp Win32/Win32App.cpp Win32/Win32NetState.cpp
include Makefile.mingw
else # not supported
$(error Not supported platform)
@@ -66,6 +66,7 @@ mk_obj_dir:
@mkdir -p obj/$(DAEMON_SRC_DIR)
api: mk_obj_dir $(SHLIB) $(ARLIB)
client: mk_obj_dir $(SHLIB_CLIENT) $(ARLIB_CLIENT)
api_client: mk_obj_dir $(SHLIB) $(ARLIB) $(SHLIB_CLIENT) $(ARLIB_CLIENT)
## NOTE: The NEEDED_CXXFLAGS are here so that CXXFLAGS can be specified at build time
@@ -128,6 +129,7 @@ doxygen:
.PHONY: last-dist
.PHONY: api
.PHONY: api_client
.PHONY: client
.PHONY: mk_obj_dir
.PHONY: install
.PHONY: strip

View File

@@ -14,7 +14,6 @@
#include "Log.h"
#ifdef _WIN32
#include "Win32Service.h"
#ifdef WIN32_APP
#include <windows.h>
#include "Win32App.h"
@@ -35,45 +34,11 @@ namespace util
i2p::log::SetThrowFunction ([](const std::string& s)
{
MessageBox(0, TEXT(s.c_str ()), TEXT("i2pd"), MB_ICONERROR | MB_TASKMODAL | MB_OK );
});
}
);
if (!Daemon_Singleton::init(argc, argv))
return false;
std::string serviceControl; i2p::config::GetOption("svcctl", serviceControl);
if (serviceControl == "install")
{
LogPrint(eLogInfo, "WinSVC: installing ", SERVICE_NAME, " as service");
InstallService(
SERVICE_NAME, // Name of service
SERVICE_DISPLAY_NAME, // Name to display
SERVICE_START_TYPE, // Service start type
SERVICE_DEPENDENCIES, // Dependencies
SERVICE_ACCOUNT, // Service running account
SERVICE_PASSWORD // Password of the account
);
return false;
}
else if (serviceControl == "remove")
{
LogPrint(eLogInfo, "WinSVC: uninstalling ", SERVICE_NAME, " service");
UninstallService(SERVICE_NAME);
return false;
}
if (isDaemon)
{
LogPrint(eLogDebug, "Daemon: running as service");
I2PService service((PSTR)SERVICE_NAME);
if (!I2PService::Run(service))
{
LogPrint(eLogError, "Daemon: Service failed to run w/err 0x%08lx\n", GetLastError());
return false;
}
return false;
}
else
LogPrint(eLogDebug, "Daemon: running as user");
return true;
}
@@ -86,9 +51,6 @@ namespace util
setlocale(LC_TIME, "C");
#ifdef WIN32_APP
if (!i2p::win32::StartWin32App ()) return false;
// override log
i2p::config::SetOption("log", std::string ("file"));
#endif
bool ret = Daemon_Singleton::start();
if (ret && i2p::log::Logger().GetLogType() == eLogFile)

View File

@@ -142,25 +142,47 @@ namespace win32
s << bytes << " Bytes\n";
}
static void ShowNetworkStatus (std::stringstream& s, RouterStatus status)
{
switch (status)
{
case eRouterStatusOK: s << "OK"; break;
case eRouterStatusTesting: s << "Test"; break;
case eRouterStatusFirewalled: s << "FW"; break;
case eRouterStatusUnknown: s << "Unk"; break;
case eRouterStatusProxy: s << "Proxy"; break;
case eRouterStatusMesh: s << "Mesh"; break;
case eRouterStatusError:
{
s << "Err";
switch (i2p::context.GetError ())
{
case eRouterErrorClockSkew:
s << " - Clock skew";
break;
case eRouterErrorOffline:
s << " - Offline";
break;
case eRouterErrorSymmetricNAT:
s << " - Symmetric NAT";
break;
default: ;
}
break;
}
default: s << "Unk";
}
}
static void PrintMainWindowText (std::stringstream& s)
{
s << "\n";
s << "Status: ";
switch (i2p::context.GetStatus())
ShowNetworkStatus (s, i2p::context.GetStatus ());
if (i2p::context.SupportsV6 ())
{
case eRouterStatusOK: s << "OK"; break;
case eRouterStatusTesting: s << "Testing"; break;
case eRouterStatusFirewalled: s << "Firewalled"; break;
case eRouterStatusError:
{
switch (i2p::context.GetError())
{
case eRouterErrorClockSkew: s << "Clock skew"; break;
default: s << "Error";
}
break;
}
default: s << "Unknown";
s << " / ";
ShowNetworkStatus (s, i2p::context.GetStatusV6 ());
}
s << "; ";
s << "Success Rate: " << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate() << "%\n";

View File

@@ -1,414 +0,0 @@
/*
* Copyright (c) 2013-2020, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
* See full license text in LICENSE file at top of project tree
*/
#ifdef _WIN32
#define _CRT_SECURE_NO_WARNINGS // to use freopen
#endif
#include "Win32Service.h"
#include <assert.h>
//#include <strsafe.h>
#include <windows.h>
#include "Daemon.h"
#include "Log.h"
I2PService *I2PService::s_service = NULL;
BOOL I2PService::isService()
{
BOOL bIsService = FALSE;
HWINSTA hWinStation = GetProcessWindowStation();
if (hWinStation != NULL)
{
USEROBJECTFLAGS uof = { 0 };
if (GetUserObjectInformation(hWinStation, UOI_FLAGS, &uof, sizeof(USEROBJECTFLAGS), NULL) && ((uof.dwFlags & WSF_VISIBLE) == 0))
{
bIsService = TRUE;
}
}
return bIsService;
}
BOOL I2PService::Run(I2PService &service)
{
s_service = &service;
SERVICE_TABLE_ENTRY serviceTable[] =
{
{ service.m_name, ServiceMain },
{ NULL, NULL }
};
return StartServiceCtrlDispatcher(serviceTable);
}
void WINAPI I2PService::ServiceMain(DWORD dwArgc, PSTR *pszArgv)
{
assert(s_service != NULL);
s_service->m_statusHandle = RegisterServiceCtrlHandler(
s_service->m_name, ServiceCtrlHandler);
if (s_service->m_statusHandle == NULL)
{
throw GetLastError();
}
s_service->Start(dwArgc, pszArgv);
}
void WINAPI I2PService::ServiceCtrlHandler(DWORD dwCtrl)
{
switch (dwCtrl)
{
case SERVICE_CONTROL_STOP: s_service->Stop(); break;
case SERVICE_CONTROL_PAUSE: s_service->Pause(); break;
case SERVICE_CONTROL_CONTINUE: s_service->Continue(); break;
case SERVICE_CONTROL_SHUTDOWN: s_service->Shutdown(); break;
case SERVICE_CONTROL_INTERROGATE: break;
default: break;
}
}
I2PService::I2PService(PSTR pszServiceName,
BOOL fCanStop,
BOOL fCanShutdown,
BOOL fCanPauseContinue)
{
m_name = (pszServiceName == NULL) ? (PSTR)"" : pszServiceName;
m_statusHandle = NULL;
m_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
m_status.dwCurrentState = SERVICE_START_PENDING;
DWORD dwControlsAccepted = 0;
if (fCanStop)
dwControlsAccepted |= SERVICE_ACCEPT_STOP;
if (fCanShutdown)
dwControlsAccepted |= SERVICE_ACCEPT_SHUTDOWN;
if (fCanPauseContinue)
dwControlsAccepted |= SERVICE_ACCEPT_PAUSE_CONTINUE;
m_status.dwControlsAccepted = dwControlsAccepted;
m_status.dwWin32ExitCode = NO_ERROR;
m_status.dwServiceSpecificExitCode = 0;
m_status.dwCheckPoint = 0;
m_status.dwWaitHint = 0;
m_fStopping = FALSE;
// Create a manual-reset event that is not signaled at first to indicate
// the stopped signal of the service.
m_hStoppedEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (m_hStoppedEvent == NULL)
{
throw GetLastError();
}
}
I2PService::~I2PService(void)
{
if (m_hStoppedEvent)
{
CloseHandle(m_hStoppedEvent);
m_hStoppedEvent = NULL;
}
}
void I2PService::Start(DWORD dwArgc, PSTR *pszArgv)
{
try
{
SetServiceStatus(SERVICE_START_PENDING);
OnStart(dwArgc, pszArgv);
SetServiceStatus(SERVICE_RUNNING);
}
catch (DWORD dwError)
{
LogPrint(eLogError, "Win32Service Start", dwError);
SetServiceStatus(SERVICE_STOPPED, dwError);
}
catch (...)
{
LogPrint(eLogError, "Win32Service failed to start.", EVENTLOG_ERROR_TYPE);
SetServiceStatus(SERVICE_STOPPED);
}
}
void I2PService::OnStart(DWORD dwArgc, PSTR *pszArgv)
{
LogPrint(eLogInfo, "Win32Service in OnStart", EVENTLOG_INFORMATION_TYPE);
Daemon.start();
//i2p::util::config::OptionParser(dwArgc, pszArgv);
//i2p::util::filesystem::ReadConfigFile(i2p::util::config::mapArgs, i2p::util::config::mapMultiArgs);
//i2p::context.OverrideNTCPAddress(i2p::util::config::GetCharArg("-host", "127.0.0.1"),
// i2p::util::config::GetArg("-port", 17070));
_worker = new std::thread(std::bind(&I2PService::WorkerThread, this));
}
void I2PService::WorkerThread()
{
while (!m_fStopping)
{
::Sleep(1000); // Simulate some lengthy operations.
}
// Signal the stopped event.
SetEvent(m_hStoppedEvent);
}
void I2PService::Stop()
{
DWORD dwOriginalState = m_status.dwCurrentState;
try
{
SetServiceStatus(SERVICE_STOP_PENDING);
OnStop();
SetServiceStatus(SERVICE_STOPPED);
}
catch (DWORD dwError)
{
LogPrint(eLogInfo, "Win32Service Stop", dwError);
SetServiceStatus(dwOriginalState);
}
catch (...)
{
LogPrint(eLogError, "Win32Service failed to stop.", EVENTLOG_ERROR_TYPE);
SetServiceStatus(dwOriginalState);
}
}
void I2PService::OnStop()
{
// Log a service stop message to the Application log.
LogPrint(eLogInfo, "Win32Service in OnStop", EVENTLOG_INFORMATION_TYPE);
Daemon.stop();
m_fStopping = TRUE;
if (WaitForSingleObject(m_hStoppedEvent, INFINITE) != WAIT_OBJECT_0)
{
throw GetLastError();
}
_worker->join();
delete _worker;
}
void I2PService::Pause()
{
try
{
SetServiceStatus(SERVICE_PAUSE_PENDING);
OnPause();
SetServiceStatus(SERVICE_PAUSED);
}
catch (DWORD dwError)
{
LogPrint(eLogError, "Win32Service Pause", dwError);
SetServiceStatus(SERVICE_RUNNING);
}
catch (...)
{
LogPrint(eLogError, "Win32Service failed to pause.", EVENTLOG_ERROR_TYPE);
SetServiceStatus(SERVICE_RUNNING);
}
}
void I2PService::OnPause()
{
}
void I2PService::Continue()
{
try
{
SetServiceStatus(SERVICE_CONTINUE_PENDING);
OnContinue();
SetServiceStatus(SERVICE_RUNNING);
}
catch (DWORD dwError)
{
LogPrint(eLogError, "Win32Service Continue", dwError);
SetServiceStatus(SERVICE_PAUSED);
}
catch (...)
{
LogPrint(eLogError, "Win32Service failed to resume.", EVENTLOG_ERROR_TYPE);
SetServiceStatus(SERVICE_PAUSED);
}
}
void I2PService::OnContinue()
{
}
void I2PService::Shutdown()
{
try
{
OnShutdown();
SetServiceStatus(SERVICE_STOPPED);
}
catch (DWORD dwError)
{
LogPrint(eLogError, "Win32Service Shutdown", dwError);
}
catch (...)
{
LogPrint(eLogError, "Win32Service failed to shut down.", EVENTLOG_ERROR_TYPE);
}
}
void I2PService::OnShutdown()
{
}
void I2PService::SetServiceStatus(DWORD dwCurrentState,
DWORD dwWin32ExitCode,
DWORD dwWaitHint)
{
static DWORD dwCheckPoint = 1;
m_status.dwCurrentState = dwCurrentState;
m_status.dwWin32ExitCode = dwWin32ExitCode;
m_status.dwWaitHint = dwWaitHint;
m_status.dwCheckPoint =
((dwCurrentState == SERVICE_RUNNING) ||
(dwCurrentState == SERVICE_STOPPED)) ?
0 : dwCheckPoint++;
::SetServiceStatus(m_statusHandle, &m_status);
}
//*****************************************************************************
void FreeHandles(SC_HANDLE schSCManager, SC_HANDLE schService)
{
if (schSCManager)
{
CloseServiceHandle(schSCManager);
schSCManager = NULL;
}
if (schService)
{
CloseServiceHandle(schService);
schService = NULL;
}
}
void InstallService(PCSTR pszServiceName, PCSTR pszDisplayName, DWORD dwStartType, PCSTR pszDependencies, PCSTR pszAccount, PCSTR pszPassword)
{
printf("Try to install Win32Service (%s).\n", pszServiceName);
char szPath[MAX_PATH];
SC_HANDLE schSCManager = NULL;
SC_HANDLE schService = NULL;
if (GetModuleFileName(NULL, szPath, ARRAYSIZE(szPath)) == 0)
{
printf("GetModuleFileName failed w/err 0x%08lx\n", GetLastError());
FreeHandles(schSCManager, schService);
return;
}
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);
if (schSCManager == NULL)
{
printf("OpenSCManager failed w/err 0x%08lx\n", GetLastError());
FreeHandles(schSCManager, schService);
return;
}
// Install the service into SCM by calling CreateService
schService = CreateService(
schSCManager, // SCManager database
pszServiceName, // Name of service
pszDisplayName, // Name to display
SERVICE_QUERY_STATUS, // Desired access
SERVICE_WIN32_OWN_PROCESS, // Service type
dwStartType, // Service start type
SERVICE_ERROR_NORMAL, // Error control type
szPath, // Service's binary
NULL, // No load ordering group
NULL, // No tag identifier
pszDependencies, // Dependencies
pszAccount, // Service running account
pszPassword // Password of the account
);
if (schService == NULL)
{
printf("CreateService failed w/err 0x%08lx\n", GetLastError());
FreeHandles(schSCManager, schService);
return;
}
printf("Win32Service is installed as %s.\n", pszServiceName);
// Centralized cleanup for all allocated resources.
FreeHandles(schSCManager, schService);
}
void UninstallService(PCSTR pszServiceName)
{
printf("Try to uninstall Win32Service (%s).\n", pszServiceName);
SC_HANDLE schSCManager = NULL;
SC_HANDLE schService = NULL;
SERVICE_STATUS ssSvcStatus = {};
// Open the local default service control manager database
schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
if (schSCManager == NULL)
{
printf("OpenSCManager failed w/err 0x%08lx\n", GetLastError());
FreeHandles(schSCManager, schService);
return;
}
// Open the service with delete, stop, and query status permissions
schService = OpenService(schSCManager, pszServiceName, SERVICE_STOP | SERVICE_QUERY_STATUS | DELETE);
if (schService == NULL)
{
printf("OpenService failed w/err 0x%08lx\n", GetLastError());
FreeHandles(schSCManager, schService);
return;
}
// Try to stop the service
if (ControlService(schService, SERVICE_CONTROL_STOP, &ssSvcStatus))
{
printf("Stopping %s.\n", pszServiceName);
Sleep(1000);
while (QueryServiceStatus(schService, &ssSvcStatus))
{
if (ssSvcStatus.dwCurrentState == SERVICE_STOP_PENDING)
{
printf(".");
Sleep(1000);
}
else break;
}
if (ssSvcStatus.dwCurrentState == SERVICE_STOPPED)
{
printf("\n%s is stopped.\n", pszServiceName);
}
else
{
printf("\n%s failed to stop.\n", pszServiceName);
}
}
// Now remove the service by calling DeleteService.
if (!DeleteService(schService))
{
printf("DeleteService failed w/err 0x%08lx\n", GetLastError());
FreeHandles(schSCManager, schService);
return;
}
printf("%s is removed.\n", pszServiceName);
// Centralized cleanup for all allocated resources.
FreeHandles(schSCManager, schService);
}

View File

@@ -1,92 +0,0 @@
/*
* Copyright (c) 2013-2020, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
* See full license text in LICENSE file at top of project tree
*/
#ifndef WIN_32_SERVICE_H__
#define WIN_32_SERVICE_H__
#include <thread>
#include <windows.h>
#ifdef _WIN32
// Internal name of the service
#define SERVICE_NAME "i2pdService"
// Displayed name of the service
#define SERVICE_DISPLAY_NAME "i2pd router service"
// Service start options.
#define SERVICE_START_TYPE SERVICE_DEMAND_START
// List of service dependencies - "dep1\0dep2\0\0"
#define SERVICE_DEPENDENCIES ""
// The name of the account under which the service should run
#define SERVICE_ACCOUNT "NT AUTHORITY\\LocalService"
// The password to the service account name
#define SERVICE_PASSWORD NULL
#endif
class I2PService
{
public:
I2PService(PSTR pszServiceName,
BOOL fCanStop = TRUE,
BOOL fCanShutdown = TRUE,
BOOL fCanPauseContinue = FALSE);
virtual ~I2PService(void);
static BOOL isService();
static BOOL Run(I2PService &service);
void Stop();
protected:
virtual void OnStart(DWORD dwArgc, PSTR *pszArgv);
virtual void OnStop();
virtual void OnPause();
virtual void OnContinue();
virtual void OnShutdown();
void SetServiceStatus(DWORD dwCurrentState,
DWORD dwWin32ExitCode = NO_ERROR,
DWORD dwWaitHint = 0);
private:
static void WINAPI ServiceMain(DWORD dwArgc, LPSTR *lpszArgv);
static void WINAPI ServiceCtrlHandler(DWORD dwCtrl);
void WorkerThread();
void Start(DWORD dwArgc, PSTR *pszArgv);
void Pause();
void Continue();
void Shutdown();
static I2PService* s_service;
PSTR m_name;
SERVICE_STATUS m_status;
SERVICE_STATUS_HANDLE m_statusHandle;
BOOL m_fStopping;
HANDLE m_hStoppedEvent;
std::thread* _worker;
};
void InstallService(
PCSTR pszServiceName,
PCSTR pszDisplayName,
DWORD dwStartType,
PCSTR pszDependencies,
PCSTR pszAccount,
PCSTR pszPassword
);
void UninstallService(PCSTR pszServiceName);
#endif // WIN_32_SERVICE_H__

View File

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

View File

@@ -20,6 +20,7 @@
## Logging configuration section
## By default logs go to stdout with level 'info' and higher
## For Windows OS by default logs go to file with level 'warn' and higher
##
## Logs destination (valid values: stdout, file, syslog)
## * stdout - print log entries to stdout
@@ -34,14 +35,30 @@
## Write full CLF-formatted date and time to log (default: write only time)
# logclftime = true
## Daemon mode. Router will go to background after start
## Daemon mode. Router will go to background after start. Ignored on Windows
# daemon = true
## Specify a family, router belongs to (default - none)
# family =
## External IP address to listen for connections
## Network interface to bind to
## Updates address4/6 options if they are not set
# ifname =
## You can specify different interfaces for IPv4 and IPv6
# ifname4 =
# ifname6 =
## Local address to bind transport sockets to
## Overrides host option if:
## For ipv4: if ipv4 = true and nat = false
## For ipv6: if 'host' is not set or ipv4 = true
# address4 =
# address6 =
## External IPv4 or IPv6 address to listen for connections
## By default i2pd sets IP automatically
## Sets published NTCP2v4/SSUv4 address to 'host' value if nat = true
## Sets published NTCP2v6/SSUv6 address to 'host' value if ipv4 = false
# host = 1.2.3.4
## Port to listen for connections
@@ -54,23 +71,9 @@ ipv4 = true
## Enable communication through ipv6
ipv6 = false
## Network interface to bind to
# ifname =
## You can specify different interfaces for IPv4 and IPv6
# ifname4 =
# ifname6 =
## Enable NTCP transport (default = true)
# ntcp = true
## If you run i2pd behind a proxy server, you can only use NTCP transport with ntcpproxy option
## Should be http://address:port or socks://address:port
# ntcpproxy = http://127.0.0.1:8118
## Enable SSU transport (default = true)
# ssu = true
## Should we assume we are behind NAT? (false only in MeshNet)
# nat = true
## Bandwidth configuration
## L limit bandwidth to 32KBs/sec, O - to 256KBs/sec, P - to 2048KBs/sec,
## X - unlimited
@@ -84,6 +87,7 @@ ipv6 = false
# notransit = true
## Router will be floodfill
## Note: that mode uses much more network connections and CPU!
# floodfill = true
[http]
@@ -95,7 +99,7 @@ address = 127.0.0.1
port = 7070
## Path to web console, default "/"
# webroot = /
## Uncomment following lines to enable Web Console authentication
## Uncomment following lines to enable Web Console authentication
# auth = true
# user = i2pd
# pass = changeme
@@ -131,7 +135,7 @@ port = 4447
## socksproxy section also accepts I2CP parameters, like "inbound.length" etc.
[sam]
## Uncomment and set to 'true' to enable SAM Bridge
## Comment or set to 'false' to disable SAM Bridge
enabled = true
## Address and port service will listen on
# address = 127.0.0.1
@@ -171,6 +175,13 @@ enabled = true
## Name i2pd appears in UPnP forwardings list (default = I2Pd)
# name = I2Pd
[meshnets]
## Enable connectivity over the Yggdrasil network
# yggdrasil = false
## You can bind address from your Yggdrasil subnet 300::/64
## The address must first be added to the network interface
# yggaddress =
[reseed]
## Options for bootstrapping into I2P network, aka reseeding
## Enable or disable reseed data verification.
@@ -178,6 +189,8 @@ verify = true
## URLs to request reseed data from, separated by comma
## Default: "mainline" I2P Network reseeds
# urls = https://reseed.i2p-projekt.de/,https://i2p.mooo.com/netDb/,https://netdb.i2p2.no/
## Reseed URLs through the Yggdrasil, separated by comma
# yggurls = http://[324:9de3:fea4:f6ac::ace]:7070/
## Path to local reseed data file (.su3) for manual reseeding
# file = /path/to/i2pseeds.su3
## or HTTPS URL to reseed from
@@ -195,19 +208,15 @@ verify = true
## Default: reg.i2p at "mainline" I2P Network
# defaulturl = http://shx5vqsw7usdaunyzr2qmes2fq37oumybpudrd4jjj4e4vk4uusa.b32.i2p/hosts.txt
## Optional subscriptions URLs, separated by comma
# subscriptions = http://inr.i2p/export/alive-hosts.txt,http://stats.i2p/cgi-bin/newhosts.txt,http://rus.i2p/hosts.txt
# subscriptions = http://reg.i2p/hosts.txt,http://identiguy.i2p/hosts.txt,http://stats.i2p/cgi-bin/newhosts.txt,http://rus.i2p/hosts.txt
[limits]
## Maximum active transit sessions (default:2500)
# transittunnels = 2500
## Limit number of open file descriptors (0 - use system limit)
## Limit number of open file descriptors (0 - use system limit)
# openfiles = 0
## Maximum size of corefile in Kb (0 - use system limit)
## Maximum size of corefile in Kb (0 - use system limit)
# coresize = 0
## Threshold to start probabalistic backoff with ntcp sessions (0 - use system limit)
# ntcpsoft = 0
## Maximum number of ntcp sessions (0 - use system limit)
# ntcphard = 0
[trust]
## Enable explicit trust options. false by default
@@ -215,13 +224,13 @@ verify = true
## Make direct I2P connections only to routers in specified Family.
# family = MyFamily
## Make direct I2P connections only to routers specified here. Comma separated list of base64 identities.
# routers =
# routers =
## Should we hide our router from other routers? false by default
# hidden = true
[exploratory]
## Exploratory tunnels settings with default values
# inbound.length = 2
# inbound.length = 2
# inbound.quantity = 3
# outbound.length = 2
# outbound.quantity = 3
@@ -229,6 +238,8 @@ verify = true
[persist]
## Save peer profiles on disk (default: true)
# profiles = true
## Save full addresses on disk (default: true)
# addressbook = true
[cpuext]
## Use CPU AES-NI instructions set when work with cryptography when available (default: true)

View File

@@ -17,7 +17,12 @@ PIDFile=/run/i2pd/i2pd.pid
### Uncomment, if auto restart needed
#Restart=on-failure
KillSignal=SIGQUIT
# Use SIGTERM to stop i2pd immediately.
# Some cleanup processes can delay stopping, so we set 30 seconds timeout and then SIGKILL i2pd.
KillSignal=SIGTERM
TimeoutStopSec=30s
SendSIGKILL=yes
# If you have the patience waiting 10 min on restarting/stopping it, uncomment this.
# i2pd stops accepting new tunnels and waits ~10 min while old ones do not die.
#KillSignal=SIGINT

View File

@@ -1,7 +1,7 @@
%define git_hash %(git rev-parse HEAD | cut -c -7)
Name: i2pd-git
Version: 2.36.0
Version: 2.38.0
Release: git%{git_hash}%{?dist}
Summary: I2P router written in C++
Conflicts: i2pd
@@ -137,6 +137,12 @@ getent passwd i2pd >/dev/null || \
%changelog
* Mon May 17 2021 orignal <i2porignal@yandex.ru> - 2.38.0
- update to 2.38.0
* Mon Mar 15 2021 orignal <i2porignal@yandex.ru> - 2.37.0
- update to 2.37.0
* Mon Feb 15 2021 orignal <i2porignal@yandex.ru> - 2.36.0
- update to 2.36.0

View File

@@ -1,5 +1,5 @@
Name: i2pd
Version: 2.36.0
Version: 2.38.0
Release: 1%{?dist}
Summary: I2P router written in C++
Conflicts: i2pd-git
@@ -135,6 +135,12 @@ getent passwd i2pd >/dev/null || \
%changelog
* Mon May 17 2021 orignal <i2porignal@yandex.ru> - 2.38.0
- update to 2.38.0
* Mon Mar 15 2021 orignal <i2porignal@yandex.ru> - 2.37.0
- update to 2.37.0
* Mon Feb 15 2021 orignal <i2porignal@yandex.ru> - 2.36.0
- update to 2.36.0

View File

@@ -102,8 +102,13 @@ namespace util
if (logclftime)
i2p::log::Logger().SetTimeFormat ("[%d/%b/%Y:%H:%M:%S %z]");
#ifdef WIN32_APP
// Win32 app with GUI supports only logging to file
logs = "file";
#else
if (isDaemon && (logs == "" || logs == "stdout"))
logs = "file";
#endif
i2p::log::Logger().SetLogLevel(loglevel);
if (logstream) {
@@ -123,7 +128,7 @@ namespace util
// use stdout -- default
}
LogPrint(eLogInfo, "i2pd v", VERSION, " starting");
LogPrint(eLogNone, "i2pd v", VERSION, " starting");
LogPrint(eLogDebug, "FS: main config file: ", config);
LogPrint(eLogDebug, "FS: data directory: ", datadir);
@@ -137,13 +142,32 @@ namespace util
i2p::context.SetNetID (netID);
i2p::context.Init ();
bool ipv6; i2p::config::GetOption("ipv6", ipv6);
bool ipv4; i2p::config::GetOption("ipv4", ipv4);
bool ipv6; i2p::config::GetOption("ipv6", ipv6);
bool ipv4; i2p::config::GetOption("ipv4", ipv4);
#ifdef MESHNET
// manual override for meshnet
ipv4 = false;
ipv6 = true;
#endif
// ifname -> address
std::string ifname; i2p::config::GetOption("ifname", ifname);
if (ipv4 && i2p::config::IsDefault ("address4"))
{
std::string ifname4; i2p::config::GetOption("ifname4", ifname4);
if (!ifname4.empty ())
i2p::config::SetOption ("address4", i2p::util::net::GetInterfaceAddress(ifname4, false).to_string ()); // v4
else if (!ifname.empty ())
i2p::config::SetOption ("address4", i2p::util::net::GetInterfaceAddress(ifname, false).to_string ()); // v4
}
if (ipv6 && i2p::config::IsDefault ("address6"))
{
std::string ifname6; i2p::config::GetOption("ifname6", ifname6);
if (!ifname6.empty ())
i2p::config::SetOption ("address6", i2p::util::net::GetInterfaceAddress(ifname6, true).to_string ()); // v6
else if (!ifname.empty ())
i2p::config::SetOption ("address6", i2p::util::net::GetInterfaceAddress(ifname, true).to_string ()); // v6
}
bool ygg; i2p::config::GetOption("meshnets.yggdrasil", ygg);
boost::asio::ip::address_v6 yggaddr;
if (ygg)
@@ -186,10 +210,15 @@ namespace util
{
bool published; i2p::config::GetOption("ntcp2.published", published);
if (published)
{
std::string ntcp2proxy; i2p::config::GetOption("ntcp2.proxy", ntcp2proxy);
if (!ntcp2proxy.empty ()) published = false;
}
if (published)
{
uint16_t ntcp2port; i2p::config::GetOption("ntcp2.port", ntcp2port);
if (!ntcp2port) ntcp2port = port; // use standard port
i2p::context.PublishNTCP2Address (ntcp2port, true); // publish
i2p::context.PublishNTCP2Address (ntcp2port, true, ipv4, ipv6, false); // publish
if (ipv6)
{
std::string ipv6Addr; i2p::config::GetOption("ntcp2.addressv6", ipv6Addr);
@@ -199,15 +228,16 @@ namespace util
}
}
else
i2p::context.PublishNTCP2Address (port, false); // unpublish
i2p::context.PublishNTCP2Address (port, false, ipv4, ipv6, false); // unpublish
}
if (ygg)
{
if (!ntcp2)
i2p::context.PublishNTCP2Address (port, true);
i2p::context.PublishNTCP2Address (port, true, false, false, true);
i2p::context.UpdateNTCP2V6Address (yggaddr);
}
if (!ipv4 && !ipv6)
i2p::context.SetStatus (eRouterStatusMesh);
}
bool transit; i2p::config::GetOption("notransit", transit);
i2p::context.SetAcceptsTunnels (!transit);
uint16_t transitTunnels; i2p::config::GetOption("limits.transittunnels", transitTunnels);
@@ -250,7 +280,7 @@ namespace util
else if (isFloodfill)
{
LogPrint(eLogInfo, "Daemon: floodfill bandwidth set to 'extra'");
i2p::context.SetBandwidth (i2p::data::CAPS_FLAG_EXTRA_BANDWIDTH1);
i2p::context.SetBandwidth (i2p::data::CAPS_FLAG_EXTRA_BANDWIDTH2);
}
else
{
@@ -343,7 +373,7 @@ namespace util
if(!ntcp2) LogPrint(eLogInfo, "Daemon: ntcp2 disabled");
i2p::transport::transports.SetCheckReserved(checkInReserved);
i2p::transport::transports.Start(ntcp2 || i2p::context.SupportsMesh (), ssu);
i2p::transport::transports.Start(ntcp2, ssu);
if (i2p::transport::transports.IsBoundSSU() || i2p::transport::transports.IsBoundNTCP2())
LogPrint(eLogInfo, "Daemon: Transports started");
else

View File

@@ -123,6 +123,7 @@ namespace http {
const char HTTP_COMMAND_LOGLEVEL[] = "set_loglevel";
const char HTTP_COMMAND_KILLSTREAM[] = "closestream";
const char HTTP_COMMAND_LIMITTRANSIT[] = "limittransit";
const char HTTP_COMMAND_GET_REG_STRING[] = "get_reg_string";
const char HTTP_PARAM_SAM_SESSION_ID[] = "id";
const char HTTP_PARAM_ADDRESS[] = "address";
@@ -241,17 +242,16 @@ namespace http {
s << "<b>ERROR:</b>&nbsp;" << string << "<br>\r\n";
}
void ShowStatus (std::stringstream& s, bool includeHiddenContent, i2p::http::OutputFormatEnum outputFormat)
static void ShowNetworkStatus (std::stringstream& s, RouterStatus status)
{
s << "<b>Uptime:</b> ";
ShowUptime(s, i2p::context.GetUptime ());
s << "<br>\r\n";
s << "<b>Network status:</b> ";
switch (i2p::context.GetStatus ())
switch (status)
{
case eRouterStatusOK: s << "OK"; break;
case eRouterStatusTesting: s << "Testing"; break;
case eRouterStatusFirewalled: s << "Firewalled"; break;
case eRouterStatusUnknown: s << "Unknown"; break;
case eRouterStatusProxy: s << "Proxy"; break;
case eRouterStatusMesh: s << "Mesh"; break;
case eRouterStatusError:
{
s << "Error";
@@ -263,13 +263,31 @@ namespace http {
case eRouterErrorOffline:
s << " - Offline";
break;
case eRouterErrorSymmetricNAT:
s << " - Symmetric NAT";
break;
default: ;
}
break;
}
default: s << "Unknown";
}
}
void ShowStatus (std::stringstream& s, bool includeHiddenContent, i2p::http::OutputFormatEnum outputFormat)
{
s << "<b>Uptime:</b> ";
ShowUptime(s, i2p::context.GetUptime ());
s << "<br>\r\n";
s << "<b>Network status:</b> ";
ShowNetworkStatus (s, i2p::context.GetStatus ());
s << "<br>\r\n";
if (i2p::context.SupportsV6 ())
{
s << "<b>Network status 6:</b> ";
ShowNetworkStatus (s, i2p::context.GetStatusV6 ());
s << "<br>\r\n";
}
#if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY))
if (auto remains = Daemon.gracefulShutdownInterval) {
s << "<b>Stopping in:</b> ";
@@ -307,6 +325,7 @@ namespace http {
if (!i2p::context.GetRouterInfo().GetProperty("family").empty())
s << "<b>Router Family:</b> " << i2p::context.GetRouterInfo().GetProperty("family") << "<br>\r\n";
s << "<b>Router Caps:</b> " << i2p::context.GetRouterInfo().GetProperty("caps") << "<br>\r\n";
s << "<b>Version:</b> " VERSION "<br>\r\n";
s << "<b>Our external address:</b>" << "<br>\r\n<table class=\"extaddr\"><tbody>\r\n";
for (const auto& address : i2p::context.GetRouterInfo().GetAddresses())
{
@@ -402,9 +421,9 @@ namespace http {
}
}
static void ShowLeaseSetDestination (std::stringstream& s, std::shared_ptr<const i2p::client::LeaseSetDestination> dest)
static void ShowLeaseSetDestination (std::stringstream& s, std::shared_ptr<const i2p::client::LeaseSetDestination> dest, uint32_t token)
{
s << "<b>Base64:</b><br>\r\n<textarea readonly cols=\"80\" rows=\"8\" wrap=\"on\">";
s << "<b>Base64:</b><br>\r\n<textarea readonly cols=\"80\" rows=\"8\">";
s << dest->GetIdentity ()->ToBase64 () << "</textarea><br>\r\n<br>\r\n";
if (dest->IsEncryptedLeaseSet ())
{
@@ -414,6 +433,20 @@ namespace http {
s << "</div>\r\n</div>\r\n";
}
if(dest->IsPublic())
{
std::string webroot; i2p::config::GetOption("http.webroot", webroot);
auto base32 = dest->GetIdentHash ().ToBase32 ();
s << "<div class='slide'><label for='slide-regaddr'><b>Address registration line</b></label>\r\n<input type=\"checkbox\" id=\"slide-regaddr\" />\r\n<div class=\"slidecontent\">\r\n"
"<form method=\"get\" action=\"" << webroot << "\">\r\n"
" <input type=\"hidden\" name=\"cmd\" value=\"" << HTTP_COMMAND_GET_REG_STRING << "\">\r\n"
" <input type=\"hidden\" name=\"token\" value=\"" << token << "\">\r\n"
" <input type=\"hidden\" name=\"b32\" value=\"" << base32 << "\">\r\n"
" <b>Domain:</b>\r\n<input type=\"text\" maxlength=\"67\" name=\"name\" placeholder=\"domain.i2p\" required>\r\n"
" <button type=\"submit\">Generate</button>\r\n"
"</form>\r\n<small><b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.</small>\r\n</div>\r\n</div>\r\n<br>\r\n";
}
if(dest->GetNumRemoteLeaseSets())
{
s << "<div class='slide'><label for='slide-lease'><b>LeaseSets:</b> <i>" << dest->GetNumRemoteLeaseSets ()
@@ -489,7 +522,7 @@ namespace http {
if (dest)
{
ShowLeaseSetDestination (s, dest);
ShowLeaseSetDestination (s, dest, token);
// show streams
s << "<table>\r\n<caption>Streams</caption>\r\n<thead>\r\n<tr>";
s << "<th style=\"width:25px;\">StreamID</th>";
@@ -532,7 +565,7 @@ namespace http {
}
}
void ShowI2CPLocalDestination (std::stringstream& s, const std::string& id)
void ShowI2CPLocalDestination (std::stringstream& s, const std::string& id)
{
auto i2cpServer = i2p::client::context.GetI2CPServer ();
if (i2cpServer)
@@ -540,7 +573,7 @@ namespace http {
s << "<b>I2CP Local Destination:</b><br>\r\n<br>\r\n";
auto it = i2cpServer->GetSessions ().find (std::stoi (id));
if (it != i2cpServer->GetSessions ().end ())
ShowLeaseSetDestination (s, it->second->GetDestination ());
ShowLeaseSetDestination (s, it->second->GetDestination (), 0);
else
ShowError(s, "I2CP session not found");
}
@@ -706,23 +739,23 @@ namespace http {
std::stringstream tmp_s, tmp_s6; uint16_t cnt = 0, cnt6 = 0;
for (const auto& it: sessions )
{
if (it.second && it.second->IsEstablished () && !it.second->GetSocket ().remote_endpoint ().address ().is_v6 ())
if (it.second && it.second->IsEstablished () && !it.second->GetRemoteEndpoint ().address ().is_v6 ())
{
tmp_s << "<div class=\"listitem\">\r\n";
if (it.second->IsOutgoing ()) tmp_s << " &#8658; ";
tmp_s << i2p::data::GetIdentHashAbbreviation (it.second->GetRemoteIdentity ()->GetIdentHash ()) << ": "
<< it.second->GetSocket ().remote_endpoint().address ().to_string ();
<< it.second->GetRemoteEndpoint ().address ().to_string ();
if (!it.second->IsOutgoing ()) tmp_s << " &#8658; ";
tmp_s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
tmp_s << "</div>\r\n" << std::endl;
cnt++;
}
if (it.second && it.second->IsEstablished () && it.second->GetSocket ().remote_endpoint ().address ().is_v6 ())
if (it.second && it.second->IsEstablished () && it.second->GetRemoteEndpoint ().address ().is_v6 ())
{
tmp_s6 << "<div class=\"listitem\">\r\n";
if (it.second->IsOutgoing ()) tmp_s6 << " &#8658; ";
tmp_s6 << i2p::data::GetIdentHashAbbreviation (it.second->GetRemoteIdentity ()->GetIdentHash ()) << ": "
<< "[" << it.second->GetSocket ().remote_endpoint().address ().to_string () << "]";
<< "[" << it.second->GetRemoteEndpoint ().address ().to_string () << "]";
if (!it.second->IsOutgoing ()) tmp_s6 << " &#8658; ";
tmp_s6 << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
tmp_s6 << "</div>\r\n" << std::endl;
@@ -810,7 +843,7 @@ namespace http {
s << "<b>SAM Sessions:</b><br>\r\n<div class=\"list\">\r\n";
for (auto& it: sam->GetSessions ())
{
auto& name = it.second->localDestination->GetNickname ();
auto& name = it.second->GetLocalDestination ()->GetNickname ();
s << "<div class=\"listitem\"><a href=\"" << webroot << "?page=" << HTTP_PAGE_SAM_SESSION << "&sam_id=" << it.first << "\">";
s << name << " (" << it.first << ")</a></div>\r\n" << std::endl;
}
@@ -820,7 +853,7 @@ namespace http {
s << "<b>SAM Sessions:</b> no sessions currently running.<br>\r\n";
}
void ShowSAMSession (std::stringstream& s, const std::string& id)
void ShowSAMSession (std::stringstream& s, const std::string& id)
{
auto sam = i2p::client::context.GetSAMBridge ();
if (!sam) {
@@ -836,7 +869,7 @@ namespace http {
std::string webroot; i2p::config::GetOption("http.webroot", webroot);
s << "<b>SAM Session:</b><br>\r\n<div class=\"list\">\r\n";
auto& ident = session->localDestination->GetIdentHash();
auto& ident = session->GetLocalDestination ()->GetIdentHash();
s << "<div class=\"listitem\"><a href=\"" << webroot << "?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
s << i2p::client::context.GetAddressBook ().ToAddress(ident) << "</a></div>\r\n";
s << "<br>\r\n";
@@ -1205,15 +1238,15 @@ namespace http {
if (dest)
{
if(dest->DeleteStream (streamID))
s << "<b>SUCCESS</b>:&nbsp;Stream closed<br><br>\r\n";
s << "<b>SUCCESS</b>:&nbsp;Stream closed<br>\r\n<br>\r\n";
else
s << "<b>ERROR</b>:&nbsp;Stream not found or already was closed<br><br>\r\n";
s << "<b>ERROR</b>:&nbsp;Stream not found or already was closed<br>\r\n<br>\r\n";
}
else
s << "<b>ERROR</b>:&nbsp;Destination not found<br><br>\r\n";
s << "<b>ERROR</b>:&nbsp;Destination not found<br>\r\n<br>\r\n";
}
else
s << "<b>ERROR</b>:&nbsp;StreamID can be null<br><br>\r\n";
s << "<b>ERROR</b>:&nbsp;StreamID can be null<br>\r\n<br>\r\n";
s << "<a href=\"" << webroot << "?page=local_destination&b32=" << b32 << "\">Return to destination page</a><br>\r\n";
s << "<p>You will be redirected back in 5 seconds</b>";
@@ -1227,13 +1260,62 @@ namespace http {
if (limit > 0 && limit <= 65535)
SetMaxNumTransitTunnels (limit);
else {
s << "<b>ERROR</b>:&nbsp;Transit tunnels count must not exceed 65535<br><br>\r\n";
s << "<a href=\"" << webroot << "?page=commands\">Back to commands list</a><br>\r\n";
s << "<b>ERROR</b>:&nbsp;Transit tunnels count must not exceed 65535\r\n<br>\r\n<br>\r\n";
s << "<a href=\"" << webroot << "?page=commands\">Back to commands list</a>\r\n<br>\r\n";
s << "<p>You will be redirected back in 5 seconds</b>";
res.add_header("Refresh", redirect.c_str());
return;
}
}
else if (cmd == HTTP_COMMAND_GET_REG_STRING)
{
std::string b32 = params["b32"];
std::string name = params["name"];
i2p::data::IdentHash ident;
ident.FromBase32 (b32);
auto dest = i2p::client::context.FindLocalDestination (ident);
if (dest)
{
std::size_t pos;
pos = name.find (".i2p");
if (pos == (name.length () - 4))
{
pos = name.find (".b32.i2p");
if (pos == std::string::npos)
{
auto signatureLen = dest->GetIdentity ()->GetSignatureLen ();
uint8_t * signature = new uint8_t[signatureLen];
char * sig = new char[signatureLen*2];
std::stringstream out;
out << name << "=" << dest->GetIdentity ()->ToBase64 ();
dest->Sign ((uint8_t *)out.str ().c_str (), out.str ().length (), signature);
auto len = i2p::data::ByteStreamToBase64 (signature, signatureLen, sig, signatureLen*2);
sig[len] = 0;
out << "#!sig=" << sig;
s << "<b>SUCCESS</b>:<br>\r\n<form action=\"http://shx5vqsw7usdaunyzr2qmes2fq37oumybpudrd4jjj4e4vk4uusa.b32.i2p/add\" method=\"post\" rel=\"noreferrer\" target=\"_blank\">\r\n"
"<textarea readonly name=\"record\" cols=\"80\" rows=\"10\">" << out.str () << "</textarea>\r\n<br>\r\n<br>\r\n"
"<b>Register at reg.i2p:</b>\r\n<br>\r\n"
"<b>Description:</b>\r\n<input type=\"text\" maxlength=\"64\" name=\"desc\" placeholder=\"A bit information about service on domain\">\r\n"
"<input type=\"submit\" value=\"Submit\">\r\n"
"</form>\r\n<br>\r\n";
delete[] signature;
delete[] sig;
}
else
s << "<b>ERROR</b>:&nbsp;Domain can't end with .b32.i2p\r\n<br>\r\n<br>\r\n";
}
else
s << "<b>ERROR</b>:&nbsp;Domain must end with .i2p\r\n<br>\r\n<br>\r\n";
}
else
s << "<b>ERROR</b>:&nbsp;Such destination is not found\r\n<br>\r\n<br>\r\n";
s << "<a href=\"" << webroot << "?page=local_destination&b32=" << b32 << "\">Return to destination page</a>\r\n";
return;
}
else
{
res.code = 400;

View File

@@ -2,6 +2,10 @@
#include <sstream>
#include <openssl/x509.h>
#include <openssl/pem.h>
// Use global placeholders from boost introduced when local_time.hpp is loaded
#define BOOST_BIND_GLOBAL_PLACEHOLDERS
#include <boost/lexical_cast.hpp>
#include <boost/date_time/local_time/local_time.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
@@ -714,8 +718,8 @@ namespace client
for (auto& it: sam->GetSessions ())
{
boost::property_tree::ptree sam_session, sam_session_sockets;
auto& name = it.second->localDestination->GetNickname ();
auto& ident = it.second->localDestination->GetIdentHash();
auto& name = it.second->GetLocalDestination ()->GetNickname ();
auto& ident = it.second->GetLocalDestination ()->GetIdentHash();
sam_session.put("name", name);
sam_session.put("address", i2p::client::context.GetAddressBook ().ToAddress(ident));

View File

@@ -81,10 +81,10 @@ namespace transport
void UPnP::Discover ()
{
bool isError;
int err;
int err;
#if ((MINIUPNPC_API_VERSION >= 8) || defined (UPNPDISCOVER_SUCCESS))
err = UPNPDISCOVER_SUCCESS;
err = UPNPDISCOVER_SUCCESS;
#if (MINIUPNPC_API_VERSION >= 14)
m_Devlist = upnpDiscover (UPNP_RESPONSE_TIMEOUT, NULL, NULL, 0, 0, 2, &err);
@@ -94,8 +94,8 @@ namespace transport
isError = err != UPNPDISCOVER_SUCCESS;
#else // MINIUPNPC_API_VERSION >= 8
err = 0;
m_Devlist = upnpDiscover (UPNP_RESPONSE_TIMEOUT, NULL, NULL, 0);
err = 0;
m_Devlist = upnpDiscover (UPNP_RESPONSE_TIMEOUT, NULL, NULL, 0);
isError = m_Devlist == NULL;
#endif // MINIUPNPC_API_VERSION >= 8
{
@@ -106,15 +106,15 @@ namespace transport
if (isError)
{
LogPrint (eLogError, "UPnP: unable to discover Internet Gateway Devices: error ", err);
LogPrint (eLogError, "UPnP: unable to discover Internet Gateway Devices: error ", err);
return;
}
err = UPNP_GetValidIGD (m_Devlist, &m_upnpUrls, &m_upnpData, m_NetworkAddr, sizeof (m_NetworkAddr));
m_upnpUrlsInitialized=err!=0;
m_upnpUrlsInitialized=err!=0;
if (err == UPNP_IGD_VALID_CONNECTED)
{
err = UPNP_GetExternalIPAddress (m_upnpUrls.controlURL, m_upnpData.first.servicetype, m_externalIPAddress);
err = UPNP_GetExternalIPAddress (m_upnpUrls.controlURL, m_upnpData.first.servicetype, m_externalIPAddress);
if(err != UPNPCOMMAND_SUCCESS)
{
LogPrint (eLogError, "UPnP: unable to get external address: error ", err);
@@ -125,14 +125,14 @@ namespace transport
LogPrint (eLogError, "UPnP: found Internet Gateway Device ", m_upnpUrls.controlURL);
if (!m_externalIPAddress[0])
{
LogPrint (eLogError, "UPnP: found Internet Gateway Device doesn't know our external address");
LogPrint (eLogError, "UPnP: found Internet Gateway Device doesn't know our external address");
return;
}
}
}
else
{
LogPrint (eLogError, "UPnP: unable to find valid Internet Gateway Device: error ", err);
LogPrint (eLogError, "UPnP: unable to find valid Internet Gateway Device: error ", err);
return;
}
@@ -183,7 +183,7 @@ namespace transport
err = CheckMapping (strPort.c_str (), strType.c_str ());
if (err != UPNPCOMMAND_SUCCESS) // if mapping not found
{
LogPrint (eLogDebug, "UPnP: possibly port ", strPort, " is not forwarded: return code ", err);
LogPrint (eLogDebug, "UPnP: possibly port ", strPort, " is not forwarded: return code ", err);
#if ((MINIUPNPC_API_VERSION >= 8) || defined (UPNPDISCOVER_SUCCESS))
err = UPNP_AddPortMapping (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strPort.c_str (), m_NetworkAddr, strDesc.c_str (), strType.c_str (), NULL, NULL);
@@ -203,7 +203,7 @@ namespace transport
}
else
{
LogPrint (eLogDebug, "UPnP: external forward from ", m_NetworkAddr, ":", strPort, " exists on current Internet Gateway Device");
LogPrint (eLogDebug, "UPnP: external forward from ", m_NetworkAddr, ":", strPort, " exists on current Internet Gateway Device");
return;
}
}
@@ -220,14 +220,14 @@ namespace transport
void UPnP::CloseMapping (std::shared_ptr<i2p::data::RouterInfo::Address> address)
{
if(!m_upnpUrlsInitialized) {
return;
}
if(!m_upnpUrlsInitialized) {
return;
}
std::string strType (GetProto (address)), strPort (std::to_string (address->port));
int err = UPNPCOMMAND_SUCCESS;
err = CheckMapping (strPort.c_str (), strType.c_str ());
if (err == UPNPCOMMAND_SUCCESS)
if (err == UPNPCOMMAND_SUCCESS)
{
err = UPNP_DeletePortMapping (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strType.c_str (), NULL);
LogPrint (eLogError, "UPnP: DeletePortMapping() returned : ", err);
@@ -238,11 +238,11 @@ namespace transport
{
freeUPNPDevlist (m_Devlist);
m_Devlist = 0;
if(m_upnpUrlsInitialized){
FreeUPNPUrls (&m_upnpUrls);
m_upnpUrlsInitialized=false;
}
}
if(m_upnpUrlsInitialized){
FreeUPNPUrls (&m_upnpUrls);
m_upnpUrlsInitialized=false;
}
}
std::string UPnP::GetProto (std::shared_ptr<i2p::data::RouterInfo::Address> address)
{

12
debian/changelog vendored
View File

@@ -1,3 +1,15 @@
i2pd (2.38.0-1) unstable; urgency=medium
* updated to version 2.38.0/0.9.50
-- orignal <orignal@i2pmail.org> Mon, 17 May 2021 16:00:00 +0000
i2pd (2.37.0-1) unstable; urgency=medium
* updated to version 2.37.0
-- orignal <orignal@i2pmail.org> Mon, 15 Mar 2021 16:00:00 +0000
i2pd (2.36.0-1) unstable; urgency=high
* updated to version 2.36.0/0.9.49

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2020, The PurpleI2P Project
* Copyright (c) 2013-2021, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@@ -272,11 +272,19 @@ namespace data
case i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA384_P384:
case i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA512_P521:
publicKeyLength = BlindECDSA (m_SigType, priv, seed, BlindEncodedPrivateKeyECDSA, blindedPriv, blindedPub);
break;
break;
case i2p::data::SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519:
i2p::crypto::GetEd25519 ()->BlindPrivateKey (priv, seed, blindedPriv, blindedPub);
publicKeyLength = i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH;
break;
case i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519:
{
uint8_t exp[64];
i2p::crypto::Ed25519::ExpandPrivateKey (priv, exp);
i2p::crypto::GetEd25519 ()->BlindPrivateKey (exp, seed, blindedPriv, blindedPub);
publicKeyLength = i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH;
break;
}
default:
LogPrint (eLogError, "Blinding: can't blind signature type ", (int)m_SigType);
}

View File

@@ -19,6 +19,7 @@
#include "Identity.h"
#include "Config.h"
#include "version.h"
#include "Log.h"
using namespace boost::program_options;
@@ -50,7 +51,9 @@ namespace config {
("nat", bool_switch()->default_value(true), "Should we assume we are behind NAT? (default: enabled)")
("port", value<uint16_t>()->default_value(0), "Port to listen for incoming connections (default: auto)")
("ipv4", bool_switch()->default_value(true), "Enable communication through ipv4 (default: enabled)")
("address4", value<std::string>()->default_value(""), "Local address to bind ipv4 transport sockets to")
("ipv6", bool_switch()->default_value(false), "Enable communication through ipv6 (default: disabled)")
("address6", value<std::string>()->default_value(""), "Local address to bind ipv6 transport sockets to")
("reservedrange", bool_switch()->default_value(true), "Check remote RI for being in blacklist of reserved IP ranges (default: enabled)")
("netid", value<int>()->default_value(I2PD_NET_ID), "Specify NetID. Main I2P is 2")
("daemon", bool_switch()->default_value(false), "Router will go to background after start (default: disabled)")
@@ -59,11 +62,11 @@ namespace config {
("floodfill", bool_switch()->default_value(false), "Router will be floodfill (default: disabled)")
("bandwidth", value<std::string>()->default_value(""), "Bandwidth limit: integer in KBps or letters: L (32), O (256), P (2048), X (>9000)")
("share", value<int>()->default_value(100), "Limit of transit traffic from max bandwidth in percents. (default: 100)")
("ntcp", bool_switch()->default_value(false), "Ignored. Always false")
("ntcp", bool_switch()->default_value(false), "Deprecated option. Always false")
("ssu", bool_switch()->default_value(true), "Enable SSU transport (default: enabled)")
("ntcpproxy", value<std::string>()->default_value(""), "Ignored")
("ntcpproxy", value<std::string>()->default_value(""), "Deprecated option")
#ifdef _WIN32
("svcctl", value<std::string>()->default_value(""), "Windows service management ('install' or 'remove')")
("svcctl", value<std::string>()->default_value(""), "Deprecated option")
("insomnia", bool_switch()->default_value(false), "Prevent system from sleeping (default: disabled)")
("close", value<std::string>()->default_value("ask"), "Action on close: minimize, exit, ask")
#endif
@@ -74,9 +77,9 @@ 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 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)")
("limits.ntcpsoft", value<uint16_t>()->default_value(0), "Deprecated option")
("limits.ntcphard", value<uint16_t>()->default_value(0), "Deprecated option")
("limits.ntcpthreads", value<uint16_t>()->default_value(1), "Deprecated option")
;
options_description httpserver("HTTP Server options");
@@ -207,7 +210,7 @@ namespace config {
), "Reseed URLs, separated by comma")
("reseed.yggurls", value<std::string>()->default_value(
"http://[324:9de3:fea4:f6ac::ace]:7070/"
), "Reseed URLs through the Yggdrasil, separated by comma")
), "Reseed URLs through the Yggdrasil, separated by comma")
;
options_description addressbook("AddressBook options");
@@ -216,12 +219,12 @@ namespace config {
"http://shx5vqsw7usdaunyzr2qmes2fq37oumybpudrd4jjj4e4vk4uusa.b32.i2p/hosts.txt"
), "AddressBook subscription URL for initial setup")
("addressbook.subscriptions", value<std::string>()->default_value(""), "AddressBook subscriptions URLs, separated by comma")
("addressbook.hostsfile", value<std::string>()->default_value(""), "File to dump addresses in hosts.txt format");
("addressbook.hostsfile", value<std::string>()->default_value(""), "File to dump addresses in hosts.txt format");
options_description trust("Trust options");
trust.add_options()
("trust.enabled", value<bool>()->default_value(false), "Enable explicit trust options")
("trust.family", value<std::string>()->default_value(""), "Router Familiy to trust for first hops")
("trust.family", value<std::string>()->default_value(""), "Router Family to trust for first hops")
("trust.routers", value<std::string>()->default_value(""), "Only Connect to these routers")
("trust.hidden", value<bool>()->default_value(false), "Should we hide our router from other routers?")
;
@@ -247,7 +250,7 @@ namespace config {
("ntcp2.enabled", value<bool>()->default_value(true), "Enable NTCP2 (default: enabled)")
("ntcp2.published", value<bool>()->default_value(true), "Publish NTCP2 (default: enabled)")
("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")
("ntcp2.addressv6", value<std::string>()->default_value("::"), "Address to publish NTCP2 with")
("ntcp2.proxy", value<std::string>()->default_value(""), "Proxy URL for NTCP2 transport")
;
@@ -278,10 +281,10 @@ namespace config {
options_description meshnets("Meshnet transports options");
meshnets.add_options()
("meshnets.yggdrasil", bool_switch()->default_value(false), "Support transports through the Yggdrasil (deafult: false)")
("meshnets.yggaddress", value<std::string>()->default_value(""), "Yggdrasil address to publish")
;
("meshnets.yggdrasil", bool_switch()->default_value(false), "Support transports through the Yggdrasil (default: false)")
("meshnets.yggaddress", value<std::string>()->default_value(""), "Yggdrasil address to publish")
;
m_OptionsDesc
.add(general)
.add(limits)
@@ -312,7 +315,7 @@ namespace config {
try
{
auto style = boost::program_options::command_line_style::unix_style
| boost::program_options::command_line_style::allow_long_disguise;
| boost::program_options::command_line_style::allow_long_disguise;
style &= ~ boost::program_options::command_line_style::allow_guessing;
if (ignoreUnknown)
store(command_line_parser(argc, argv).options(m_OptionsDesc).style (style).allow_unregistered().run(), m_Options);
@@ -321,6 +324,7 @@ namespace config {
}
catch (boost::program_options::error& e)
{
ThrowFatal ("Error while parsing arguments: ", e.what());
std::cerr << "args: " << e.what() << std::endl;
exit(EXIT_FAILURE);
}
@@ -358,6 +362,7 @@ namespace config {
if (!config.is_open())
{
ThrowFatal ("Missing or unreadable config file: ", path);
std::cerr << "missing/unreadable config file: " << path << std::endl;
exit(EXIT_FAILURE);
}
@@ -368,6 +373,7 @@ namespace config {
}
catch (boost::program_options::error& e)
{
ThrowFatal ("Error while parsing config file: ", e.what());
std::cerr << e.what() << std::endl;
exit(EXIT_FAILURE);
};

View File

@@ -353,7 +353,7 @@ namespace crypto
bool X25519Keys::Agree (const uint8_t * pub, uint8_t * shared)
{
if (pub[31] & 0x80) return false; // not x25519 key
if (!pub || (pub[31] & 0x80)) return false; // not x25519 key
#if OPENSSL_X25519
EVP_PKEY_derive_init (m_Ctx);
auto pkey = EVP_PKEY_new_raw_public_key (EVP_PKEY_X25519, NULL, pub, 32);

View File

@@ -300,7 +300,11 @@ namespace client
{
int numTunnels = m_Pool->GetNumInboundTunnels () + 2; // 2 backup tunnels
if (numTunnels > i2p::data::MAX_NUM_LEASES) numTunnels = i2p::data::MAX_NUM_LEASES; // 16 tunnels maximum
CreateNewLeaseSet (m_Pool->GetInboundTunnels (numTunnels));
auto tunnels = m_Pool->GetInboundTunnels (numTunnels);
if (!tunnels.empty ())
CreateNewLeaseSet (tunnels);
else
LogPrint (eLogInfo, "Destination: No inbound tunnels for LeaseSet");
}
bool LeaseSetDestination::SubmitSessionKey (const uint8_t * key, const uint8_t * tag)
@@ -386,7 +390,7 @@ namespace client
if (leaseSet->IsNewer (buf + offset, len - offset))
{
leaseSet->Update (buf + offset, len - offset);
if (leaseSet->IsValid () && leaseSet->GetIdentHash () == key)
if (leaseSet->IsValid () && leaseSet->GetIdentHash () == key && !leaseSet->IsExpired ())
LogPrint (eLogDebug, "Destination: Remote LeaseSet updated");
else
{
@@ -405,7 +409,7 @@ namespace client
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, true, GetPreferredCryptoType () ); // LeaseSet2
if (leaseSet->IsValid () && leaseSet->GetIdentHash () == key)
if (leaseSet->IsValid () && leaseSet->GetIdentHash () == key && !leaseSet->IsExpired ())
{
if (leaseSet->GetIdentHash () != GetIdentHash ())
{
@@ -505,7 +509,7 @@ namespace client
// schedule verification
m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_VERIFICATION_TIMEOUT));
m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer,
shared_from_this (), std::placeholders::_1));
shared_from_this (), std::placeholders::_1));
}
else
i2p::garlic::GarlicDestination::HandleDeliveryStatusMessage (msgID);
@@ -588,8 +592,7 @@ namespace client
// 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,
shared_from_this (), std::placeholders::_1));
shared_from_this (), std::placeholders::_1));
}
}
}
@@ -1127,13 +1130,28 @@ namespace client
return dest;
}
std::shared_ptr<i2p::stream::StreamingDestination> ClientDestination::RemoveStreamingDestination (int port)
{
if (port)
{
auto it = m_StreamingDestinationsByPorts.find (port);
if (it != m_StreamingDestinationsByPorts.end ())
{
auto ret = it->second;
m_StreamingDestinationsByPorts.erase (it);
return ret;
}
}
return nullptr;
}
i2p::datagram::DatagramDestination * ClientDestination::CreateDatagramDestination (bool gzip)
{
if (m_DatagramDestination == nullptr)
m_DatagramDestination = new i2p::datagram::DatagramDestination (GetSharedFromThis (), gzip);
return m_DatagramDestination;
}
}
std::vector<std::shared_ptr<const i2p::stream::Stream> > ClientDestination::GetAllStreams () const
{
std::vector<std::shared_ptr<const i2p::stream::Stream> > ret;
@@ -1176,7 +1194,7 @@ namespace client
LogPrint(eLogError, "Destinations: Can't save keys to ", path);
}
void ClientDestination::CreateNewLeaseSet (std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels)
void ClientDestination::CreateNewLeaseSet (const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels)
{
std::shared_ptr<i2p::data::LocalLeaseSet> leaseSet;
if (GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_LEASESET)

View File

@@ -137,6 +137,8 @@ namespace client
void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
void SetLeaseSetUpdated ();
bool IsPublic () const { return m_IsPublic; };
protected:
// implements GarlicDestination
@@ -147,11 +149,10 @@ namespace client
int GetLeaseSetType () const { return m_LeaseSetType; };
void SetLeaseSetType (int leaseSetType) { m_LeaseSetType = leaseSetType; };
int GetAuthType () const { return m_AuthType; };
bool IsPublic () const { return m_IsPublic; };
virtual void CleanupDestination () {}; // additional clean up in derived classes
// I2CP
virtual void HandleDataMessage (const uint8_t * buf, size_t len) = 0;
virtual void CreateNewLeaseSet (std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels) = 0;
virtual void CreateNewLeaseSet (const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels) = 0;
private:
@@ -235,6 +236,7 @@ namespace client
// streaming
std::shared_ptr<i2p::stream::StreamingDestination> CreateStreamingDestination (int port, bool gzip = true); // additional
std::shared_ptr<i2p::stream::StreamingDestination> GetStreamingDestination (int port = 0) const;
std::shared_ptr<i2p::stream::StreamingDestination> RemoveStreamingDestination (int port);
// 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);
@@ -261,7 +263,7 @@ namespace client
void CleanupDestination ();
// I2CP
void HandleDataMessage (const uint8_t * buf, size_t len);
void CreateNewLeaseSet (std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels);
void CreateNewLeaseSet (const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels);
private:

View File

@@ -160,9 +160,10 @@ namespace garlic
return true;
}
ECIESX25519AEADRatchetSession::ECIESX25519AEADRatchetSession (GarlicDestination * owner, bool attachLeaseSet):
GarlicRoutingSession (owner, attachLeaseSet)
ECIESX25519AEADRatchetSession::ECIESX25519AEADRatchetSession (GarlicDestination * owner, bool attachLeaseSetNS):
GarlicRoutingSession (owner, true)
{
if (!attachLeaseSetNS) SetLeaseSetUpdateStatus (eLeaseSetUpToDate);
RAND_bytes (m_PaddingSizes, 32); m_NextPaddingSize = 0;
}
@@ -195,7 +196,7 @@ namespace garlic
i2p::transport::transports.ReuseX25519KeysPair (m_EphemeralKeys);
}
// we still didn't find elligator eligible pair
for (int i = 0; i < 10; i++)
for (int i = 0; i < 25; i++)
{
// create new
m_EphemeralKeys = std::make_shared<i2p::crypto::X25519Keys>();
@@ -227,7 +228,7 @@ namespace garlic
if (!GetOwner ()) return false;
// we are Bob
// KDF1
i2p::crypto::InitNoiseIKState (*this, GetOwner ()->GetEncryptionPublicKey (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)); // bpk
i2p::crypto::InitNoiseIKState (GetNoiseState (), GetOwner ()->GetEncryptionPublicKey (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)); // bpk
if (!i2p::crypto::GetElligator ()->Decode (buf, m_Aepk))
{
@@ -460,7 +461,7 @@ namespace garlic
offset += 32;
// KDF1
i2p::crypto::InitNoiseIKState (*this, m_RemoteStaticKey); // bpk
i2p::crypto::InitNoiseIKState (GetNoiseState (), m_RemoteStaticKey); // bpk
MixHash (m_EphemeralKeys->GetPublicKey (), 32); // h = SHA256(h || aepk)
uint8_t sharedSecret[32];
if (!m_EphemeralKeys->Agree (m_RemoteStaticKey, sharedSecret)) // x25519(aesk, bpk)
@@ -511,6 +512,7 @@ namespace garlic
{
auto tagsetNsr = std::make_shared<ReceiveRatchetTagSet>(shared_from_this (), true);
InitNewSessionTagset (tagsetNsr);
tagsetNsr->Expire (); // let non-replied session expire
GenerateMoreReceiveTags (tagsetNsr, ECIESX25519_NSR_NUM_GENERATED_TAGS);
}
}
@@ -520,7 +522,7 @@ namespace garlic
bool ECIESX25519AEADRatchetSession::NewOutgoingMessageForRouter (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen)
{
// we are Alice, router's bpk is m_RemoteStaticKey
i2p::crypto::InitNoiseNState (*this, m_RemoteStaticKey);
i2p::crypto::InitNoiseNState (GetNoiseState (), m_RemoteStaticKey);
size_t offset = 0;
m_EphemeralKeys = i2p::transport::transports.GetNextX25519KeysPair ();
memcpy (out + offset, m_EphemeralKeys->GetPublicKey (), 32);
@@ -656,7 +658,7 @@ namespace garlic
}
buf += 32; len -= 32;
// KDF for Reply Key Section
i2p::util::SaveStateHelper<i2p::crypto::NoiseSymmetricState> s(*this); // restore noise state on exit
i2p::util::SaveStateHelper<i2p::crypto::NoiseSymmetricState> s(GetNoiseState ()); // restore noise state on exit
MixHash (tag, 8); // h = SHA256(h || tag)
MixHash (bepk, 32); // h = SHA256(h || bepk)
uint8_t sharedSecret[32];
@@ -813,39 +815,12 @@ namespace garlic
case eSessionStateNew:
return HandleNewIncomingSession (buf, len);
case eSessionStateNewSessionSent:
receiveTagset->Expire (); // NSR tagset
return HandleNewOutgoingSessionReply (buf, len);
default:
return false;
}
return true;
}
bool ECIESX25519AEADRatchetSession::HandleNextMessageForRouter (const uint8_t * buf, size_t len)
{
if (!GetOwner ()) return false;
// we are Bob
i2p::crypto::InitNoiseNState (*this, GetOwner ()->GetEncryptionPublicKey (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)); // bpk
MixHash (buf, 32);
uint8_t sharedSecret[32];
if (!GetOwner ()->Decrypt (buf, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) // x25519(bsk, aepk)
{
LogPrint (eLogWarning, "Garlic: Incorrect N ephemeral public key");
return false;
}
MixKey (sharedSecret);
buf += 32; len -= 32;
uint8_t nonce[12];
CreateNonce (0, nonce);
std::vector<uint8_t> payload (len - 16);
if (!i2p::crypto::AEADChaCha20Poly1305 (buf, len - 16, m_H, 32, m_CK + 32, nonce, payload.data (), len - 16, false)) // decrypt
{
LogPrint (eLogWarning, "Garlic: Payload for router AEAD verification failed");
return false;
}
HandlePayload (payload.data (), len - 16, nullptr, 0);
return true;
}
std::shared_ptr<I2NPMessage> ECIESX25519AEADRatchetSession::WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg)
{
@@ -1124,6 +1099,39 @@ namespace garlic
ts*1000 > m_LastSentTimestamp + ECIESX25519_SEND_EXPIRATION_TIMEOUT*1000; // milliseconds
}
RouterIncomingRatchetSession::RouterIncomingRatchetSession (const i2p::crypto::NoiseSymmetricState& initState):
ECIESX25519AEADRatchetSession (&i2p::context, false)
{
SetLeaseSetUpdateStatus (eLeaseSetDoNotSend);
SetNoiseState (initState);
}
bool RouterIncomingRatchetSession::HandleNextMessage (const uint8_t * buf, size_t len)
{
if (!GetOwner ()) return false;
i2p::crypto::NoiseSymmetricState state (GetNoiseState ());
// we are Bob
state.MixHash (buf, 32);
uint8_t sharedSecret[32];
if (!GetOwner ()->Decrypt (buf, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) // x25519(bsk, aepk)
{
LogPrint (eLogWarning, "Garlic: Incorrect N ephemeral public key");
return false;
}
state.MixKey (sharedSecret);
buf += 32; len -= 32;
uint8_t nonce[12];
CreateNonce (0, nonce);
std::vector<uint8_t> payload (len - 16);
if (!i2p::crypto::AEADChaCha20Poly1305 (buf, len - 16, state.m_H, 32, state.m_CK + 32, nonce, payload.data (), len - 16, false)) // decrypt
{
LogPrint (eLogWarning, "Garlic: Payload for router AEAD verification failed");
return false;
}
HandlePayload (payload.data (), len - 16, nullptr, 0);
return true;
}
std::shared_ptr<I2NPMessage> WrapECIESX25519AEADRatchetMessage (std::shared_ptr<const I2NPMessage> msg, const uint8_t * key, uint64_t tag)
{
auto m = NewI2NPMessage ();

View File

@@ -88,7 +88,8 @@ namespace garlic
bool IsNS () const { return m_IsNS; };
std::shared_ptr<ECIESX25519AEADRatchetSession> GetSession () { return m_Session; };
void SetTrimBehind (int index) { if (index > m_TrimBehindIndex) m_TrimBehindIndex = index; };
int GetTrimBehind () const { return m_TrimBehindIndex; };
void Expire ();
bool IsExpired (uint64_t ts) const;
@@ -160,11 +161,10 @@ namespace garlic
public:
ECIESX25519AEADRatchetSession (GarlicDestination * owner, bool attachLeaseSet);
ECIESX25519AEADRatchetSession (GarlicDestination * owner, bool attachLeaseSetNS);
~ECIESX25519AEADRatchetSession ();
bool HandleNextMessage (uint8_t * buf, size_t len, std::shared_ptr<ReceiveRatchetTagSet> receiveTagset, int index = 0);
bool HandleNextMessageForRouter (const uint8_t * buf, size_t len);
std::shared_ptr<I2NPMessage> WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg);
std::shared_ptr<I2NPMessage> WrapOneTimeMessage (std::shared_ptr<const I2NPMessage> msg, bool isForRouter = false);
@@ -186,16 +186,21 @@ namespace garlic
bool IsTerminated () const { return m_IsTerminated; }
uint64_t GetLastActivityTimestamp () const { return m_LastActivityTimestamp; };
protected:
i2p::crypto::NoiseSymmetricState& GetNoiseState () { return *this; };
void SetNoiseState (const i2p::crypto::NoiseSymmetricState& state) { GetNoiseState () = state; };
void CreateNonce (uint64_t seqn, uint8_t * nonce);
void HandlePayload (const uint8_t * buf, size_t len, const std::shared_ptr<ReceiveRatchetTagSet>& receiveTagset, int index);
private:
void CreateNonce (uint64_t seqn, uint8_t * nonce);
bool GenerateEphemeralKeysAndEncode (uint8_t * buf); // buf is 32 bytes
void InitNewSessionTagset (std::shared_ptr<RatchetTagSet> tagsetNsr) const;
bool HandleNewIncomingSession (const uint8_t * buf, size_t len);
bool HandleNewOutgoingSessionReply (uint8_t * buf, size_t len);
bool HandleExistingSessionMessage (uint8_t * buf, size_t len, std::shared_ptr<ReceiveRatchetTagSet> receiveTagset, int index);
void HandlePayload (const uint8_t * buf, size_t len, const std::shared_ptr<ReceiveRatchetTagSet>& receiveTagset, int index);
void HandleNextKey (const uint8_t * buf, size_t len, const std::shared_ptr<ReceiveRatchetTagSet>& receiveTagset);
bool NewOutgoingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen, bool isStatic = true);
@@ -237,6 +242,15 @@ namespace garlic
}
};
// single session for all incoming messages
class RouterIncomingRatchetSession: public ECIESX25519AEADRatchetSession
{
public:
RouterIncomingRatchetSession (const i2p::crypto::NoiseSymmetricState& initState);
bool HandleNextMessage (const uint8_t * buf, size_t len);
};
std::shared_ptr<I2NPMessage> WrapECIESX25519AEADRatchetMessage (std::shared_ptr<const I2NPMessage> msg, const uint8_t * key, uint64_t tag);
}
}

View File

@@ -553,9 +553,10 @@ namespace garlic
if (!session->HandleNextMessage (buf, length, nullptr, 0))
{
// try to gererate more tags for last tagset
if (m_LastTagset && m_LastTagset->GetNextIndex () < 2*ECIESX25519_TAGSET_MAX_NUM_TAGS)
if (m_LastTagset && (m_LastTagset->GetNextIndex () - m_LastTagset->GetTrimBehind () < 3*ECIESX25519_MAX_NUM_GENERATED_TAGS))
{
auto maxTags = std::max (m_NumRatchetInboundTags, ECIESX25519_MAX_NUM_GENERATED_TAGS);
LogPrint (eLogWarning, "Garlic: trying to generate more ECIES-X25519-AEAD-Ratchet tags");
for (int i = 0; i < maxTags; i++)
{
auto nextTag = AddECIESx25519SessionNextTag (m_LastTagset);
@@ -879,8 +880,12 @@ namespace garlic
{
auto session = it->second.tagset->GetSession ();
if (!session || session->IsTerminated())
it->second.tagset->Expire ();
++it;
{
it = m_ECIESx25519Tags.erase (it);
numExpiredTags++;
}
else
++it;
}
}
if (numExpiredTags > 0)

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2020, The PurpleI2P Project
* Copyright (c) 2013-2021, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@@ -9,12 +9,15 @@
#include <algorithm>
#include <utility>
#include <stdio.h>
#include "util.h"
#include "HTTP.h"
#include <ctime>
#include "util.h"
#include "Base.h"
#include "HTTP.h"
namespace i2p {
namespace http {
namespace i2p
{
namespace http
{
const std::vector<std::string> HTTP_METHODS = {
"GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "CONNECT", // HTTP basic methods
"COPY", "LOCK", "MKCOL", "MOVE", "PROPFIND", "PROPPATCH", "UNLOCK", "SEARCH" // WebDAV methods, for SEARCH see rfc5323
@@ -471,12 +474,15 @@ namespace http {
return ptr;
}
std::string UrlDecode(const std::string& data, bool allow_null) {
std::string UrlDecode(const std::string& data, bool allow_null)
{
std::string decoded(data);
size_t pos = 0;
while ((pos = decoded.find('%', pos)) != std::string::npos) {
while ((pos = decoded.find('%', pos)) != std::string::npos)
{
char c = strtol(decoded.substr(pos + 1, 2).c_str(), NULL, 16);
if (c == '\0' && !allow_null) {
if (c == '\0' && !allow_null)
{
pos += 3;
continue;
}
@@ -486,9 +492,11 @@ namespace http {
return decoded;
}
bool MergeChunkedResponse (std::istream& in, std::ostream& out) {
bool MergeChunkedResponse (std::istream& in, std::ostream& out)
{
std::string hexLen;
while (!in.eof ()) {
while (!in.eof ())
{
std::getline (in, hexLen);
errno = 0;
long int len = strtoul(hexLen.c_str(), (char **) NULL, 16);
@@ -506,5 +514,12 @@ namespace http {
}
return true;
}
std::string CreateBasicAuthorizationString (const std::string& user, const std::string& pass)
{
if (user.empty () && pass.empty ()) return "";
return "Basic " + i2p::data::ToBase64Standard (user + ":" + pass);
}
} // http
} // i2p

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2020, The PurpleI2P Project
* Copyright (c) 2013-2021, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@@ -166,6 +166,9 @@ namespace http
* @return true on success, false otherwise
*/
bool MergeChunkedResponse (std::istream& in, std::ostream& out);
std::string CreateBasicAuthorizationString (const std::string& user, const std::string& pass);
} // http
} // i2p

View File

@@ -371,10 +371,7 @@ namespace i2p
if (!memcmp (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)i2p::context.GetRouterInfo ().GetIdentHash (), 16))
{
LogPrint (eLogDebug, "I2NP: Build request record ", i, " is ours");
BN_CTX * ctx = BN_CTX_new ();
bool success = i2p::context.DecryptTunnelBuildRecord (record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText, ctx);
BN_CTX_free (ctx);
if(!success) return false;
if (!i2p::context.DecryptTunnelBuildRecord (record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText)) return false;
uint8_t retCode = 0;
bool isECIES = i2p::context.IsECIES ();
// replace record to reply

View File

@@ -72,6 +72,12 @@ namespace data
}
size += 256; // encryption key
size += m_Identity->GetSigningPublicKeyLen (); // unused signing key
if (size + 1 > m_BufferLen)
{
LogPrint (eLogError, "LeaseSet: ", size, " exceeds buffer size ", m_BufferLen);
m_IsValid = false;
return;
}
uint8_t num = m_Buffer[size];
size++; // num
LogPrint (eLogDebug, "LeaseSet: read num=", (int)num);
@@ -81,9 +87,14 @@ namespace data
m_IsValid = false;
return;
}
if (size + num*LEASE_SIZE > m_BufferLen)
{
LogPrint (eLogError, "LeaseSet: ", size, " exceeds buffer size ", m_BufferLen);
m_IsValid = false;
return;
}
UpdateLeasesBegin ();
// process leases
m_ExpirationTime = 0;
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
@@ -106,14 +117,22 @@ namespace data
return;
}
m_ExpirationTime += LEASE_ENDDATE_THRESHOLD;
UpdateLeasesEnd ();
// verify
if (verifySignature && !m_Identity->Verify (m_Buffer, leases - m_Buffer, leases))
{
LogPrint (eLogWarning, "LeaseSet: verification failed");
m_IsValid = false;
if (verifySignature)
{
auto signedSize = leases - m_Buffer;
if (signedSize + m_Identity->GetSignatureLen () > m_BufferLen)
{
LogPrint (eLogError, "LeaseSet: Signature exceeds buffer size ", m_BufferLen);
m_IsValid = false;
}
else if (!m_Identity->Verify (m_Buffer, signedSize, leases))
{
LogPrint (eLogWarning, "LeaseSet: verification failed");
m_IsValid = false;
}
}
}
@@ -778,7 +797,7 @@ namespace data
}
LocalLeaseSet2::LocalLeaseSet2 (uint8_t storeType, const i2p::data::PrivateKeys& keys,
const KeySections& encryptionKeys, std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels,
const KeySections& encryptionKeys, const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels,
bool isPublic, bool isPublishedEncrypted):
LocalLeaseSet (keys.GetPublic (), nullptr, 0)
{
@@ -843,9 +862,18 @@ namespace data
offset += 4; // end date
}
// update expiration
SetExpirationTime (expirationTime*1000LL);
auto expires = expirationTime - timestamp;
htobe16buf (expiresBuf, expires > 0 ? expires : 0);
if (expirationTime)
{
SetExpirationTime (expirationTime*1000LL);
auto expires = (int)expirationTime - timestamp;
htobe16buf (expiresBuf, expires > 0 ? expires : 0);
}
else
{
// no tunnels or withdraw
SetExpirationTime (timestamp*1000LL);
memset (expiresBuf, 0, 2); // expires immeditely
}
// sign
keys.Sign (m_Buffer, offset, m_Buffer + offset); // LS + leading store type
}

View File

@@ -251,7 +251,7 @@ namespace data
LocalLeaseSet2 (uint8_t storeType, const i2p::data::PrivateKeys& keys,
const KeySections& encryptionKeys,
std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels,
const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels,
bool isPublic, bool isPublishedEncrypted = false);
LocalLeaseSet2 (uint8_t storeType, std::shared_ptr<const IdentityEx> identity, const uint8_t * buf, size_t len); // from I2CP

View File

@@ -148,7 +148,7 @@ namespace log {
LogLevel level; /**< message level */
std::thread::id tid; /**< id of thread that generated message */
LogMsg (LogLevel lvl, std::time_t ts, const std::string & txt): timestamp(ts), text(txt), level(lvl) {};
LogMsg (LogLevel lvl, std::time_t ts, std::string&& txt): timestamp(ts), text(std::move(txt)), level(lvl) {}
};
Log & Logger();
@@ -189,7 +189,7 @@ void LogPrint (LogLevel level, TArgs&&... args) noexcept
return;
// fold message to single string
std::stringstream ss("");
std::stringstream ss;
#if (__cplusplus >= 201703L) // C++ 17 or higher
(LogPrint (ss, std::forward<TArgs>(args)), ...);
@@ -197,7 +197,7 @@ void LogPrint (LogLevel level, TArgs&&... args) noexcept
LogPrint (ss, std::forward<TArgs>(args)...);
#endif
auto msg = std::make_shared<i2p::log::LogMsg>(level, std::time(nullptr), ss.str());
auto msg = std::make_shared<i2p::log::LogMsg>(level, std::time(nullptr), std::move(ss).str());
msg->tid = std::this_thread::get_id();
log.Append(msg);
}

View File

@@ -333,17 +333,16 @@ namespace transport
if (in_RemoteRouter) // Alice
{
m_Establisher->m_RemoteIdentHash = GetRemoteIdentity ()->GetIdentHash ();
if (!addr)
addr = in_RemoteRouter->GetNTCP2Address (true); // we need a published address
if (addr)
{
memcpy (m_Establisher->m_RemoteStaticKey, addr->ntcp2->staticKey, 32);
memcpy (m_Establisher->m_IV, addr->ntcp2->iv, 16);
m_RemoteEndpoint = boost::asio::ip::tcp::endpoint (addr->host, addr->port);
}
else
LogPrint (eLogWarning, "NTCP2: Missing NTCP2 parameters");
LogPrint (eLogWarning, "NTCP2: Missing NTCP2 address");
}
m_NextRouterInfoResendTime = i2p::util::GetSecondsSinceEpoch () + NTCP2_ROUTERINFO_RESEND_INTERVAL +
m_NextRouterInfoResendTime = i2p::util::GetSecondsSinceEpoch () + NTCP2_ROUTERINFO_RESEND_INTERVAL +
rand ()%NTCP2_ROUTERINFO_RESEND_INTERVAL_THRESHOLD;
}
@@ -657,19 +656,13 @@ namespace transport
SendTerminationAndTerminate (eNTCP2Message3Error);
return;
}
auto addr = ri.GetNTCP2Address (false); // any NTCP2 address
auto addr = ri.GetNTCP2AddressWithStaticKey (m_Establisher->m_RemoteStaticKey);
if (!addr)
{
LogPrint (eLogError, "NTCP2: No NTCP2 address found in SessionConfirmed");
LogPrint (eLogError, "NTCP2: No NTCP2 address wth static key found in SessionConfirmed");
Terminate ();
return;
}
if (memcmp (addr->ntcp2->staticKey, m_Establisher->m_RemoteStaticKey, 32))
{
LogPrint (eLogError, "NTCP2: Static key mismatch in SessionConfirmed");
SendTerminationAndTerminate (eNTCP2IncorrectSParameter);
return;
}
i2p::data::netdb.PostI2NPMsg (CreateI2NPMessage (eI2NPDummyMsg, buf.data () + 3, size)); // TODO: should insert ri and not parse it twice
// TODO: process options
@@ -724,7 +717,7 @@ namespace transport
m_Establisher->m_SessionRequestBuffer = new uint8_t[287]; // 287 bytes max for now
boost::asio::async_read (m_Socket, boost::asio::buffer(m_Establisher->m_SessionRequestBuffer, 64), boost::asio::transfer_all (),
std::bind(&NTCP2Session::HandleSessionRequestReceived, shared_from_this (),
std::placeholders::_1, std::placeholders::_2));
std::placeholders::_1, std::placeholders::_2));
}
void NTCP2Session::ReceiveLength ()
@@ -733,7 +726,7 @@ namespace transport
#ifdef __linux__
const int one = 1;
setsockopt(m_Socket.native_handle(), IPPROTO_TCP, TCP_QUICKACK, &one, sizeof(one));
#endif
#endif
boost::asio::async_read (m_Socket, boost::asio::buffer(&m_NextReceivedLen, 2), boost::asio::transfer_all (),
std::bind(&NTCP2Session::HandleReceivedLength, shared_from_this (), std::placeholders::_1, std::placeholders::_2));
}
@@ -787,8 +780,8 @@ namespace transport
if (IsTerminated ()) return;
#ifdef __linux__
const int one = 1;
setsockopt(m_Socket.native_handle(), IPPROTO_TCP, TCP_QUICKACK, &one, sizeof(one));
#endif
setsockopt(m_Socket.native_handle(), IPPROTO_TCP, TCP_QUICKACK, &one, sizeof(one));
#endif
boost::asio::async_read (m_Socket, boost::asio::buffer(m_NextReceivedBuffer, m_NextReceivedLen), boost::asio::transfer_all (),
std::bind(&NTCP2Session::HandleReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2));
}
@@ -1016,11 +1009,11 @@ namespace transport
LogPrint (eLogDebug, "NTCP2: Next frame sent ", bytes_transferred);
if (m_LastActivityTimestamp > m_NextRouterInfoResendTime)
{
m_NextRouterInfoResendTime += NTCP2_ROUTERINFO_RESEND_INTERVAL +
m_NextRouterInfoResendTime += NTCP2_ROUTERINFO_RESEND_INTERVAL +
rand ()%NTCP2_ROUTERINFO_RESEND_INTERVAL_THRESHOLD;
SendRouterInfo ();
}
else
SendRouterInfo ();
}
else
SendQueue ();
}
}
@@ -1120,7 +1113,7 @@ namespace transport
SendQueue ();
else if (m_SendQueue.size () > NTCP2_MAX_OUTGOING_QUEUE_SIZE)
{
LogPrint (eLogWarning, "NTCP2: outgoing messages queue size to ",
LogPrint (eLogWarning, "NTCP2: outgoing messages queue size to ",
GetIdentHashBase64(), " exceeds ", NTCP2_MAX_OUTGOING_QUEUE_SIZE);
Terminate ();
}
@@ -1165,52 +1158,58 @@ namespace transport
}
}
else
{
LogPrint(eLogInfo, "NTCP2: Proxy is not used");
auto& addresses = context.GetRouterInfo ().GetAddresses ();
for (const auto& address: addresses)
// start acceptors
auto& addresses = context.GetRouterInfo ().GetAddresses ();
for (const auto& address: addresses)
{
if (!address) continue;
if (address->IsPublishedNTCP2 () && address->port)
{
if (!address) continue;
if (address->IsPublishedNTCP2 ())
if (address->host.is_v4())
{
if (address->host.is_v4())
try
{
try
{
m_NTCP2Acceptor.reset (new boost::asio::ip::tcp::acceptor (GetService (), boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), address->port)));
}
catch ( std::exception & ex )
{
LogPrint(eLogError, "NTCP2: Failed to bind to v4 port ", address->port, ex.what());
ThrowFatal ("Unable to start IPv4 NTCP2 transport at port ", address->port, ": ", ex.what ());
continue;
}
LogPrint (eLogInfo, "NTCP2: Start listening v4 TCP port ", address->port);
auto conn = std::make_shared<NTCP2Session>(*this);
m_NTCP2Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCP2Server::HandleAccept, this, conn, std::placeholders::_1));
auto ep = m_Address4 ? boost::asio::ip::tcp::endpoint (m_Address4->address(), address->port):
boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), address->port);
m_NTCP2Acceptor.reset (new boost::asio::ip::tcp::acceptor (GetService (), ep));
}
else if (address->host.is_v6() && (context.SupportsV6 () || context.SupportsMesh ()))
catch ( std::exception & ex )
{
m_NTCP2V6Acceptor.reset (new boost::asio::ip::tcp::acceptor (GetService ()));
try
{
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 ();
LogPrint(eLogError, "NTCP2: Failed to bind to v4 port ", address->port, ex.what());
ThrowFatal ("Unable to start IPv4 NTCP2 transport at port ", address->port, ": ", ex.what ());
continue;
}
LogPrint (eLogInfo, "NTCP2: Start listening v6 TCP port ", address->port);
auto conn = std::make_shared<NTCP2Session> (*this);
m_NTCP2V6Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCP2Server::HandleAcceptV6, this, conn, std::placeholders::_1));
}
catch ( std::exception & ex )
{
LogPrint(eLogError, "NTCP2: failed to bind to v6 port ", address->port, ": ", ex.what());
ThrowFatal ("Unable to start IPv6 NTCP2 transport at port ", address->port, ": ", ex.what ());
continue;
}
LogPrint (eLogInfo, "NTCP2: Start listening v4 TCP port ", address->port);
auto conn = std::make_shared<NTCP2Session>(*this);
m_NTCP2Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCP2Server::HandleAccept, this, conn, std::placeholders::_1));
}
else if (address->host.is_v6() && (context.SupportsV6 () || context.SupportsMesh ()))
{
m_NTCP2V6Acceptor.reset (new boost::asio::ip::tcp::acceptor (GetService ()));
try
{
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));
auto ep = boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v6(), address->port);
if (m_Address6 && !context.SupportsMesh ())
ep = boost::asio::ip::tcp::endpoint (m_Address6->address(), address->port);
else if (m_YggdrasilAddress && !context.SupportsV6 ())
ep = boost::asio::ip::tcp::endpoint (m_YggdrasilAddress->address(), address->port);
m_NTCP2V6Acceptor->bind (ep);
m_NTCP2V6Acceptor->listen ();
LogPrint (eLogInfo, "NTCP2: Start listening v6 TCP port ", address->port);
auto conn = std::make_shared<NTCP2Session> (*this);
m_NTCP2V6Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCP2Server::HandleAcceptV6, this, conn, std::placeholders::_1));
}
catch ( std::exception & ex )
{
LogPrint(eLogError, "NTCP2: failed to bind to v6 port ", address->port, ": ", ex.what());
ThrowFatal ("Unable to start IPv6 NTCP2 transport at port ", address->port, ": ", ex.what ());
continue;
}
}
}
@@ -1274,10 +1273,15 @@ namespace transport
return nullptr;
}
void NTCP2Server::Connect(const boost::asio::ip::address & address, uint16_t port, std::shared_ptr<NTCP2Session> conn)
void NTCP2Server::Connect(std::shared_ptr<NTCP2Session> conn)
{
LogPrint (eLogDebug, "NTCP2: Connecting to ", address ,":", port);
GetService ().post([this, address, port, conn]()
if (!conn || conn->GetRemoteEndpoint ().address ().is_unspecified ())
{
LogPrint (eLogError, "NTCP2: Can't connect to unspecified address");
return;
}
LogPrint (eLogDebug, "NTCP2: Connecting to ", conn->GetRemoteEndpoint ());
GetService ().post([this, conn]()
{
if (this->AddNTCP2Session (conn))
{
@@ -1290,12 +1294,32 @@ namespace transport
if (ecode != boost::asio::error::operation_aborted)
{
LogPrint (eLogInfo, "NTCP2: Not connected in ", timeout, " seconds");
if (conn->GetRemoteIdentity ())
i2p::data::netdb.SetUnreachable (conn->GetRemoteIdentity ()->GetIdentHash (), true);
conn->Terminate ();
}
});
conn->GetSocket ().async_connect (boost::asio::ip::tcp::endpoint (address, port), std::bind (&NTCP2Server::HandleConnect, this, std::placeholders::_1, conn, timer));
// bind to local address
std::shared_ptr<boost::asio::ip::tcp::endpoint> localAddress;
if (conn->GetRemoteEndpoint ().address ().is_v6 ())
{
if (i2p::util::net::IsYggdrasilAddress (conn->GetRemoteEndpoint ().address ()))
localAddress = m_YggdrasilAddress;
else
localAddress = m_Address6;
conn->GetSocket ().open (boost::asio::ip::tcp::v6 ());
}
else
{
localAddress = m_Address4;
conn->GetSocket ().open (boost::asio::ip::tcp::v4 ());
}
if (localAddress)
{
boost::system::error_code ec;
conn->GetSocket ().bind (*localAddress, ec);
if (ec)
LogPrint (eLogError, "NTCP2: can't bind to ", localAddress->address ().to_string (), ": ", ec.message ());
}
conn->GetSocket ().async_connect (conn->GetRemoteEndpoint (), std::bind (&NTCP2Server::HandleConnect, this, std::placeholders::_1, conn, timer));
}
else
conn->Terminate ();
@@ -1312,7 +1336,7 @@ namespace transport
}
else
{
LogPrint (eLogDebug, "NTCP2: Connected to ", conn->GetSocket ().remote_endpoint ());
LogPrint (eLogDebug, "NTCP2: Connected to ", conn->GetRemoteEndpoint ());
conn->ClientLogin ();
}
}
@@ -1328,6 +1352,7 @@ namespace transport
LogPrint (eLogDebug, "NTCP2: Connected from ", ep);
if (conn)
{
conn->SetRemoteEndpoint (ep);
conn->ServerLogin ();
m_PendingIncomingSessions.push_back (conn);
conn = nullptr;
@@ -1361,6 +1386,7 @@ namespace transport
LogPrint (eLogDebug, "NTCP2: Connected from ", ep);
if (conn)
{
conn->SetRemoteEndpoint (ep);
conn->ServerLogin ();
m_PendingIncomingSessions.push_back (conn);
}
@@ -1415,13 +1441,18 @@ namespace transport
}
}
void NTCP2Server::ConnectWithProxy (const std::string& host, uint16_t port, RemoteAddressType addrtype, std::shared_ptr<NTCP2Session> conn)
void NTCP2Server::ConnectWithProxy (std::shared_ptr<NTCP2Session> conn)
{
if(!m_ProxyEndpoint) return;
GetService().post([this, host, port, addrtype, conn]() {
if (!conn || conn->GetRemoteEndpoint ().address ().is_unspecified ())
{
LogPrint (eLogError, "NTCP2: Can't connect to unspecified address");
return;
}
GetService().post([this, conn]()
{
if (this->AddNTCP2Session (conn))
{
auto timer = std::make_shared<boost::asio::deadline_timer>(GetService());
auto timeout = NTCP2_CONNECT_TIMEOUT * 5;
conn->SetTerminationTimeout(timeout * 2);
@@ -1431,23 +1462,25 @@ namespace transport
if (ecode != boost::asio::error::operation_aborted)
{
LogPrint (eLogInfo, "NTCP2: Not connected in ", timeout, " seconds");
i2p::data::netdb.SetUnreachable (conn->GetRemoteIdentity ()->GetIdentHash (), true);
conn->Terminate ();
}
});
conn->GetSocket ().async_connect (*m_ProxyEndpoint, std::bind (&NTCP2Server::HandleProxyConnect, this, std::placeholders::_1, conn, timer, host, port, addrtype));
conn->GetSocket ().async_connect (*m_ProxyEndpoint, std::bind (&NTCP2Server::HandleProxyConnect, this, std::placeholders::_1, conn, timer));
}
});
}
void NTCP2Server::UseProxy(ProxyType proxytype, const std::string & addr, uint16_t port)
void NTCP2Server::UseProxy(ProxyType proxytype, const std::string& addr, uint16_t port,
const std::string& user, const std::string& pass)
{
m_ProxyType = proxytype;
m_ProxyAddress = addr;
m_ProxyPort = port;
if (m_ProxyType == eHTTPProxy )
m_ProxyAuthorization = i2p::http::CreateBasicAuthorizationString (user, pass);
}
void NTCP2Server::HandleProxyConnect(const boost::system::error_code& ecode, std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer, const std::string & host, uint16_t port, RemoteAddressType addrtype)
void NTCP2Server::HandleProxyConnect(const boost::system::error_code& ecode, std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer)
{
if (ecode)
{
@@ -1473,7 +1506,7 @@ namespace transport
});
auto readbuff = std::make_shared<std::vector<uint8_t> >(2);
boost::asio::async_read(conn->GetSocket(), boost::asio::buffer(readbuff->data (), 2),
[this, readbuff, timer, conn, host, port, addrtype](const boost::system::error_code & ec, std::size_t transferred)
[this, readbuff, timer, conn](const boost::system::error_code & ec, std::size_t transferred)
{
if(ec)
{
@@ -1486,7 +1519,7 @@ namespace transport
{
if((*readbuff)[1] == 0x00)
{
AfterSocksHandshake(conn, timer, host, port, addrtype);
AfterSocksHandshake(conn, timer);
return;
}
else if ((*readbuff)[1] == 0xff)
@@ -1506,13 +1539,16 @@ namespace transport
}
case eHTTPProxy:
{
auto& ep = conn->GetRemoteEndpoint ();
i2p::http::HTTPReq req;
req.method = "CONNECT";
req.version ="HTTP/1.1";
if(addrtype == eIP6Address)
req.uri = "[" + host + "]:" + std::to_string(port);
if(ep.address ().is_v6 ())
req.uri = "[" + ep.address ().to_string() + "]:" + std::to_string(ep.port ());
else
req.uri = host + ":" + std::to_string(port);
req.uri = ep.address ().to_string() + ":" + std::to_string(ep.port ());
if (!m_ProxyAuthorization.empty ())
req.AddHeader("Proxy-Authorization", m_ProxyAuthorization);
boost::asio::streambuf writebuff;
std::ostream out(&writebuff);
@@ -1566,7 +1602,7 @@ namespace transport
}
}
void NTCP2Server::AfterSocksHandshake(std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer, const std::string & host, uint16_t port, RemoteAddressType addrtype)
void NTCP2Server::AfterSocksHandshake(std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer)
{
// build request
size_t sz = 6; // header + port
@@ -1576,27 +1612,28 @@ namespace transport
(*buff)[1] = 0x01;
(*buff)[2] = 0x00;
if(addrtype == eIP4Address)
auto& ep = conn->GetRemoteEndpoint ();
if(ep.address ().is_v4 ())
{
(*buff)[3] = 0x01;
auto addrbytes = boost::asio::ip::address::from_string(host).to_v4().to_bytes();
auto addrbytes = ep.address ().to_v4().to_bytes();
sz += 4;
memcpy(buff->data () + 4, addrbytes.data(), 4);
}
else if (addrtype == eIP6Address)
else if (ep.address ().is_v6 ())
{
(*buff)[3] = 0x04;
auto addrbytes = boost::asio::ip::address::from_string(host).to_v6().to_bytes();
auto addrbytes = ep.address ().to_v6().to_bytes();
sz += 16;
memcpy(buff->data () + 4, addrbytes.data(), 16);
}
else if (addrtype == eHostname)
else
{
// We mustn't really fall here because all connections are made to IP addresses
LogPrint(eLogError, "NTCP2: Tried to connect to domain name via socks proxy");
LogPrint(eLogError, "NTCP2: Tried to connect to unexpected address via proxy");
return;
}
htobe16buf(buff->data () + sz - 2, port);
htobe16buf(buff->data () + sz - 2, ep.port ());
boost::asio::async_write(conn->GetSocket(), boost::asio::buffer(buff->data (), sz), boost::asio::transfer_all(),
[buff](const boost::system::error_code & ec, std::size_t written)
{
@@ -1623,11 +1660,23 @@ namespace transport
return;
}
}
if(!e)
i2p::data::netdb.SetUnreachable (conn->GetRemoteIdentity ()->GetIdentHash (), true);
timer->cancel();
conn->Terminate();
});
}
void NTCP2Server::SetLocalAddress (const boost::asio::ip::address& localAddress)
{
auto addr = std::make_shared<boost::asio::ip::tcp::endpoint>(boost::asio::ip::tcp::endpoint(localAddress, 0));
if (localAddress.is_v6 ())
{
if (i2p::util::net::IsYggdrasilAddress (localAddress))
m_YggdrasilAddress = addr;
else
m_Address6 = addr;
}
else
m_Address4 = addr;
}
}
}

View File

@@ -134,6 +134,8 @@ namespace transport
void Close () { m_Socket.close (); }; // for accept
boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; };
const boost::asio::ip::tcp::endpoint& GetRemoteEndpoint () { return m_RemoteEndpoint; };
void SetRemoteEndpoint (const boost::asio::ip::tcp::endpoint& ep) { m_RemoteEndpoint = ep; };
bool IsEstablished () const { return m_IsEstablished; };
bool IsTerminated () const { return m_IsTerminated; };
@@ -189,6 +191,7 @@ namespace transport
NTCP2Server& m_Server;
boost::asio::ip::tcp::socket m_Socket;
boost::asio::ip::tcp::endpoint m_RemoteEndpoint;
bool m_IsEstablished, m_IsTerminated;
std::unique_ptr<NTCP2Establisher> m_Establisher;
@@ -221,13 +224,6 @@ namespace transport
{
public:
enum RemoteAddressType
{
eIP4Address,
eIP6Address,
eHostname
};
enum ProxyType
{
eNoProxy,
@@ -246,23 +242,23 @@ namespace transport
void RemoveNTCP2Session (std::shared_ptr<NTCP2Session> session);
std::shared_ptr<NTCP2Session> FindNTCP2Session (const i2p::data::IdentHash& ident);
void ConnectWithProxy (const std::string& addr, uint16_t port, RemoteAddressType addrtype, std::shared_ptr<NTCP2Session> conn);
void Connect(const boost::asio::ip::address & address, uint16_t port, std::shared_ptr<NTCP2Session> conn);
void AfterSocksHandshake(std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer, const std::string & host, uint16_t port, RemoteAddressType addrtype);
void ConnectWithProxy (std::shared_ptr<NTCP2Session> conn);
void Connect(std::shared_ptr<NTCP2Session> conn);
bool UsingProxy() const { return m_ProxyType != eNoProxy; };
void UseProxy(ProxyType proxy, const std::string & address, uint16_t port);
void UseProxy(ProxyType proxy, const std::string& address, uint16_t port, const std::string& user, const std::string& pass);
void SetLocalAddress (const boost::asio::ip::address& localAddress);
private:
void HandleAccept (std::shared_ptr<NTCP2Session> conn, const boost::system::error_code& error);
void HandleAcceptV6 (std::shared_ptr<NTCP2Session> conn, const boost::system::error_code& error);
void HandleConnect (const boost::system::error_code& ecode, std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer);
void HandleProxyConnect(const boost::system::error_code& ecode, std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer, const std::string & host, uint16_t port, RemoteAddressType adddrtype);
void HandleProxyConnect(const boost::system::error_code& ecode, std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer);
void AfterSocksHandshake(std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer);
// timer
void ScheduleTermination ();
void HandleTerminationTimer (const boost::system::error_code& ecode);
@@ -275,11 +271,12 @@ namespace transport
std::list<std::shared_ptr<NTCP2Session> > m_PendingIncomingSessions;
ProxyType m_ProxyType;
std::string m_ProxyAddress;
std::string m_ProxyAddress, m_ProxyAuthorization;
uint16_t m_ProxyPort;
boost::asio::ip::tcp::resolver m_Resolver;
std::unique_ptr<boost::asio::ip::tcp::endpoint> m_ProxyEndpoint;
std::shared_ptr<boost::asio::ip::tcp::endpoint> m_Address4, m_Address6, m_YggdrasilAddress;
public:
// for HTTP/I2PControl

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2020, The PurpleI2P Project
* Copyright (c) 2013-2021, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@@ -57,7 +57,7 @@ namespace data
uint16_t threshold; i2p::config::GetOption("reseed.threshold", threshold);
if (m_RouterInfos.size () < threshold) // reseed if # of router less than threshold
Reseed ();
else if (!GetRandomRouter (i2p::context.GetSharedRouterInfo ()))
else if (!GetRandomRouter (i2p::context.GetSharedRouterInfo (), false))
Reseed (); // we don't have a router we can connect to. Trying to reseed
i2p::config::GetOption("persist.profiles", m_PersistProfiles);
@@ -336,7 +336,7 @@ namespace data
if (it == m_LeaseSets.end () || it->second->GetStoreType () != storeType ||
leaseSet->GetPublishedTimestamp () > it->second->GetPublishedTimestamp ())
{
if (leaseSet->IsPublic ())
if (leaseSet->IsPublic () && !leaseSet->IsExpired ())
{
// TODO: implement actual update
LogPrint (eLogInfo, "NetDb: LeaseSet2 updated: ", ident.ToBase32());
@@ -345,7 +345,7 @@ namespace data
}
else
{
LogPrint (eLogWarning, "NetDb: Unpublished LeaseSet2 received: ", ident.ToBase32());
LogPrint (eLogWarning, "NetDb: Unpublished or expired LeaseSet2 received: ", ident.ToBase32());
m_LeaseSets.erase (ident);
}
}
@@ -454,7 +454,7 @@ namespace data
{
auto r = std::make_shared<RouterInfo>(path);
if (r->GetRouterIdentity () && !r->IsUnreachable () &&
(!r->UsesIntroducer () || m_LastLoad < r->GetTimestamp () + NETDB_INTRODUCEE_EXPIRATION_TIMEOUT*1000LL)) // 1 hour
(r->IsReachable () || !r->IsSSU (false) || m_LastLoad < r->GetTimestamp () + NETDB_INTRODUCEE_EXPIRATION_TIMEOUT*1000LL)) // 1 hour
{
r->DeleteBuffer ();
r->ClearProperties (); // properties are not used for regular routers
@@ -584,7 +584,7 @@ namespace data
if (it.second->IsUnreachable () && total - deletedCount < NETDB_MIN_ROUTERS)
it.second->SetUnreachable (false);
// find & mark expired routers
if (it.second->UsesIntroducer ())
if (!it.second->IsReachable () && it.second->IsSSU (false))
{
if (ts > it.second->GetTimestamp () + NETDB_INTRODUCEE_EXPIRATION_TIMEOUT*1000LL)
// RouterInfo expires after 1 hour if uses introducer
@@ -645,7 +645,9 @@ namespace data
auto floodfill = GetClosestFloodfill (destination, dest->GetExcludedPeers ());
if (floodfill)
{
if (direct && !floodfill->IsCompatible (i2p::context.GetRouterInfo ())) direct = false; // check if fllodfill is reachable
if (direct && !floodfill->IsReachableFrom (i2p::context.GetRouterInfo ()) &&
!i2p::transport::transports.IsConnected (floodfill->GetIdentHash ()))
direct = false; // floodfill can't be reached directly
if (direct)
transports.SendMessage (floodfill->GetIdentHash (), dest->CreateRequestMessage (floodfill->GetIdentHash ()));
else
@@ -1090,7 +1092,8 @@ namespace data
LogPrint (eLogInfo, "NetDb: Publishing our RouterInfo to ", i2p::data::GetIdentHashAbbreviation(floodfill->GetIdentHash ()), ". reply token=", replyToken);
m_PublishExcluded.insert (floodfill->GetIdentHash ());
m_PublishReplyToken = replyToken;
if (floodfill->IsCompatible (i2p::context.GetRouterInfo ())) // able to connect?
if (floodfill->IsReachableFrom (i2p::context.GetRouterInfo ()) || // are we able to connect?
i2p::transport::transports.IsConnected (floodfill->GetIdentHash ())) // already connected ?
// send directly
transports.SendMessage (floodfill->GetIdentHash (), CreateDatabaseStoreMsg (i2p::context.GetSharedRouterInfo (), replyToken));
else
@@ -1135,22 +1138,24 @@ namespace data
});
}
std::shared_ptr<const RouterInfo> NetDb::GetRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith) const
std::shared_ptr<const RouterInfo> NetDb::GetRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith, bool reverse) const
{
return GetRandomRouter (
[compatibleWith](std::shared_ptr<const RouterInfo> router)->bool
[compatibleWith, reverse](std::shared_ptr<const RouterInfo> router)->bool
{
return !router->IsHidden () && router != compatibleWith &&
router->IsCompatible (*compatibleWith);
(reverse ? compatibleWith->IsReachableFrom (*router) :
router->IsReachableFrom (*compatibleWith));
});
}
std::shared_ptr<const RouterInfo> NetDb::GetRandomPeerTestRouter (bool v4only) const
std::shared_ptr<const RouterInfo> NetDb::GetRandomPeerTestRouter (bool v4, const std::set<IdentHash>& excluded) const
{
return GetRandomRouter (
[v4only](std::shared_ptr<const RouterInfo> router)->bool
[v4, &excluded](std::shared_ptr<const RouterInfo> router)->bool
{
return !router->IsHidden () && router->IsPeerTesting () && router->IsSSU (v4only);
return !router->IsHidden () && router->IsPeerTesting (v4) &&
!excluded.count (router->GetIdentHash ());
});
}
@@ -1163,22 +1168,24 @@ namespace data
});
}
std::shared_ptr<const RouterInfo> NetDb::GetRandomIntroducer () const
std::shared_ptr<const RouterInfo> NetDb::GetRandomIntroducer (bool v4, const std::set<IdentHash>& excluded) const
{
return GetRandomRouter (
[](std::shared_ptr<const RouterInfo> router)->bool
[v4, &excluded](std::shared_ptr<const RouterInfo> router)->bool
{
return !router->IsHidden () && router->IsIntroducer ();
return router->IsIntroducer (v4) && !excluded.count (router->GetIdentHash ()) &&
!router->IsHidden () && !router->IsFloodfill (); // floodfills don't send relay tag
});
}
std::shared_ptr<const RouterInfo> NetDb::GetHighBandwidthRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith) const
std::shared_ptr<const RouterInfo> NetDb::GetHighBandwidthRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith, bool reverse) const
{
return GetRandomRouter (
[compatibleWith](std::shared_ptr<const RouterInfo> router)->bool
[compatibleWith, reverse](std::shared_ptr<const RouterInfo> router)->bool
{
return !router->IsHidden () && router != compatibleWith &&
router->IsCompatible (*compatibleWith) &&
(reverse ? compatibleWith->IsReachableFrom (*router) :
router->IsReachableFrom (*compatibleWith)) &&
(router->GetCaps () & RouterInfo::eHighBandwidth) &&
router->GetVersion () >= NETDB_MIN_HIGHBANDWIDTH_VERSION;
});

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2020, The PurpleI2P Project
* Copyright (c) 2013-2021, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@@ -83,11 +83,11 @@ namespace data
void HandleDeliveryStatusMsg (std::shared_ptr<const I2NPMessage> msg);
std::shared_ptr<const RouterInfo> GetRandomRouter () const;
std::shared_ptr<const RouterInfo> GetRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith) const;
std::shared_ptr<const RouterInfo> GetHighBandwidthRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith) const;
std::shared_ptr<const RouterInfo> GetRandomPeerTestRouter (bool v4only = true) const;
std::shared_ptr<const RouterInfo> GetRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith, bool reverse) const;
std::shared_ptr<const RouterInfo> GetHighBandwidthRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith, bool reverse) const;
std::shared_ptr<const RouterInfo> GetRandomPeerTestRouter (bool v4, const std::set<IdentHash>& excluded) const;
std::shared_ptr<const RouterInfo> GetRandomSSUV6Router () const; // TODO: change to v6 peer test later
std::shared_ptr<const RouterInfo> GetRandomIntroducer () const;
std::shared_ptr<const RouterInfo> GetRandomIntroducer (bool v4, const std::set<IdentHash>& excluded) const;
std::shared_ptr<const RouterInfo> GetClosestFloodfill (const IdentHash& destination, const std::set<IdentHash>& excluded, bool closeThanUsOnly = false) const;
std::vector<IdentHash> GetClosestFloodfills (const IdentHash& destination, size_t num,
std::set<IdentHash>& excluded, bool closeThanUsOnly = false) const;

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2020, The PurpleI2P Project
* Copyright (c) 2013-2021, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@@ -574,9 +574,11 @@ namespace data
proxyReq.method = "CONNECT";
proxyReq.version = "HTTP/1.1";
proxyReq.uri = url.host + ":" + std::to_string(url.port);
auto auth = i2p::http::CreateBasicAuthorizationString (proxyUrl.user, proxyUrl.pass);
if (!auth.empty ())
proxyReq.AddHeader("Proxy-Authorization", auth);
boost::asio::streambuf writebuf, readbuf;
std::ostream out(&writebuf);
out << proxyReq.to_string();
@@ -676,8 +678,31 @@ namespace data
// direct connection
auto it = boost::asio::ip::tcp::resolver(service).resolve (
boost::asio::ip::tcp::resolver::query (url.host, std::to_string(url.port)), ecode);
if(!ecode)
s.lowest_layer().connect (*it, ecode);
if (!ecode)
{
bool connected = false;
boost::asio::ip::tcp::resolver::iterator end;
while (it != end)
{
boost::asio::ip::tcp::endpoint ep = *it;
if ((ep.address ().is_v4 () && i2p::context.SupportsV4 ()) ||
(ep.address ().is_v6 () && i2p::context.SupportsV6 ()))
{
s.lowest_layer().connect (ep, ecode);
if (!ecode)
{
connected = true;
break;
}
}
it++;
}
if (!connected)
{
LogPrint(eLogError, "Reseed: Failed to connect to ", url.host);
return "";
}
}
}
if (!ecode)
{

View File

@@ -28,7 +28,7 @@ namespace i2p
RouterContext::RouterContext ():
m_LastUpdateTime (0), m_AcceptsTunnels (true), m_IsFloodfill (false),
m_ShareRatio (100), m_Status (eRouterStatusUnknown),
m_ShareRatio (100), m_Status (eRouterStatusUnknown), m_StatusV6 (eRouterStatusUnknown),
m_Error (eRouterErrorNone), m_NetID (I2PD_NET_ID)
{
}
@@ -41,13 +41,15 @@ namespace i2p
if (!Load ())
CreateNewRouter ();
m_Decryptor = m_Keys.CreateDecryptor (nullptr);
m_TunnelDecryptor = m_Keys.CreateDecryptor (nullptr);
UpdateRouterInfo ();
if (IsECIES ())
{
{
auto initState = new i2p::crypto::NoiseSymmetricState ();
i2p::crypto::InitNoiseNState (*initState, GetIdentity ()->GetEncryptionPublicKey ());
m_InitialNoiseState.reset (initState);
}
m_InitialNoiseState.reset (initState);
m_ECIESSession = std::make_shared<i2p::garlic::RouterIncomingRatchetSession>(*initState);
}
}
void RouterContext::CreateNewRouter ()
@@ -72,88 +74,100 @@ namespace i2p
bool ipv6; i2p::config::GetOption("ipv6", ipv6);
bool ssu; i2p::config::GetOption("ssu", ssu);
bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2);
bool ygg; i2p::config::GetOption("meshnets.yggdrasil", ygg);
bool ygg; i2p::config::GetOption("meshnets.yggdrasil", ygg);
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);
uint8_t caps = 0;
if ((ntcp2 || ygg) && !m_NTCP2Keys)
NewNTCP2Keys ();
bool ntcp2Published = false;
if (ntcp2)
{
i2p::config::GetOption("ntcp2.published", ntcp2Published);
if (ntcp2Published)
{
std::string ntcp2proxy; i2p::config::GetOption("ntcp2.proxy", ntcp2proxy);
if (!ntcp2proxy.empty ()) ntcp2Published = false;
}
}
uint8_t caps = 0, addressCaps = 0;
if (ipv4)
{
std::string host = "127.0.0.1";
if (!i2p::config::IsDefault("host"))
i2p::config::GetOption("host", host);
else if (!nat && !ifname.empty())
/* bind to interface, we have no NAT so set external address too */
host = i2p::util::net::GetInterfaceAddress(ifname, false).to_string(); // v4
if(ifname4.size())
host = i2p::util::net::GetInterfaceAddress(ifname4, false).to_string();
else if (!nat)
{
// we have no NAT so set external address from local address
std::string address4; i2p::config::GetOption("address4", address4);
if (!address4.empty ()) host = address4;
}
if (ntcp2)
{
if (ntcp2Published)
routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address_v4::from_string (host), port);
else // add non-published NTCP2 address
{
addressCaps = i2p::data::RouterInfo::AddressCaps::eV4;
routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv);
}
}
if (ssu)
{
{
routerInfo.AddSSUAddress (host.c_str(), port, nullptr);
caps |= i2p::data::RouterInfo::eReachable | i2p::data::RouterInfo::eSSUTesting | i2p::data::RouterInfo::eSSUIntroducer; // R, BC
}
caps |= i2p::data::RouterInfo::eReachable; // R
}
}
if (ipv6)
{
std::string host = "::1";
if (!i2p::config::IsDefault("host") && !ipv4) // override if v6 only
i2p::config::GetOption("host", host);
else if (!ifname.empty())
host = i2p::util::net::GetInterfaceAddress(ifname, true).to_string(); // v6
if(ifname6.size())
host = i2p::util::net::GetInterfaceAddress(ifname6, true).to_string();
if (ssu)
{
routerInfo.AddSSUAddress (host.c_str(), port, nullptr);
caps |= i2p::data::RouterInfo::eReachable; // R
}
}
routerInfo.SetCaps (caps); // caps + L
routerInfo.SetProperty ("netId", std::to_string (m_NetID));
routerInfo.SetProperty ("router.version", I2P_VERSION);
routerInfo.CreateBuffer (m_Keys);
m_RouterInfo.SetRouterIdentity (GetIdentity ());
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 ();
bool published; i2p::config::GetOption("ntcp2.published", published);
if (ipv4 || !published) UpdateNTCP2Address (true); // create not published NTCP2 address
if (published)
else
{
if (ipv4)
PublishNTCP2Address (port, true);
if (ipv6)
std::string address6; i2p::config::GetOption("address6", address6);
if (!address6.empty ()) host = address6;
}
if (ntcp2)
{
if (ntcp2Published)
{
// add NTCP2 ipv6 address
std::string host = "::1";
std::string ntcp2Host;
if (!i2p::config::IsDefault ("ntcp2.addressv6"))
i2p::config::GetOption ("ntcp2.addressv6", host);
m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address_v6::from_string (host), port);
i2p::config::GetOption ("ntcp2.addressv6", ntcp2Host);
else
ntcp2Host = host;
routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address_v6::from_string (ntcp2Host), port);
}
else
{
if (!ipv4) // no other ntcp2 addresses yet
routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv);
addressCaps |= i2p::data::RouterInfo::AddressCaps::eV6;
}
}
// enable added NTCP2 addresses
if (ipv4) m_RouterInfo.EnableV4 ();
if (ipv6) m_RouterInfo.EnableV6 ();
if (ssu)
{
routerInfo.AddSSUAddress (host.c_str(), port, nullptr);
caps |= i2p::data::RouterInfo::eReachable; // R
}
}
if (ygg)
{
auto yggaddr = i2p::util::net::GetYggdrasilAddress ();
if (!yggaddr.is_unspecified ())
{
if (!m_NTCP2Keys) NewNTCP2Keys ();
m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, yggaddr, port);
m_RouterInfo.EnableMesh ();
UpdateRouterInfo ();
}
}
routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, yggaddr, port);
}
if (addressCaps)
routerInfo.SetUnreachableAddressesTransportCaps (addressCaps);
routerInfo.SetCaps (caps); // caps + L
routerInfo.SetProperty ("netId", std::to_string (m_NetID));
routerInfo.SetProperty ("router.version", I2P_VERSION);
routerInfo.CreateBuffer (m_Keys);
m_RouterInfo.SetRouterIdentity (GetIdentity ());
m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ());
}
void RouterContext::UpdateRouterInfo ()
@@ -185,10 +199,29 @@ namespace i2p
switch (m_Status)
{
case eRouterStatusOK:
SetReachable ();
SetReachable (true, false); // ipv4
break;
case eRouterStatusFirewalled:
SetUnreachable ();
SetUnreachable (true, false); // ipv4
break;
default:
;
}
}
}
void RouterContext::SetStatusV6 (RouterStatus status)
{
if (status != m_StatusV6)
{
m_StatusV6 = status;
switch (m_StatusV6)
{
case eRouterStatusOK:
SetReachable (false, true); // ipv6
break;
case eRouterStatusFirewalled:
SetUnreachable (false, true); // ipv6
break;
default:
;
@@ -211,25 +244,35 @@ namespace i2p
UpdateRouterInfo ();
}
void RouterContext::PublishNTCP2Address (int port, bool publish, bool v4only)
void RouterContext::PublishNTCP2Address (int port, bool publish, bool v4, bool v6, bool ygg)
{
if (!m_NTCP2Keys) return;
bool updated = false;
for (auto& address : m_RouterInfo.GetAddresses ())
{
if (address->IsNTCP2 () && (address->port != port || address->ntcp2->isPublished != publish) && (!v4only || address->host.is_v4 ()))
if (address->IsNTCP2 () && (address->port != port || address->published != publish))
{
if (!port && !address->port)
bool isAddr = v4 && address->IsV4 ();
if (!isAddr && (v6 || ygg))
{
// select random port only if address's port is not set
port = rand () % (30777 - 9111) + 9111; // I2P network ports range
if (port == 9150) port = 9151; // Tor browser
if (i2p::util::net::IsYggdrasilAddress (address->host))
isAddr = ygg;
else
isAddr = v6 && address->IsV6 ();
}
if (isAddr)
{
if (!port && !address->port)
{
// select random port only if address's port is not set
port = rand () % (30777 - 9111) + 9111; // I2P network ports range
if (port == 9150) port = 9151; // Tor browser
}
if (port) address->port = port;
address->published = publish;
address->ntcp2->iv = m_NTCP2Keys->iv;
updated = true;
}
if (port) address->port = port;
address->cost = publish ? 3 : 14;
address->ntcp2->isPublished = publish;
address->ntcp2->iv = m_NTCP2Keys->iv;
updated = true;
}
}
if (updated)
@@ -267,7 +310,7 @@ namespace i2p
bool updated = false;
for (auto& address : m_RouterInfo.GetAddresses ())
{
if (address->host != host && address->IsCompatible (host) &&
if (address->host != host && address->IsCompatible (host) &&
!i2p::util::net::IsYggdrasilAddress (address->host))
{
address->host = host;
@@ -359,7 +402,7 @@ namespace i2p
case i2p::data::CAPS_FLAG_EXTRA_BANDWIDTH1 : limit = 2048; type = extra; break;
case i2p::data::CAPS_FLAG_EXTRA_BANDWIDTH2 : limit = 1000000; type = unlim; break; // 1Gbyte/s
default:
limit = 48; type = low;
limit = 48; type = low;
}
/* update caps & flags in RI */
auto caps = m_RouterInfo.GetCaps ();
@@ -373,8 +416,8 @@ namespace i2p
#if (__cplusplus >= 201703L) // C++ 17 or higher
[[fallthrough]];
#endif
// no break here, extra + high means 'X'
case high : caps |= i2p::data::RouterInfo::eHighBandwidth; break;
// no break here, extra + high means 'X'
case high : caps |= i2p::data::RouterInfo::eHighBandwidth; break;
}
m_RouterInfo.SetCaps (caps);
UpdateRouterInfo ();
@@ -390,6 +433,7 @@ namespace i2p
else if (limit > 48) { SetBandwidth('M'); }
else if (limit > 12) { SetBandwidth('L'); }
else { SetBandwidth('K'); }
m_BandwidthLimit = limit; // set precise limit
}
void RouterContext::SetShareRatio (int percents)
@@ -420,52 +464,60 @@ namespace i2p
}
}
void RouterContext::SetUnreachable ()
void RouterContext::SetUnreachable (bool v4, bool v6)
{
// set caps
uint8_t caps = m_RouterInfo.GetCaps ();
caps &= ~i2p::data::RouterInfo::eReachable;
caps |= i2p::data::RouterInfo::eUnreachable;
caps &= ~i2p::data::RouterInfo::eFloodfill; // can't be floodfill
caps &= ~i2p::data::RouterInfo::eSSUIntroducer; // can't be introducer
m_RouterInfo.SetCaps (caps);
if (v4 || (v6 && !SupportsV4 ()))
{
// set caps
uint8_t caps = m_RouterInfo.GetCaps ();
caps &= ~i2p::data::RouterInfo::eReachable;
caps |= i2p::data::RouterInfo::eUnreachable;
caps &= ~i2p::data::RouterInfo::eFloodfill; // can't be floodfill
m_RouterInfo.SetCaps (caps);
}
uint16_t port = 0;
// delete previous introducers
auto& addresses = m_RouterInfo.GetAddresses ();
for (auto& addr : addresses)
if (addr->ssu)
if (addr->ssu && ((v4 && addr->IsV4 ()) || (v6 && addr->IsV6 ())))
{
addr->published = false;
addr->caps &= ~i2p::data::RouterInfo::eSSUIntroducer; // can't be introducer
addr->ssu->introducers.clear ();
port = addr->port;
}
// remove NTCP2 v4 address
// unpiblish NTCP2 addreeses
bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2);
if (ntcp2)
PublishNTCP2Address (port, false, true);
PublishNTCP2Address (port, false, v4, v6, false);
// update
UpdateRouterInfo ();
}
void RouterContext::SetReachable ()
void RouterContext::SetReachable (bool v4, bool v6)
{
// update caps
uint8_t caps = m_RouterInfo.GetCaps ();
caps &= ~i2p::data::RouterInfo::eUnreachable;
caps |= i2p::data::RouterInfo::eReachable;
caps |= i2p::data::RouterInfo::eSSUIntroducer;
if (m_IsFloodfill)
caps |= i2p::data::RouterInfo::eFloodfill;
m_RouterInfo.SetCaps (caps);
if (v4 || (v6 && !SupportsV4 ()))
{
// update caps
uint8_t caps = m_RouterInfo.GetCaps ();
caps &= ~i2p::data::RouterInfo::eUnreachable;
caps |= i2p::data::RouterInfo::eReachable;
if (m_IsFloodfill)
caps |= i2p::data::RouterInfo::eFloodfill;
m_RouterInfo.SetCaps (caps);
}
uint16_t port = 0;
// delete previous introducers
auto& addresses = m_RouterInfo.GetAddresses ();
for (auto& addr : addresses)
if (addr->ssu)
if (addr->ssu && ((v4 && addr->IsV4 ()) || (v6 && addr->IsV6 ())))
{
addr->published = true;
addr->caps |= i2p::data::RouterInfo::eSSUIntroducer;
addr->ssu->introducers.clear ();
port = addr->port;
}
// insert NTCP2 back
// publish NTCP2
bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2);
if (ntcp2)
{
@@ -474,7 +526,7 @@ namespace i2p
{
uint16_t ntcp2Port; i2p::config::GetOption ("ntcp2.port", ntcp2Port);
if (!ntcp2Port) ntcp2Port = port;
PublishNTCP2Address (ntcp2Port, true, true);
PublishNTCP2Address (ntcp2Port, true, v4, v6, false);
}
}
// update
@@ -485,18 +537,17 @@ namespace i2p
{
if (supportsV6)
{
m_RouterInfo.EnableV6 ();
// insert v6 addresses if necessary
bool foundSSU = false, foundNTCP2 = false;
uint16_t port = 0;
auto& addresses = m_RouterInfo.GetAddresses ();
for (auto& addr: addresses)
{
if (addr->host.is_v6 ())
if (addr->IsV6 () && !i2p::util::net::IsYggdrasilAddress (addr->host))
{
if (addr->transportStyle == i2p::data::RouterInfo::eTransportSSU)
foundSSU = true;
else if (addr->IsPublishedNTCP2 ())
else if (addr->transportStyle == i2p::data::RouterInfo::eTransportNTCP)
foundNTCP2 = true;
}
port = addr->port;
@@ -529,6 +580,7 @@ namespace i2p
m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address::from_string (ntcp2Host), ntcp2Port);
}
}
m_RouterInfo.EnableV6 ();
}
else
m_RouterInfo.DisableV6 ();
@@ -537,17 +589,63 @@ namespace i2p
void RouterContext::SetSupportsV4 (bool supportsV4)
{
// check if updates
if (supportsV4 && SupportsV4 ()) return;
if (!supportsV4 && !SupportsV4 ()) return;
// update
if (supportsV4)
{
bool foundSSU = false, foundNTCP2 = false;
std::string host = "127.0.0.1";
uint16_t port = 0;
auto& addresses = m_RouterInfo.GetAddresses ();
for (auto& addr: addresses)
{
if (addr->IsV4 ())
{
if (addr->transportStyle == i2p::data::RouterInfo::eTransportSSU)
foundSSU = true;
else if (addr->transportStyle == i2p::data::RouterInfo::eTransportNTCP)
foundNTCP2 = true;
}
if (addr->port) port = addr->port;
}
if (!port) i2p::config::GetOption("port", port);
// SSU
if (!foundSSU)
{
bool ssu; i2p::config::GetOption("ssu", ssu);
if (ssu)
m_RouterInfo.AddSSUAddress (host.c_str (), port, nullptr);
}
// NTCP2
if (!foundNTCP2)
{
bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2);
if (ntcp2)
{
bool ntcp2Published; i2p::config::GetOption("ntcp2.published", ntcp2Published);
if (ntcp2Published)
{
uint16_t ntcp2Port; i2p::config::GetOption ("ntcp2.port", ntcp2Port);
if (!ntcp2Port) ntcp2Port = port;
m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address::from_string (host), ntcp2Port);
}
else
m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv);
}
}
m_RouterInfo.EnableV4 ();
}
else
m_RouterInfo.DisableV4 ();
UpdateRouterInfo ();
}
void RouterContext::SetSupportsMesh (bool supportsmesh, const boost::asio::ip::address_v6& host)
{
{
if (supportsmesh)
{
{
m_RouterInfo.EnableMesh ();
uint16_t port = 0;
i2p::config::GetOption ("ntcp2.port", port);
@@ -561,16 +659,16 @@ namespace i2p
{
foundMesh = true;
break;
}
}
}
if (!foundMesh)
m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, host, port);
}
}
else
m_RouterInfo.DisableMesh ();
UpdateRouterInfo ();
}
void RouterContext::UpdateNTCP2V6Address (const boost::asio::ip::address& host)
{
bool isYgg = i2p::util::net::IsYggdrasilAddress (host);
@@ -638,7 +736,14 @@ namespace i2p
}
}
std::shared_ptr<const i2p::data::IdentityEx> oldIdentity;
if (m_Keys.GetPublic ()->GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1)
bool rekey = m_Keys.GetPublic ()->GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1;
if (!rekey && m_Keys.GetPublic ()->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ELGAMAL)
{
// rekey routers with bandwidth = L (or default) this time
bool isFloodfill; i2p::config::GetOption("floodfill", isFloodfill);
if (!isFloodfill) rekey = true;
}
if (rekey)
{
// update keys
LogPrint (eLogInfo, "Router: router keys are obsolete. Creating new");
@@ -646,7 +751,7 @@ namespace i2p
m_Keys = i2p::data::PrivateKeys::CreateRandomKeys (i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519,
i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD);
SaveKeys ();
}
}
// read NTCP2 keys if available
std::ifstream n2k (i2p::fs::DataDirPath (NTCP2_KEYS), std::ifstream::in | std::ifstream::binary);
if (n2k)
@@ -669,8 +774,8 @@ namespace i2p
m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ());
if (oldIdentity)
m_RouterInfo.SetRouterIdentity (GetIdentity ()); // from new keys
m_RouterInfo.SetProperty ("coreVersion", I2P_VERSION);
m_RouterInfo.SetProperty ("router.version", I2P_VERSION);
m_RouterInfo.DeleteProperty ("coreVersion"); // TODO: remove later
}
else
{
@@ -679,11 +784,11 @@ namespace i2p
}
if (IsUnreachable ())
SetReachable (); // we assume reachable until we discover firewall through peer tests
SetReachable (true, true); // we assume reachable until we discover firewall through peer tests
// read NTCP2
bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2);
bool ygg; i2p::config::GetOption("meshnets.yggdrasil", ygg);
bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2);
bool ygg; i2p::config::GetOption("meshnets.yggdrasil", ygg);
if (ntcp2 || ygg)
{
if (!m_NTCP2Keys) NewNTCP2Keys ();
@@ -716,15 +821,15 @@ namespace i2p
i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf, len)));
}
bool RouterContext::HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len)
{
bool RouterContext::HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len)
{
auto msg = CreateI2NPMessage (typeID, payload, len);
if (!msg) return false;
i2p::HandleI2NPMessage (msg);
return true;
}
return true;
}
void RouterContext::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
{
std::unique_lock<std::mutex> l(m_GarlicMutex);
@@ -738,10 +843,12 @@ namespace i2p
return;
}
buf += 4;
auto session = std::make_shared<i2p::garlic::ECIESX25519AEADRatchetSession>(this, false);
session->HandleNextMessageForRouter (buf, len);
}
else
if (m_ECIESSession)
m_ECIESSession->HandleNextMessage (buf, len);
else
LogPrint (eLogError, "Router: Session is not set for ECIES router");
}
else
i2p::garlic::GarlicDestination::ProcessGarlicMessage (msg);
}
@@ -750,10 +857,10 @@ namespace i2p
if (i2p::data::netdb.GetPublishReplyToken () == bufbe32toh (msg->GetPayload () + DELIVERY_STATUS_MSGID_OFFSET))
i2p::data::netdb.PostI2NPMsg (msg);
else
{
{
std::unique_lock<std::mutex> l(m_GarlicMutex);
i2p::garlic::GarlicDestination::ProcessDeliveryStatusMessage (msg);
}
}
}
void RouterContext::CleanupDestination ()
@@ -772,36 +879,41 @@ namespace i2p
return m_Decryptor ? m_Decryptor->Decrypt (encrypted, data, ctx, true) : false;
}
bool RouterContext::DecryptTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx)
bool RouterContext::DecryptTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data)
{
if (!m_Decryptor) return false;
if (!m_TunnelDecryptor) return false;
if (IsECIES ())
{
if (!m_InitialNoiseState) return false;
// m_InitialNoiseState is h = SHA256(h || hepk)
m_CurrentNoiseState.reset (new i2p::crypto::NoiseSymmetricState (*m_InitialNoiseState));
m_CurrentNoiseState.reset (new i2p::crypto::NoiseSymmetricState (*m_InitialNoiseState));
m_CurrentNoiseState->MixHash (encrypted, 32); // h = SHA256(h || sepk)
uint8_t sharedSecret[32];
if (!m_Decryptor->Decrypt (encrypted, sharedSecret, ctx, false))
if (!m_TunnelDecryptor->Decrypt (encrypted, sharedSecret, nullptr, false))
{
LogPrint (eLogWarning, "Router: Incorrect ephemeral public key");
return false;
}
m_CurrentNoiseState->MixKey (sharedSecret);
}
m_CurrentNoiseState->MixKey (sharedSecret);
encrypted += 32;
uint8_t nonce[12];
memset (nonce, 0, 12);
if (!i2p::crypto::AEADChaCha20Poly1305 (encrypted, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE,
if (!i2p::crypto::AEADChaCha20Poly1305 (encrypted, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE,
m_CurrentNoiseState->m_H, 32, m_CurrentNoiseState->m_CK + 32, nonce, data, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE, false)) // decrypt
{
LogPrint (eLogWarning, "Router: Tunnel record AEAD decryption failed");
return false;
}
}
m_CurrentNoiseState->MixHash (encrypted, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE + 16); // h = SHA256(h || ciphertext)
return true;
}
else
return m_Decryptor->Decrypt (encrypted, data, ctx, false);
}
else
{
BN_CTX * ctx = BN_CTX_new ();
bool success = m_TunnelDecryptor->Decrypt (encrypted, data, ctx, false);
BN_CTX_free (ctx);
return success;
}
}
i2p::crypto::X25519Keys& RouterContext::GetStaticKeys ()

View File

@@ -21,6 +21,11 @@
namespace i2p
{
namespace garlic
{
class RouterIncomingRatchetSession;
}
const char ROUTER_INFO[] = "router.info";
const char ROUTER_KEYS[] = "router.keys";
const char NTCP2_KEYS[] = "ntcp2.keys";
@@ -32,16 +37,19 @@ namespace i2p
eRouterStatusTesting = 1,
eRouterStatusFirewalled = 2,
eRouterStatusError = 3,
eRouterStatusUnknown = 4
eRouterStatusUnknown = 4,
eRouterStatusProxy = 5,
eRouterStatusMesh = 6
};
enum RouterError
{
eRouterErrorNone = 0,
eRouterErrorClockSkew = 1,
eRouterErrorOffline = 2
eRouterErrorOffline = 2,
eRouterErrorSymmetricNAT = 3
};
class RouterContext: public i2p::garlic::GarlicDestination
{
private:
@@ -83,20 +91,22 @@ namespace i2p
void SetStatus (RouterStatus status);
RouterError GetError () const { return m_Error; };
void SetError (RouterError error) { m_Status = eRouterStatusError; m_Error = error; };
RouterStatus GetStatusV6 () const { return m_StatusV6; };
void SetStatusV6 (RouterStatus status);
int GetNetID () const { return m_NetID; };
void SetNetID (int netID) { m_NetID = netID; };
bool DecryptTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx);
bool DecryptTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data);
void UpdatePort (int port); // called from Daemon
void UpdateAddress (const boost::asio::ip::address& host); // called from SSU or Daemon
void PublishNTCP2Address (int port, bool publish = true, bool v4only = false);
void PublishNTCP2Address (int port, bool publish, bool v4, bool v6, bool ygg);
void UpdateNTCP2Address (bool enable);
void RemoveNTCPAddress (bool v4only = true); // delete NTCP address for older routers. TODO: remove later
bool AddIntroducer (const i2p::data::RouterInfo::Introducer& introducer);
void RemoveIntroducer (const boost::asio::ip::udp::endpoint& e);
bool IsUnreachable () const;
void SetUnreachable ();
void SetReachable ();
void SetUnreachable (bool v4, bool v6);
void SetReachable (bool v4, bool v6);
bool IsFloodfill () const { return m_IsFloodfill; };
void SetFloodfill (bool floodfill);
void SetFamily (const std::string& family);
@@ -153,13 +163,14 @@ namespace i2p
i2p::data::RouterInfo m_RouterInfo;
i2p::data::PrivateKeys m_Keys;
std::shared_ptr<i2p::crypto::CryptoKeyDecryptor> m_Decryptor;
std::shared_ptr<i2p::crypto::CryptoKeyDecryptor> m_Decryptor, m_TunnelDecryptor;
std::shared_ptr<i2p::garlic::RouterIncomingRatchetSession> m_ECIESSession;
uint64_t m_LastUpdateTime; // in seconds
bool m_AcceptsTunnels, m_IsFloodfill;
std::chrono::time_point<std::chrono::steady_clock> m_StartupTime;
uint64_t m_BandwidthLimit; // allowed bandwidth
int m_ShareRatio;
RouterStatus m_Status;
RouterStatus m_Status, m_StatusV6;
RouterError m_Error;
int m_NetID;
std::mutex m_GarlicMutex;

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2020, The PurpleI2P Project
* Copyright (c) 2013-2021, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@@ -186,18 +186,19 @@ namespace data
void RouterInfo::ReadFromStream (std::istream& s)
{
m_Caps = 0;
s.read ((char *)&m_Timestamp, sizeof (m_Timestamp));
m_Timestamp = be64toh (m_Timestamp);
// read addresses
auto addresses = boost::make_shared<Addresses>();
uint8_t numAddresses;
s.read ((char *)&numAddresses, sizeof (numAddresses)); if (!s) return;
bool introducers = false;
for (int i = 0; i < numAddresses; i++)
{
uint8_t supportedTransports = 0;
auto address = std::make_shared<Address>();
s.read ((char *)&address->cost, sizeof (address->cost));
uint8_t cost; // ignore
s.read ((char *)&cost, sizeof (cost));
s.read ((char *)&address->date, sizeof (address->date));
bool isHost = false, isIntroKey = false, isStaticKey = false;
char transportStyle[6];
@@ -215,6 +216,7 @@ namespace data
}
else
address->transportStyle = eTransportUnknown;
address->caps = 0;
address->port = 0;
uint16_t size, r = 0;
s.read ((char *)&size, sizeof (size)); if (!s) return;
@@ -250,7 +252,7 @@ namespace data
LogPrint (eLogWarning, "RouterInfo: Unexpected field 'key' for NTCP");
}
else if (!strcmp (key, "caps"))
ExtractCaps (value);
address->caps = ExtractAddressCaps (value);
else if (!strcmp (key, "s")) // ntcp2 static key
{
Base64ToByteStream (value, strlen (value), address->ntcp2->staticKey, 32);
@@ -259,7 +261,7 @@ namespace data
else if (!strcmp (key, "i")) // ntcp2 iv
{
Base64ToByteStream (value, strlen (value), address->ntcp2->iv, 16);
address->ntcp2->isPublished = true; // presence if "i" means "published"
address->published = true; // presence if "i" means "published"
}
else if (key[0] == 'i')
{
@@ -269,7 +271,6 @@ namespace data
LogPrint (eLogError, "RouterInfo: Introducer is presented for non-SSU address. Skipped");
continue;
}
introducers = true;
size_t l = strlen(key);
unsigned char index = key[l-1] - '0'; // TODO:
key[l-1] = 0;
@@ -308,8 +309,16 @@ namespace data
else
supportedTransports |= eNTCP2V4;
}
else if (!address->ntcp2->isPublished)
supportedTransports |= eNTCP2V4; // most likely, since we don't have host
else if (!address->published)
{
if (address->caps)
{
if (address->caps & AddressCaps::eV4) supportedTransports |= eNTCP2V4;
if (address->caps & AddressCaps::eV6) supportedTransports |= eNTCP2V6;
}
else
supportedTransports |= eNTCP2V4; // most likely, since we don't have host
}
}
}
else if (address->transportStyle == eTransportSSU)
@@ -318,8 +327,30 @@ namespace data
{
if (isHost)
supportedTransports |= address->host.is_v4 () ? eSSUV4 : eSSUV6;
else if (address->caps & AddressCaps::eV6)
{
supportedTransports |= eSSUV6;
if (address->caps & AddressCaps::eV4) supportedTransports |= eSSUV4; // in additional to v6
}
else
if (introducers) supportedTransports |= eSSUV4; // in case if host is not presented
supportedTransports |= eSSUV4; // in case if host or 6 caps is not preasented, we assume 4
if (address->ssu && !address->ssu->introducers.empty ())
{
// exclude invalid introducers
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
int numValid = 0;
for (auto& it: address->ssu->introducers)
{
if ((!it.iExp || ts <= it.iExp) && it.iPort > 0 &&
((it.iHost.is_v4 () && address->IsV4 ()) || (it.iHost.is_v6 () && address->IsV6 ())))
numValid++;
else
it.iPort = 0;
}
if (!numValid) address->ssu->introducers.resize (0);
}
else if (isHost && address->port)
address->published = true;
}
}
if (supportedTransports)
@@ -393,7 +424,7 @@ namespace data
if (!s) return;
}
if (!m_SupportedTransports || !m_Addresses->size() || (UsesIntroducer () && !introducers))
if (!m_SupportedTransports)
SetUnreachable (true);
}
@@ -430,18 +461,39 @@ namespace data
case CAPS_FLAG_UNREACHABLE:
m_Caps |= Caps::eUnreachable;
break;
case CAPS_FLAG_SSU_TESTING:
m_Caps |= Caps::eSSUTesting;
break;
case CAPS_FLAG_SSU_INTRODUCER:
m_Caps |= Caps::eSSUIntroducer;
break;
default: ;
}
cap++;
}
}
uint8_t RouterInfo::ExtractAddressCaps (const char * value) const
{
uint8_t caps = 0;
const char * cap = value;
while (*cap)
{
switch (*cap)
{
case CAPS_FLAG_V4:
caps |= AddressCaps::eV4;
break;
case CAPS_FLAG_V6:
caps |= AddressCaps::eV6;
break;
case CAPS_FLAG_SSU_TESTING:
caps |= AddressCaps::eSSUTesting;
break;
case CAPS_FLAG_SSU_INTRODUCER:
caps |= AddressCaps::eSSUIntroducer;
break;
default: ;
}
cap++;
}
return caps;
}
void RouterInfo::UpdateCapsProperty ()
{
std::string caps;
@@ -479,13 +531,35 @@ namespace data
for (const auto& addr_ptr : *m_Addresses)
{
const Address& address = *addr_ptr;
s.write ((const char *)&address.cost, sizeof (address.cost));
// calculate cost
uint8_t cost = 0x7f;
if (address.transportStyle == eTransportNTCP)
cost = address.published ? COST_NTCP2_PUBLISHED : COST_NTCP2_NON_PUBLISHED;
else if (address.transportStyle == eTransportSSU)
cost = address.published ? COST_SSU_DIRECT : COST_SSU_THROUGH_INTRODUCERS;
s.write ((const char *)&cost, sizeof (cost));
s.write ((const char *)&address.date, sizeof (address.date));
std::stringstream properties;
bool isPublished = false;
if (address.transportStyle == eTransportNTCP)
{
if (address.IsNTCP2 ())
{
WriteString ("NTCP2", s);
if (address.IsPublishedNTCP2 () && !address.host.is_unspecified ())
isPublished = true;
else
{
WriteString ("caps", properties);
properties << '=';
std::string caps;
if (address.IsV4 ()) caps += CAPS_FLAG_V4;
if (address.IsV6 ()) caps += CAPS_FLAG_V6;
if (caps.empty ()) caps += CAPS_FLAG_V4;
WriteString (caps, properties);
properties << ';';
}
}
else
continue; // don't write NTCP address
}
@@ -495,16 +569,41 @@ namespace data
// caps
WriteString ("caps", properties);
properties << '=';
std::string caps;
if (IsPeerTesting ()) caps += CAPS_FLAG_SSU_TESTING;
if (IsIntroducer ()) caps += CAPS_FLAG_SSU_INTRODUCER;
std::string caps;
if (address.IsPeerTesting ()) caps += CAPS_FLAG_SSU_TESTING;
if (address.host.is_v4 ())
{
if (address.published)
{
isPublished = true;
if (address.IsIntroducer ()) caps += CAPS_FLAG_SSU_INTRODUCER;
}
else
caps += CAPS_FLAG_V4;
}
else if (address.host.is_v6 ())
{
if (address.published)
{
isPublished = true;
if (address.IsIntroducer ()) caps += CAPS_FLAG_SSU_INTRODUCER;
}
else
caps += CAPS_FLAG_V6;
}
else
{
if (address.IsV4 ()) caps += CAPS_FLAG_V4;
if (address.IsV6 ()) caps += CAPS_FLAG_V6;
if (caps.empty ()) caps += CAPS_FLAG_V4;
}
WriteString (caps, properties);
properties << ';';
}
else
WriteString ("", s);
if (!address.IsNTCP2 () || address.IsPublishedNTCP2 ())
if (isPublished)
{
WriteString ("host", properties);
properties << '=';
@@ -514,10 +613,22 @@ namespace data
if (address.transportStyle == eTransportSSU)
{
// write introducers if any
if (address.ssu->introducers.size () > 0)
if (!address.ssu->introducers.empty())
{
int i = 0;
for (const auto& introducer: address.ssu->introducers)
{
if (introducer.iExp) // expiration is specified
{
WriteString ("iexp" + boost::lexical_cast<std::string>(i), properties);
properties << '=';
WriteString (boost::lexical_cast<std::string>(introducer.iExp), properties);
properties << ';';
}
i++;
}
i = 0;
for (const auto& introducer: address.ssu->introducers)
{
WriteString ("ihost" + boost::lexical_cast<std::string>(i), properties);
properties << '=';
@@ -555,18 +666,6 @@ namespace data
properties << ';';
i++;
}
i = 0;
for (const auto& introducer: address.ssu->introducers)
{
if (introducer.iExp) // expiration is specified
{
WriteString ("iexp" + boost::lexical_cast<std::string>(i), properties);
properties << '=';
WriteString (boost::lexical_cast<std::string>(introducer.iExp), properties);
properties << ';';
}
i++;
}
}
// write intro key
WriteString ("key", properties);
@@ -586,14 +685,14 @@ namespace data
}
}
if (address.IsPublishedNTCP2 ())
if (address.IsNTCP2 () && isPublished)
{
// publish i for NTCP2
WriteString ("i", properties); properties << '=';
WriteString (address.ntcp2->iv.ToBase64 (), properties); properties << ';';
}
if (!address.IsNTCP2 () || address.IsPublishedNTCP2 ())
if (isPublished || address.ssu)
{
WriteString ("port", properties);
properties << '=';
@@ -721,7 +820,8 @@ namespace data
addr->host = boost::asio::ip::address::from_string (host);
addr->port = port;
addr->transportStyle = eTransportSSU;
addr->cost = 10; // NTCP should have priority over SSU
addr->published = true;
addr->caps = i2p::data::RouterInfo::eSSUTesting | i2p::data::RouterInfo::eSSUIntroducer; // BC;
addr->date = 0;
addr->ssu.reset (new SSUExt ());
addr->ssu->mtu = mtu;
@@ -733,9 +833,6 @@ namespace data
if (*it == *addr) return;
m_SupportedTransports |= addr->host.is_v6 () ? eSSUV6 : eSSUV4;
m_Addresses->push_back(std::move(addr));
m_Caps |= eSSUTesting;
m_Caps |= eSSUIntroducer;
}
void RouterInfo::AddNTCP2Address (const uint8_t * staticKey, const uint8_t * iv, const boost::asio::ip::address& host, int port)
@@ -743,11 +840,11 @@ namespace data
auto addr = std::make_shared<Address>();
addr->host = host;
addr->port = port;
addr->transportStyle = eTransportNTCP;
addr->cost = port ? 3 : 14; // override from RouterContext::PublishNTCP2Address
addr->transportStyle = eTransportNTCP;
addr->caps = 0;
addr->date = 0;
addr->ntcp2.reset (new NTCP2Ext ());
if (port) addr->ntcp2->isPublished = true;
if (port) addr->published = true;
memcpy (addr->ntcp2->staticKey, staticKey, 32);
memcpy (addr->ntcp2->iv, iv, 16);
m_Addresses->push_back(std::move(addr));
@@ -757,7 +854,8 @@ namespace data
{
for (auto& addr : *m_Addresses)
{
if (addr->transportStyle == eTransportSSU && addr->host.is_v4 ())
if (addr->transportStyle == eTransportSSU &&
((addr->IsV4 () && introducer.iHost.is_v4 ()) || (addr->IsV6 () && introducer.iHost.is_v6 ())))
{
for (auto& intro: addr->ssu->introducers)
if (intro.iTag == introducer.iTag) return false; // already presented
@@ -772,10 +870,11 @@ namespace data
{
for (auto& addr: *m_Addresses)
{
if (addr->transportStyle == eTransportSSU && addr->host.is_v4 ())
if (addr->transportStyle == eTransportSSU &&
((addr->IsV4 () && e.address ().is_v4 ()) || (addr->IsV6 () && e.address ().is_v6 ())))
{
for (auto it = addr->ssu->introducers.begin (); it != addr->ssu->introducers.end (); ++it)
if ( boost::asio::ip::udp::endpoint (it->iHost, it->iPort) == e)
if (boost::asio::ip::udp::endpoint (it->iHost, it->iPort) == e)
{
addr->ssu->introducers.erase (it);
return true;
@@ -860,13 +959,23 @@ namespace data
void RouterInfo::EnableV6 ()
{
if (!IsV6 ())
{
m_SupportedTransports |= eSSUV6 | eNTCP2V6;
uint8_t addressCaps = AddressCaps::eV6;
if (IsV4 ()) addressCaps |= AddressCaps::eV4;
SetUnreachableAddressesTransportCaps (addressCaps);
}
}
void RouterInfo::EnableV4 ()
{
if (!IsV4 ())
{
m_SupportedTransports |= eSSUV4 | eNTCP2V4;
uint8_t addressCaps = AddressCaps::eV4;
if (IsV6 ()) addressCaps |= AddressCaps::eV6;
SetUnreachableAddressesTransportCaps (addressCaps);
}
}
@@ -878,6 +987,7 @@ namespace data
for (auto it = m_Addresses->begin (); it != m_Addresses->end ();)
{
auto addr = *it;
addr->caps &= ~AddressCaps::eV6;
if (addr->host.is_v6 ())
it = m_Addresses->erase (it);
else
@@ -894,6 +1004,7 @@ namespace data
for (auto it = m_Addresses->begin (); it != m_Addresses->end ();)
{
auto addr = *it;
addr->caps &= ~AddressCaps::eV4;
if (addr->host.is_v4 ())
it = m_Addresses->erase (it);
else
@@ -924,17 +1035,12 @@ namespace data
}
}
bool RouterInfo::UsesIntroducer () const
{
return m_Caps & Caps::eUnreachable; // non-reachable
}
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetSSUAddress (bool v4only) const
{
return GetAddress (
[v4only](std::shared_ptr<const RouterInfo::Address> address)->bool
{
return (address->transportStyle == eTransportSSU) && (!v4only || address->host.is_v4 ());
return (address->transportStyle == eTransportSSU) && (!v4only || address->IsV4 ());
});
}
@@ -943,7 +1049,7 @@ namespace data
return GetAddress (
[](std::shared_ptr<const RouterInfo::Address> address)->bool
{
return (address->transportStyle == eTransportSSU) && address->host.is_v6 ();
return (address->transportStyle == eTransportSSU) && address->IsV6();
});
}
@@ -962,12 +1068,13 @@ namespace data
return nullptr;
}
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetNTCP2Address (bool publishedOnly) const
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetNTCP2AddressWithStaticKey (const uint8_t * key) const
{
if (!key) return nullptr;
return GetAddress (
[publishedOnly](std::shared_ptr<const RouterInfo::Address> address)->bool
[key](std::shared_ptr<const RouterInfo::Address> address)->bool
{
return address->IsNTCP2 () && (!publishedOnly || address->IsPublishedNTCP2 ());
return address->IsNTCP2 () && !memcmp (address->ntcp2->staticKey, key, 32);
});
}
@@ -1015,9 +1122,67 @@ namespace data
bool RouterInfo::IsEligibleFloodfill () const
{
// floodfill must be reachable, >= 0.9.28 and not DSA
return IsReachable () && m_Version >= NETDB_MIN_FLOODFILL_VERSION &&
// floodfill must be reachable somehow, >= 0.9.28 and not DSA
return (IsReachable () || (m_SupportedTransports & eSSUV4)) &&
m_Version >= NETDB_MIN_FLOODFILL_VERSION &&
GetIdentity ()->GetSigningKeyType () != SIGNING_KEY_TYPE_DSA_SHA1;
}
bool RouterInfo::IsPeerTesting (bool v4) const
{
if (!(m_SupportedTransports & (v4 ? eSSUV4 : eSSUV6))) return false;
return (bool)GetAddress (
[v4](std::shared_ptr<const RouterInfo::Address> address)->bool
{
return (address->transportStyle == eTransportSSU) && address->IsPeerTesting () &&
((v4 && address->IsV4 ()) || (!v4 && address->IsV6 ())) && address->IsReachableSSU ();
});
}
bool RouterInfo::IsIntroducer (bool v4) const
{
if (!(m_SupportedTransports & (v4 ? eSSUV4 : eSSUV6))) return false;
return (bool)GetAddress (
[v4](std::shared_ptr<const RouterInfo::Address> address)->bool
{
return (address->transportStyle == eTransportSSU) && address->IsIntroducer () &&
((v4 && address->IsV4 ()) || (!v4 && address->IsV6 ())) && !address->host.is_unspecified ();
});
}
bool RouterInfo::IsReachableFrom (const RouterInfo& other) const
{
auto commonTransports = m_SupportedTransports & other.m_SupportedTransports;
if (!commonTransports) return false;
if (commonTransports & eNTCP2V6Mesh) return true;
return (bool)GetAddress (
[commonTransports](std::shared_ptr<const RouterInfo::Address> address)->bool
{
if (address->IsPublishedNTCP2 ())
{
if ((commonTransports & eNTCP2V4) && address->IsV4 ()) return true;
if ((commonTransports & eNTCP2V6) && address->IsV6 ()) return true;
}
else if (address->IsReachableSSU ())
{
if ((commonTransports & eSSUV4) && address->IsV4 ()) return true;
if ((commonTransports & eSSUV6) && address->IsV6 ()) return true;
}
return false;
});
}
void RouterInfo::SetUnreachableAddressesTransportCaps (uint8_t transports)
{
for (auto& addr: *m_Addresses)
{
// TODO: implement SSU
if (addr->transportStyle == eTransportNTCP && (!addr->IsPublishedNTCP2 () || addr->port))
{
addr->caps &= ~(eV4 | eV6);
addr->caps |= transports;
}
}
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2020, The PurpleI2P Project
* Copyright (c) 2013-2021, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@@ -44,9 +44,16 @@ namespace data
const char CAPS_FLAG_EXTRA_BANDWIDTH1 = 'P'; /* 256-2000 KBps */
const char CAPS_FLAG_EXTRA_BANDWIDTH2 = 'X'; /* > 2000 KBps */
const char CAPS_FLAG_V4 = '4';
const char CAPS_FLAG_V6 = '6';
const char CAPS_FLAG_SSU_TESTING = 'B';
const char CAPS_FLAG_SSU_INTRODUCER = 'C';
const uint8_t COST_NTCP2_PUBLISHED = 3;
const uint8_t COST_NTCP2_NON_PUBLISHED = 14;
const uint8_t COST_SSU_DIRECT = 9;
const uint8_t COST_SSU_THROUGH_INTRODUCERS = 11;
const int MAX_RI_BUFFER_SIZE = 2048; // if RouterInfo exceeds 2048 we consider it as malformed, might be changed later
class RouterInfo: public RoutingDestination
{
@@ -67,12 +74,18 @@ namespace data
eHighBandwidth = 0x02,
eExtraBandwidth = 0x04,
eReachable = 0x08,
eSSUTesting = 0x10,
eSSUIntroducer = 0x20,
eHidden = 0x40,
eUnreachable = 0x80
eHidden = 0x10,
eUnreachable = 0x20
};
enum AddressCaps
{
eV4 = 0x01,
eV6 = 0x02,
eSSUTesting = 0x04,
eSSUIntroducer = 0x08
};
enum TransportStyle
{
eTransportUnknown = 0,
@@ -83,7 +96,7 @@ namespace data
typedef Tag<32> IntroKey; // should be castable to MacKey and AESKey
struct Introducer
{
Introducer (): iExp (0) {};
Introducer (): iPort (0), iExp (0) {};
boost::asio::ip::address iHost;
int iPort;
IntroKey iKey;
@@ -102,7 +115,6 @@ namespace data
{
Tag<32> staticKey;
Tag<16> iv;
bool isPublished = false;
};
struct Address
@@ -111,14 +123,15 @@ namespace data
boost::asio::ip::address host;
int port;
uint64_t date;
uint8_t cost;
uint8_t caps;
bool published = false;
std::unique_ptr<SSUExt> ssu; // not null for SSU
std::unique_ptr<NTCP2Ext> ntcp2; // not null for NTCP2
bool IsCompatible (const boost::asio::ip::address& other) const
{
return (host.is_v4 () && other.is_v4 ()) ||
(host.is_v6 () && other.is_v6 ());
return (IsV4 () && other.is_v4 ()) ||
(IsV6 () && other.is_v6 ());
}
bool operator==(const Address& other) const
@@ -133,7 +146,15 @@ namespace data
}
bool IsNTCP2 () const { return (bool)ntcp2; };
bool IsPublishedNTCP2 () const { return IsNTCP2 () && ntcp2->isPublished; };
bool IsPublishedNTCP2 () const { return IsNTCP2 () && published; };
bool IsReachableSSU () const { return (bool)ssu && (!host.is_unspecified () || !ssu->introducers.empty ()); };
bool UsesIntroducer () const { return (bool)ssu && !ssu->introducers.empty (); };
bool IsIntroducer () const { return caps & eSSUIntroducer; };
bool IsPeerTesting () const { return caps & eSSUTesting; };
bool IsV4 () const { return (caps & AddressCaps::eV4) || (host.is_v4 () && !host.is_unspecified ()); };
bool IsV6 () const { return (caps & AddressCaps::eV6) || (host.is_v6 () && !host.is_unspecified ()); };
};
typedef std::list<std::shared_ptr<Address> > Addresses;
@@ -150,7 +171,7 @@ namespace data
uint64_t GetTimestamp () const { return m_Timestamp; };
int GetVersion () const { return m_Version; };
Addresses& GetAddresses () { return *m_Addresses; }; // should be called for local RI only, otherwise must return shared_ptr
std::shared_ptr<const Address> GetNTCP2Address (bool publishedOnly) const;
std::shared_ptr<const Address> GetNTCP2AddressWithStaticKey (const uint8_t * key) const;
std::shared_ptr<const Address> GetPublishedNTCP2V4Address () const;
std::shared_ptr<const Address> GetPublishedNTCP2V6Address () const;
std::shared_ptr<const Address> GetSSUAddress (bool v4only = true) const;
@@ -165,6 +186,7 @@ namespace data
void DeleteProperty (const std::string& key); // called from RouterContext only
std::string GetProperty (const std::string& key) const; // called from RouterContext only
void ClearProperties () { m_Properties.clear (); };
void SetUnreachableAddressesTransportCaps (uint8_t transports); // bitmask of AddressCaps
bool IsFloodfill () const { return m_Caps & Caps::eFloodfill; };
bool IsReachable () const { return m_Caps & Caps::eReachable; };
bool IsSSU (bool v4only = true) const;
@@ -180,15 +202,15 @@ namespace data
void DisableV4 ();
void EnableMesh ();
void DisableMesh ();
bool IsCompatible (const RouterInfo& other) const { return m_SupportedTransports & other.m_SupportedTransports; };
bool IsCompatible (const RouterInfo& other) const { return m_SupportedTransports & other.m_SupportedTransports; };
bool IsReachableFrom (const RouterInfo& other) const;
bool HasValidAddresses () const { return m_SupportedTransports; };
bool UsesIntroducer () const;
bool IsIntroducer () const { return m_Caps & eSSUIntroducer; };
bool IsPeerTesting () const { return m_Caps & eSSUTesting; };
bool IsHidden () const { return m_Caps & eHidden; };
bool IsHighBandwidth () const { return m_Caps & RouterInfo::eHighBandwidth; };
bool IsExtraBandwidth () const { return m_Caps & RouterInfo::eExtraBandwidth; };
bool IsEligibleFloodfill () const;
bool IsPeerTesting (bool v4) const;
bool IsIntroducer (bool v4) const;
uint8_t GetCaps () const { return m_Caps; };
void SetCaps (uint8_t caps);
@@ -232,6 +254,7 @@ namespace data
size_t ReadString (char* str, size_t len, std::istream& s) const;
void WriteString (const std::string& str, std::ostream& s) const;
void ExtractCaps (const char * value);
uint8_t ExtractAddressCaps (const char * value) const;
template<typename Filter>
std::shared_ptr<const Address> GetAddress (Filter filter) const;
void UpdateCapsProperty ();

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2020, The PurpleI2P Project
* Copyright (c) 2013-2021, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@@ -22,31 +22,15 @@ namespace i2p
{
namespace transport
{
SSUServer::SSUServer (const boost::asio::ip::address & addr, int port):
m_OnlyV6(true), m_IsRunning(false), m_Thread (nullptr),
m_ReceiversThread (nullptr), m_ReceiversThreadV6 (nullptr), m_Work (m_Service),
m_ReceiversWork (m_ReceiversService), m_ReceiversWorkV6 (m_ReceiversServiceV6),
m_EndpointV6 (addr, port), m_Socket (m_ReceiversService, m_Endpoint),
m_SocketV6 (m_ReceiversServiceV6), m_IntroducersUpdateTimer (m_Service),
m_PeerTestsCleanupTimer (m_Service), m_TerminationTimer (m_Service),
m_TerminationTimerV6 (m_Service)
{
OpenSocketV6 ();
}
SSUServer::SSUServer (int port):
m_OnlyV6(false), m_IsRunning(false), m_Thread (nullptr),
m_IsRunning(false), m_Thread (nullptr),
m_ReceiversThread (nullptr), m_ReceiversThreadV6 (nullptr), m_Work (m_Service),
m_ReceiversWork (m_ReceiversService), m_ReceiversWorkV6 (m_ReceiversServiceV6),
m_Endpoint (boost::asio::ip::udp::v4 (), port), m_EndpointV6 (boost::asio::ip::udp::v6 (), port),
m_Socket (m_ReceiversService), m_SocketV6 (m_ReceiversServiceV6),
m_IntroducersUpdateTimer (m_Service), m_PeerTestsCleanupTimer (m_Service),
m_TerminationTimer (m_Service), m_TerminationTimerV6 (m_Service)
m_IntroducersUpdateTimer (m_Service), m_IntroducersUpdateTimerV6 (m_Service),
m_PeerTestsCleanupTimer (m_Service), m_TerminationTimer (m_Service), m_TerminationTimerV6 (m_Service)
{
OpenSocket ();
if (context.SupportsV6 ())
OpenSocketV6 ();
}
SSUServer::~SSUServer ()
@@ -91,23 +75,24 @@ namespace transport
void SSUServer::Start ()
{
m_IsRunning = true;
if (!m_OnlyV6)
m_Thread = new std::thread (std::bind (&SSUServer::Run, this));
if (context.SupportsV4 ())
{
OpenSocket ();
m_ReceiversThread = new std::thread (std::bind (&SSUServer::RunReceivers, this));
m_Thread = new std::thread (std::bind (&SSUServer::Run, this));
m_ReceiversService.post (std::bind (&SSUServer::Receive, this));
ScheduleTermination ();
ScheduleIntroducersUpdateTimer (); // wait for 30 seconds and decide if we need introducers
}
if (context.SupportsV6 ())
{
OpenSocketV6 ();
m_ReceiversThreadV6 = new std::thread (std::bind (&SSUServer::RunReceiversV6, this));
if (!m_Thread)
m_Thread = new std::thread (std::bind (&SSUServer::Run, this));
m_ReceiversServiceV6.post (std::bind (&SSUServer::ReceiveV6, this));
ScheduleTerminationV6 ();
ScheduleIntroducersUpdateTimerV6 (); // wait for 30 seconds and decide if we need introducers
}
SchedulePeerTestsCleanupTimer ();
ScheduleIntroducersUpdateTimer (); // wait for 30 seconds and decide if we need introducers
}
void SSUServer::Stop ()
@@ -116,6 +101,8 @@ namespace transport
m_IsRunning = false;
m_TerminationTimer.cancel ();
m_TerminationTimerV6.cancel ();
m_IntroducersUpdateTimer.cancel ();
m_IntroducersUpdateTimerV6.cancel ();
m_Service.stop ();
m_Socket.close ();
m_SocketV6.close ();
@@ -138,7 +125,7 @@ namespace transport
m_Thread->join ();
delete m_Thread;
m_Thread = nullptr;
}
}
}
void SSUServer::Run ()
@@ -205,6 +192,14 @@ namespace transport
}
}
void SSUServer::SetLocalAddress (const boost::asio::ip::address& localAddress)
{
if (localAddress.is_v6 ())
m_EndpointV6.address (localAddress);
else if (localAddress.is_v4 ())
m_Endpoint.address (localAddress);
}
void SSUServer::AddRelay (uint32_t tag, std::shared_ptr<SSUSession> relay)
{
m_Relays[tag] = relay;
@@ -393,7 +388,7 @@ namespace transport
auto it = sessions->find (packet->from);
if (it != sessions->end ())
session = it->second;
if (!session)
if (!session && packet->len > 0)
{
session = std::make_shared<SSUSession> (*this, packet->from);
session->WaitForConnect ();
@@ -401,7 +396,8 @@ namespace transport
LogPrint (eLogDebug, "SSU: new session from ", packet->from.address ().to_string (), ":", packet->from.port (), " created");
}
}
session->ProcessNextMessage (packet->buf, packet->len, packet->from);
if (session)
session->ProcessNextMessage (packet->buf, packet->len, packet->from);
}
catch (std::exception& ex)
{
@@ -414,23 +410,9 @@ namespace transport
if (session) session->FlushData ();
}
std::shared_ptr<SSUSession> SSUServer::FindSession (std::shared_ptr<const i2p::data::RouterInfo> router) const
{
if (!router) return nullptr;
auto address = router->GetSSUAddress (true); // v4 only
if (!address) return nullptr;
auto session = FindSession (boost::asio::ip::udp::endpoint (address->host, address->port));
if (session || !context.SupportsV6 ())
return session;
// try v6
address = router->GetSSUV6Address ();
if (!address) return nullptr;
return FindSession (boost::asio::ip::udp::endpoint (address->host, address->port));
}
std::shared_ptr<SSUSession> SSUServer::FindSession (const boost::asio::ip::udp::endpoint& e) const
{
auto& sessions = e.address ().is_v6 () ? m_SessionsV6 : m_Sessions;
auto& sessions = e.address ().is_v6 () ? m_SessionsV6 : m_Sessions;
auto it = sessions.find (e);
if (it != sessions.end ())
return it->second;
@@ -438,28 +420,33 @@ namespace transport
return nullptr;
}
void SSUServer::CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest, bool v4only)
bool SSUServer::CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest, bool v4only)
{
auto address = router->GetSSUAddress (v4only || !context.SupportsV6 ());
if (address)
CreateSession (router, address->host, address->port, peerTest);
return CreateSession (router, address, peerTest);
else
LogPrint (eLogWarning, "SSU: Router ", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()), " doesn't have SSU address");
return false;
}
void SSUServer::CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router,
const boost::asio::ip::address& addr, int port, bool peerTest)
bool SSUServer::CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router,
std::shared_ptr<const i2p::data::RouterInfo::Address> address, bool peerTest)
{
if (router)
if (router && address)
{
if (router->UsesIntroducer ())
m_Service.post (std::bind (&SSUServer::CreateSessionThroughIntroducer, this, router, peerTest)); // always V4 thread
if (address->UsesIntroducer ())
m_Service.post (std::bind (&SSUServer::CreateSessionThroughIntroducer, this, router, address, peerTest)); // always V4 thread
else
{
boost::asio::ip::udp::endpoint remoteEndpoint (addr, port);
if (address->host.is_unspecified () || !address->port) return false;
boost::asio::ip::udp::endpoint remoteEndpoint (address->host, address->port);
m_Service.post (std::bind (&SSUServer::CreateDirectSession, this, router, remoteEndpoint, peerTest));
}
}
else
return false;
return true;
}
void SSUServer::CreateDirectSession (std::shared_ptr<const i2p::data::RouterInfo> router, boost::asio::ip::udp::endpoint remoteEndpoint, bool peerTest)
@@ -485,87 +472,107 @@ namespace transport
}
}
void SSUServer::CreateSessionThroughIntroducer (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest)
void SSUServer::CreateSessionThroughIntroducer (std::shared_ptr<const i2p::data::RouterInfo> router,
std::shared_ptr<const i2p::data::RouterInfo::Address> address, bool peerTest)
{
if (router && router->UsesIntroducer ())
if (router && address && address->UsesIntroducer ())
{
auto address = router->GetSSUAddress (true); // v4 only for now
if (address)
if (address->IsV4 () && !i2p::context.SupportsV4 ()) return;
if (address->IsV6 () && !i2p::context.SupportsV6 ()) return;
if (!address->host.is_unspecified () && address->port)
{
// we rarely come here
auto& sessions = address->host.is_v6 () ? m_SessionsV6 : m_Sessions;
boost::asio::ip::udp::endpoint remoteEndpoint (address->host, address->port);
auto it = m_Sessions.find (remoteEndpoint);
auto it = sessions.find (remoteEndpoint);
// check if session is presented already
if (it != m_Sessions.end ())
if (it != sessions.end ())
{
auto session = it->second;
if (peerTest && session->GetState () == eSessionStateEstablished)
session->SendPeerTest ();
return;
}
// create new session
int numIntroducers = address->ssu->introducers.size ();
if (numIntroducers > 0)
}
// create new session
int numIntroducers = address->ssu->introducers.size ();
if (numIntroducers > 0)
{
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
std::shared_ptr<SSUSession> introducerSession;
const i2p::data::RouterInfo::Introducer * introducer = nullptr;
// we might have a session to introducer already
auto offset = rand ();
for (int i = 0; i < numIntroducers; i++)
{
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
std::shared_ptr<SSUSession> introducerSession;
const i2p::data::RouterInfo::Introducer * introducer = nullptr;
// we might have a session to introducer already
for (int i = 0; i < numIntroducers; i++)
auto intr = &(address->ssu->introducers[(offset + i)%numIntroducers]);
if (!intr->iPort) continue; // skip invalid introducer
if (intr->iExp > 0 && ts > intr->iExp) continue; // skip expired introducer
boost::asio::ip::udp::endpoint ep (intr->iHost, intr->iPort);
if (ep.address ().is_v4 () && address->IsV4 ()) // ipv4
{
auto intr = &(address->ssu->introducers[i]);
if (intr->iExp > 0 && ts > intr->iExp) continue; // skip expired introducer
boost::asio::ip::udp::endpoint ep (intr->iHost, intr->iPort);
if (ep.address ().is_v4 ()) // ipv4 only
if (!introducer) introducer = intr;
auto it = m_Sessions.find (ep);
if (it != m_Sessions.end ())
{
if (!introducer) introducer = intr; // we pick first one for now
it = m_Sessions.find (ep);
if (it != m_Sessions.end ())
{
introducerSession = it->second;
break;
}
introducerSession = it->second;
break;
}
}
if (!introducer)
if (ep.address ().is_v6 () && address->IsV6 ()) // ipv6
{
LogPrint (eLogWarning, "SSU: Can't connect to unreachable router and no ipv4 non-expired introducers presented");
return;
}
if (introducerSession) // session found
LogPrint (eLogWarning, "SSU: Session to introducer already exists");
else // create new
{
LogPrint (eLogDebug, "SSU: Creating new session to introducer ", introducer->iHost);
boost::asio::ip::udp::endpoint introducerEndpoint (introducer->iHost, introducer->iPort);
introducerSession = std::make_shared<SSUSession> (*this, introducerEndpoint, router);
m_Sessions[introducerEndpoint] = introducerSession;
}
#if BOOST_VERSION >= 104900
if (!address->host.is_unspecified () && address->port)
#endif
{
// create session
auto session = std::make_shared<SSUSession> (*this, remoteEndpoint, router, peerTest);
m_Sessions[remoteEndpoint] = session;
// introduce
LogPrint (eLogInfo, "SSU: Introduce new session to [", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()),
"] through introducer ", introducer->iHost, ":", introducer->iPort);
session->WaitForIntroduction ();
if (i2p::context.GetRouterInfo ().UsesIntroducer ()) // if we are unreachable
if (!introducer) introducer = intr;
auto it = m_SessionsV6.find (ep);
if (it != m_SessionsV6.end ())
{
uint8_t buf[1];
Send (buf, 0, remoteEndpoint); // send HolePunch
introducerSession = it->second;
break;
}
}
introducerSession->Introduce (*introducer, router);
}
else
LogPrint (eLogWarning, "SSU: Can't connect to unreachable router and no introducers present");
if (!introducer)
{
LogPrint (eLogWarning, "SSU: Can't connect to unreachable router and no compatibe non-expired introducers presented");
return;
}
if (introducerSession) // session found
LogPrint (eLogWarning, "SSU: Session to introducer already exists");
else // create new
{
LogPrint (eLogDebug, "SSU: Creating new session to introducer ", introducer->iHost);
boost::asio::ip::udp::endpoint introducerEndpoint (introducer->iHost, introducer->iPort);
introducerSession = std::make_shared<SSUSession> (*this, introducerEndpoint, router);
if (introducerEndpoint.address ().is_v4 ())
m_Sessions[introducerEndpoint] = introducerSession;
else if (introducerEndpoint.address ().is_v6 ())
m_SessionsV6[introducerEndpoint] = introducerSession;
}
if (!address->host.is_unspecified () && address->port)
{
// create session
boost::asio::ip::udp::endpoint remoteEndpoint (address->host, address->port);
auto session = std::make_shared<SSUSession> (*this, remoteEndpoint, router, peerTest);
if (address->host.is_v4 ())
m_Sessions[remoteEndpoint] = session;
else if (address->host.is_v6 ())
m_SessionsV6[remoteEndpoint] = session;
// introduce
LogPrint (eLogInfo, "SSU: Introduce new session to [", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()),
"] through introducer ", introducer->iHost, ":", introducer->iPort);
session->WaitForIntroduction ();
if ((address->host.is_v4 () && i2p::context.GetStatus () == eRouterStatusFirewalled) ||
(address->host.is_v6 () && i2p::context.GetStatusV6 () == eRouterStatusFirewalled))
{
uint8_t buf[1];
Send (buf, 0, remoteEndpoint); // send HolePunch
}
}
introducerSession->Introduce (*introducer, router);
}
else
LogPrint (eLogWarning, "SSU: Router ", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()), " doesn't have SSU address");
LogPrint (eLogWarning, "SSU: Can't connect to unreachable router and no introducers present");
}
}
@@ -641,71 +648,151 @@ namespace transport
);
}
std::set<SSUSession *> SSUServer::FindIntroducers (int maxNumIntroducers)
std::list<std::shared_ptr<SSUSession> > SSUServer::FindIntroducers (int maxNumIntroducers,
bool v4, std::set<i2p::data::IdentHash>& excluded)
{
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
std::set<SSUSession *> ret;
for (int i = 0; i < maxNumIntroducers; i++)
std::list<std::shared_ptr<SSUSession> > ret;
const auto& sessions = v4 ? m_Sessions : m_SessionsV6;
for (const auto& s : sessions)
{
auto session = GetRandomV4Session (
[&ret, ts](std::shared_ptr<SSUSession> session)->bool
{
return session->GetRelayTag () && !ret.count (session.get ()) &&
session->GetState () == eSessionStateEstablished &&
ts < session->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_DURATION;
}
);
if (session)
if (s.second->GetRelayTag () && s.second->GetState () == eSessionStateEstablished &&
ts < s.second->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_EXPIRATION)
ret.push_back (s.second);
else if (s.second->GetRemoteIdentity ())
excluded.insert (s.second->GetRemoteIdentity ()->GetIdentHash ());
}
if ((int)ret.size () > maxNumIntroducers)
{
// shink ret randomly
int sz = ret.size () - maxNumIntroducers;
for (int i = 0; i < sz; i++)
{
ret.insert (session.get ());
break;
auto ind = rand () % ret.size ();
auto it = ret.begin ();
std::advance (it, ind);
ret.erase (it);
}
}
return ret;
}
void SSUServer::RescheduleIntroducersUpdateTimer ()
{
m_IntroducersUpdateTimer.cancel ();
m_IntroducersUpdateTimer.expires_from_now (boost::posix_time::seconds(SSU_KEEP_ALIVE_INTERVAL/2));
m_IntroducersUpdateTimer.async_wait (std::bind (&SSUServer::HandleIntroducersUpdateTimer,
this, std::placeholders::_1, true));
}
void SSUServer::ScheduleIntroducersUpdateTimer ()
{
m_IntroducersUpdateTimer.expires_from_now (boost::posix_time::seconds(SSU_KEEP_ALIVE_INTERVAL));
m_IntroducersUpdateTimer.async_wait (std::bind (&SSUServer::HandleIntroducersUpdateTimer,
this, std::placeholders::_1));
this, std::placeholders::_1, true));
}
void SSUServer::HandleIntroducersUpdateTimer (const boost::system::error_code& ecode)
void SSUServer::RescheduleIntroducersUpdateTimerV6 ()
{
m_IntroducersUpdateTimerV6.cancel ();
m_IntroducersUpdateTimerV6.expires_from_now (boost::posix_time::seconds(SSU_KEEP_ALIVE_INTERVAL/2));
m_IntroducersUpdateTimerV6.async_wait (std::bind (&SSUServer::HandleIntroducersUpdateTimer,
this, std::placeholders::_1, false));
}
void SSUServer::ScheduleIntroducersUpdateTimerV6 ()
{
m_IntroducersUpdateTimerV6.expires_from_now (boost::posix_time::seconds(SSU_KEEP_ALIVE_INTERVAL));
m_IntroducersUpdateTimerV6.async_wait (std::bind (&SSUServer::HandleIntroducersUpdateTimer,
this, std::placeholders::_1, false));
}
void SSUServer::HandleIntroducersUpdateTimer (const boost::system::error_code& ecode, bool v4)
{
if (ecode != boost::asio::error::operation_aborted)
{
// timeout expired
if (i2p::context.GetStatus () == eRouterStatusTesting)
if (v4)
{
// we still don't know if we need introducers
ScheduleIntroducersUpdateTimer ();
return;
if (i2p::context.GetStatus () == eRouterStatusTesting)
{
// we still don't know if we need introducers
ScheduleIntroducersUpdateTimer ();
return;
}
if (i2p::context.GetStatus () != eRouterStatusFirewalled)
{
// we don't need introducers
m_Introducers.clear ();
return;
}
// we are firewalled
if (!i2p::context.IsUnreachable ()) i2p::context.SetUnreachable (true, false); // v4
}
if (i2p::context.GetStatus () == eRouterStatusOK) return; // we don't need introducers anymore
// we are firewalled
if (!i2p::context.IsUnreachable ()) i2p::context.SetUnreachable ();
else
{
if (i2p::context.GetStatusV6 () == eRouterStatusTesting)
{
// we still don't know if we need introducers
ScheduleIntroducersUpdateTimerV6 ();
return;
}
if (i2p::context.GetStatusV6 () != eRouterStatusFirewalled)
{
// we don't need introducers
m_IntroducersV6.clear ();
return;
}
// we are firewalled
auto addr = i2p::context.GetRouterInfo ().GetSSUV6Address ();
if (addr && addr->ssu && addr->ssu->introducers.empty ())
i2p::context.SetUnreachable (false, true); // v6
}
std::list<boost::asio::ip::udp::endpoint> newList;
size_t numIntroducers = 0;
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
for (const auto& it : m_Introducers)
std::set<i2p::data::IdentHash> excluded;
auto& introducers = v4 ? m_Introducers : m_IntroducersV6;
for (const auto& it : introducers)
{
auto session = FindSession (it);
if (session && ts < session->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_DURATION)
if (session)
{
session->SendKeepAlive ();
newList.push_back (it);
numIntroducers++;
if (ts < session->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_EXPIRATION)
session->SendKeepAlive ();
if (ts < session->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_DURATION)
{
newList.push_back (it);
numIntroducers++;
if (session->GetRemoteIdentity ())
excluded.insert (session->GetRemoteIdentity ()->GetIdentHash ());
}
else
session = nullptr;
}
else
if (!session)
i2p::context.RemoveIntroducer (it);
}
if (numIntroducers < SSU_MAX_NUM_INTRODUCERS)
{
// create new
auto introducers = FindIntroducers (SSU_MAX_NUM_INTRODUCERS);
for (const auto& it1: introducers)
auto sessions = FindIntroducers (SSU_MAX_NUM_INTRODUCERS, v4, excluded); // try to find if duplicates
if (sessions.empty () && !introducers.empty ())
{
// bump creation time for previous introducers if no new sessions found
LogPrint (eLogDebug, "SSU: no new introducers found. Trying to reuse existing");
for (const auto& it : introducers)
{
auto session = FindSession (it);
if (session)
session->SetCreationTime (session->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_DURATION);
}
// try again
excluded.clear ();
sessions = FindIntroducers (SSU_MAX_NUM_INTRODUCERS, v4, excluded);
}
for (const auto& it1: sessions)
{
const auto& ep = it1->GetRemoteEndpoint ();
i2p::data::RouterInfo::Introducer introducer;
@@ -713,21 +800,46 @@ namespace transport
introducer.iPort = ep.port ();
introducer.iTag = it1->GetRelayTag ();
introducer.iKey = it1->GetIntroKey ();
introducer.iExp = it1->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_EXPIRATION;
if (i2p::context.AddIntroducer (introducer))
{
newList.push_back (ep);
if (newList.size () >= SSU_MAX_NUM_INTRODUCERS) break;
}
if (it1->GetRemoteIdentity ())
excluded.insert (it1->GetRemoteIdentity ()->GetIdentHash ());
}
}
m_Introducers = newList;
if (m_Introducers.size () < SSU_MAX_NUM_INTRODUCERS)
introducers = newList;
if (introducers.size () < SSU_MAX_NUM_INTRODUCERS)
{
auto introducer = i2p::data::netdb.GetRandomIntroducer ();
if (introducer)
CreateSession (introducer);
for (auto i = introducers.size (); i < SSU_MAX_NUM_INTRODUCERS; i++)
{
auto introducer = i2p::data::netdb.GetRandomIntroducer (v4, excluded);
if (introducer)
{
auto address = v4 ? introducer->GetSSUAddress (true) : introducer->GetSSUV6Address ();
if (address && !address->host.is_unspecified () && address->port)
{
boost::asio::ip::udp::endpoint ep (address->host, address->port);
if (std::find (introducers.begin (), introducers.end (), ep) == introducers.end ()) // not connected yet
{
CreateDirectSession (introducer, ep, false);
excluded.insert (introducer->GetIdentHash ());
}
}
}
else
{
LogPrint (eLogDebug, "SSU: can't find more introducers");
break;
}
}
}
ScheduleIntroducersUpdateTimer ();
if (v4)
ScheduleIntroducersUpdateTimer ();
else
ScheduleIntroducersUpdateTimerV6 ();
}
}

View File

@@ -31,6 +31,7 @@ namespace transport
const int SSU_KEEP_ALIVE_INTERVAL = 30; // 30 seconds
const int SSU_PEER_TEST_TIMEOUT = 60; // 60 seconds
const int SSU_TO_INTRODUCER_SESSION_DURATION = 3600; // 1 hour
const int SSU_TO_INTRODUCER_SESSION_EXPIRATION = 4800; // 80 minutes
const int SSU_TERMINATION_CHECK_TIMEOUT = 30; // 30 seconds
const size_t SSU_MAX_NUM_INTRODUCERS = 3;
const size_t SSU_SOCKET_RECEIVE_BUFFER_SIZE = 0x1FFFF; // 128K
@@ -48,15 +49,13 @@ namespace transport
public:
SSUServer (int port);
SSUServer (const boost::asio::ip::address & addr, int port); // ipv6 only constructor
~SSUServer ();
void Start ();
void Stop ();
void CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest = false, bool v4only = false);
void CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router,
const boost::asio::ip::address& addr, int port, bool peerTest = false);
bool CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest = false, bool v4only = false);
bool CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router,
std::shared_ptr<const i2p::data::RouterInfo::Address> address, bool peerTest = false);
void CreateDirectSession (std::shared_ptr<const i2p::data::RouterInfo> router, boost::asio::ip::udp::endpoint remoteEndpoint, bool peerTest);
std::shared_ptr<SSUSession> FindSession (std::shared_ptr<const i2p::data::RouterInfo> router) const;
std::shared_ptr<SSUSession> FindSession (const boost::asio::ip::udp::endpoint& e) const;
std::shared_ptr<SSUSession> GetRandomEstablishedV4Session (std::shared_ptr<const SSUSession> excluded);
std::shared_ptr<SSUSession> GetRandomEstablishedV6Session (std::shared_ptr<const SSUSession> excluded);
@@ -64,12 +63,16 @@ namespace transport
void DeleteAllSessions ();
boost::asio::io_service& GetService () { return m_Service; };
const boost::asio::ip::udp::endpoint& GetEndpoint () const { return m_Endpoint; };
uint16_t GetPort () const { return m_Endpoint.port (); };
void SetLocalAddress (const boost::asio::ip::address& localAddress);
void Send (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& to);
void AddRelay (uint32_t tag, std::shared_ptr<SSUSession> relay);
void RemoveRelay (uint32_t tag);
std::shared_ptr<SSUSession> FindRelaySession (uint32_t tag);
void RescheduleIntroducersUpdateTimer ();
void RescheduleIntroducersUpdateTimerV6 ();
void NewPeerTest (uint32_t nonce, PeerTestParticipant role, std::shared_ptr<SSUSession> session = nullptr);
PeerTestParticipant GetPeerTestParticipant (uint32_t nonce);
std::shared_ptr<SSUSession> GetPeerTestSession (uint32_t nonce);
@@ -90,15 +93,17 @@ namespace transport
void HandleReceivedPackets (std::vector<SSUPacket *> packets,
std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<SSUSession> >* sessions);
void CreateSessionThroughIntroducer (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest = false);
void CreateSessionThroughIntroducer (std::shared_ptr<const i2p::data::RouterInfo> router,
std::shared_ptr<const i2p::data::RouterInfo::Address> address, bool peerTest = false);
template<typename Filter>
std::shared_ptr<SSUSession> GetRandomV4Session (Filter filter);
template<typename Filter>
std::shared_ptr<SSUSession> GetRandomV6Session (Filter filter);
std::set<SSUSession *> FindIntroducers (int maxNumIntroducers);
std::list<std::shared_ptr<SSUSession> > FindIntroducers (int maxNumIntroducers, bool v4, std::set<i2p::data::IdentHash>& excluded);
void ScheduleIntroducersUpdateTimer ();
void HandleIntroducersUpdateTimer (const boost::system::error_code& ecode);
void ScheduleIntroducersUpdateTimerV6 ();
void HandleIntroducersUpdateTimer (const boost::system::error_code& ecode, bool v4);
void SchedulePeerTestsCleanupTimer ();
void HandlePeerTestsCleanupTimer (const boost::system::error_code& ecode);
@@ -118,16 +123,15 @@ namespace transport
std::shared_ptr<SSUSession> session; // for Bob to Alice
};
bool m_OnlyV6;
volatile bool m_IsRunning;
std::thread * m_Thread, * m_ReceiversThread, * m_ReceiversThreadV6;
boost::asio::io_service m_Service, m_ReceiversService, m_ReceiversServiceV6;
boost::asio::io_service::work m_Work, m_ReceiversWork, m_ReceiversWorkV6;
boost::asio::ip::udp::endpoint m_Endpoint, m_EndpointV6;
boost::asio::ip::udp::socket m_Socket, m_SocketV6;
boost::asio::deadline_timer m_IntroducersUpdateTimer, m_PeerTestsCleanupTimer,
m_TerminationTimer, m_TerminationTimerV6;
std::list<boost::asio::ip::udp::endpoint> m_Introducers; // introducers we are connected to
boost::asio::deadline_timer m_IntroducersUpdateTimer, m_IntroducersUpdateTimerV6,
m_PeerTestsCleanupTimer, m_TerminationTimer, m_TerminationTimerV6;
std::list<boost::asio::ip::udp::endpoint> m_Introducers, m_IntroducersV6; // introducers we are connected to
std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<SSUSession> > m_Sessions, m_SessionsV6;
std::map<uint32_t, std::shared_ptr<SSUSession> > m_Relays; // we are introducer
std::map<uint32_t, PeerTest> m_PeerTests; // nonce -> creation time in milliseconds

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2020, The PurpleI2P Project
* Copyright (c) 2013-2021, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@@ -120,7 +120,8 @@ namespace transport
else
{
// try own intro key
auto address = i2p::context.GetRouterInfo ().GetSSUAddress (false);
auto address = IsV6 () ? i2p::context.GetRouterInfo ().GetSSUV6Address () :
i2p::context.GetRouterInfo ().GetSSUAddress (true);
if (!address)
{
LogPrint (eLogInfo, "SSU is not supported");
@@ -212,7 +213,7 @@ namespace transport
{
uint8_t extendedOptionsLen = buf[headerSize];
headerSize++;
if (extendedOptionsLen >= 3) // options are presented
if (extendedOptionsLen >= 2) // options are presented
{
uint16_t flags = bufbe16toh (buf + headerSize);
sendRelayTag = flags & EXTENDED_OPTIONS_FLAG_REQUEST_RELAY_TAG;
@@ -257,27 +258,14 @@ namespace transport
s.Insert (m_DHKeysPair->GetPublicKey (), 256); // x
s.Insert (y, 256); // y
payload += 256;
uint8_t addressSize = *payload;
payload += 1; // size
uint8_t * ourAddress = payload;
boost::asio::ip::address ourIP;
if (addressSize == 4) // v4
{
boost::asio::ip::address_v4::bytes_type bytes;
memcpy (bytes.data (), ourAddress, 4);
ourIP = boost::asio::ip::address_v4 (bytes);
}
else // v6
{
boost::asio::ip::address_v6::bytes_type bytes;
memcpy (bytes.data (), ourAddress, 16);
ourIP = boost::asio::ip::address_v6 (bytes);
}
s.Insert (ourAddress, addressSize); // our IP
payload += addressSize; // address
uint16_t ourPort = bufbe16toh (payload);
s.Insert (payload, 2); // our port
payload += 2; // port
uint16_t ourPort = 0;
auto addressAndPortLen = ExtractIPAddressAndPort (payload, len, ourIP, ourPort);
if (!addressAndPortLen) return;
uint8_t * ourAddressAndPort = payload + 1;
payload += addressAndPortLen;
addressAndPortLen--; // -1 byte address size
s.Insert (ourAddressAndPort, addressAndPortLen); // address + port
if (m_RemoteEndpoint.address ().is_v4 ())
s.Insert (m_RemoteEndpoint.address ().to_v4 ().to_bytes ().data (), 4); // remote IP v4
else
@@ -286,7 +274,7 @@ namespace transport
s.Insert (payload, 8); // relayTag and signed on time
m_RelayTag = bufbe32toh (payload);
payload += 4; // relayTag
if (i2p::context.GetStatus () == eRouterStatusTesting)
if (ourIP.is_v4 () && i2p::context.GetStatus () == eRouterStatusTesting)
{
auto ts = i2p::util::GetSecondsSinceEpoch ();
uint32_t signedOnTime = bufbe32toh(payload);
@@ -308,8 +296,16 @@ namespace transport
if (s.Verify (m_RemoteIdentity, payload))
{
LogPrint (eLogInfo, "SSU: Our external address is ", ourIP.to_string (), ":", ourPort);
i2p::context.UpdateAddress (ourIP);
SendSessionConfirmed (y, ourAddress, addressSize + 2);
if (!i2p::util::net::IsInReservedRange (ourIP))
{
i2p::context.UpdateAddress (ourIP);
SendSessionConfirmed (y, ourAddressAndPort, addressAndPortLen);
}
else
{
LogPrint (eLogError, "SSU: Wrong external address ", ourIP.to_string ());
Failed ();
}
}
else
{
@@ -321,15 +317,21 @@ namespace transport
void SSUSession::ProcessSessionConfirmed (const uint8_t * buf, size_t len)
{
LogPrint (eLogDebug, "SSU: Session confirmed received");
m_ConnectTimer.cancel ();
auto headerSize = GetSSUHeaderSize (buf);
if (headerSize >= len)
{
LogPrint (eLogError, "SSU: Session confirmed header size ", len, " exceeds packet length ", len);
LogPrint (eLogError, "SSU: Session confirmed header size ", headerSize, " exceeds packet length ", len);
return;
}
const uint8_t * payload = buf + headerSize;
payload++; // identity fragment info
uint16_t identitySize = bufbe16toh (payload);
if (identitySize + headerSize + 7 > len) // 7 = fragment info + fragment size + signed on time
{
LogPrint (eLogError, "SSU: Session confirmed identity size ", identitySize, " exceeds packet length ", len);
return;
}
payload += 2; // size of identity fragment
auto identity = std::make_shared<i2p::data::IdentityEx> (payload, identitySize);
auto existing = i2p::data::netdb.FindRouter (identity->GetIdentHash ()); // check if exists already
@@ -347,10 +349,15 @@ namespace transport
if (m_SignedData)
m_SignedData->Insert (payload, 4); // insert Alice's signed on time
payload += 4; // signed-on time
size_t paddingSize = (payload - buf) + m_RemoteIdentity->GetSignatureLen ();
paddingSize &= 0x0F; // %16
size_t fullSize = (payload - buf) + m_RemoteIdentity->GetSignatureLen ();
size_t paddingSize = fullSize & 0x0F; // %16
if (paddingSize > 0) paddingSize = 16 - paddingSize;
payload += paddingSize;
if (fullSize + paddingSize > len)
{
LogPrint (eLogError, "SSU: Session confirmed message is too short ", len);
return;
}
// verify signature
if (m_SignedData && m_SignedData->Verify (m_RemoteIdentity, payload))
{
@@ -370,18 +377,19 @@ namespace transport
uint8_t * payload = buf + sizeof (SSUHeader);
uint8_t flag = 0;
// fill extended options, 3 bytes extended options don't change message size
if (i2p::context.GetStatus () == eRouterStatusOK) // we don't need relays
bool isV4 = m_RemoteEndpoint.address ().is_v4 ();
if ((isV4 && i2p::context.GetStatus () == eRouterStatusOK) ||
(!isV4 && i2p::context.GetStatusV6 () == eRouterStatusOK)) // we don't need relays
{
// tell out peer to now assign relay tag
flag = SSU_HEADER_EXTENDED_OPTIONS_INCLUDED;
*payload = 2; payload++; // 1 byte length
*payload = 2; payload++; // 1 byte length
uint16_t flags = 0; // clear EXTENDED_OPTIONS_FLAG_REQUEST_RELAY_TAG
htobe16buf (payload, flags);
payload += 2;
}
// fill payload
memcpy (payload, m_DHKeysPair->GetPublicKey (), 256); // x
bool isV4 = m_RemoteEndpoint.address ().is_v4 ();
if (isV4)
{
payload[256] = 4;
@@ -401,7 +409,8 @@ namespace transport
void SSUSession::SendRelayRequest (const i2p::data::RouterInfo::Introducer& introducer, uint32_t nonce)
{
auto address = i2p::context.GetRouterInfo ().GetSSUAddress (false);
auto address = IsV6 () ? i2p::context.GetRouterInfo ().GetSSUV6Address () :
i2p::context.GetRouterInfo ().GetSSUAddress (true);
if (!address)
{
LogPrint (eLogInfo, "SSU is not supported");
@@ -429,6 +438,7 @@ namespace transport
else
FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_REQUEST, buf, 96, introducer.iKey, iv, introducer.iKey);
m_Server.Send (buf, 96, m_RemoteEndpoint);
LogPrint (eLogDebug, "SSU: relay request sent");
}
void SSUSession::SendSessionCreated (const uint8_t * x, bool sendRelayTag)
@@ -474,7 +484,7 @@ namespace transport
else
s.Insert (address->host.to_v6 ().to_bytes ().data (), 16); // our IP V6
s.Insert<uint16_t> (htobe16 (address->port)); // our port
if (sendRelayTag && i2p::context.GetRouterInfo ().IsIntroducer () && !IsV6 ())
if (sendRelayTag && i2p::context.GetRouterInfo ().IsIntroducer (!IsV6 ()))
{
RAND_bytes((uint8_t *)&m_SentRelayTag, 4);
if (!m_SentRelayTag) m_SentRelayTag = 1;
@@ -578,22 +588,33 @@ namespace transport
void SSUSession::SendRelayResponse (uint32_t nonce, const boost::asio::ip::udp::endpoint& from,
const uint8_t * introKey, const boost::asio::ip::udp::endpoint& to)
{
// Charlie's address always v4
if (!to.address ().is_v4 ())
bool isV4 = to.address ().is_v4 (); // Charle's
bool isV4A = from.address ().is_v4 (); // Alice's
if ((isV4 && !isV4A) || (!isV4 && isV4A))
{
LogPrint (eLogWarning, "SSU: Charlie's IP must be v4");
LogPrint (eLogWarning, "SSU: Charlie's IP and Alice's IP belong to different networks for relay response");
return;
}
uint8_t buf[80 + 18] = {0}; // 64 Alice's ipv4 and 80 Alice's ipv6
uint8_t buf[80 + 18] = {0}; // 64 for ipv4 and 80 for ipv6
uint8_t * payload = buf + sizeof (SSUHeader);
*payload = 4;
payload++; // size
htobe32buf (payload, to.address ().to_v4 ().to_ulong ()); // Charlie's IP
payload += 4; // address
// Charlie
if (isV4)
{
*payload = 4;
payload++; // size
memcpy (payload, to.address ().to_v4 ().to_bytes ().data (), 4); // Charlie's IP V4
payload += 4; // address
}
else
{
*payload = 16;
payload++; // size
memcpy (payload, to.address ().to_v6 ().to_bytes ().data (), 16); // Alice's IP V6
payload += 16; // address
}
htobe16buf (payload, to.port ()); // Charlie's port
payload += 2; // port
// Alice
bool isV4 = from.address ().is_v4 (); // Alice's
if (isV4)
{
*payload = 4;
@@ -632,57 +653,67 @@ namespace transport
void SSUSession::SendRelayIntro (std::shared_ptr<SSUSession> session, const boost::asio::ip::udp::endpoint& from)
{
if (!session) return;
// Alice's address always v4
if (!from.address ().is_v4 ())
bool isV4 = from.address ().is_v4 (); // Alice's
bool isV4C = session->m_RemoteEndpoint.address ().is_v4 (); // Charlie's
if ((isV4 && !isV4C) || (!isV4 && isV4C))
{
LogPrint (eLogWarning, "SSU: Alice's IP must be v4");
LogPrint (eLogWarning, "SSU: Charlie's IP and Alice's IP belong to different networks for relay intro");
return;
}
uint8_t buf[48 + 18] = {0};
uint8_t buf[64 + 18] = {0}; // 48 for ipv4 and 64 for ipv6
uint8_t * payload = buf + sizeof (SSUHeader);
*payload = 4;
payload++; // size
htobe32buf (payload, from.address ().to_v4 ().to_ulong ()); // Alice's IP
payload += 4; // address
if (isV4)
{
*payload = 4;
payload++; // size
memcpy (payload, from.address ().to_v4 ().to_bytes ().data (), 4); // Alice's IP V4
payload += 4; // address
}
else
{
*payload = 16;
payload++; // size
memcpy (payload, from.address ().to_v6 ().to_bytes ().data (), 16); // Alice's IP V6
payload += 16; // address
}
htobe16buf (payload, from.port ()); // Alice's port
payload += 2; // port
*payload = 0; // challenge size
uint8_t iv[16];
RAND_bytes (iv, 16); // random iv
FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_INTRO, buf, 48, session->m_SessionKey, iv, session->m_MacKey);
m_Server.Send (buf, 48, session->m_RemoteEndpoint);
FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_INTRO, buf, isV4 ? 48 : 64, session->m_SessionKey, iv, session->m_MacKey);
m_Server.Send (buf, isV4 ? 48 : 64, session->m_RemoteEndpoint);
LogPrint (eLogDebug, "SSU: relay intro sent");
}
void SSUSession::ProcessRelayResponse (const uint8_t * buf, size_t len)
{
LogPrint (eLogDebug, "SSU message: Relay response received");
uint8_t remoteSize = *buf;
buf++; // remote size
boost::asio::ip::address_v4 remoteIP (bufbe32toh (buf));
buf += remoteSize; // remote address
uint16_t remotePort = bufbe16toh (buf);
buf += 2; // remote port
uint8_t ourSize = *buf;
buf++; // our size
boost::asio::ip::address remoteIP;
uint16_t remotePort = 0;
auto remoteSize = ExtractIPAddressAndPort (buf, len, remoteIP, remotePort);
if (!remoteSize) return;
buf += remoteSize; len -= remoteSize;
boost::asio::ip::address ourIP;
if (ourSize == 4)
{
boost::asio::ip::address_v4::bytes_type bytes;
memcpy (bytes.data (), buf, 4);
ourIP = boost::asio::ip::address_v4 (bytes);
}
else
{
boost::asio::ip::address_v6::bytes_type bytes;
memcpy (bytes.data (), buf, 16);
ourIP = boost::asio::ip::address_v6 (bytes);
}
buf += ourSize; // our address
uint16_t ourPort = bufbe16toh (buf);
buf += 2; // our port
uint16_t ourPort = 0;
auto ourSize = ExtractIPAddressAndPort (buf, len, ourIP, ourPort);
if (!ourSize) return;
buf += ourSize; len -= ourSize;
LogPrint (eLogInfo, "SSU: Our external address is ", ourIP.to_string (), ":", ourPort);
i2p::context.UpdateAddress (ourIP);
if (!i2p::util::net::IsInReservedRange (ourIP))
i2p::context.UpdateAddress (ourIP);
else
LogPrint (eLogWarning, "SSU: Wrong external address ", ourIP.to_string ());
if (ourIP.is_v4 ())
{
if (ourPort != m_Server.GetPort ())
{
if (i2p::context.GetStatus () == eRouterStatusTesting)
i2p::context.SetError (eRouterErrorSymmetricNAT);
}
else if (i2p::context.GetStatus () == eRouterStatusError && i2p::context.GetError () == eRouterErrorSymmetricNAT)
i2p::context.SetStatus (eRouterStatusTesting);
}
uint32_t nonce = bufbe32toh (buf);
buf += 4; // nonce
auto it = m_RelayRequests.find (nonce);
@@ -695,12 +726,16 @@ namespace transport
// we didn't have correct endpoint when sent relay request
// now we do
LogPrint (eLogInfo, "SSU: RelayReponse connecting to endpoint ", remoteEndpoint);
if (i2p::context.GetRouterInfo ().UsesIntroducer ()) // if we are unreachable
if ((remoteIP.is_v4 () && i2p::context.GetStatus () == eRouterStatusFirewalled) ||
(remoteIP.is_v6 () && i2p::context.GetStatusV6 () == eRouterStatusFirewalled))
m_Server.Send (buf, 0, remoteEndpoint); // send HolePunch
// we assume that HolePunch has been sent by this time and our SessionRequest will go through
m_Server.CreateDirectSession (it->second, remoteEndpoint, false);
}
// delete request
m_RelayRequests.erase (it);
// cancel connect timer
m_ConnectTimer.cancel ();
}
else
LogPrint (eLogError, "SSU: Unsolicited RelayResponse, nonce=", nonce);
@@ -708,18 +743,12 @@ namespace transport
void SSUSession::ProcessRelayIntro (const uint8_t * buf, size_t len)
{
uint8_t size = *buf;
if (size == 4)
{
buf++; // size
boost::asio::ip::address_v4 address (bufbe32toh (buf));
buf += 4; // address
uint16_t port = bufbe16toh (buf);
boost::asio::ip::address ip;
uint16_t port = 0;
ExtractIPAddressAndPort (buf, len, ip, port);
if (!ip.is_unspecified () && port)
// send hole punch of 0 bytes
m_Server.Send (buf, 0, boost::asio::ip::udp::endpoint (address, port));
}
else
LogPrint (eLogWarning, "SSU: Address size ", size, " is not supported");
m_Server.Send (buf, 0, boost::asio::ip::udp::endpoint (ip, port));
}
void SSUSession::FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len,
@@ -978,15 +1007,15 @@ namespace transport
void SSUSession::ProcessPeerTest (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint)
{
uint32_t nonce = bufbe32toh (buf); // 4 bytes
uint8_t size = buf[4]; // 1 byte
const uint8_t * address = buf + 5; // big endian, size bytes
uint16_t port = buf16toh(buf + size + 5); // big endian, 2 bytes
const uint8_t * introKey = buf + size + 7;
if (port && (size != 4) && (size != 16))
boost::asio::ip::address addr; // Alice's addresss
uint16_t port = 0; // and port
auto size = ExtractIPAddressAndPort (buf + 4, len - 4, addr, port);
if (port && (size != 7) && (size != 19))
{
LogPrint (eLogWarning, "SSU: Address of ", size, " bytes not supported");
LogPrint (eLogWarning, "SSU: Address of ", size - 3, " bytes not supported");
return;
}
const uint8_t * introKey = buf + 4 + size;
switch (m_Server.GetPeerTestParticipant (nonce))
{
// existing test
@@ -995,15 +1024,29 @@ namespace transport
if (m_Server.GetPeerTestSession (nonce) == shared_from_this ()) // Alice-Bob
{
LogPrint (eLogDebug, "SSU: peer test from Bob. We are Alice");
if (i2p::context.GetStatus () == eRouterStatusTesting) // still not OK
if (IsV6 ())
{
if (i2p::context.GetStatusV6 () == eRouterStatusTesting)
{
i2p::context.SetStatusV6 (eRouterStatusFirewalled);
m_Server.RescheduleIntroducersUpdateTimerV6 ();
}
}
else if (i2p::context.GetStatus () == eRouterStatusTesting) // still not OK
{
i2p::context.SetStatus (eRouterStatusFirewalled);
m_Server.RescheduleIntroducersUpdateTimer ();
}
}
else
{
LogPrint (eLogDebug, "SSU: first peer test from Charlie. We are Alice");
if (m_State == eSessionStateEstablished)
LogPrint (eLogWarning, "SSU: first peer test from Charlie through established session. We are Alice");
i2p::context.SetStatus (eRouterStatusOK);
if (IsV6 ())
i2p::context.SetStatusV6 (eRouterStatusOK);
else
i2p::context.SetStatus (eRouterStatusOK);
m_Server.UpdatePeerTest (nonce, ePeerTestParticipantAlice2);
SendPeerTest (nonce, senderEndpoint.address (), senderEndpoint.port (), introKey, true, false); // to Charlie
}
@@ -1017,7 +1060,10 @@ namespace transport
{
// peer test successive
LogPrint (eLogDebug, "SSU: second peer test from Charlie. We are Alice");
i2p::context.SetStatus (eRouterStatusOK);
if (IsV6 ())
i2p::context.SetStatusV6 (eRouterStatusOK);
else
i2p::context.SetStatus (eRouterStatusOK);
m_Server.RemovePeerTest (nonce);
}
break;
@@ -1049,20 +1095,7 @@ namespace transport
LogPrint (eLogDebug, "SSU: peer test from Bob. We are Charlie");
m_Server.NewPeerTest (nonce, ePeerTestParticipantCharlie);
Send (PAYLOAD_TYPE_PEER_TEST, buf, len); // back to Bob
boost::asio::ip::address addr; // Alice's address
if (size == 4) // v4
{
boost::asio::ip::address_v4::bytes_type bytes;
memcpy (bytes.data (), address, 4);
addr = boost::asio::ip::address_v4 (bytes);
}
else // v6
{
boost::asio::ip::address_v6::bytes_type bytes;
memcpy (bytes.data (), address, 16);
addr = boost::asio::ip::address_v6 (bytes);
}
SendPeerTest (nonce, addr, be16toh (port), introKey); // to Alice with her address received from Bob
SendPeerTest (nonce, addr, port, introKey); // to Alice with her address received from Bob
}
else
{
@@ -1119,7 +1152,8 @@ namespace transport
if (toAddress)
{
// send our intro key to address instead of its own
auto addr = i2p::context.GetRouterInfo ().GetSSUAddress ();
auto addr = address.is_v4 () ? i2p::context.GetRouterInfo ().GetSSUAddress (true) : // ipv4
i2p::context.GetRouterInfo ().GetSSUV6Address ();
if (addr)
memcpy (payload, addr->ssu->key, 32); // intro key
else
@@ -1149,7 +1183,7 @@ namespace transport
{
// we are Alice
LogPrint (eLogDebug, "SSU: sending peer test");
auto address = i2p::context.GetRouterInfo ().GetSSUAddress (i2p::context.SupportsV4 ());
auto address = IsV6 () ? i2p::context.GetRouterInfo ().GetSSUV6Address () : i2p::context.GetRouterInfo ().GetSSUAddress (true);
if (!address)
{
LogPrint (eLogInfo, "SSU is not supported. Can't send peer test");
@@ -1222,5 +1256,36 @@ namespace transport
i2p::transport::transports.UpdateSentBytes (size);
m_Server.Send (buf, size, m_RemoteEndpoint);
}
size_t SSUSession::ExtractIPAddressAndPort (const uint8_t * buf, size_t len, boost::asio::ip::address& ip, uint16_t& port)
{
if (!len) return 0;
uint8_t size = *buf;
size_t s = 1 + size + 2; // size + address + port
if (len < s)
{
LogPrint (eLogWarning, "SSU: Address is too short ", len);
port = 0;
return len;
}
buf++; // size
if (size == 4)
{
boost::asio::ip::address_v4::bytes_type bytes;
memcpy (bytes.data (), buf, 4);
ip = boost::asio::ip::address_v4 (bytes);
}
else if (size == 16)
{
boost::asio::ip::address_v6::bytes_type bytes;
memcpy (bytes.data (), buf, 16);
ip = boost::asio::ip::address_v6 (bytes);
}
else
LogPrint (eLogWarning, "SSU: Address size ", size, " is not supported");
buf += size;
port = bufbe16toh (buf);
return s;
}
}
}

View File

@@ -102,7 +102,8 @@ namespace transport
uint32_t GetRelayTag () const { return m_RelayTag; };
const i2p::data::RouterInfo::IntroKey& GetIntroKey () const { return m_IntroKey; };
uint32_t GetCreationTime () const { return m_CreationTime; };
void SetCreationTime (uint32_t ts) { m_CreationTime = ts; }; // for introducers
void FlushData ();
private:
@@ -145,6 +146,8 @@ namespace transport
void Reset ();
static size_t ExtractIPAddressAndPort (const uint8_t * buf, size_t len, boost::asio::ip::address& ip, uint16_t& port); // returns actual buf size
private:
friend class SSUData; // TODO: change in later

View File

@@ -942,15 +942,26 @@ namespace stream
{
if (!m_RemoteLeaseSet || m_RemoteLeaseSet->IsExpired ())
{
m_RemoteLeaseSet = m_LocalDestination.GetOwner ()->FindLeaseSet (m_RemoteIdentity->GetIdentHash ());
if (!m_RemoteLeaseSet)
auto remoteLeaseSet = m_LocalDestination.GetOwner ()->FindLeaseSet (m_RemoteIdentity->GetIdentHash ());
if (!remoteLeaseSet)
{
LogPrint (eLogWarning, "Streaming: LeaseSet ", m_RemoteIdentity->GetIdentHash ().ToBase64 (), " not found");
m_LocalDestination.GetOwner ()->RequestDestination (m_RemoteIdentity->GetIdentHash ()); // try to request for a next attempt
LogPrint (eLogWarning, "Streaming: LeaseSet ", m_RemoteIdentity->GetIdentHash ().ToBase64 (), m_RemoteLeaseSet ? " expired" : " not found");
if (m_RemoteLeaseSet && m_RemoteLeaseSet->IsPublishedEncrypted ())
{
m_LocalDestination.GetOwner ()->RequestDestinationWithEncryptedLeaseSet (
std::make_shared<i2p::data::BlindedPublicKey>(m_RemoteIdentity));
return; // we keep m_RemoteLeaseSet for possible next request
}
else
{
m_RemoteLeaseSet = nullptr;
m_LocalDestination.GetOwner ()->RequestDestination (m_RemoteIdentity->GetIdentHash ()); // try to request for a next attempt
}
}
else
{
// LeaseSet updated
m_RemoteLeaseSet = remoteLeaseSet;
m_RemoteIdentity = m_RemoteLeaseSet->GetIdentity ();
m_TransientVerifier = m_RemoteLeaseSet->GetTransientVerifier ();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2020, The PurpleI2P Project
* Copyright (c) 2013-2021, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@@ -137,7 +137,7 @@ namespace transport
m_IsOnline (true), m_IsRunning (false), m_IsNAT (true), m_CheckReserved(true), m_Thread (nullptr),
m_Service (nullptr), m_Work (nullptr), m_PeerCleanupTimer (nullptr), m_PeerTestTimer (nullptr),
m_SSUServer (nullptr), m_NTCP2Server (nullptr),
m_X25519KeysPairSupplier (5), // 5 pre-generated keys
m_X25519KeysPairSupplier (15), // 15 pre-generated keys
m_TotalSentBytes(0), m_TotalReceivedBytes(0), m_TotalTransitTransmittedBytes (0),
m_InBandwidth (0), m_OutBandwidth (0), m_TransitBandwidth(0),
m_LastInBandwidthUpdateBytes (0), m_LastOutBandwidthUpdateBytes (0),
@@ -174,9 +174,9 @@ namespace transport
std::string ntcp2proxy; i2p::config::GetOption("ntcp2.proxy", ntcp2proxy);
i2p::http::URL proxyurl;
// create NTCP2. TODO: move to acceptor
if (enableNTCP2)
if (enableNTCP2 || i2p::context.SupportsMesh ())
{
if(!ntcp2proxy.empty())
if(!ntcp2proxy.empty() && enableNTCP2)
{
if(proxyurl.parse(ntcp2proxy))
{
@@ -188,51 +188,101 @@ namespace transport
if (proxyurl.schema == "http")
proxytype = NTCP2Server::eHTTPProxy;
m_NTCP2Server->UseProxy(proxytype, proxyurl.host, proxyurl.port);
m_NTCP2Server->Start();
m_NTCP2Server->UseProxy(proxytype, proxyurl.host, proxyurl.port, proxyurl.user, proxyurl.pass);
i2p::context.SetStatus (eRouterStatusProxy);
}
else
LogPrint(eLogError, "Transports: unsupported NTCP2 proxy URL ", ntcp2proxy);
}
else
LogPrint(eLogError, "Transports: invalid NTCP2 proxy url ", ntcp2proxy);
return;
}
else
{
m_NTCP2Server = new NTCP2Server ();
m_NTCP2Server->Start ();
}
}
// create acceptors
auto& addresses = context.GetRouterInfo ().GetAddresses ();
for (const auto& address : addresses)
{
if (!address) continue;
if (address->transportStyle == RouterInfo::eTransportSSU)
// create SSU server
int ssuPort = 0;
if (enableSSU)
{
auto& addresses = context.GetRouterInfo ().GetAddresses ();
for (const auto& address: addresses)
{
if (m_SSUServer == nullptr && enableSSU)
if (!address) continue;
if (address->transportStyle == RouterInfo::eTransportSSU)
{
if (address->host.is_v4())
m_SSUServer = new SSUServer (address->port);
else
m_SSUServer = new SSUServer (address->host, address->port);
LogPrint (eLogInfo, "Transports: Start listening UDP port ", address->port);
try {
m_SSUServer->Start ();
} catch ( std::exception & ex ) {
LogPrint(eLogError, "Transports: Failed to bind to UDP port", address->port);
delete m_SSUServer;
m_SSUServer = nullptr;
continue;
}
DetectExternalIP ();
ssuPort = address->port;
m_SSUServer = new SSUServer (address->port);
break;
}
else
LogPrint (eLogError, "Transports: SSU server already exists");
}
}
// bind to interfaces
bool ipv4; i2p::config::GetOption("ipv4", ipv4);
if (ipv4)
{
std::string address; i2p::config::GetOption("address4", address);
if (!address.empty ())
{
boost::system::error_code ec;
auto addr = boost::asio::ip::address::from_string (address, ec);
if (!ec)
{
if (m_NTCP2Server) m_NTCP2Server->SetLocalAddress (addr);
if (m_SSUServer) m_SSUServer->SetLocalAddress (addr);
}
}
}
bool ipv6; i2p::config::GetOption("ipv6", ipv6);
if (ipv6)
{
std::string address; i2p::config::GetOption("address6", address);
if (!address.empty ())
{
boost::system::error_code ec;
auto addr = boost::asio::ip::address::from_string (address, ec);
if (!ec)
{
if (m_NTCP2Server) m_NTCP2Server->SetLocalAddress (addr);
if (m_SSUServer) m_SSUServer->SetLocalAddress (addr);
}
}
}
bool ygg; i2p::config::GetOption("meshnets.yggdrasil", ygg);
if (ygg)
{
std::string address; i2p::config::GetOption("meshnets.yggaddress", address);
if (!address.empty ())
{
boost::system::error_code ec;
auto addr = boost::asio::ip::address::from_string (address, ec);
if (!ec && m_NTCP2Server && i2p::util::net::IsYggdrasilAddress (addr))
m_NTCP2Server->SetLocalAddress (addr);
}
}
// start servers
if (m_NTCP2Server) m_NTCP2Server->Start ();
if (m_SSUServer)
{
LogPrint (eLogInfo, "Transports: Start listening UDP port ", ssuPort);
try
{
m_SSUServer->Start ();
}
catch (std::exception& ex )
{
LogPrint(eLogError, "Transports: Failed to bind to UDP port", ssuPort);
m_SSUServer->Stop ();
delete m_SSUServer;
m_SSUServer = nullptr;
}
if (m_SSUServer) DetectExternalIP ();
}
m_PeerCleanupTimer->expires_from_now (boost::posix_time::seconds(5*SESSION_CREATION_TIMEOUT));
m_PeerCleanupTimer->async_wait (std::bind (&Transports::HandlePeerCleanupTimer, this, std::placeholders::_1));
@@ -351,7 +401,7 @@ namespace transport
try
{
auto r = netdb.FindRouter (ident);
if (!r || !r->IsCompatible (i2p::context.GetRouterInfo ())) return;
if (!r || r->IsUnreachable () || !r->IsCompatible (i2p::context.GetRouterInfo ())) return;
{
std::unique_lock<std::mutex> l(m_PeersMutex);
it = m_Peers.insert (std::pair<i2p::data::IdentHash, Peer>(ident, { 0, r, {},
@@ -418,19 +468,10 @@ namespace transport
if (address)
{
auto s = std::make_shared<NTCP2Session> (*m_NTCP2Server, peer.router, address);
if(m_NTCP2Server->UsingProxy())
{
NTCP2Server::RemoteAddressType remote = NTCP2Server::eIP4Address;
std::string addr = address->host.to_string();
if(address->host.is_v6())
remote = NTCP2Server::eIP6Address;
m_NTCP2Server->ConnectWithProxy(addr, address->port, remote, s);
}
if( m_NTCP2Server->UsingProxy())
m_NTCP2Server->ConnectWithProxy(s);
else
m_NTCP2Server->Connect (address->host, address->port, s);
m_NTCP2Server->Connect (s);
return true;
}
}
@@ -462,10 +503,10 @@ namespace transport
}
peer.numAttempts++;
}
if (address)
if (address && address->IsReachableSSU ())
{
m_SSUServer->CreateSession (peer.router, address->host, address->port);
return true;
if (m_SSUServer->CreateSession (peer.router, address))
return true;
}
}
else
@@ -480,7 +521,7 @@ namespace transport
if (address)
{
auto s = std::make_shared<NTCP2Session> (*m_NTCP2Server, peer.router, address);
m_NTCP2Server->Connect (address->host, address->port, s);
m_NTCP2Server->Connect (s);
return true;
}
}
@@ -535,68 +576,66 @@ namespace transport
return;
}
if (m_SSUServer)
{
bool isv4 = i2p::context.SupportsV4 ();
if (m_IsNAT && isv4)
i2p::context.SetStatus (eRouterStatusTesting);
for (int i = 0; i < 5; i++)
{
auto router = i2p::data::netdb.GetRandomPeerTestRouter (isv4); // v4 only if v4
if (router)
m_SSUServer->CreateSession (router, true, isv4); // peer test
else
{
// if not peer test capable routers found pick any
router = i2p::data::netdb.GetRandomRouter ();
if (router && router->IsSSU ())
m_SSUServer->CreateSession (router); // no peer test
}
}
if (i2p::context.SupportsV6 ())
{
// try to connect to few v6 addresses to get our address back
for (int i = 0; i < 3; i++)
{
auto router = i2p::data::netdb.GetRandomSSUV6Router ();
if (router)
{
auto addr = router->GetSSUV6Address ();
if (addr)
m_SSUServer->GetService ().post ([this, router, addr]
{
m_SSUServer->CreateDirectSession (router, { addr->host, (uint16_t)addr->port }, false);
});
}
}
}
}
PeerTest ();
else
LogPrint (eLogError, "Transports: Can't detect external IP. SSU is not available");
}
void Transports::PeerTest ()
void Transports::PeerTest (bool ipv4, bool ipv6)
{
if (RoutesRestricted() || !i2p::context.SupportsV4 ()) return;
if (m_SSUServer)
if (RoutesRestricted() || !m_SSUServer) return;
if (ipv4 && i2p::context.SupportsV4 ())
{
LogPrint (eLogInfo, "Transports: Started peer test");
LogPrint (eLogInfo, "Transports: Started peer test ipv4");
std::set<i2p::data::IdentHash> excluded;
bool statusChanged = false;
for (int i = 0; i < 5; i++)
{
auto router = i2p::data::netdb.GetRandomPeerTestRouter (true); // v4 only
auto router = i2p::data::netdb.GetRandomPeerTestRouter (true, excluded); // v4
if (router)
{
if (!statusChanged)
auto addr = router->GetSSUAddress (true); // ipv4
if (addr && !i2p::util::net::IsInReservedRange(addr->host))
{
statusChanged = true;
i2p::context.SetStatus (eRouterStatusTesting); // first time only
}
m_SSUServer->CreateSession (router, true, true); // peer test v4
if (!statusChanged)
{
statusChanged = true;
i2p::context.SetStatus (eRouterStatusTesting); // first time only
}
m_SSUServer->CreateSession (router, addr, true); // peer test v4
}
excluded.insert (router->GetIdentHash ());
}
}
if (!statusChanged)
LogPrint (eLogWarning, "Transports: Can't find routers for peer test");
LogPrint (eLogWarning, "Transports: Can't find routers for peer test ipv4");
}
if (ipv6 && i2p::context.SupportsV6 ())
{
LogPrint (eLogInfo, "Transports: Started peer test ipv6");
std::set<i2p::data::IdentHash> excluded;
bool statusChanged = false;
for (int i = 0; i < 5; i++)
{
auto router = i2p::data::netdb.GetRandomPeerTestRouter (false, excluded); // v6
if (router)
{
auto addr = router->GetSSUV6Address ();
if (addr && !i2p::util::net::IsInReservedRange(addr->host))
{
if (!statusChanged)
{
statusChanged = true;
i2p::context.SetStatusV6 (eRouterStatusTesting); // first time only
}
m_SSUServer->CreateSession (router, addr, true); // peer test v6
}
excluded.insert (router->GetIdentHash ());
}
}
if (!statusChanged)
LogPrint (eLogWarning, "Transports: Can't find routers for peer test ipv6");
}
}
std::shared_ptr<i2p::crypto::X25519Keys> Transports::GetNextX25519KeysPair ()
@@ -711,9 +750,12 @@ namespace transport
++it;
}
UpdateBandwidth (); // TODO: use separate timer(s) for it
if (i2p::context.GetStatus () == eRouterStatusTesting) // if still testing, repeat peer test
DetectExternalIP ();
m_PeerCleanupTimer->expires_from_now (boost::posix_time::seconds(5*SESSION_CREATION_TIMEOUT));
bool ipv4Testing = i2p::context.GetStatus () == eRouterStatusTesting;
bool ipv6Testing = i2p::context.GetStatusV6 () == eRouterStatusTesting;
// if still testing, repeat peer test
if (ipv4Testing || ipv6Testing)
PeerTest (ipv4Testing, ipv6Testing);
m_PeerCleanupTimer->expires_from_now (boost::posix_time::seconds(3*SESSION_CREATION_TIMEOUT));
m_PeerCleanupTimer->async_wait (std::bind (&Transports::HandlePeerCleanupTimer, this, std::placeholders::_1));
}
}
@@ -731,10 +773,15 @@ namespace transport
std::shared_ptr<const i2p::data::RouterInfo> Transports::GetRandomPeer () const
{
if (m_Peers.empty ()) return nullptr;
std::unique_lock<std::mutex> l(m_PeersMutex);
auto it = m_Peers.begin ();
std::advance (it, rand () % m_Peers.size ());
return it != m_Peers.end () ? it->second.router : nullptr;
i2p::data::IdentHash ident;
{
std::unique_lock<std::mutex> l(m_PeersMutex);
auto it = m_Peers.begin ();
std::advance (it, rand () % m_Peers.size ());
if (it == m_Peers.end () || it->second.router) return nullptr; // not connected
ident = it->first;
}
return i2p::data::netdb.FindRouter (ident);
}
void Transports::RestrictRoutesToFamilies(std::set<std::string> families)
{

View File

@@ -76,9 +76,9 @@ namespace transport
}
};
const size_t SESSION_CREATION_TIMEOUT = 10; // in seconds
const size_t SESSION_CREATION_TIMEOUT = 15; // in seconds
const int PEER_TEST_INTERVAL = 71; // in minutes
const int MAX_NUM_DELAYED_MESSAGES = 50;
const int MAX_NUM_DELAYED_MESSAGES = 150;
class Transports
{
public:
@@ -131,7 +131,7 @@ namespace transport
bool IsRestrictedPeer(const i2p::data::IdentHash & ident) const;
void PeerTest ();
void PeerTest (bool ipv4 = true, bool ipv6 = true);
void SetCheckReserved (bool check) { m_CheckReserved = check; };
bool IsCheckReserved () { return m_CheckReserved; };

View File

@@ -698,7 +698,7 @@ namespace tunnel
auto inboundTunnel = GetNextInboundTunnel ();
auto router = i2p::transport::transports.RoutesRestricted() ?
i2p::transport::transports.GetRestrictedPeer() :
i2p::data::netdb.GetRandomRouter ();
i2p::data::netdb.GetRandomRouter (i2p::context.GetSharedRouterInfo (), false); // reachable by us
if (!inboundTunnel || !router) return;
LogPrint (eLogDebug, "Tunnel: creating one hop outbound tunnel");
CreateTunnel<OutboundTunnel> (
@@ -771,7 +771,8 @@ namespace tunnel
// trying to create one more inbound tunnel
auto router = i2p::transport::transports.RoutesRestricted() ?
i2p::transport::transports.GetRestrictedPeer() :
i2p::data::netdb.GetRandomRouter ();
// should be reachable by us because we send build request directly
i2p::data::netdb.GetRandomRouter (i2p::context.GetSharedRouterInfo (), false);
if (!router) {
LogPrint (eLogWarning, "Tunnel: can't find any router, skip creating tunnel");
return;

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2020, The PurpleI2P Project
* Copyright (c) 2013-2021, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@@ -393,19 +393,20 @@ namespace tunnel
}
}
std::shared_ptr<const i2p::data::RouterInfo> TunnelPool::SelectNextHop (std::shared_ptr<const i2p::data::RouterInfo> prevHop) const
std::shared_ptr<const i2p::data::RouterInfo> TunnelPool::SelectNextHop (std::shared_ptr<const i2p::data::RouterInfo> prevHop, bool reverse) const
{
bool isExploratory = (i2p::tunnel::tunnels.GetExploratoryPool () == shared_from_this ());
auto hop = isExploratory ? i2p::data::netdb.GetRandomRouter (prevHop):
i2p::data::netdb.GetHighBandwidthRandomRouter (prevHop);
auto hop = isExploratory ? i2p::data::netdb.GetRandomRouter (prevHop, reverse):
i2p::data::netdb.GetHighBandwidthRandomRouter (prevHop, reverse);
if (!hop || hop->GetProfile ()->IsBad ())
hop = i2p::data::netdb.GetRandomRouter (prevHop);
hop = i2p::data::netdb.GetRandomRouter (prevHop, reverse);
return hop;
}
bool StandardSelectPeers(Path & peers, int numHops, bool inbound, SelectHopFunc nextHop)
{
int start = 0;
auto prevHop = i2p::context.GetSharedRouterInfo ();
if(i2p::transport::transports.RoutesRestricted())
{
@@ -414,31 +415,38 @@ namespace tunnel
if(!hop) return false;
peers.push_back(hop->GetRouterIdentity());
prevHop = hop;
start++;
}
else if (i2p::transport::transports.GetNumPeers () > 25)
else if (i2p::transport::transports.GetNumPeers () > 100 ||
(inbound && i2p::transport::transports.GetNumPeers () > 25))
{
auto r = i2p::transport::transports.GetRandomPeer ();
if (r && !r->GetProfile ()->IsBad () &&
(numHops > 1 || !inbound || r->IsReachable ())) // first must be reachable
(numHops > 1 || (r->IsV4 () && (!inbound || r->IsReachable ())))) // first inbound must be reachable
{
prevHop = r;
peers.push_back (r->GetRouterIdentity ());
numHops--;
start++;
}
}
for(int i = 0; i < numHops; i++ )
for(int i = start; i < numHops; i++ )
{
auto hop = nextHop (prevHop);
auto hop = nextHop (prevHop, inbound);
if (!hop && !i) // if no suitable peer found for first hop, try already connected
{
LogPrint (eLogInfo, "Tunnels: Can't select first hop for a tunnel. Trying already connected");
hop = i2p::transport::transports.GetRandomPeer ();
}
if (!hop)
{
LogPrint (eLogError, "Tunnels: Can't select next hop for ", prevHop->GetIdentHashBase64 ());
return false;
}
if (inbound && (i == numHops - 1) && !hop->IsReachable ())
if ((i == numHops - 1) && (!hop->IsV4 () || // doesn't support ipv4
(inbound && !hop->IsReachable ()))) // IBGW is not reachable
{
// if first is not reachable try again
auto hop1 = nextHop (prevHop);
auto hop1 = nextHop (prevHop, true);
if (hop1) hop = hop1;
}
prevHop = hop;
@@ -460,7 +468,7 @@ namespace tunnel
}
// explicit peers in use
if (m_ExplicitPeers) return SelectExplicitPeers (peers, isInbound);
return StandardSelectPeers(peers, numHops, isInbound, std::bind(&TunnelPool::SelectNextHop, this, std::placeholders::_1));
return StandardSelectPeers(peers, numHops, isInbound, std::bind(&TunnelPool::SelectNextHop, this, std::placeholders::_1, std::placeholders::_2));
}
bool TunnelPool::SelectExplicitPeers (std::vector<std::shared_ptr<const i2p::data::IdentityEx> >& peers, bool isInbound)

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2020, The PurpleI2P Project
* Copyright (c) 2013-2021, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@@ -46,7 +46,7 @@ namespace tunnel
};
typedef std::function<std::shared_ptr<const i2p::data::RouterInfo>(std::shared_ptr<const i2p::data::RouterInfo>)> SelectHopFunc;
typedef std::function<std::shared_ptr<const i2p::data::RouterInfo>(std::shared_ptr<const i2p::data::RouterInfo>, bool)> SelectHopFunc;
// standard peer selection algorithm
bool StandardSelectPeers(Path & path, int hops, bool inbound, SelectHopFunc nextHop);
@@ -104,7 +104,7 @@ namespace tunnel
std::shared_ptr<OutboundTunnel> GetLowestLatencyOutboundTunnel(std::shared_ptr<OutboundTunnel> exclude = nullptr) const;
// for overriding tunnel peer selection
std::shared_ptr<const i2p::data::RouterInfo> SelectNextHop (std::shared_ptr<const i2p::data::RouterInfo> prevHop) const;
std::shared_ptr<const i2p::data::RouterInfo> SelectNextHop (std::shared_ptr<const i2p::data::RouterInfo> prevHop, bool reverse) const;
private:

View File

@@ -67,8 +67,12 @@ int inet_pton_xp (int af, const char *src, void *dst)
}
#else /* !_WIN32 => UNIX */
#include <sys/types.h>
#ifdef ANDROID
#include "ifaddrs.h"
#else
#include <ifaddrs.h>
#endif
#endif
#define address_pair_v4(a,b) { boost::asio::ip::address_v4::from_string (a).to_ulong (), boost::asio::ip::address_v4::from_string (b).to_ulong () }
#define address_pair_v6(a,b) { boost::asio::ip::address_v6::from_string (a).to_bytes (), boost::asio::ip::address_v6::from_string (b).to_bytes () }
@@ -380,11 +384,11 @@ namespace net
return boost::asio::ip::address::from_string("127.0.0.1");
#else
int af = (ipv6 ? AF_INET6 : AF_INET);
ifaddrs * addrs = nullptr;
ifaddrs *addrs, *cur = nullptr;
if(getifaddrs(&addrs) == 0)
{
// got ifaddrs
ifaddrs * cur = addrs;
cur = addrs;
while(cur)
{
std::string cur_ifname(cur->ifa_name);
@@ -431,10 +435,7 @@ namespace net
boost::asio::ip::address_v6 GetYggdrasilAddress ()
{
#if defined(ANDROID)
// TODO: implement
return boost::asio::ip::address_v6 ();
#elif defined(_WIN32)
#if defined(_WIN32)
ULONG outBufLen = 0;
PIP_ADAPTER_ADDRESSES pAddresses = nullptr;
PIP_ADAPTER_ADDRESSES pCurrAddresses = nullptr;
@@ -482,11 +483,11 @@ namespace net
FREE(pAddresses);
return boost::asio::ip::address_v6 ();
#else
ifaddrs * addrs = nullptr;
ifaddrs *addrs, *cur = nullptr;
auto err = getifaddrs(&addrs);
if (!err)
{
ifaddrs * cur = addrs;
cur = addrs;
while(cur)
{
if (cur->ifa_addr && cur->ifa_addr->sa_family == AF_INET6)
@@ -523,6 +524,7 @@ namespace net
bool IsInReservedRange (const boost::asio::ip::address& host)
{
// https://en.wikipedia.org/wiki/Reserved_IP_addresses
if (host.is_unspecified ()) return false;
if(host.is_v4())
{
static const std::vector< std::pair<uint32_t, uint32_t> > reservedIPv4Ranges {

View File

@@ -16,7 +16,7 @@
#define MAKE_VERSION_NUMBER(a,b,c) ((a*100+b)*100+c)
#define I2PD_VERSION_MAJOR 2
#define I2PD_VERSION_MINOR 36
#define I2PD_VERSION_MINOR 38
#define I2PD_VERSION_MICRO 0
#define I2PD_VERSION_PATCH 0
#define I2PD_VERSION MAKE_VERSION(I2PD_VERSION_MAJOR, I2PD_VERSION_MINOR, I2PD_VERSION_MICRO)
@@ -30,7 +30,7 @@
#define I2P_VERSION_MAJOR 0
#define I2P_VERSION_MINOR 9
#define I2P_VERSION_MICRO 49
#define I2P_VERSION_MICRO 50
#define I2P_VERSION_PATCH 0
#define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO)
#define I2P_VERSION_NUMBER MAKE_VERSION_NUMBER(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO)

View File

@@ -645,6 +645,22 @@ namespace client
localDestination->RequestDestinationWithEncryptedLeaseSet (addr->blindedPublicKey, requstCallback);
}
void BOBCommandSession::LookupLocalCommandHandler (const char * operand, size_t len)
{
LogPrint (eLogDebug, "BOB: lookup local ", operand);
auto addr = context.GetAddressBook ().GetAddress (operand);
if (!addr)
{
SendReplyError ("Address Not found");
return;
}
auto ls = i2p::data::netdb.FindLeaseSet (addr->identHash);
if (ls)
SendReplyOK (ls->GetIdentity ()->ToBase64 ().c_str ());
else
SendReplyError ("Local LeaseSet Not found");
}
void BOBCommandSession::ClearCommandHandler (const char * operand, size_t len)
{
LogPrint (eLogDebug, "BOB: clear");
@@ -770,6 +786,7 @@ namespace client
m_CommandHandlers[BOB_COMMAND_INPORT] = &BOBCommandSession::InportCommandHandler;
m_CommandHandlers[BOB_COMMAND_QUIET] = &BOBCommandSession::QuietCommandHandler;
m_CommandHandlers[BOB_COMMAND_LOOKUP] = &BOBCommandSession::LookupCommandHandler;
m_CommandHandlers[BOB_COMMAND_LOOKUP_LOCAL] = &BOBCommandSession::LookupLocalCommandHandler;
m_CommandHandlers[BOB_COMMAND_CLEAR] = &BOBCommandSession::ClearCommandHandler;
m_CommandHandlers[BOB_COMMAND_LIST] = &BOBCommandSession::ListCommandHandler;
m_CommandHandlers[BOB_COMMAND_OPTION] = &BOBCommandSession::OptionCommandHandler;

View File

@@ -42,6 +42,7 @@ namespace client
const char BOB_COMMAND_INPORT[] = "inport";
const char BOB_COMMAND_QUIET[] = "quiet";
const char BOB_COMMAND_LOOKUP[] = "lookup";
const char BOB_COMMAND_LOOKUP_LOCAL[] = "lookuplocal";
const char BOB_COMMAND_CLEAR[] = "clear";
const char BOB_COMMAND_LIST[] = "list";
const char BOB_COMMAND_OPTION[] = "option";
@@ -206,6 +207,7 @@ namespace client
void InportCommandHandler (const char * operand, size_t len);
void QuietCommandHandler (const char * operand, size_t len);
void LookupCommandHandler (const char * operand, size_t len);
void LookupLocalCommandHandler (const char * operand, size_t len);
void ClearCommandHandler (const char * operand, size_t len);
void ListCommandHandler (const char * operand, size_t len);
void OptionCommandHandler (const char * operand, size_t len);

View File

@@ -691,7 +691,7 @@ namespace client
i2p::data::SigningKeyType sigType = section.second.get (I2P_SERVER_TUNNEL_SIGNATURE_TYPE, i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519);
i2p::data::CryptoKeyType cryptoType = section.second.get (I2P_CLIENT_TUNNEL_CRYPTO_TYPE, i2p::data::CRYPTO_KEY_TYPE_ELGAMAL);
std::string address = section.second.get<std::string> (I2P_SERVER_TUNNEL_ADDRESS, "127.0.0.1");
std::string address = section.second.get<std::string> (I2P_SERVER_TUNNEL_ADDRESS, "");
bool isUniqueLocal = section.second.get(I2P_SERVER_TUNNEL_ENABLE_UNIQUE_LOCAL, true);
// I2CP
@@ -718,6 +718,7 @@ namespace client
{
// udp server tunnel
// TODO: hostnames
if (address.empty ()) address = "127.0.0.1";
auto localAddress = boost::asio::ip::address::from_string(address);
boost::asio::ip::udp::endpoint endpoint(boost::asio::ip::address::from_string(host), port);
auto serverTunnel = std::make_shared<I2PUDPServerTunnel>(name, localDestination, localAddress, endpoint, port, gzip);
@@ -750,12 +751,13 @@ namespace client
else // regular server tunnel by default
serverTunnel = std::make_shared<I2PServerTunnel> (name, host, port, localDestination, inPort, gzip);
if (!address.empty ())
serverTunnel->SetLocalAddress (address);
if(!isUniqueLocal)
{
LogPrint(eLogInfo, "Clients: disabling loopback address mapping");
serverTunnel->SetUniqueLocal(isUniqueLocal);
}
if (accessList.length () > 0)
{
std::set<i2p::data::IdentHash> idents;

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2020, The PurpleI2P Project
* Copyright (c) 2013-2021, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@@ -433,13 +433,13 @@ namespace proxy {
if (m_ProxyURL.is_i2p())
{
m_ClientRequest.uri = origURI;
if (!m_ProxyURL.user.empty () || !m_ProxyURL.pass.empty ())
auto auth = i2p::http::CreateBasicAuthorizationString (m_ProxyURL.user, m_ProxyURL.pass);
if (!auth.empty ())
{
// remove existing authorization if any
m_ClientRequest.RemoveHeader("Proxy-");
// add own http proxy authorization
std::string s = "Basic " + i2p::data::ToBase64Standard (m_ProxyURL.user + ":" + m_ProxyURL.pass);
m_ClientRequest.AddHeader("Proxy-Authorization", s);
m_ClientRequest.AddHeader("Proxy-Authorization", auth);
}
m_send_buf = m_ClientRequest.to_string();
m_recv_buf.erase(0, m_req_len);

View File

@@ -84,7 +84,12 @@ namespace client
m_Owner->SendMessagePayloadMessage (buf + 4, length);
}
void I2CPDestination::CreateNewLeaseSet (std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels)
void I2CPDestination::CreateNewLeaseSet (const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels)
{
GetService ().post (std::bind (&I2CPDestination::PostCreateNewLeaseSet, this, tunnels));
}
void I2CPDestination::PostCreateNewLeaseSet (std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels)
{
if (m_IsCreatingLeaseSet)
{

View File

@@ -93,7 +93,7 @@ namespace client
// I2CP
void HandleDataMessage (const uint8_t * buf, size_t len);
void CreateNewLeaseSet (std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels);
void CreateNewLeaseSet (const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels);
private:
@@ -101,6 +101,8 @@ namespace client
{ return std::static_pointer_cast<I2CPDestination>(shared_from_this ()); }
bool SendMsg (std::shared_ptr<I2NPMessage> msg, std::shared_ptr<const i2p::data::LeaseSet> remote);
void PostCreateNewLeaseSet (std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels);
private:
std::shared_ptr<I2CPSession> m_Owner;

View File

@@ -84,7 +84,11 @@ namespace client
// bind to 127.x.x.x address
// where x.x.x are first three bytes from ident
auto ourIP = GetLoopbackAddressFor(addr);
sock->bind (boost::asio::ip::tcp::endpoint (ourIP, 0));
boost::system::error_code ec;
sock->bind (boost::asio::ip::tcp::endpoint (ourIP, 0), ec);
if (ec)
LogPrint (eLogError, "I2PTunnel: can't bind ourIP to ", ourIP.to_string (), ": ", ec.message ());
}
#endif
@@ -107,6 +111,22 @@ namespace client
}
}
void I2PTunnelConnection::Connect (const boost::asio::ip::address& localAddress)
{
if (m_Socket)
{
if (m_RemoteEndpoint.address().is_v6 ())
m_Socket->open (boost::asio::ip::tcp::v6 ());
else
m_Socket->open (boost::asio::ip::tcp::v4 ());
boost::system::error_code ec;
m_Socket->bind (boost::asio::ip::tcp::endpoint (localAddress, 0), ec);
if (ec)
LogPrint (eLogError, "I2PTunnel: can't bind to ", localAddress.to_string (), ": ", ec.message ());
}
Connect (false);
}
void I2PTunnelConnection::Terminate ()
{
if (Kill()) return;
@@ -600,6 +620,16 @@ namespace client
m_IsAccessList = true;
}
void I2PServerTunnel::SetLocalAddress (const std::string& localAddress)
{
boost::system::error_code ec;
auto addr = boost::asio::ip::address::from_string(localAddress, ec);
if (!ec)
m_LocalAddress.reset (new boost::asio::ip::address (addr));
else
LogPrint (eLogError, "I2PTunnel: can't set local address ", localAddress);
}
void I2PServerTunnel::Accept ()
{
if (m_PortDestination)
@@ -631,7 +661,10 @@ namespace client
// new connection
auto conn = CreateI2PConnection (stream);
AddHandler (conn);
conn->Connect (m_IsUniqueLocal);
if (m_LocalAddress)
conn->Connect (*m_LocalAddress);
else
conn->Connect (m_IsUniqueLocal);
}
}

View File

@@ -48,7 +48,8 @@ namespace client
~I2PTunnelConnection ();
void I2PConnect (const uint8_t * msg = nullptr, size_t len = 0);
void Connect (bool isUniqueLocal = true);
void Connect (const boost::asio::ip::address& localAddress);
protected:
void Terminate ();
@@ -314,6 +315,8 @@ namespace client
void SetUniqueLocal (bool isUniqueLocal) { m_IsUniqueLocal = isUniqueLocal; }
bool IsUniqueLocal () const { return m_IsUniqueLocal; }
void SetLocalAddress (const std::string& localAddress);
const std::string& GetAddress() const { return m_Address; }
int GetPort () const { return m_Port; };
uint16_t GetLocalPort () const { return m_PortDestination->GetLocalPort (); };
@@ -339,6 +342,7 @@ namespace client
std::shared_ptr<i2p::stream::StreamingDestination> m_PortDestination;
std::set<i2p::data::IdentHash> m_AccessList;
bool m_IsAccessList;
std::unique_ptr<boost::asio::ip::address> m_LocalAddress;
};
class I2PServerTunnelHTTP: public I2PServerTunnel

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2020, The PurpleI2P Project
* Copyright (c) 2013-2021, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@@ -72,7 +72,8 @@ namespace client
bool MatchedTunnelDestination::SelectPeers(i2p::tunnel::Path & path, int hops, bool inbound)
{
auto pool = GetTunnelPool();
if(!i2p::tunnel::StandardSelectPeers(path, hops, inbound, std::bind(&i2p::tunnel::TunnelPool::SelectNextHop, pool, std::placeholders::_1)))
if(!i2p::tunnel::StandardSelectPeers(path, hops, inbound,
std::bind(&i2p::tunnel::TunnelPool::SelectNextHop, pool, std::placeholders::_1, std::placeholders::_2)))
return false;
// more here for outbound tunnels
if(!inbound && m_RemoteLeaseSet)

View File

@@ -58,8 +58,8 @@ namespace client
{
if (Session)
{
if (m_IsAccepting && Session->localDestination)
Session->localDestination->StopAcceptingStreams ();
if (m_IsAccepting && Session->GetLocalDestination ())
Session->GetLocalDestination ()->StopAcceptingStreams ();
}
break;
}
@@ -270,6 +270,10 @@ namespace client
ProcessDestGenerate (separator + 1, bytes_transferred - (separator - m_Buffer) - 1);
else if (!strcmp (m_Buffer, SAM_NAMING_LOOKUP))
ProcessNamingLookup (separator + 1, bytes_transferred - (separator - m_Buffer) - 1);
else if (!strcmp (m_Buffer, SAM_SESSION_ADD))
ProcessSessionAdd (separator + 1, bytes_transferred - (separator - m_Buffer) - 1);
else if (!strcmp (m_Buffer, SAM_SESSION_REMOVE))
ProcessSessionRemove (separator + 1, bytes_transferred - (separator - m_Buffer) - 1);
else if (!strcmp (m_Buffer, SAM_DATAGRAM_SEND) || !strcmp (m_Buffer, SAM_RAW_SEND))
{
size_t len = bytes_transferred - (separator - m_Buffer) - 1;
@@ -352,6 +356,7 @@ namespace client
if (style == SAM_VALUE_STREAM) type = eSAMSessionTypeStream;
else if (style == SAM_VALUE_DATAGRAM) type = eSAMSessionTypeDatagram;
else if (style == SAM_VALUE_RAW) type = eSAMSessionTypeRaw;
else if (style == SAM_VALUE_MASTER) type = eSAMSessionTypeMaster;
if (type == eSAMSessionTypeUnknown)
{
// unknown style
@@ -409,7 +414,7 @@ namespace client
if (type == eSAMSessionTypeDatagram || type == eSAMSessionTypeRaw)
{
session->UDPEndpoint = forward;
auto dest = session->localDestination->CreateDatagramDestination ();
auto dest = session->GetLocalDestination ()->CreateDatagramDestination ();
if (type == eSAMSessionTypeDatagram)
dest->SetReceiver (std::bind (&SAMSocket::HandleI2PDatagramReceive, shared_from_this (),
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5));
@@ -418,7 +423,7 @@ namespace client
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
}
if (session->localDestination->IsReady ())
if (session->GetLocalDestination ()->IsReady ())
SendSessionCreateReplyOk ();
else
{
@@ -438,7 +443,7 @@ namespace client
auto session = m_Owner.FindSession(m_ID);
if(session)
{
if (session->localDestination->IsReady ())
if (session->GetLocalDestination ()->IsReady ())
SendSessionCreateReplyOk ();
else
{
@@ -457,7 +462,7 @@ namespace client
{
uint8_t buf[1024];
char priv[1024];
size_t l = session->localDestination->GetPrivateKeys ().ToBuffer (buf, 1024);
size_t l = session->GetLocalDestination ()->GetPrivateKeys ().ToBuffer (buf, 1024);
size_t l1 = i2p::data::ByteStreamToBase64 (buf, l, priv, 1024);
priv[l1] = 0;
#ifdef _MSC_VER
@@ -495,20 +500,38 @@ namespace client
else
m_BufferOffset = 0;
auto dest = std::make_shared<i2p::data::IdentityEx> ();
size_t l = dest->FromBase64(destination);
if (l > 0)
std::shared_ptr<const Address> addr;
if (destination.find(".i2p") != std::string::npos)
addr = context.GetAddressBook().GetAddress (destination);
else
{
context.GetAddressBook().InsertFullAddress(dest);
auto leaseSet = session->localDestination->FindLeaseSet(dest->GetIdentHash());
if (leaseSet)
Connect(leaseSet, session);
else
auto dest = std::make_shared<i2p::data::IdentityEx> ();
size_t l = dest->FromBase64(destination);
if (l > 0)
{
session->localDestination->RequestDestination(dest->GetIdentHash(),
context.GetAddressBook().InsertFullAddress(dest);
addr = std::make_shared<Address>(dest->GetIdentHash ());
}
}
if (addr && addr->IsValid ())
{
if (addr->IsIdentHash ())
{
auto leaseSet = session->GetLocalDestination ()->FindLeaseSet(addr->identHash);
if (leaseSet)
Connect(leaseSet, session);
else
{
session->GetLocalDestination ()->RequestDestination(addr->identHash,
std::bind(&SAMSocket::HandleConnectLeaseSetRequestComplete,
shared_from_this(), std::placeholders::_1));
}
}
else // B33
session->GetLocalDestination ()->RequestDestinationWithEncryptedLeaseSet (addr->blindedPublicKey,
std::bind(&SAMSocket::HandleConnectLeaseSetRequestComplete,
shared_from_this(), std::placeholders::_1));
}
}
else
SendMessageReply (SAM_STREAM_STATUS_INVALID_KEY, strlen(SAM_STREAM_STATUS_INVALID_KEY), true);
@@ -523,7 +546,7 @@ namespace client
if (session)
{
m_SocketType = eSAMSocketTypeStream;
m_Stream = session->localDestination->CreateStream (remote);
m_Stream = session->GetLocalDestination ()->CreateStream (remote);
if (m_Stream)
{
m_Stream->Send ((uint8_t *)m_Buffer, m_BufferOffset); // connect and send
@@ -567,10 +590,10 @@ namespace client
if (session)
{
m_SocketType = eSAMSocketTypeAcceptor;
if (!session->localDestination->IsAcceptingStreams ())
if (!session->GetLocalDestination ()->IsAcceptingStreams ())
{
m_IsAccepting = true;
session->localDestination->AcceptOnce (std::bind (&SAMSocket::HandleI2PAccept, shared_from_this (), std::placeholders::_1));
session->GetLocalDestination ()->AcceptOnce (std::bind (&SAMSocket::HandleI2PAccept, shared_from_this (), std::placeholders::_1));
}
SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false);
}
@@ -590,7 +613,7 @@ namespace client
SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true);
return;
}
if (session->localDestination->IsAcceptingStreams ())
if (session->GetLocalDestination ()->IsAcceptingStreams ())
{
SendI2PError ("Already accepting");
return;
@@ -620,7 +643,7 @@ namespace client
m_IsAccepting = true;
std::string& silent = params[SAM_PARAM_SILENT];
if (silent == SAM_VALUE_TRUE) m_IsSilent = true;
session->localDestination->AcceptStreams (std::bind (&SAMSocket::HandleI2PForward,
session->GetLocalDestination ()->AcceptStreams (std::bind (&SAMSocket::HandleI2PForward,
shared_from_this (), std::placeholders::_1, ep));
SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false);
}
@@ -636,7 +659,7 @@ namespace client
auto session = m_Owner.FindSession(m_ID);
if (session)
{
auto d = session->localDestination->GetDatagramDestination ();
auto d = session->GetLocalDestination ()->GetDatagramDestination ();
if (d)
{
i2p::data::IdentityEx dest;
@@ -706,7 +729,7 @@ namespace client
std::shared_ptr<const i2p::data::IdentityEx> identity;
std::shared_ptr<const Address> addr;
auto session = m_Owner.FindSession(m_ID);
auto dest = session == nullptr ? context.GetSharedLocalDestination() : session->localDestination;
auto dest = session == nullptr ? context.GetSharedLocalDestination() : session->GetLocalDestination ();
if (name == "ME")
SendNamingLookupReply (name, dest->GetIdentity ());
else if ((identity = context.GetAddressBook ().GetFullAddress (name)) != nullptr)
@@ -740,6 +763,73 @@ namespace client
}
}
void SAMSocket::ProcessSessionAdd (char * buf, size_t len)
{
auto session = m_Owner.FindSession(m_ID);
if (session && session->Type == eSAMSessionTypeMaster)
{
LogPrint (eLogDebug, "SAM: subsession add: ", buf);
auto masterSession = std::static_pointer_cast<SAMMasterSession>(session);
std::map<std::string, std::string> params;
ExtractParams (buf, params);
std::string& id = params[SAM_PARAM_ID];
if (masterSession->subsessions.count (id) > 1)
{
// session exists
SendMessageReply (SAM_SESSION_CREATE_DUPLICATED_ID, strlen(SAM_SESSION_CREATE_DUPLICATED_ID), false);
return;
}
std::string& style = params[SAM_PARAM_STYLE];
SAMSessionType type = eSAMSessionTypeUnknown;
if (style == SAM_VALUE_STREAM) type = eSAMSessionTypeStream;
// TODO: implement other styles
if (type == eSAMSessionTypeUnknown)
{
// unknown style
SendI2PError("Unsupported STYLE");
return;
}
auto fromPort = std::stoi(params[SAM_PARAM_FROM_PORT]);
if (fromPort == -1)
{
SendI2PError("Invalid from port");
return;
}
auto subsession = std::make_shared<SAMSubSession>(masterSession, id, type, fromPort);
if (m_Owner.AddSession (subsession))
{
masterSession->subsessions.insert (id);
SendSessionCreateReplyOk ();
}
else
SendMessageReply (SAM_SESSION_CREATE_DUPLICATED_ID, strlen(SAM_SESSION_CREATE_DUPLICATED_ID), false);
}
else
SendI2PError ("Wrong session type");
}
void SAMSocket::ProcessSessionRemove (char * buf, size_t len)
{
auto session = m_Owner.FindSession(m_ID);
if (session && session->Type == eSAMSessionTypeMaster)
{
LogPrint (eLogDebug, "SAM: subsession remove: ", buf);
auto masterSession = std::static_pointer_cast<SAMMasterSession>(session);
std::map<std::string, std::string> params;
ExtractParams (buf, params);
std::string& id = params[SAM_PARAM_ID];
if (!masterSession->subsessions.erase (id))
{
SendMessageReply (SAM_SESSION_STATUS_INVALID_KEY, strlen(SAM_SESSION_STATUS_INVALID_KEY), false);
return;
}
m_Owner.CloseSession (id);
SendSessionCreateReplyOk ();
}
else
SendI2PError ("Wrong session type");
}
void SAMSocket::SendI2PError(const std::string & msg)
{
LogPrint (eLogError, "SAM: i2p error ", msg);
@@ -952,7 +1042,7 @@ namespace client
if (it->m_SocketType == eSAMSocketTypeAcceptor)
{
it->m_IsAccepting = true;
session->localDestination->AcceptOnce (std::bind (&SAMSocket::HandleI2PAccept, it, std::placeholders::_1));
session->GetLocalDestination ()->AcceptOnce (std::bind (&SAMSocket::HandleI2PAccept, it, std::placeholders::_1));
break;
}
}
@@ -1090,19 +1180,11 @@ namespace client
m_Owner.GetService ().post (std::bind( !ec ? &SAMSocket::Receive : &SAMSocket::TerminateClose, shared_from_this()));
}
SAMSession::SAMSession (SAMBridge & parent, const std::string & id, SAMSessionType type, std::shared_ptr<ClientDestination> dest):
m_Bridge(parent),
localDestination (dest),
UDPEndpoint(nullptr),
Name(id), Type (type)
SAMSession::SAMSession (SAMBridge & parent, const std::string & id, SAMSessionType type):
m_Bridge(parent), Name(id), Type (type), UDPEndpoint(nullptr)
{
}
SAMSession::~SAMSession ()
{
i2p::client::context.DeleteLocalDestination (localDestination);
}
void SAMSession::CloseStreams ()
{
for(const auto & itr : m_Bridge.ListSockets(Name))
@@ -1111,6 +1193,58 @@ namespace client
}
}
SAMSingleSession::SAMSingleSession (SAMBridge & parent, const std::string & name, SAMSessionType type, std::shared_ptr<ClientDestination> dest):
SAMSession (parent, name, type),
localDestination (dest)
{
}
SAMSingleSession::~SAMSingleSession ()
{
i2p::client::context.DeleteLocalDestination (localDestination);
}
void SAMSingleSession::StopLocalDestination ()
{
localDestination->Release ();
localDestination->StopAcceptingStreams ();
}
void SAMMasterSession::Close ()
{
SAMSingleSession::Close ();
for (const auto& it: subsessions)
m_Bridge.CloseSession (it);
subsessions.clear ();
}
SAMSubSession::SAMSubSession (std::shared_ptr<SAMMasterSession> master, const std::string& name, SAMSessionType type, int port):
SAMSession (master->m_Bridge, name, type), masterSession (master), inPort (port)
{
if (Type == eSAMSessionTypeStream)
{
auto d = masterSession->GetLocalDestination ()->CreateStreamingDestination (inPort);
if (d) d->Start ();
}
// TODO: implement datagrams
}
std::shared_ptr<ClientDestination> SAMSubSession::GetLocalDestination ()
{
return masterSession ? masterSession->GetLocalDestination () : nullptr;
}
void SAMSubSession::StopLocalDestination ()
{
auto dest = GetLocalDestination ();
if (dest && Type == eSAMSessionTypeStream)
{
auto d = dest->RemoveStreamingDestination (inPort);
if (d) d->Stop ();
}
// TODO: implement datagrams
}
SAMBridge::SAMBridge (const std::string& address, int port, bool singleThread):
RunnableService ("SAM"), m_IsSingleThread (singleThread),
m_Acceptor (GetIOService (), boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), port)),
@@ -1156,7 +1290,7 @@ namespace client
{
std::unique_lock<std::mutex> l(m_SessionsMutex);
for (auto& it: m_Sessions)
it.second->CloseStreams ();
it.second->Close ();
m_Sessions.clear ();
}
StopIOService ();
@@ -1248,7 +1382,8 @@ namespace client
if (localDestination)
{
localDestination->Acquire ();
auto session = std::make_shared<SAMSession>(*this, id, type, localDestination);
auto session = (type == eSAMSessionTypeMaster) ? std::make_shared<SAMMasterSession>(*this, id, localDestination) :
std::make_shared<SAMSingleSession>(*this, id, type, localDestination);
std::unique_lock<std::mutex> l(m_SessionsMutex);
auto ret = m_Sessions.insert (std::make_pair(id, session));
if (!ret.second)
@@ -1258,6 +1393,13 @@ namespace client
return nullptr;
}
bool SAMBridge::AddSession (std::shared_ptr<SAMSession> session)
{
if (!session) return false;
auto ret = m_Sessions.emplace (session->Name, session);
return ret.second;
}
void SAMBridge::CloseSession (const std::string& id)
{
std::shared_ptr<SAMSession> session;
@@ -1272,9 +1414,8 @@ namespace client
}
if (session)
{
session->localDestination->Release ();
session->localDestination->StopAcceptingStreams ();
session->CloseStreams ();
session->StopLocalDestination ();
session->Close ();
if (m_IsSingleThread)
{
auto timer = std::make_shared<boost::asio::deadline_timer>(GetService ());
@@ -1349,10 +1490,10 @@ namespace client
i2p::data::IdentityEx dest;
dest.FromBase64 (destination);
if (session->Type == eSAMSessionTypeDatagram)
session->localDestination->GetDatagramDestination ()->
session->GetLocalDestination ()->GetDatagramDestination ()->
SendDatagramTo ((uint8_t *)eol, payloadLen, dest.GetIdentHash ());
else // raw
session->localDestination->GetDatagramDestination ()->
session->GetLocalDestination ()->GetDatagramDestination ()->
SendRawDatagramTo ((uint8_t *)eol, payloadLen, dest.GetIdentHash ());
}
else

View File

@@ -13,6 +13,7 @@
#include <string>
#include <map>
#include <list>
#include <set>
#include <thread>
#include <mutex>
#include <memory>
@@ -41,6 +42,8 @@ namespace client
const char SAM_SESSION_CREATE_INVALID_ID[] = "SESSION STATUS RESULT=INVALID_ID\n";
const char SAM_SESSION_STATUS_INVALID_KEY[] = "SESSION STATUS RESULT=INVALID_KEY\n";
const char SAM_SESSION_STATUS_I2P_ERROR[] = "SESSION STATUS RESULT=I2P_ERROR MESSAGE=%s\n";
const char SAM_SESSION_ADD[] = "SESSION ADD";
const char SAM_SESSION_REMOVE[] = "SESSION REMOVE";
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";
@@ -72,10 +75,12 @@ namespace client
const char SAM_PARAM_SIZE[] = "SIZE";
const char SAM_PARAM_HOST[] = "HOST";
const char SAM_PARAM_PORT[] = "PORT";
const char SAM_PARAM_FROM_PORT[] = "FROM_PORT";
const char SAM_VALUE_TRANSIENT[] = "TRANSIENT";
const char SAM_VALUE_STREAM[] = "STREAM";
const char SAM_VALUE_DATAGRAM[] = "DATAGRAM";
const char SAM_VALUE_RAW[] = "RAW";
const char SAM_VALUE_MASTER[] = "MASTER";
const char SAM_VALUE_TRUE[] = "true";
const char SAM_VALUE_FALSE[] = "false";
@@ -134,6 +139,8 @@ namespace client
void ProcessStreamForward (char * buf, size_t len);
void ProcessDestGenerate (char * buf, size_t len);
void ProcessNamingLookup (char * buf, size_t len);
void ProcessSessionAdd (char * buf, size_t len);
void ProcessSessionRemove (char * buf, size_t len);
void SendI2PError(const std::string & msg);
size_t ProcessDatagramSend (char * buf, size_t len, const char * data); // from SAM 1.0
void ExtractParams (char * buf, std::map<std::string, std::string>& params);
@@ -171,23 +178,57 @@ namespace client
eSAMSessionTypeUnknown,
eSAMSessionTypeStream,
eSAMSessionTypeDatagram,
eSAMSessionTypeRaw
eSAMSessionTypeRaw,
eSAMSessionTypeMaster
};
struct SAMSession
{
SAMBridge & m_Bridge;
std::shared_ptr<ClientDestination> localDestination;
std::shared_ptr<boost::asio::ip::udp::endpoint> UDPEndpoint;
std::string Name;
SAMSessionType Type;
SAMSession (SAMBridge & parent, const std::string & name, SAMSessionType type, std::shared_ptr<ClientDestination> dest);
~SAMSession ();
std::shared_ptr<boost::asio::ip::udp::endpoint> UDPEndpoint; // TODO: move
SAMSession (SAMBridge & parent, const std::string & name, SAMSessionType type);
virtual ~SAMSession () {};
virtual std::shared_ptr<ClientDestination> GetLocalDestination () = 0;
virtual void StopLocalDestination () = 0;
virtual void Close () { CloseStreams (); };
void CloseStreams ();
};
struct SAMSingleSession: public SAMSession
{
std::shared_ptr<ClientDestination> localDestination;
SAMSingleSession (SAMBridge & parent, const std::string & name, SAMSessionType type, std::shared_ptr<ClientDestination> dest);
~SAMSingleSession ();
std::shared_ptr<ClientDestination> GetLocalDestination () { return localDestination; };
void StopLocalDestination ();
};
struct SAMMasterSession: public SAMSingleSession
{
std::set<std::string> subsessions;
SAMMasterSession (SAMBridge & parent, const std::string & name, std::shared_ptr<ClientDestination> dest):
SAMSingleSession (parent, name, eSAMSessionTypeMaster, dest) {};
void Close ();
};
struct SAMSubSession: public SAMSession
{
std::shared_ptr<SAMMasterSession> masterSession;
int inPort;
SAMSubSession (std::shared_ptr<SAMMasterSession> master, const std::string& name, SAMSessionType type, int port);
// implements SAMSession
std::shared_ptr<ClientDestination> GetLocalDestination ();
void StopLocalDestination ();
};
class SAMBridge: private i2p::util::RunnableService
{
public:
@@ -201,6 +242,7 @@ namespace client
boost::asio::io_service& GetService () { return GetIOService (); };
std::shared_ptr<SAMSession> CreateSession (const std::string& id, SAMSessionType type, const std::string& destination, // empty string means transient
const std::map<std::string, std::string> * params);
bool AddSession (std::shared_ptr<SAMSession> session);
void CloseSession (const std::string& id);
std::shared_ptr<SAMSession> FindSession (const std::string& id) const;