mirror of
https://github.com/PurpleI2P/i2pd.git
synced 2025-01-25 23:07:18 +01:00
commit
1b3c3fae89
31
ChangeLog
31
ChangeLog
|
@ -1,6 +1,37 @@
|
|||
# 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
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
version: 2.37.0.{build}
|
||||
version: 2.38.0.{build}
|
||||
pull_requests:
|
||||
do_not_increment_build_number: true
|
||||
branches:
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
%define git_hash %(git rev-parse HEAD | cut -c -7)
|
||||
|
||||
Name: i2pd-git
|
||||
Version: 2.37.0
|
||||
Version: 2.38.0
|
||||
Release: git%{git_hash}%{?dist}
|
||||
Summary: I2P router written in C++
|
||||
Conflicts: i2pd
|
||||
|
@ -137,6 +137,9 @@ 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
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
Name: i2pd
|
||||
Version: 2.37.0
|
||||
Version: 2.38.0
|
||||
Release: 1%{?dist}
|
||||
Summary: I2P router written in C++
|
||||
Conflicts: i2pd-git
|
||||
|
@ -135,6 +135,9 @@ 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
|
||||
|
||||
|
|
|
@ -142,23 +142,23 @@ 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);
|
||||
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
|
||||
}
|
||||
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);
|
||||
|
@ -166,8 +166,8 @@ namespace util
|
|||
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)
|
||||
|
@ -210,15 +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);
|
||||
|
@ -228,17 +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);
|
||||
}
|
||||
|
||||
i2p::context.SetStatus (eRouterStatusMesh);
|
||||
}
|
||||
|
||||
bool transit; i2p::config::GetOption("notransit", transit);
|
||||
i2p::context.SetAcceptsTunnels (!transit);
|
||||
uint16_t transitTunnels; i2p::config::GetOption("limits.transittunnels", transitTunnels);
|
||||
|
@ -281,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
|
||||
{
|
||||
|
|
|
@ -242,13 +242,9 @@ namespace http {
|
|||
s << "<b>ERROR:</b> " << 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;
|
||||
|
@ -269,14 +265,29 @@ namespace http {
|
|||
break;
|
||||
case eRouterErrorSymmetricNAT:
|
||||
s << " - Symmetric NAT";
|
||||
break;
|
||||
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> ";
|
||||
|
@ -314,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())
|
||||
{
|
||||
|
@ -831,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;
|
||||
}
|
||||
|
@ -857,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";
|
||||
|
@ -1264,14 +1276,14 @@ namespace http {
|
|||
ident.FromBase32 (b32);
|
||||
auto dest = i2p::client::context.FindLocalDestination (ident);
|
||||
|
||||
if (dest)
|
||||
if (dest)
|
||||
{
|
||||
std::size_t pos;
|
||||
pos = name.find (".i2p");
|
||||
if (pos == (name.length () - 4))
|
||||
if (pos == (name.length () - 4))
|
||||
{
|
||||
pos = name.find (".b32.i2p");
|
||||
if (pos == std::string::npos)
|
||||
if (pos == std::string::npos)
|
||||
{
|
||||
auto signatureLen = dest->GetIdentity ()->GetSignatureLen ();
|
||||
uint8_t * signature = new uint8_t[signatureLen];
|
||||
|
@ -1291,13 +1303,13 @@ namespace http {
|
|||
"</form>\r\n<br>\r\n";
|
||||
delete[] signature;
|
||||
delete[] sig;
|
||||
}
|
||||
else
|
||||
}
|
||||
else
|
||||
s << "<b>ERROR</b>: Domain can't end with .b32.i2p\r\n<br>\r\n<br>\r\n";
|
||||
}
|
||||
}
|
||||
else
|
||||
s << "<b>ERROR</b>: Domain must end with .i2p\r\n<br>\r\n<br>\r\n";
|
||||
}
|
||||
}
|
||||
else
|
||||
s << "<b>ERROR</b>: Such destination is not found\r\n<br>\r\n<br>\r\n";
|
||||
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
6
debian/changelog
vendored
6
debian/changelog
vendored
|
@ -1,3 +1,9 @@
|
|||
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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -62,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(""), "Ignored")
|
||||
("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
|
||||
|
@ -77,9 +77,9 @@ namespace config {
|
|||
("limits.coresize", value<uint32_t>()->default_value(0), "Maximum size of corefile in Kb (0 - use system limit)")
|
||||
("limits.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");
|
||||
|
@ -281,7 +281,7 @@ 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.yggdrasil", bool_switch()->default_value(false), "Support transports through the Yggdrasil (default: false)")
|
||||
("meshnets.yggaddress", value<std::string>()->default_value(""), "Yggdrasil address to publish")
|
||||
;
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -152,7 +152,7 @@ namespace client
|
|||
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:
|
||||
|
||||
|
@ -236,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);
|
||||
|
@ -262,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:
|
||||
|
||||
|
|
|
@ -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>();
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -813,7 +815,6 @@ namespace garlic
|
|||
case eSessionStateNew:
|
||||
return HandleNewIncomingSession (buf, len);
|
||||
case eSessionStateNewSessionSent:
|
||||
receiveTagset->Expire (); // NSR tagset
|
||||
return HandleNewOutgoingSessionReply (buf, len);
|
||||
default:
|
||||
return false;
|
||||
|
@ -1101,6 +1102,7 @@ namespace garlic
|
|||
RouterIncomingRatchetSession::RouterIncomingRatchetSession (const i2p::crypto::NoiseSymmetricState& initState):
|
||||
ECIESX25519AEADRatchetSession (&i2p::context, false)
|
||||
{
|
||||
SetLeaseSetUpdateStatus (eLeaseSetDoNotSend);
|
||||
SetNoiseState (initState);
|
||||
}
|
||||
|
||||
|
|
|
@ -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,7 +161,7 @@ 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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -342,7 +342,7 @@ namespace transport
|
|||
else
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -717,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 ()
|
||||
|
@ -726,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));
|
||||
}
|
||||
|
@ -780,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));
|
||||
}
|
||||
|
@ -1009,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 ();
|
||||
}
|
||||
}
|
||||
|
@ -1113,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 ();
|
||||
}
|
||||
|
@ -1193,7 +1193,12 @@ namespace transport
|
|||
m_NTCP2V6Acceptor->open (boost::asio::ip::tcp::v6());
|
||||
m_NTCP2V6Acceptor->set_option (boost::asio::ip::v6_only (true));
|
||||
m_NTCP2V6Acceptor->set_option (boost::asio::socket_base::reuse_address (true));
|
||||
m_NTCP2V6Acceptor->bind (boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v6(), address->port));
|
||||
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);
|
||||
|
@ -1274,7 +1279,7 @@ namespace transport
|
|||
{
|
||||
LogPrint (eLogError, "NTCP2: Can't connect to unspecified address");
|
||||
return;
|
||||
}
|
||||
}
|
||||
LogPrint (eLogDebug, "NTCP2: Connecting to ", conn->GetRemoteEndpoint ());
|
||||
GetService ().post([this, conn]()
|
||||
{
|
||||
|
@ -1295,25 +1300,25 @@ namespace transport
|
|||
// 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
|
||||
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 ());
|
||||
}
|
||||
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
|
||||
|
@ -1443,8 +1448,8 @@ namespace transport
|
|||
{
|
||||
LogPrint (eLogError, "NTCP2: Can't connect to unspecified address");
|
||||
return;
|
||||
}
|
||||
GetService().post([this, conn]()
|
||||
}
|
||||
GetService().post([this, conn]()
|
||||
{
|
||||
if (this->AddNTCP2Session (conn))
|
||||
{
|
||||
|
@ -1541,10 +1546,10 @@ namespace transport
|
|||
if(ep.address ().is_v6 ())
|
||||
req.uri = "[" + ep.address ().to_string() + "]:" + std::to_string(ep.port ());
|
||||
else
|
||||
req.uri = ep.address ().to_string() + ":" + std::to_string(ep.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);
|
||||
out << req.to_string();
|
||||
|
@ -1622,7 +1627,7 @@ namespace transport
|
|||
sz += 16;
|
||||
memcpy(buff->data () + 4, addrbytes.data(), 16);
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
// We mustn't really fall here because all connections are made to IP addresses
|
||||
LogPrint(eLogError, "NTCP2: Tried to connect to unexpected address via proxy");
|
||||
|
@ -1661,17 +1666,17 @@ namespace transport
|
|||
}
|
||||
|
||||
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
|
||||
else
|
||||
m_Address6 = addr;
|
||||
}
|
||||
}
|
||||
else
|
||||
m_Address4 = addr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
@ -1149,12 +1149,13 @@ namespace data
|
|||
});
|
||||
}
|
||||
|
||||
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 (v4only);
|
||||
return !router->IsHidden () && router->IsPeerTesting (v4) &&
|
||||
!excluded.count (router->GetIdentHash ());
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1167,12 +1168,13 @@ 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->IsIntroducer () && !router->IsHidden () && !router->IsFloodfill (); // floodfills don't send relay tag
|
||||
return router->IsIntroducer (v4) && !excluded.count (router->GetIdentHash ()) &&
|
||||
!router->IsHidden () && !router->IsFloodfill (); // floodfills don't send relay tag
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -85,9 +85,9 @@ namespace data
|
|||
std::shared_ptr<const RouterInfo> GetRandomRouter () const;
|
||||
std::shared_ptr<const RouterInfo> GetRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith, bool reverse) const;
|
||||
std::shared_ptr<const RouterInfo> GetHighBandwidthRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith, bool reverse) const;
|
||||
std::shared_ptr<const RouterInfo> GetRandomPeerTestRouter (bool v4only = true) 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;
|
||||
|
|
|
@ -678,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)
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
@ -44,12 +44,12 @@ namespace i2p
|
|||
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 ()
|
||||
|
@ -74,21 +74,21 @@ 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);
|
||||
|
||||
|
||||
if ((ntcp2 || ygg) && !m_NTCP2Keys)
|
||||
NewNTCP2Keys ();
|
||||
bool ntcp2Published = false;
|
||||
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)
|
||||
{
|
||||
|
@ -100,8 +100,8 @@ namespace i2p
|
|||
// 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)
|
||||
|
@ -109,26 +109,26 @@ namespace i2p
|
|||
else // add non-published NTCP2 address
|
||||
{
|
||||
addressCaps = i2p::data::RouterInfo::AddressCaps::eV4;
|
||||
routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv);
|
||||
}
|
||||
}
|
||||
routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv);
|
||||
}
|
||||
}
|
||||
if (ssu)
|
||||
{
|
||||
{
|
||||
routerInfo.AddSSUAddress (host.c_str(), port, nullptr);
|
||||
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
|
||||
else
|
||||
{
|
||||
std::string address6; i2p::config::GetOption("address6", address6);
|
||||
if (!address6.empty ()) host = address6;
|
||||
}
|
||||
|
||||
|
||||
if (ntcp2)
|
||||
{
|
||||
if (ntcp2Published)
|
||||
|
@ -140,29 +140,29 @@ namespace i2p
|
|||
ntcp2Host = host;
|
||||
routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address_v6::from_string (ntcp2Host), port);
|
||||
}
|
||||
else
|
||||
{
|
||||
else
|
||||
{
|
||||
if (!ipv4) // no other ntcp2 addresses yet
|
||||
routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv);
|
||||
addressCaps |= i2p::data::RouterInfo::AddressCaps::eV6;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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 ())
|
||||
routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, yggaddr, port);
|
||||
}
|
||||
}
|
||||
|
||||
if (addressCaps)
|
||||
routerInfo.SetUnreachableAddressesTransportCaps (addressCaps);
|
||||
routerInfo.SetCaps (caps); // caps + L
|
||||
routerInfo.SetCaps (caps); // caps + L
|
||||
routerInfo.SetProperty ("netId", std::to_string (m_NetID));
|
||||
routerInfo.SetProperty ("router.version", I2P_VERSION);
|
||||
routerInfo.CreateBuffer (m_Keys);
|
||||
|
@ -199,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:
|
||||
;
|
||||
|
@ -225,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->IsV4 ()))
|
||||
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 ? i2p::data::COST_NTCP2_PUBLISHED : i2p::data::COST_NTCP2_NON_PUBLISHED;
|
||||
address->ntcp2->isPublished = publish;
|
||||
address->ntcp2->iv = m_NTCP2Keys->iv;
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
if (updated)
|
||||
|
@ -281,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;
|
||||
|
@ -373,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 ();
|
||||
|
@ -387,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 ();
|
||||
|
@ -435,54 +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
|
||||
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->cost = i2p::data::COST_SSU_THROUGH_INTRODUCERS;
|
||||
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;
|
||||
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->cost = i2p::data::COST_SSU_DIRECT;
|
||||
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)
|
||||
{
|
||||
|
@ -491,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
|
||||
|
@ -512,7 +547,7 @@ namespace i2p
|
|||
{
|
||||
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;
|
||||
|
@ -561,7 +596,7 @@ namespace i2p
|
|||
if (supportsV4)
|
||||
{
|
||||
bool foundSSU = false, foundNTCP2 = false;
|
||||
std::string host = "127.0.0.1";
|
||||
std::string host = "127.0.0.1";
|
||||
uint16_t port = 0;
|
||||
auto& addresses = m_RouterInfo.GetAddresses ();
|
||||
for (auto& addr: addresses)
|
||||
|
@ -591,26 +626,26 @@ namespace i2p
|
|||
{
|
||||
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);
|
||||
|
@ -624,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);
|
||||
|
@ -705,9 +740,9 @@ namespace i2p
|
|||
if (!rekey && m_Keys.GetPublic ()->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ELGAMAL)
|
||||
{
|
||||
// rekey routers with bandwidth = L (or default) this time
|
||||
std::string bandwidth; i2p::config::GetOption("bandwidth", bandwidth);
|
||||
if (bandwidth.empty () || bandwidth[0] == 'L') rekey = true;
|
||||
}
|
||||
bool isFloodfill; i2p::config::GetOption("floodfill", isFloodfill);
|
||||
if (!isFloodfill) rekey = true;
|
||||
}
|
||||
if (rekey)
|
||||
{
|
||||
// update keys
|
||||
|
@ -716,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)
|
||||
|
@ -749,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 ();
|
||||
|
@ -786,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);
|
||||
|
@ -812,8 +847,8 @@ namespace i2p
|
|||
m_ECIESSession->HandleNextMessage (buf, len);
|
||||
else
|
||||
LogPrint (eLogError, "Router: Session is not set for ECIES router");
|
||||
}
|
||||
else
|
||||
}
|
||||
else
|
||||
i2p::garlic::GarlicDestination::ProcessGarlicMessage (msg);
|
||||
}
|
||||
|
||||
|
@ -822,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 ()
|
||||
|
@ -844,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_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_TunnelDecryptor->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_TunnelDecryptor->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 ()
|
||||
|
|
|
@ -91,20 +91,22 @@ namespace garlic
|
|||
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);
|
||||
|
@ -168,7 +170,7 @@ namespace garlic
|
|||
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;
|
||||
|
|
|
@ -197,7 +197,8 @@ namespace data
|
|||
{
|
||||
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];
|
||||
|
@ -260,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')
|
||||
{
|
||||
|
@ -308,7 +309,7 @@ namespace data
|
|||
else
|
||||
supportedTransports |= eNTCP2V4;
|
||||
}
|
||||
else if (!address->ntcp2->isPublished)
|
||||
else if (!address->published)
|
||||
{
|
||||
if (address->caps)
|
||||
{
|
||||
|
@ -333,6 +334,23 @@ namespace data
|
|||
}
|
||||
else
|
||||
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)
|
||||
|
@ -513,7 +531,13 @@ 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;
|
||||
|
@ -529,8 +553,8 @@ namespace data
|
|||
WriteString ("caps", properties);
|
||||
properties << '=';
|
||||
std::string caps;
|
||||
if (address.caps & AddressCaps::eV4) caps += CAPS_FLAG_V4;
|
||||
if (address.caps & AddressCaps::eV6) caps += CAPS_FLAG_V6;
|
||||
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 << ';';
|
||||
|
@ -549,7 +573,7 @@ namespace data
|
|||
if (address.IsPeerTesting ()) caps += CAPS_FLAG_SSU_TESTING;
|
||||
if (address.host.is_v4 ())
|
||||
{
|
||||
if (IsReachable ())
|
||||
if (address.published)
|
||||
{
|
||||
isPublished = true;
|
||||
if (address.IsIntroducer ()) caps += CAPS_FLAG_SSU_INTRODUCER;
|
||||
|
@ -558,11 +582,19 @@ namespace data
|
|||
caps += CAPS_FLAG_V4;
|
||||
}
|
||||
else if (address.host.is_v6 ())
|
||||
isPublished = true;
|
||||
{
|
||||
if (address.published)
|
||||
{
|
||||
isPublished = true;
|
||||
if (address.IsIntroducer ()) caps += CAPS_FLAG_SSU_INTRODUCER;
|
||||
}
|
||||
else
|
||||
caps += CAPS_FLAG_V6;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (address.caps & AddressCaps::eV4) caps += CAPS_FLAG_V4;
|
||||
if (address.caps & AddressCaps::eV6) caps += CAPS_FLAG_V6;
|
||||
if (address.IsV4 ()) caps += CAPS_FLAG_V4;
|
||||
if (address.IsV6 ()) caps += CAPS_FLAG_V6;
|
||||
if (caps.empty ()) caps += CAPS_FLAG_V4;
|
||||
}
|
||||
WriteString (caps, properties);
|
||||
|
@ -585,6 +617,18 @@ namespace data
|
|||
{
|
||||
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 << '=';
|
||||
|
@ -622,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);
|
||||
|
@ -788,7 +820,7 @@ namespace data
|
|||
addr->host = boost::asio::ip::address::from_string (host);
|
||||
addr->port = port;
|
||||
addr->transportStyle = eTransportSSU;
|
||||
addr->cost = COST_SSU_DIRECT; // NTCP2 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 ());
|
||||
|
@ -808,12 +840,11 @@ namespace data
|
|||
auto addr = std::make_shared<Address>();
|
||||
addr->host = host;
|
||||
addr->port = port;
|
||||
addr->transportStyle = eTransportNTCP;
|
||||
addr->cost = port ? COST_NTCP2_PUBLISHED : COST_NTCP2_NON_PUBLISHED; // 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));
|
||||
|
@ -823,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
|
||||
|
@ -838,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;
|
||||
|
@ -1002,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 ());
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1021,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();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1100,26 +1128,25 @@ namespace data
|
|||
GetIdentity ()->GetSigningKeyType () != SIGNING_KEY_TYPE_DSA_SHA1;
|
||||
}
|
||||
|
||||
bool RouterInfo::IsPeerTesting (bool v4only) const
|
||||
bool RouterInfo::IsPeerTesting (bool v4) const
|
||||
{
|
||||
auto supportedTransports = m_SupportedTransports & (eSSUV4 | eSSUV6);
|
||||
if (!supportedTransports) return false; // no SSU
|
||||
if (v4only && !(supportedTransports & eSSUV4)) return false; // no SSU v4
|
||||
if (!(m_SupportedTransports & (v4 ? eSSUV4 : eSSUV6))) return false;
|
||||
return (bool)GetAddress (
|
||||
[](std::shared_ptr<const RouterInfo::Address> address)->bool
|
||||
[v4](std::shared_ptr<const RouterInfo::Address> address)->bool
|
||||
{
|
||||
return (address->transportStyle == eTransportSSU) && address->IsPeerTesting ();
|
||||
return (address->transportStyle == eTransportSSU) && address->IsPeerTesting () &&
|
||||
((v4 && address->IsV4 ()) || (!v4 && address->IsV6 ())) && address->IsReachableSSU ();
|
||||
});
|
||||
}
|
||||
|
||||
bool RouterInfo::IsIntroducer () const
|
||||
bool RouterInfo::IsIntroducer (bool v4) const
|
||||
{
|
||||
// TODO: support ipv6
|
||||
if (!(m_SupportedTransports & eSSUV4)) return false;
|
||||
if (!(m_SupportedTransports & (v4 ? eSSUV4 : eSSUV6))) return false;
|
||||
return (bool)GetAddress (
|
||||
[](std::shared_ptr<const RouterInfo::Address> address)->bool
|
||||
[v4](std::shared_ptr<const RouterInfo::Address> address)->bool
|
||||
{
|
||||
return (address->transportStyle == eTransportSSU) && address->IsIntroducer ();
|
||||
return (address->transportStyle == eTransportSSU) && address->IsIntroducer () &&
|
||||
((v4 && address->IsV4 ()) || (!v4 && address->IsV6 ())) && !address->host.is_unspecified ();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -96,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;
|
||||
|
@ -115,7 +115,6 @@ namespace data
|
|||
{
|
||||
Tag<32> staticKey;
|
||||
Tag<16> iv;
|
||||
bool isPublished = false;
|
||||
};
|
||||
|
||||
struct Address
|
||||
|
@ -124,7 +123,8 @@ namespace data
|
|||
boost::asio::ip::address host;
|
||||
int port;
|
||||
uint64_t date;
|
||||
uint8_t cost, caps;
|
||||
uint8_t caps;
|
||||
bool published = false;
|
||||
std::unique_ptr<SSUExt> ssu; // not null for SSU
|
||||
std::unique_ptr<NTCP2Ext> ntcp2; // not null for NTCP2
|
||||
|
||||
|
@ -146,14 +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 (); };
|
||||
bool IsV6 () const { return (caps & AddressCaps::eV6) || host.is_v6 (); };
|
||||
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;
|
||||
|
||||
|
@ -204,13 +205,12 @@ namespace data
|
|||
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 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 v4only) const;
|
||||
bool IsIntroducer () const;
|
||||
bool IsPeerTesting (bool v4) const;
|
||||
bool IsIntroducer (bool v4) const;
|
||||
|
||||
uint8_t GetCaps () const { return m_Caps; };
|
||||
void SetCaps (uint8_t caps);
|
||||
|
|
299
libi2pd/SSU.cpp
299
libi2pd/SSU.cpp
|
@ -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
|
||||
*
|
||||
|
@ -28,8 +28,8 @@ namespace transport
|
|||
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)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -79,9 +79,10 @@ namespace transport
|
|||
if (context.SupportsV4 ())
|
||||
{
|
||||
OpenSocket ();
|
||||
m_ReceiversThread = new std::thread (std::bind (&SSUServer::RunReceivers, this));
|
||||
m_ReceiversThread = new std::thread (std::bind (&SSUServer::RunReceivers, this));
|
||||
m_ReceiversService.post (std::bind (&SSUServer::Receive, this));
|
||||
ScheduleTermination ();
|
||||
ScheduleIntroducersUpdateTimer (); // wait for 30 seconds and decide if we need introducers
|
||||
}
|
||||
if (context.SupportsV6 ())
|
||||
{
|
||||
|
@ -89,9 +90,9 @@ namespace transport
|
|||
m_ReceiversThreadV6 = new std::thread (std::bind (&SSUServer::RunReceiversV6, this));
|
||||
m_ReceiversServiceV6.post (std::bind (&SSUServer::ReceiveV6, this));
|
||||
ScheduleTerminationV6 ();
|
||||
ScheduleIntroducersUpdateTimerV6 (); // wait for 30 seconds and decide if we need introducers
|
||||
}
|
||||
SchedulePeerTestsCleanupTimer ();
|
||||
ScheduleIntroducersUpdateTimer (); // wait for 30 seconds and decide if we need introducers
|
||||
}
|
||||
|
||||
void SSUServer::Stop ()
|
||||
|
@ -100,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 ();
|
||||
|
@ -122,7 +125,7 @@ namespace transport
|
|||
m_Thread->join ();
|
||||
delete m_Thread;
|
||||
m_Thread = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SSUServer::Run ()
|
||||
|
@ -195,8 +198,8 @@ namespace transport
|
|||
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;
|
||||
|
@ -385,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 ();
|
||||
|
@ -393,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)
|
||||
{
|
||||
|
@ -406,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;
|
||||
|
@ -430,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, 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,
|
||||
bool SSUServer::CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router,
|
||||
std::shared_ptr<const i2p::data::RouterInfo::Address> address, bool peerTest)
|
||||
{
|
||||
if (router && address)
|
||||
{
|
||||
if (router->UsesIntroducer ())
|
||||
if (address->UsesIntroducer ())
|
||||
m_Service.post (std::bind (&SSUServer::CreateSessionThroughIntroducer, this, router, address, peerTest)); // always V4 thread
|
||||
else
|
||||
{
|
||||
if (address->host.is_unspecified () || !address->port) return false;
|
||||
boost::asio::ip::udp::endpoint remoteEndpoint (address->host, address->port);
|
||||
m_Service.post (std::bind (&SSUServer::CreateDirectSession, this, router, remoteEndpoint, peerTest));
|
||||
}
|
||||
}
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void SSUServer::CreateDirectSession (std::shared_ptr<const i2p::data::RouterInfo> router, boost::asio::ip::udp::endpoint remoteEndpoint, bool peerTest)
|
||||
|
@ -477,24 +472,28 @@ namespace transport
|
|||
}
|
||||
}
|
||||
|
||||
void SSUServer::CreateSessionThroughIntroducer (std::shared_ptr<const i2p::data::RouterInfo> router,
|
||||
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 () && address)
|
||||
{
|
||||
if (router && address && address->UsesIntroducer ())
|
||||
{
|
||||
if (address->IsV4 () && !i2p::context.SupportsV4 ()) return;
|
||||
if (address->IsV6 () && !i2p::context.SupportsV6 ()) return;
|
||||
if (!address->host.is_unspecified () && address->port)
|
||||
{
|
||||
{
|
||||
// we rarely come here
|
||||
auto& sessions = address->host.is_v6 () ? m_SessionsV6 : m_Sessions;
|
||||
boost::asio::ip::udp::endpoint remoteEndpoint (address->host, address->port);
|
||||
auto it = 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)
|
||||
|
@ -503,14 +502,16 @@ namespace transport
|
|||
std::shared_ptr<SSUSession> introducerSession;
|
||||
const i2p::data::RouterInfo::Introducer * introducer = nullptr;
|
||||
// we might have a session to introducer already
|
||||
auto offset = rand ();
|
||||
for (int i = 0; i < numIntroducers; i++)
|
||||
{
|
||||
auto intr = &(address->ssu->introducers[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 ()) // ipv4 only
|
||||
if (ep.address ().is_v4 () && address->IsV4 ()) // ipv4
|
||||
{
|
||||
if (!introducer) introducer = intr; // we pick first one for now
|
||||
if (!introducer) introducer = intr;
|
||||
auto it = m_Sessions.find (ep);
|
||||
if (it != m_Sessions.end ())
|
||||
{
|
||||
|
@ -518,10 +519,20 @@ namespace transport
|
|||
break;
|
||||
}
|
||||
}
|
||||
if (ep.address ().is_v6 () && address->IsV6 ()) // ipv6
|
||||
{
|
||||
if (!introducer) introducer = intr;
|
||||
auto it = m_SessionsV6.find (ep);
|
||||
if (it != m_SessionsV6.end ())
|
||||
{
|
||||
introducerSession = it->second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!introducer)
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU: Can't connect to unreachable router and no ipv4 non-expired introducers presented");
|
||||
LogPrint (eLogWarning, "SSU: Can't connect to unreachable router and no compatibe non-expired introducers presented");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -532,20 +543,27 @@ namespace transport
|
|||
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 (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);
|
||||
m_Sessions[remoteEndpoint] = session;
|
||||
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 (i2p::context.GetRouterInfo ().UsesIntroducer ()) // if we are unreachable
|
||||
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
|
||||
|
@ -630,24 +648,30 @@ 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;
|
||||
|
@ -658,56 +682,117 @@ namespace transport
|
|||
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));
|
||||
}
|
||||
|
||||
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 () != eRouterStatusFirewalled)
|
||||
{
|
||||
// we don't need introducers
|
||||
m_Introducers.clear ();
|
||||
return;
|
||||
}
|
||||
// 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;
|
||||
|
@ -715,36 +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)
|
||||
{
|
||||
std::set<std::shared_ptr<const i2p::data::RouterInfo> > requested;
|
||||
for (auto i = m_Introducers.size (); i < SSU_MAX_NUM_INTRODUCERS; i++)
|
||||
{
|
||||
auto introducer = i2p::data::netdb.GetRandomIntroducer ();
|
||||
if (introducer && !requested.count (introducer)) // not requested already
|
||||
{
|
||||
auto address = introducer->GetSSUAddress (true); // v4
|
||||
if (address && !address->host.is_unspecified ())
|
||||
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 (m_Introducers.begin (), m_Introducers.end (), ep) == m_Introducers.end ()) // not connected yet
|
||||
{
|
||||
CreateDirectSession (introducer, ep, false);
|
||||
requested.insert (introducer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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 ();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
@ -51,11 +52,10 @@ namespace transport
|
|||
~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,
|
||||
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);
|
||||
|
@ -71,6 +71,7 @@ namespace transport
|
|||
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);
|
||||
|
@ -99,9 +100,10 @@ namespace transport
|
|||
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);
|
||||
|
@ -127,9 +129,9 @@ namespace transport
|
|||
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
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
@ -325,12 +321,17 @@ namespace transport
|
|||
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
|
||||
|
@ -348,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))
|
||||
{
|
||||
|
@ -371,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;
|
||||
|
@ -402,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");
|
||||
|
@ -430,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)
|
||||
|
@ -475,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;
|
||||
|
@ -579,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;
|
||||
|
@ -633,64 +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 (ourPort != m_Server.GetPort ())
|
||||
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 (i2p::context.GetStatus () == eRouterStatusTesting)
|
||||
i2p::context.SetError (eRouterErrorSymmetricNAT);
|
||||
}
|
||||
else if (i2p::context.GetStatus () == eRouterStatusError && i2p::context.GetError () == eRouterErrorSymmetricNAT)
|
||||
i2p::context.SetStatus (eRouterStatusTesting);
|
||||
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);
|
||||
|
@ -703,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);
|
||||
|
@ -716,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,
|
||||
|
@ -986,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
|
||||
|
@ -1003,7 +1024,15 @@ 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 ();
|
||||
|
@ -1014,7 +1043,10 @@ namespace transport
|
|||
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
|
||||
}
|
||||
|
@ -1028,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;
|
||||
|
@ -1060,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
|
||||
{
|
||||
|
@ -1130,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
|
||||
|
@ -1160,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");
|
||||
|
@ -1233,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ();
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
|
@ -505,8 +505,8 @@ namespace transport
|
|||
}
|
||||
if (address && address->IsReachableSSU ())
|
||||
{
|
||||
m_SSUServer->CreateSession (peer.router, address);
|
||||
return true;
|
||||
if (m_SSUServer->CreateSession (peer.router, address))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -576,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 ()
|
||||
|
@ -752,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));
|
||||
}
|
||||
}
|
||||
|
@ -772,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)
|
||||
{
|
||||
|
|
|
@ -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; };
|
||||
|
|
|
@ -406,6 +406,7 @@ namespace tunnel
|
|||
|
||||
bool StandardSelectPeers(Path & peers, int numHops, bool inbound, SelectHopFunc nextHop)
|
||||
{
|
||||
int start = 0;
|
||||
auto prevHop = i2p::context.GetSharedRouterInfo ();
|
||||
if(i2p::transport::transports.RoutesRestricted())
|
||||
{
|
||||
|
@ -414,20 +415,22 @@ 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->IsV4 ()) || r->IsReachable ())) // first inbound 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, inbound);
|
||||
if (!hop && !i) // if no suitable peer found for first hop, try already connected
|
||||
|
@ -440,9 +443,8 @@ namespace tunnel
|
|||
LogPrint (eLogError, "Tunnels: Can't select next hop for ", prevHop->GetIdentHashBase64 ());
|
||||
return false;
|
||||
}
|
||||
if ((i == numHops - 1) &&
|
||||
((inbound && !hop->IsReachable ()) || // IBGW is not reachable
|
||||
(!inbound && !hop->IsV4 ()))) // OBEP is not ipv4
|
||||
if ((i == numHops - 1) && (!hop->IsV4 () || // doesn't support ipv4
|
||||
(inbound && !hop->IsReachable ()))) // IBGW is not reachable
|
||||
{
|
||||
auto hop1 = nextHop (prevHop, true);
|
||||
if (hop1) hop = hop1;
|
||||
|
|
|
@ -524,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 {
|
||||
|
|
|
@ -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 37
|
||||
#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)
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Reference in a new issue