Browse Source

Merge pull request #1638 from PurpleI2P/openssl

recent changes
pull/1777/head
orignal 4 years ago committed by GitHub
parent
commit
a518320e3b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      .github/workflows/build-windows.yml
  2. 2
      Makefile
  3. 7
      contrib/i2pd.service
  4. 11
      daemon/HTTPServer.cpp
  5. 4
      libi2pd/Config.cpp
  6. 2
      libi2pd/Crypto.cpp
  7. 120
      libi2pd/NTCP2.cpp
  8. 23
      libi2pd/NTCP2.h
  9. 2
      libi2pd/NetDb.cpp
  10. 86
      libi2pd/RouterContext.cpp
  11. 5
      libi2pd/RouterContext.h
  12. 103
      libi2pd/RouterInfo.cpp
  13. 33
      libi2pd/RouterInfo.h
  14. 61
      libi2pd/SSU.cpp
  15. 11
      libi2pd/SSU.h
  16. 3
      libi2pd/SSUSession.cpp
  17. 109
      libi2pd/Transports.cpp
  18. 17
      libi2pd_client/BOB.cpp
  19. 2
      libi2pd_client/BOB.h
  20. 6
      libi2pd_client/ClientContext.cpp
  21. 29
      libi2pd_client/I2PTunnel.cpp
  22. 4
      libi2pd_client/I2PTunnel.h

4
.github/workflows/build-windows.yml

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

2
Makefile

@ -66,6 +66,7 @@ mk_obj_dir:
@mkdir -p obj/$(DAEMON_SRC_DIR) @mkdir -p obj/$(DAEMON_SRC_DIR)
api: mk_obj_dir $(SHLIB) $(ARLIB) api: mk_obj_dir $(SHLIB) $(ARLIB)
client: mk_obj_dir $(SHLIB_CLIENT) $(ARLIB_CLIENT)
api_client: mk_obj_dir $(SHLIB) $(ARLIB) $(SHLIB_CLIENT) $(ARLIB_CLIENT) api_client: mk_obj_dir $(SHLIB) $(ARLIB) $(SHLIB_CLIENT) $(ARLIB_CLIENT)
## NOTE: The NEEDED_CXXFLAGS are here so that CXXFLAGS can be specified at build time ## NOTE: The NEEDED_CXXFLAGS are here so that CXXFLAGS can be specified at build time
@ -128,6 +129,7 @@ doxygen:
.PHONY: last-dist .PHONY: last-dist
.PHONY: api .PHONY: api
.PHONY: api_client .PHONY: api_client
.PHONY: client
.PHONY: mk_obj_dir .PHONY: mk_obj_dir
.PHONY: install .PHONY: install
.PHONY: strip .PHONY: strip

7
contrib/i2pd.service

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

11
daemon/HTTPServer.cpp

@ -263,6 +263,9 @@ namespace http {
case eRouterErrorOffline: case eRouterErrorOffline:
s << " - Offline"; s << " - Offline";
break; break;
case eRouterErrorSymmetricNAT:
s << " - Symmetric NAT";
break;
default: ; default: ;
} }
break; break;
@ -706,23 +709,23 @@ namespace http {
std::stringstream tmp_s, tmp_s6; uint16_t cnt = 0, cnt6 = 0; std::stringstream tmp_s, tmp_s6; uint16_t cnt = 0, cnt6 = 0;
for (const auto& it: sessions ) for (const auto& it: sessions )
{ {
if (it.second && it.second->IsEstablished () && !it.second->GetSocket ().remote_endpoint ().address ().is_v6 ()) if (it.second && it.second->IsEstablished () && !it.second->GetRemoteEndpoint ().address ().is_v6 ())
{ {
tmp_s << "<div class=\"listitem\">\r\n"; tmp_s << "<div class=\"listitem\">\r\n";
if (it.second->IsOutgoing ()) tmp_s << " &#8658; "; if (it.second->IsOutgoing ()) tmp_s << " &#8658; ";
tmp_s << i2p::data::GetIdentHashAbbreviation (it.second->GetRemoteIdentity ()->GetIdentHash ()) << ": " tmp_s << i2p::data::GetIdentHashAbbreviation (it.second->GetRemoteIdentity ()->GetIdentHash ()) << ": "
<< it.second->GetSocket ().remote_endpoint().address ().to_string (); << it.second->GetRemoteEndpoint ().address ().to_string ();
if (!it.second->IsOutgoing ()) tmp_s << " &#8658; "; if (!it.second->IsOutgoing ()) tmp_s << " &#8658; ";
tmp_s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]"; tmp_s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
tmp_s << "</div>\r\n" << std::endl; tmp_s << "</div>\r\n" << std::endl;
cnt++; cnt++;
} }
if (it.second && it.second->IsEstablished () && it.second->GetSocket ().remote_endpoint ().address ().is_v6 ()) if (it.second && it.second->IsEstablished () && it.second->GetRemoteEndpoint ().address ().is_v6 ())
{ {
tmp_s6 << "<div class=\"listitem\">\r\n"; tmp_s6 << "<div class=\"listitem\">\r\n";
if (it.second->IsOutgoing ()) tmp_s6 << " &#8658; "; if (it.second->IsOutgoing ()) tmp_s6 << " &#8658; ";
tmp_s6 << i2p::data::GetIdentHashAbbreviation (it.second->GetRemoteIdentity ()->GetIdentHash ()) << ": " tmp_s6 << i2p::data::GetIdentHashAbbreviation (it.second->GetRemoteIdentity ()->GetIdentHash ()) << ": "
<< "[" << it.second->GetSocket ().remote_endpoint().address ().to_string () << "]"; << "[" << it.second->GetRemoteEndpoint ().address ().to_string () << "]";
if (!it.second->IsOutgoing ()) tmp_s6 << " &#8658; "; if (!it.second->IsOutgoing ()) tmp_s6 << " &#8658; ";
tmp_s6 << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]"; tmp_s6 << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
tmp_s6 << "</div>\r\n" << std::endl; tmp_s6 << "</div>\r\n" << std::endl;

4
libi2pd/Config.cpp

@ -50,7 +50,9 @@ namespace config {
("nat", bool_switch()->default_value(true), "Should we assume we are behind NAT? (default: enabled)") ("nat", bool_switch()->default_value(true), "Should we assume we are behind NAT? (default: enabled)")
("port", value<uint16_t>()->default_value(0), "Port to listen for incoming connections (default: auto)") ("port", value<uint16_t>()->default_value(0), "Port to listen for incoming connections (default: auto)")
("ipv4", bool_switch()->default_value(true), "Enable communication through ipv4 (default: enabled)") ("ipv4", bool_switch()->default_value(true), "Enable communication through ipv4 (default: enabled)")
("address4", value<std::string>()->default_value(""), "Local address to bind ipv4 transport sockets to")
("ipv6", bool_switch()->default_value(false), "Enable communication through ipv6 (default: disabled)") ("ipv6", bool_switch()->default_value(false), "Enable communication through ipv6 (default: disabled)")
("address6", value<std::string>()->default_value(""), "Local address to bind ipv6 transport sockets to")
("reservedrange", bool_switch()->default_value(true), "Check remote RI for being in blacklist of reserved IP ranges (default: enabled)") ("reservedrange", bool_switch()->default_value(true), "Check remote RI for being in blacklist of reserved IP ranges (default: enabled)")
("netid", value<int>()->default_value(I2PD_NET_ID), "Specify NetID. Main I2P is 2") ("netid", value<int>()->default_value(I2PD_NET_ID), "Specify NetID. Main I2P is 2")
("daemon", bool_switch()->default_value(false), "Router will go to background after start (default: disabled)") ("daemon", bool_switch()->default_value(false), "Router will go to background after start (default: disabled)")
@ -247,7 +249,7 @@ namespace config {
("ntcp2.enabled", value<bool>()->default_value(true), "Enable NTCP2 (default: enabled)") ("ntcp2.enabled", value<bool>()->default_value(true), "Enable NTCP2 (default: enabled)")
("ntcp2.published", value<bool>()->default_value(true), "Publish NTCP2 (default: enabled)") ("ntcp2.published", value<bool>()->default_value(true), "Publish NTCP2 (default: enabled)")
("ntcp2.port", value<uint16_t>()->default_value(0), "Port to listen for incoming NTCP2 connections (default: auto)") ("ntcp2.port", value<uint16_t>()->default_value(0), "Port to listen for incoming NTCP2 connections (default: auto)")
("ntcp2.addressv6", value<std::string>()->default_value("::"), "Address to bind NTCP2 on") ("ntcp2.addressv6", value<std::string>()->default_value("::"), "Address to publish NTCP2 with")
("ntcp2.proxy", value<std::string>()->default_value(""), "Proxy URL for NTCP2 transport") ("ntcp2.proxy", value<std::string>()->default_value(""), "Proxy URL for NTCP2 transport")
; ;

2
libi2pd/Crypto.cpp

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

120
libi2pd/NTCP2.cpp

@ -333,15 +333,14 @@ namespace transport
if (in_RemoteRouter) // Alice if (in_RemoteRouter) // Alice
{ {
m_Establisher->m_RemoteIdentHash = GetRemoteIdentity ()->GetIdentHash (); m_Establisher->m_RemoteIdentHash = GetRemoteIdentity ()->GetIdentHash ();
if (!addr)
addr = in_RemoteRouter->GetNTCP2Address (true); // we need a published address
if (addr) if (addr)
{ {
memcpy (m_Establisher->m_RemoteStaticKey, addr->ntcp2->staticKey, 32); memcpy (m_Establisher->m_RemoteStaticKey, addr->ntcp2->staticKey, 32);
memcpy (m_Establisher->m_IV, addr->ntcp2->iv, 16); memcpy (m_Establisher->m_IV, addr->ntcp2->iv, 16);
m_RemoteEndpoint = boost::asio::ip::tcp::endpoint (addr->host, addr->port);
} }
else else
LogPrint (eLogWarning, "NTCP2: Missing NTCP2 parameters"); LogPrint (eLogWarning, "NTCP2: Missing NTCP2 address");
} }
m_NextRouterInfoResendTime = i2p::util::GetSecondsSinceEpoch () + NTCP2_ROUTERINFO_RESEND_INTERVAL + m_NextRouterInfoResendTime = i2p::util::GetSecondsSinceEpoch () + NTCP2_ROUTERINFO_RESEND_INTERVAL +
rand ()%NTCP2_ROUTERINFO_RESEND_INTERVAL_THRESHOLD; rand ()%NTCP2_ROUTERINFO_RESEND_INTERVAL_THRESHOLD;
@ -657,19 +656,13 @@ namespace transport
SendTerminationAndTerminate (eNTCP2Message3Error); SendTerminationAndTerminate (eNTCP2Message3Error);
return; return;
} }
auto addr = ri.GetNTCP2Address (false); // any NTCP2 address auto addr = ri.GetNTCP2AddressWithStaticKey (m_Establisher->m_RemoteStaticKey);
if (!addr) if (!addr)
{ {
LogPrint (eLogError, "NTCP2: No NTCP2 address found in SessionConfirmed"); LogPrint (eLogError, "NTCP2: No NTCP2 address wth static key found in SessionConfirmed");
Terminate (); Terminate ();
return; return;
} }
if (memcmp (addr->ntcp2->staticKey, m_Establisher->m_RemoteStaticKey, 32))
{
LogPrint (eLogError, "NTCP2: Static key mismatch in SessionConfirmed");
SendTerminationAndTerminate (eNTCP2IncorrectSParameter);
return;
}
i2p::data::netdb.PostI2NPMsg (CreateI2NPMessage (eI2NPDummyMsg, buf.data () + 3, size)); // TODO: should insert ri and not parse it twice i2p::data::netdb.PostI2NPMsg (CreateI2NPMessage (eI2NPDummyMsg, buf.data () + 3, size)); // TODO: should insert ri and not parse it twice
// TODO: process options // TODO: process options
@ -1177,7 +1170,9 @@ namespace transport
{ {
try try
{ {
m_NTCP2Acceptor.reset (new boost::asio::ip::tcp::acceptor (GetService (), boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), address->port))); auto ep = m_Address4 ? boost::asio::ip::tcp::endpoint (m_Address4->address(), address->port):
boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), address->port);
m_NTCP2Acceptor.reset (new boost::asio::ip::tcp::acceptor (GetService (), ep));
} }
catch ( std::exception & ex ) catch ( std::exception & ex )
{ {
@ -1274,10 +1269,15 @@ namespace transport
return nullptr; return nullptr;
} }
void NTCP2Server::Connect(const boost::asio::ip::address & address, uint16_t port, std::shared_ptr<NTCP2Session> conn) void NTCP2Server::Connect(std::shared_ptr<NTCP2Session> conn)
{ {
LogPrint (eLogDebug, "NTCP2: Connecting to ", address ,":", port); if (!conn || conn->GetRemoteEndpoint ().address ().is_unspecified ())
GetService ().post([this, address, port, conn]() {
LogPrint (eLogError, "NTCP2: Can't connect to unspecified address");
return;
}
LogPrint (eLogDebug, "NTCP2: Connecting to ", conn->GetRemoteEndpoint ());
GetService ().post([this, conn]()
{ {
if (this->AddNTCP2Session (conn)) if (this->AddNTCP2Session (conn))
{ {
@ -1290,12 +1290,32 @@ namespace transport
if (ecode != boost::asio::error::operation_aborted) if (ecode != boost::asio::error::operation_aborted)
{ {
LogPrint (eLogInfo, "NTCP2: Not connected in ", timeout, " seconds"); LogPrint (eLogInfo, "NTCP2: Not connected in ", timeout, " seconds");
if (conn->GetRemoteIdentity ())
i2p::data::netdb.SetUnreachable (conn->GetRemoteIdentity ()->GetIdentHash (), true);
conn->Terminate (); conn->Terminate ();
} }
}); });
conn->GetSocket ().async_connect (boost::asio::ip::tcp::endpoint (address, port), std::bind (&NTCP2Server::HandleConnect, this, std::placeholders::_1, conn, timer)); // bind to local address
std::shared_ptr<boost::asio::ip::tcp::endpoint> localAddress;
if (conn->GetRemoteEndpoint ().address ().is_v6 ())
{
if (i2p::util::net::IsYggdrasilAddress (conn->GetRemoteEndpoint ().address ()))
localAddress = m_YggdrasilAddress;
else
localAddress = m_Address6;
conn->GetSocket ().open (boost::asio::ip::tcp::v6 ());
}
else
{
localAddress = m_Address4;
conn->GetSocket ().open (boost::asio::ip::tcp::v4 ());
}
if (localAddress)
{
boost::system::error_code ec;
conn->GetSocket ().bind (*localAddress, ec);
if (ec)
LogPrint (eLogError, "NTCP2: can't bind to ", localAddress->address ().to_string (), ": ", ec.message ());
}
conn->GetSocket ().async_connect (conn->GetRemoteEndpoint (), std::bind (&NTCP2Server::HandleConnect, this, std::placeholders::_1, conn, timer));
} }
else else
conn->Terminate (); conn->Terminate ();
@ -1312,7 +1332,7 @@ namespace transport
} }
else else
{ {
LogPrint (eLogDebug, "NTCP2: Connected to ", conn->GetSocket ().remote_endpoint ()); LogPrint (eLogDebug, "NTCP2: Connected to ", conn->GetRemoteEndpoint ());
conn->ClientLogin (); conn->ClientLogin ();
} }
} }
@ -1328,6 +1348,7 @@ namespace transport
LogPrint (eLogDebug, "NTCP2: Connected from ", ep); LogPrint (eLogDebug, "NTCP2: Connected from ", ep);
if (conn) if (conn)
{ {
conn->SetRemoteEndpoint (ep);
conn->ServerLogin (); conn->ServerLogin ();
m_PendingIncomingSessions.push_back (conn); m_PendingIncomingSessions.push_back (conn);
conn = nullptr; conn = nullptr;
@ -1361,6 +1382,7 @@ namespace transport
LogPrint (eLogDebug, "NTCP2: Connected from ", ep); LogPrint (eLogDebug, "NTCP2: Connected from ", ep);
if (conn) if (conn)
{ {
conn->SetRemoteEndpoint (ep);
conn->ServerLogin (); conn->ServerLogin ();
m_PendingIncomingSessions.push_back (conn); m_PendingIncomingSessions.push_back (conn);
} }
@ -1415,13 +1437,18 @@ namespace transport
} }
} }
void NTCP2Server::ConnectWithProxy (const std::string& host, uint16_t port, RemoteAddressType addrtype, std::shared_ptr<NTCP2Session> conn) void NTCP2Server::ConnectWithProxy (std::shared_ptr<NTCP2Session> conn)
{ {
if(!m_ProxyEndpoint) return; if(!m_ProxyEndpoint) return;
GetService().post([this, host, port, addrtype, conn]() { if (!conn || conn->GetRemoteEndpoint ().address ().is_unspecified ())
{
LogPrint (eLogError, "NTCP2: Can't connect to unspecified address");
return;
}
GetService().post([this, conn]()
{
if (this->AddNTCP2Session (conn)) if (this->AddNTCP2Session (conn))
{ {
auto timer = std::make_shared<boost::asio::deadline_timer>(GetService()); auto timer = std::make_shared<boost::asio::deadline_timer>(GetService());
auto timeout = NTCP2_CONNECT_TIMEOUT * 5; auto timeout = NTCP2_CONNECT_TIMEOUT * 5;
conn->SetTerminationTimeout(timeout * 2); conn->SetTerminationTimeout(timeout * 2);
@ -1431,11 +1458,10 @@ namespace transport
if (ecode != boost::asio::error::operation_aborted) if (ecode != boost::asio::error::operation_aborted)
{ {
LogPrint (eLogInfo, "NTCP2: Not connected in ", timeout, " seconds"); LogPrint (eLogInfo, "NTCP2: Not connected in ", timeout, " seconds");
i2p::data::netdb.SetUnreachable (conn->GetRemoteIdentity ()->GetIdentHash (), true);
conn->Terminate (); conn->Terminate ();
} }
}); });
conn->GetSocket ().async_connect (*m_ProxyEndpoint, std::bind (&NTCP2Server::HandleProxyConnect, this, std::placeholders::_1, conn, timer, host, port, addrtype)); conn->GetSocket ().async_connect (*m_ProxyEndpoint, std::bind (&NTCP2Server::HandleProxyConnect, this, std::placeholders::_1, conn, timer));
} }
}); });
} }
@ -1447,7 +1473,7 @@ namespace transport
m_ProxyPort = port; m_ProxyPort = port;
} }
void NTCP2Server::HandleProxyConnect(const boost::system::error_code& ecode, std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer, const std::string & host, uint16_t port, RemoteAddressType addrtype) void NTCP2Server::HandleProxyConnect(const boost::system::error_code& ecode, std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer)
{ {
if (ecode) if (ecode)
{ {
@ -1473,7 +1499,7 @@ namespace transport
}); });
auto readbuff = std::make_shared<std::vector<uint8_t> >(2); auto readbuff = std::make_shared<std::vector<uint8_t> >(2);
boost::asio::async_read(conn->GetSocket(), boost::asio::buffer(readbuff->data (), 2), boost::asio::async_read(conn->GetSocket(), boost::asio::buffer(readbuff->data (), 2),
[this, readbuff, timer, conn, host, port, addrtype](const boost::system::error_code & ec, std::size_t transferred) [this, readbuff, timer, conn](const boost::system::error_code & ec, std::size_t transferred)
{ {
if(ec) if(ec)
{ {
@ -1486,7 +1512,7 @@ namespace transport
{ {
if((*readbuff)[1] == 0x00) if((*readbuff)[1] == 0x00)
{ {
AfterSocksHandshake(conn, timer, host, port, addrtype); AfterSocksHandshake(conn, timer);
return; return;
} }
else if ((*readbuff)[1] == 0xff) else if ((*readbuff)[1] == 0xff)
@ -1506,13 +1532,14 @@ namespace transport
} }
case eHTTPProxy: case eHTTPProxy:
{ {
auto& ep = conn->GetRemoteEndpoint ();
i2p::http::HTTPReq req; i2p::http::HTTPReq req;
req.method = "CONNECT"; req.method = "CONNECT";
req.version ="HTTP/1.1"; req.version ="HTTP/1.1";
if(addrtype == eIP6Address) if(ep.address ().is_v6 ())
req.uri = "[" + host + "]:" + std::to_string(port); req.uri = "[" + ep.address ().to_string() + "]:" + std::to_string(ep.port ());
else else
req.uri = host + ":" + std::to_string(port); req.uri = ep.address ().to_string() + ":" + std::to_string(ep.port ());
boost::asio::streambuf writebuff; boost::asio::streambuf writebuff;
std::ostream out(&writebuff); std::ostream out(&writebuff);
@ -1566,7 +1593,7 @@ namespace transport
} }
} }
void NTCP2Server::AfterSocksHandshake(std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer, const std::string & host, uint16_t port, RemoteAddressType addrtype) void NTCP2Server::AfterSocksHandshake(std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer)
{ {
// build request // build request
size_t sz = 6; // header + port size_t sz = 6; // header + port
@ -1576,27 +1603,28 @@ namespace transport
(*buff)[1] = 0x01; (*buff)[1] = 0x01;
(*buff)[2] = 0x00; (*buff)[2] = 0x00;
if(addrtype == eIP4Address) auto& ep = conn->GetRemoteEndpoint ();
if(ep.address ().is_v4 ())
{ {
(*buff)[3] = 0x01; (*buff)[3] = 0x01;
auto addrbytes = boost::asio::ip::address::from_string(host).to_v4().to_bytes(); auto addrbytes = ep.address ().to_v4().to_bytes();
sz += 4; sz += 4;
memcpy(buff->data () + 4, addrbytes.data(), 4); memcpy(buff->data () + 4, addrbytes.data(), 4);
} }
else if (addrtype == eIP6Address) else if (ep.address ().is_v6 ())
{ {
(*buff)[3] = 0x04; (*buff)[3] = 0x04;
auto addrbytes = boost::asio::ip::address::from_string(host).to_v6().to_bytes(); auto addrbytes = ep.address ().to_v6().to_bytes();
sz += 16; sz += 16;
memcpy(buff->data () + 4, addrbytes.data(), 16); memcpy(buff->data () + 4, addrbytes.data(), 16);
} }
else if (addrtype == eHostname) else
{ {
// We mustn't really fall here because all connections are made to IP addresses // We mustn't really fall here because all connections are made to IP addresses
LogPrint(eLogError, "NTCP2: Tried to connect to domain name via socks proxy"); LogPrint(eLogError, "NTCP2: Tried to connect to unexpected address via proxy");
return; return;
} }
htobe16buf(buff->data () + sz - 2, port); htobe16buf(buff->data () + sz - 2, ep.port ());
boost::asio::async_write(conn->GetSocket(), boost::asio::buffer(buff->data (), sz), boost::asio::transfer_all(), boost::asio::async_write(conn->GetSocket(), boost::asio::buffer(buff->data (), sz), boost::asio::transfer_all(),
[buff](const boost::system::error_code & ec, std::size_t written) [buff](const boost::system::error_code & ec, std::size_t written)
{ {
@ -1623,11 +1651,23 @@ namespace transport
return; return;
} }
} }
if(!e)
i2p::data::netdb.SetUnreachable (conn->GetRemoteIdentity ()->GetIdentHash (), true);
timer->cancel(); timer->cancel();
conn->Terminate(); conn->Terminate();
}); });
} }
void NTCP2Server::SetLocalAddress (const boost::asio::ip::address& localAddress)
{
auto addr = std::make_shared<boost::asio::ip::tcp::endpoint>(boost::asio::ip::tcp::endpoint(localAddress, 0));
if (localAddress.is_v6 ())
{
if (i2p::util::net::IsYggdrasilAddress (localAddress))
m_YggdrasilAddress = addr;
else
m_Address6 = addr;
}
else
m_Address4 = addr;
}
} }
} }

23
libi2pd/NTCP2.h

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

2
libi2pd/NetDb.cpp

@ -1150,7 +1150,7 @@ namespace data
return GetRandomRouter ( return GetRandomRouter (
[v4only](std::shared_ptr<const RouterInfo> router)->bool [v4only](std::shared_ptr<const RouterInfo> router)->bool
{ {
return !router->IsHidden () && router->IsPeerTesting () && router->IsSSU (v4only); return !router->IsHidden () && router->IsPeerTesting (v4only);
}); });
} }

86
libi2pd/RouterContext.cpp

@ -41,6 +41,7 @@ namespace i2p
if (!Load ()) if (!Load ())
CreateNewRouter (); CreateNewRouter ();
m_Decryptor = m_Keys.CreateDecryptor (nullptr); m_Decryptor = m_Keys.CreateDecryptor (nullptr);
m_TunnelDecryptor = m_Keys.CreateDecryptor (nullptr);
UpdateRouterInfo (); UpdateRouterInfo ();
if (IsECIES ()) if (IsECIES ())
{ {
@ -77,6 +78,12 @@ namespace i2p
std::string ifname; i2p::config::GetOption("ifname", ifname); std::string ifname; i2p::config::GetOption("ifname", ifname);
std::string ifname4; i2p::config::GetOption("ifname4", ifname4); std::string ifname4; i2p::config::GetOption("ifname4", ifname4);
std::string ifname6; i2p::config::GetOption("ifname6", ifname6); std::string ifname6; i2p::config::GetOption("ifname6", ifname6);
if ((ntcp2 || ygg) && !m_NTCP2Keys)
NewNTCP2Keys ();
bool ntcp2Published = false;
if (ntcp2)
i2p::config::GetOption("ntcp2.published", ntcp2Published);
uint8_t caps = 0; uint8_t caps = 0;
if (ipv4) if (ipv4)
{ {
@ -86,14 +93,20 @@ namespace i2p
else if (!nat && !ifname.empty()) else if (!nat && !ifname.empty())
/* bind to interface, we have no NAT so set external address too */ /* bind to interface, we have no NAT so set external address too */
host = i2p::util::net::GetInterfaceAddress(ifname, false).to_string(); // v4 host = i2p::util::net::GetInterfaceAddress(ifname, false).to_string(); // v4
if(ifname4.size()) if(ifname4.size())
host = i2p::util::net::GetInterfaceAddress(ifname4, false).to_string(); host = i2p::util::net::GetInterfaceAddress(ifname4, false).to_string();
if (ntcp2)
{
if (ntcp2Published)
routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address_v4::from_string (host), port);
else // add non-published NTCP2 address
routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv);
}
if (ssu) if (ssu)
{ {
routerInfo.AddSSUAddress (host.c_str(), port, nullptr); routerInfo.AddSSUAddress (host.c_str(), port, nullptr);
caps |= i2p::data::RouterInfo::eReachable | i2p::data::RouterInfo::eSSUTesting | i2p::data::RouterInfo::eSSUIntroducer; // R, BC caps |= i2p::data::RouterInfo::eReachable; // R
} }
} }
if (ipv6) if (ipv6)
@ -103,16 +116,35 @@ namespace i2p
i2p::config::GetOption("host", host); i2p::config::GetOption("host", host);
else if (!ifname.empty()) else if (!ifname.empty())
host = i2p::util::net::GetInterfaceAddress(ifname, true).to_string(); // v6 host = i2p::util::net::GetInterfaceAddress(ifname, true).to_string(); // v6
if(ifname6.size()) if(ifname6.size())
host = i2p::util::net::GetInterfaceAddress(ifname6, true).to_string(); host = i2p::util::net::GetInterfaceAddress(ifname6, true).to_string();
if (ntcp2)
{
if (ntcp2Published)
{
std::string ntcp2Host;
if (!i2p::config::IsDefault ("ntcp2.addressv6"))
i2p::config::GetOption ("ntcp2.addressv6", ntcp2Host);
else
ntcp2Host = host;
routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address_v6::from_string (ntcp2Host), port);
}
else if (!ipv4) // no other ntcp2 addresses yet
routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv);
}
if (ssu) if (ssu)
{ {
routerInfo.AddSSUAddress (host.c_str(), port, nullptr); routerInfo.AddSSUAddress (host.c_str(), port, nullptr);
caps |= i2p::data::RouterInfo::eReachable; // R 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);
}
routerInfo.SetCaps (caps); // caps + L routerInfo.SetCaps (caps); // caps + L
routerInfo.SetProperty ("netId", std::to_string (m_NetID)); routerInfo.SetProperty ("netId", std::to_string (m_NetID));
@ -120,40 +152,6 @@ namespace i2p
routerInfo.CreateBuffer (m_Keys); routerInfo.CreateBuffer (m_Keys);
m_RouterInfo.SetRouterIdentity (GetIdentity ()); m_RouterInfo.SetRouterIdentity (GetIdentity ());
m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ()); m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ());
if (ntcp2) // we don't store iv in the address if non published so we must update it from keys
{
if (!m_NTCP2Keys) NewNTCP2Keys ();
bool published; i2p::config::GetOption("ntcp2.published", published);
if (ipv4 || !published) UpdateNTCP2Address (true); // create not published NTCP2 address
if (published)
{
if (ipv4)
PublishNTCP2Address (port, true);
if (ipv6)
{
// add NTCP2 ipv6 address
std::string host = "::1";
if (!i2p::config::IsDefault ("ntcp2.addressv6"))
i2p::config::GetOption ("ntcp2.addressv6", host);
m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address_v6::from_string (host), port);
}
}
// enable added NTCP2 addresses
if (ipv4) m_RouterInfo.EnableV4 ();
if (ipv6) m_RouterInfo.EnableV6 ();
}
if (ygg)
{
auto yggaddr = i2p::util::net::GetYggdrasilAddress ();
if (!yggaddr.is_unspecified ())
{
if (!m_NTCP2Keys) NewNTCP2Keys ();
m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, yggaddr, port);
m_RouterInfo.EnableMesh ();
UpdateRouterInfo ();
}
}
} }
void RouterContext::UpdateRouterInfo () void RouterContext::UpdateRouterInfo ()
@ -226,7 +224,7 @@ namespace i2p
if (port == 9150) port = 9151; // Tor browser if (port == 9150) port = 9151; // Tor browser
} }
if (port) address->port = port; if (port) address->port = port;
address->cost = publish ? 3 : 14; address->cost = publish ? i2p::data::COST_NTCP2_PUBLISHED : i2p::data::COST_NTCP2_NON_PUBLISHED;
address->ntcp2->isPublished = publish; address->ntcp2->isPublished = publish;
address->ntcp2->iv = m_NTCP2Keys->iv; address->ntcp2->iv = m_NTCP2Keys->iv;
updated = true; updated = true;
@ -427,7 +425,6 @@ namespace i2p
caps &= ~i2p::data::RouterInfo::eReachable; caps &= ~i2p::data::RouterInfo::eReachable;
caps |= i2p::data::RouterInfo::eUnreachable; caps |= i2p::data::RouterInfo::eUnreachable;
caps &= ~i2p::data::RouterInfo::eFloodfill; // can't be floodfill caps &= ~i2p::data::RouterInfo::eFloodfill; // can't be floodfill
caps &= ~i2p::data::RouterInfo::eSSUIntroducer; // can't be introducer
m_RouterInfo.SetCaps (caps); m_RouterInfo.SetCaps (caps);
uint16_t port = 0; uint16_t port = 0;
// delete previous introducers // delete previous introducers
@ -435,6 +432,8 @@ namespace i2p
for (auto& addr : addresses) for (auto& addr : addresses)
if (addr->ssu) if (addr->ssu)
{ {
addr->cost = i2p::data::COST_SSU_THROUGH_INTRODUCERS;
addr->caps &= ~i2p::data::RouterInfo::eSSUIntroducer; // can't be introducer
addr->ssu->introducers.clear (); addr->ssu->introducers.clear ();
port = addr->port; port = addr->port;
} }
@ -452,7 +451,6 @@ namespace i2p
uint8_t caps = m_RouterInfo.GetCaps (); uint8_t caps = m_RouterInfo.GetCaps ();
caps &= ~i2p::data::RouterInfo::eUnreachable; caps &= ~i2p::data::RouterInfo::eUnreachable;
caps |= i2p::data::RouterInfo::eReachable; caps |= i2p::data::RouterInfo::eReachable;
caps |= i2p::data::RouterInfo::eSSUIntroducer;
if (m_IsFloodfill) if (m_IsFloodfill)
caps |= i2p::data::RouterInfo::eFloodfill; caps |= i2p::data::RouterInfo::eFloodfill;
m_RouterInfo.SetCaps (caps); m_RouterInfo.SetCaps (caps);
@ -462,6 +460,8 @@ namespace i2p
for (auto& addr : addresses) for (auto& addr : addresses)
if (addr->ssu) if (addr->ssu)
{ {
addr->cost = i2p::data::COST_SSU_DIRECT;
addr->caps |= i2p::data::RouterInfo::eSSUIntroducer;
addr->ssu->introducers.clear (); addr->ssu->introducers.clear ();
port = addr->port; port = addr->port;
} }
@ -774,7 +774,7 @@ namespace i2p
bool RouterContext::DecryptTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx) bool RouterContext::DecryptTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx)
{ {
if (!m_Decryptor) return false; if (!m_TunnelDecryptor) return false;
if (IsECIES ()) if (IsECIES ())
{ {
if (!m_InitialNoiseState) return false; if (!m_InitialNoiseState) return false;
@ -782,7 +782,7 @@ namespace i2p
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) m_CurrentNoiseState->MixHash (encrypted, 32); // h = SHA256(h || sepk)
uint8_t sharedSecret[32]; uint8_t sharedSecret[32];
if (!m_Decryptor->Decrypt (encrypted, sharedSecret, ctx, false)) if (!m_TunnelDecryptor->Decrypt (encrypted, sharedSecret, ctx, false))
{ {
LogPrint (eLogWarning, "Router: Incorrect ephemeral public key"); LogPrint (eLogWarning, "Router: Incorrect ephemeral public key");
return false; return false;
@ -801,7 +801,7 @@ namespace i2p
return true; return true;
} }
else else
return m_Decryptor->Decrypt (encrypted, data, ctx, false); return m_TunnelDecryptor->Decrypt (encrypted, data, ctx, false);
} }
i2p::crypto::X25519Keys& RouterContext::GetStaticKeys () i2p::crypto::X25519Keys& RouterContext::GetStaticKeys ()

5
libi2pd/RouterContext.h

@ -39,7 +39,8 @@ namespace i2p
{ {
eRouterErrorNone = 0, eRouterErrorNone = 0,
eRouterErrorClockSkew = 1, eRouterErrorClockSkew = 1,
eRouterErrorOffline = 2 eRouterErrorOffline = 2,
eRouterErrorSymmetricNAT = 3
}; };
class RouterContext: public i2p::garlic::GarlicDestination class RouterContext: public i2p::garlic::GarlicDestination
@ -153,7 +154,7 @@ namespace i2p
i2p::data::RouterInfo m_RouterInfo; i2p::data::RouterInfo m_RouterInfo;
i2p::data::PrivateKeys m_Keys; i2p::data::PrivateKeys m_Keys;
std::shared_ptr<i2p::crypto::CryptoKeyDecryptor> m_Decryptor; std::shared_ptr<i2p::crypto::CryptoKeyDecryptor> m_Decryptor, m_TunnelDecryptor;
uint64_t m_LastUpdateTime; // in seconds uint64_t m_LastUpdateTime; // in seconds
bool m_AcceptsTunnels, m_IsFloodfill; bool m_AcceptsTunnels, m_IsFloodfill;
std::chrono::time_point<std::chrono::steady_clock> m_StartupTime; std::chrono::time_point<std::chrono::steady_clock> m_StartupTime;

103
libi2pd/RouterInfo.cpp

@ -186,6 +186,7 @@ namespace data
void RouterInfo::ReadFromStream (std::istream& s) void RouterInfo::ReadFromStream (std::istream& s)
{ {
m_Caps = 0;
s.read ((char *)&m_Timestamp, sizeof (m_Timestamp)); s.read ((char *)&m_Timestamp, sizeof (m_Timestamp));
m_Timestamp = be64toh (m_Timestamp); m_Timestamp = be64toh (m_Timestamp);
// read addresses // read addresses
@ -215,6 +216,7 @@ namespace data
} }
else else
address->transportStyle = eTransportUnknown; address->transportStyle = eTransportUnknown;
address->caps = 0;
address->port = 0; address->port = 0;
uint16_t size, r = 0; uint16_t size, r = 0;
s.read ((char *)&size, sizeof (size)); if (!s) return; s.read ((char *)&size, sizeof (size)); if (!s) return;
@ -250,7 +252,7 @@ namespace data
LogPrint (eLogWarning, "RouterInfo: Unexpected field 'key' for NTCP"); LogPrint (eLogWarning, "RouterInfo: Unexpected field 'key' for NTCP");
} }
else if (!strcmp (key, "caps")) else if (!strcmp (key, "caps"))
ExtractCaps (value); address->caps = ExtractAddressCaps (value);
else if (!strcmp (key, "s")) // ntcp2 static key else if (!strcmp (key, "s")) // ntcp2 static key
{ {
Base64ToByteStream (value, strlen (value), address->ntcp2->staticKey, 32); Base64ToByteStream (value, strlen (value), address->ntcp2->staticKey, 32);
@ -309,9 +311,17 @@ namespace data
supportedTransports |= eNTCP2V4; supportedTransports |= eNTCP2V4;
} }
else if (!address->ntcp2->isPublished) else if (!address->ntcp2->isPublished)
{
if (address->caps)
{
if (address->caps | AddressCaps::eV4) supportedTransports |= eNTCP2V4;
if (address->caps | AddressCaps::eV6) supportedTransports |= eNTCP2V6;
}
else
supportedTransports |= eNTCP2V4; // most likely, since we don't have host supportedTransports |= eNTCP2V4; // most likely, since we don't have host
} }
} }
}
else if (address->transportStyle == eTransportSSU) else if (address->transportStyle == eTransportSSU)
{ {
if (isIntroKey) if (isIntroKey)
@ -430,16 +440,37 @@ namespace data
case CAPS_FLAG_UNREACHABLE: case CAPS_FLAG_UNREACHABLE:
m_Caps |= Caps::eUnreachable; m_Caps |= Caps::eUnreachable;
break; break;
default: ;
}
cap++;
}
}
uint8_t RouterInfo::ExtractAddressCaps (const char * value) const
{
uint8_t caps = 0;
const char * cap = value;
while (*cap)
{
switch (*cap)
{
case CAPS_FLAG_V4:
caps |= AddressCaps::eV4;
break;
case CAPS_FLAG_V6:
caps |= AddressCaps::eV6;
break;
case CAPS_FLAG_SSU_TESTING: case CAPS_FLAG_SSU_TESTING:
m_Caps |= Caps::eSSUTesting; caps |= AddressCaps::eSSUTesting;
break; break;
case CAPS_FLAG_SSU_INTRODUCER: case CAPS_FLAG_SSU_INTRODUCER:
m_Caps |= Caps::eSSUIntroducer; caps |= AddressCaps::eSSUIntroducer;
break; break;
default: ; default: ;
} }
cap++; cap++;
} }
return caps;
} }
void RouterInfo::UpdateCapsProperty () void RouterInfo::UpdateCapsProperty ()
@ -482,10 +513,26 @@ namespace data
s.write ((const char *)&address.cost, sizeof (address.cost)); s.write ((const char *)&address.cost, sizeof (address.cost));
s.write ((const char *)&address.date, sizeof (address.date)); s.write ((const char *)&address.date, sizeof (address.date));
std::stringstream properties; std::stringstream properties;
bool isPublished = false;
if (address.transportStyle == eTransportNTCP) if (address.transportStyle == eTransportNTCP)
{ {
if (address.IsNTCP2 ()) if (address.IsNTCP2 ())
{
WriteString ("NTCP2", s); WriteString ("NTCP2", s);
if (address.IsPublishedNTCP2 ())
isPublished = true;
else
{
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 (caps.empty ()) caps += CAPS_FLAG_V4;
WriteString (caps, properties);
properties << ';';
}
}
else else
continue; // don't write NTCP address continue; // don't write NTCP address
} }
@ -496,15 +543,19 @@ namespace data
WriteString ("caps", properties); WriteString ("caps", properties);
properties << '='; properties << '=';
std::string caps; std::string caps;
if (IsPeerTesting ()) caps += CAPS_FLAG_SSU_TESTING; if (address.IsPeerTesting ()) caps += CAPS_FLAG_SSU_TESTING;
if (IsIntroducer ()) caps += CAPS_FLAG_SSU_INTRODUCER; if (address.IsIntroducer ()) caps += CAPS_FLAG_SSU_INTRODUCER;
if (IsReachable ())
isPublished = true;
else
caps += CAPS_FLAG_V4;
WriteString (caps, properties); WriteString (caps, properties);
properties << ';'; properties << ';';
} }
else else
WriteString ("", s); WriteString ("", s);
if (!address.IsNTCP2 () || address.IsPublishedNTCP2 ()) if (isPublished)
{ {
WriteString ("host", properties); WriteString ("host", properties);
properties << '='; properties << '=';
@ -514,7 +565,7 @@ namespace data
if (address.transportStyle == eTransportSSU) if (address.transportStyle == eTransportSSU)
{ {
// write introducers if any // write introducers if any
if (address.ssu->introducers.size () > 0) if (!address.ssu->introducers.empty())
{ {
int i = 0; int i = 0;
for (const auto& introducer: address.ssu->introducers) for (const auto& introducer: address.ssu->introducers)
@ -593,7 +644,7 @@ namespace data
WriteString (address.ntcp2->iv.ToBase64 (), properties); properties << ';'; WriteString (address.ntcp2->iv.ToBase64 (), properties); properties << ';';
} }
if (!address.IsNTCP2 () || address.IsPublishedNTCP2 ()) if (isPublished || address.ssu)
{ {
WriteString ("port", properties); WriteString ("port", properties);
properties << '='; properties << '=';
@ -721,7 +772,8 @@ namespace data
addr->host = boost::asio::ip::address::from_string (host); addr->host = boost::asio::ip::address::from_string (host);
addr->port = port; addr->port = port;
addr->transportStyle = eTransportSSU; addr->transportStyle = eTransportSSU;
addr->cost = 10; // NTCP should have priority over SSU addr->cost = COST_SSU_DIRECT; // NTCP2 should have priority over SSU
addr->caps = i2p::data::RouterInfo::eSSUTesting | i2p::data::RouterInfo::eSSUIntroducer; // BC;
addr->date = 0; addr->date = 0;
addr->ssu.reset (new SSUExt ()); addr->ssu.reset (new SSUExt ());
addr->ssu->mtu = mtu; addr->ssu->mtu = mtu;
@ -744,7 +796,8 @@ namespace data
addr->host = host; addr->host = host;
addr->port = port; addr->port = port;
addr->transportStyle = eTransportNTCP; addr->transportStyle = eTransportNTCP;
addr->cost = port ? 3 : 14; // override from RouterContext::PublishNTCP2Address addr->cost = port ? COST_NTCP2_PUBLISHED : COST_NTCP2_NON_PUBLISHED; // override from RouterContext::PublishNTCP2Address
addr->caps = 0;
addr->date = 0; addr->date = 0;
addr->ntcp2.reset (new NTCP2Ext ()); addr->ntcp2.reset (new NTCP2Ext ());
if (port) addr->ntcp2->isPublished = true; if (port) addr->ntcp2->isPublished = true;
@ -962,12 +1015,13 @@ namespace data
return nullptr; return nullptr;
} }
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetNTCP2Address (bool publishedOnly) const std::shared_ptr<const RouterInfo::Address> RouterInfo::GetNTCP2AddressWithStaticKey (const uint8_t * key) const
{ {
if (!key) return nullptr;
return GetAddress ( return GetAddress (
[publishedOnly](std::shared_ptr<const RouterInfo::Address> address)->bool [key](std::shared_ptr<const RouterInfo::Address> address)->bool
{ {
return address->IsNTCP2 () && (!publishedOnly || address->IsPublishedNTCP2 ()); return address->IsNTCP2 () && !memcmp (address->ntcp2->staticKey, key, 32);
}); });
} }
@ -1019,5 +1073,28 @@ namespace data
return IsReachable () && m_Version >= NETDB_MIN_FLOODFILL_VERSION && return IsReachable () && m_Version >= NETDB_MIN_FLOODFILL_VERSION &&
GetIdentity ()->GetSigningKeyType () != SIGNING_KEY_TYPE_DSA_SHA1; GetIdentity ()->GetSigningKeyType () != SIGNING_KEY_TYPE_DSA_SHA1;
} }
bool RouterInfo::IsPeerTesting (bool v4only) const
{
auto supportedTransports = m_SupportedTransports & (eSSUV4 | eSSUV6);
if (!supportedTransports) return false; // no SSU
if (v4only && !(supportedTransports & eSSUV4)) return false; // no SSU v4
return GetAddress (
[](std::shared_ptr<const RouterInfo::Address> address)->bool
{
return (address->transportStyle == eTransportSSU) && address->IsPeerTesting ();
}) != nullptr;
}
bool RouterInfo::IsIntroducer () const
{
// TODO: support ipv6
if (!(m_SupportedTransports & eSSUV4)) return false;
return GetAddress (
[](std::shared_ptr<const RouterInfo::Address> address)->bool
{
return (address->transportStyle == eTransportSSU) && address->IsIntroducer ();
}) != nullptr;
}
} }
} }

33
libi2pd/RouterInfo.h

@ -44,9 +44,16 @@ namespace data
const char CAPS_FLAG_EXTRA_BANDWIDTH1 = 'P'; /* 256-2000 KBps */ const char CAPS_FLAG_EXTRA_BANDWIDTH1 = 'P'; /* 256-2000 KBps */
const char CAPS_FLAG_EXTRA_BANDWIDTH2 = 'X'; /* > 2000 KBps */ const char CAPS_FLAG_EXTRA_BANDWIDTH2 = 'X'; /* > 2000 KBps */
const char CAPS_FLAG_V4 = '4';
const char CAPS_FLAG_V6 = '6';
const char CAPS_FLAG_SSU_TESTING = 'B'; const char CAPS_FLAG_SSU_TESTING = 'B';
const char CAPS_FLAG_SSU_INTRODUCER = 'C'; const char CAPS_FLAG_SSU_INTRODUCER = 'C';
const uint8_t COST_NTCP2_PUBLISHED = 3;
const uint8_t COST_NTCP2_NON_PUBLISHED = 14;
const uint8_t COST_SSU_DIRECT = 9;
const uint8_t COST_SSU_THROUGH_INTRODUCERS = 11;
const int MAX_RI_BUFFER_SIZE = 2048; // if RouterInfo exceeds 2048 we consider it as malformed, might be changed later const int MAX_RI_BUFFER_SIZE = 2048; // if RouterInfo exceeds 2048 we consider it as malformed, might be changed later
class RouterInfo: public RoutingDestination class RouterInfo: public RoutingDestination
{ {
@ -67,10 +74,16 @@ namespace data
eHighBandwidth = 0x02, eHighBandwidth = 0x02,
eExtraBandwidth = 0x04, eExtraBandwidth = 0x04,
eReachable = 0x08, eReachable = 0x08,
eSSUTesting = 0x10, eHidden = 0x10,
eSSUIntroducer = 0x20, eUnreachable = 0x20
eHidden = 0x40, };
eUnreachable = 0x80
enum AddressCaps
{
eV4 = 0x01,
eV6 = 0x02,
eSSUTesting = 0x04,
eSSUIntroducer = 0x08
}; };
enum TransportStyle enum TransportStyle
@ -111,7 +124,7 @@ namespace data
boost::asio::ip::address host; boost::asio::ip::address host;
int port; int port;
uint64_t date; uint64_t date;
uint8_t cost; uint8_t cost, caps;
std::unique_ptr<SSUExt> ssu; // not null for SSU std::unique_ptr<SSUExt> ssu; // not null for SSU
std::unique_ptr<NTCP2Ext> ntcp2; // not null for NTCP2 std::unique_ptr<NTCP2Ext> ntcp2; // not null for NTCP2
@ -134,6 +147,9 @@ namespace data
bool IsNTCP2 () const { return (bool)ntcp2; }; bool IsNTCP2 () const { return (bool)ntcp2; };
bool IsPublishedNTCP2 () const { return IsNTCP2 () && ntcp2->isPublished; }; bool IsPublishedNTCP2 () const { return IsNTCP2 () && ntcp2->isPublished; };
bool IsIntroducer () const { return caps & eSSUIntroducer; };
bool IsPeerTesting () const { return caps & eSSUTesting; };
}; };
typedef std::list<std::shared_ptr<Address> > Addresses; typedef std::list<std::shared_ptr<Address> > Addresses;
@ -150,7 +166,7 @@ namespace data
uint64_t GetTimestamp () const { return m_Timestamp; }; uint64_t GetTimestamp () const { return m_Timestamp; };
int GetVersion () const { return m_Version; }; int GetVersion () const { return m_Version; };
Addresses& GetAddresses () { return *m_Addresses; }; // should be called for local RI only, otherwise must return shared_ptr Addresses& GetAddresses () { return *m_Addresses; }; // should be called for local RI only, otherwise must return shared_ptr
std::shared_ptr<const Address> GetNTCP2Address (bool publishedOnly) const; std::shared_ptr<const Address> GetNTCP2AddressWithStaticKey (const uint8_t * key) const;
std::shared_ptr<const Address> GetPublishedNTCP2V4Address () const; std::shared_ptr<const Address> GetPublishedNTCP2V4Address () const;
std::shared_ptr<const Address> GetPublishedNTCP2V6Address () const; std::shared_ptr<const Address> GetPublishedNTCP2V6Address () const;
std::shared_ptr<const Address> GetSSUAddress (bool v4only = true) const; std::shared_ptr<const Address> GetSSUAddress (bool v4only = true) const;
@ -183,12 +199,12 @@ namespace data
bool IsCompatible (const RouterInfo& other) const { return m_SupportedTransports & other.m_SupportedTransports; }; bool IsCompatible (const RouterInfo& other) const { return m_SupportedTransports & other.m_SupportedTransports; };
bool HasValidAddresses () const { return m_SupportedTransports; }; bool HasValidAddresses () const { return m_SupportedTransports; };
bool UsesIntroducer () const; bool UsesIntroducer () const;
bool IsIntroducer () const { return m_Caps & eSSUIntroducer; };
bool IsPeerTesting () const { return m_Caps & eSSUTesting; };
bool IsHidden () const { return m_Caps & eHidden; }; bool IsHidden () const { return m_Caps & eHidden; };
bool IsHighBandwidth () const { return m_Caps & RouterInfo::eHighBandwidth; }; bool IsHighBandwidth () const { return m_Caps & RouterInfo::eHighBandwidth; };
bool IsExtraBandwidth () const { return m_Caps & RouterInfo::eExtraBandwidth; }; bool IsExtraBandwidth () const { return m_Caps & RouterInfo::eExtraBandwidth; };
bool IsEligibleFloodfill () const; bool IsEligibleFloodfill () const;
bool IsPeerTesting (bool v4only) const;
bool IsIntroducer () const;
uint8_t GetCaps () const { return m_Caps; }; uint8_t GetCaps () const { return m_Caps; };
void SetCaps (uint8_t caps); void SetCaps (uint8_t caps);
@ -232,6 +248,7 @@ namespace data
size_t ReadString (char* str, size_t len, std::istream& s) const; size_t ReadString (char* str, size_t len, std::istream& s) const;
void WriteString (const std::string& str, std::ostream& s) const; void WriteString (const std::string& str, std::ostream& s) const;
void ExtractCaps (const char * value); void ExtractCaps (const char * value);
uint8_t ExtractAddressCaps (const char * value) const;
template<typename Filter> template<typename Filter>
std::shared_ptr<const Address> GetAddress (Filter filter) const; std::shared_ptr<const Address> GetAddress (Filter filter) const;
void UpdateCapsProperty (); void UpdateCapsProperty ();

61
libi2pd/SSU.cpp

@ -22,21 +22,8 @@ namespace i2p
{ {
namespace transport namespace transport
{ {
SSUServer::SSUServer (const boost::asio::ip::address & addr, int port):
m_OnlyV6(true), m_IsRunning(false), m_Thread (nullptr),
m_ReceiversThread (nullptr), m_ReceiversThreadV6 (nullptr), m_Work (m_Service),
m_ReceiversWork (m_ReceiversService), m_ReceiversWorkV6 (m_ReceiversServiceV6),
m_EndpointV6 (addr, port), m_Socket (m_ReceiversService, m_Endpoint),
m_SocketV6 (m_ReceiversServiceV6), m_IntroducersUpdateTimer (m_Service),
m_PeerTestsCleanupTimer (m_Service), m_TerminationTimer (m_Service),
m_TerminationTimerV6 (m_Service)
{
OpenSocketV6 ();
}
SSUServer::SSUServer (int port): SSUServer::SSUServer (int port):
m_OnlyV6(false), m_IsRunning(false), m_Thread (nullptr), m_IsRunning(false), m_Thread (nullptr),
m_ReceiversThread (nullptr), m_ReceiversThreadV6 (nullptr), m_Work (m_Service), m_ReceiversThread (nullptr), m_ReceiversThreadV6 (nullptr), m_Work (m_Service),
m_ReceiversWork (m_ReceiversService), m_ReceiversWorkV6 (m_ReceiversServiceV6), 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_Endpoint (boost::asio::ip::udp::v4 (), port), m_EndpointV6 (boost::asio::ip::udp::v6 (), port),
@ -44,9 +31,6 @@ namespace transport
m_IntroducersUpdateTimer (m_Service), m_PeerTestsCleanupTimer (m_Service), m_IntroducersUpdateTimer (m_Service), m_PeerTestsCleanupTimer (m_Service),
m_TerminationTimer (m_Service), m_TerminationTimerV6 (m_Service) m_TerminationTimer (m_Service), m_TerminationTimerV6 (m_Service)
{ {
OpenSocket ();
if (context.SupportsV6 ())
OpenSocketV6 ();
} }
SSUServer::~SSUServer () SSUServer::~SSUServer ()
@ -91,18 +75,18 @@ namespace transport
void SSUServer::Start () void SSUServer::Start ()
{ {
m_IsRunning = true; m_IsRunning = true;
if (!m_OnlyV6) m_Thread = new std::thread (std::bind (&SSUServer::Run, this));
if (context.SupportsV4 ())
{ {
OpenSocket ();
m_ReceiversThread = new std::thread (std::bind (&SSUServer::RunReceivers, this)); m_ReceiversThread = new std::thread (std::bind (&SSUServer::RunReceivers, this));
m_Thread = new std::thread (std::bind (&SSUServer::Run, this));
m_ReceiversService.post (std::bind (&SSUServer::Receive, this)); m_ReceiversService.post (std::bind (&SSUServer::Receive, this));
ScheduleTermination (); ScheduleTermination ();
} }
if (context.SupportsV6 ()) if (context.SupportsV6 ())
{ {
OpenSocketV6 ();
m_ReceiversThreadV6 = new std::thread (std::bind (&SSUServer::RunReceiversV6, this)); m_ReceiversThreadV6 = new std::thread (std::bind (&SSUServer::RunReceiversV6, this));
if (!m_Thread)
m_Thread = new std::thread (std::bind (&SSUServer::Run, this));
m_ReceiversServiceV6.post (std::bind (&SSUServer::ReceiveV6, this)); m_ReceiversServiceV6.post (std::bind (&SSUServer::ReceiveV6, this));
ScheduleTerminationV6 (); ScheduleTerminationV6 ();
} }
@ -205,6 +189,14 @@ namespace transport
} }
} }
void SSUServer::SetLocalAddress (const boost::asio::ip::address& localAddress)
{
if (localAddress.is_v6 ())
m_EndpointV6.address (localAddress);
else if (localAddress.is_v4 ())
m_Endpoint.address (localAddress);
}
void SSUServer::AddRelay (uint32_t tag, std::shared_ptr<SSUSession> relay) void SSUServer::AddRelay (uint32_t tag, std::shared_ptr<SSUSession> relay)
{ {
m_Relays[tag] = relay; m_Relays[tag] = relay;
@ -442,21 +434,21 @@ namespace transport
{ {
auto address = router->GetSSUAddress (v4only || !context.SupportsV6 ()); auto address = router->GetSSUAddress (v4only || !context.SupportsV6 ());
if (address) if (address)
CreateSession (router, address->host, address->port, peerTest); CreateSession (router, address, peerTest);
else else
LogPrint (eLogWarning, "SSU: Router ", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()), " doesn't have SSU address"); LogPrint (eLogWarning, "SSU: Router ", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()), " doesn't have SSU address");
} }
void SSUServer::CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router, void SSUServer::CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router,
const boost::asio::ip::address& addr, int port, bool peerTest) std::shared_ptr<const i2p::data::RouterInfo::Address> address, bool peerTest)
{ {
if (router) if (router && address)
{ {
if (router->UsesIntroducer ()) if (router->UsesIntroducer ())
m_Service.post (std::bind (&SSUServer::CreateSessionThroughIntroducer, this, router, peerTest)); // always V4 thread m_Service.post (std::bind (&SSUServer::CreateSessionThroughIntroducer, this, router, address, peerTest)); // always V4 thread
else else
{ {
boost::asio::ip::udp::endpoint remoteEndpoint (addr, port); boost::asio::ip::udp::endpoint remoteEndpoint (address->host, address->port);
m_Service.post (std::bind (&SSUServer::CreateDirectSession, this, router, remoteEndpoint, peerTest)); m_Service.post (std::bind (&SSUServer::CreateDirectSession, this, router, remoteEndpoint, peerTest));
} }
} }
@ -485,12 +477,12 @@ namespace transport
} }
} }
void SSUServer::CreateSessionThroughIntroducer (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest) void SSUServer::CreateSessionThroughIntroducer (std::shared_ptr<const i2p::data::RouterInfo> router,
std::shared_ptr<const i2p::data::RouterInfo::Address> address, bool peerTest)
{ {
if (router && router->UsesIntroducer ()) if (router && router->UsesIntroducer () && address)
{ {
auto address = router->GetSSUAddress (true); // v4 only for now if (!address->host.is_unspecified () && address->port)
if (address)
{ {
boost::asio::ip::udp::endpoint remoteEndpoint (address->host, address->port); boost::asio::ip::udp::endpoint remoteEndpoint (address->host, address->port);
auto it = m_Sessions.find (remoteEndpoint); auto it = m_Sessions.find (remoteEndpoint);
@ -502,6 +494,7 @@ namespace transport
session->SendPeerTest (); session->SendPeerTest ();
return; return;
} }
}
// create new session // create new session
int numIntroducers = address->ssu->introducers.size (); int numIntroducers = address->ssu->introducers.size ();
if (numIntroducers > 0) if (numIntroducers > 0)
@ -518,7 +511,7 @@ namespace transport
if (ep.address ().is_v4 ()) // ipv4 only if (ep.address ().is_v4 ()) // ipv4 only
{ {
if (!introducer) introducer = intr; // we pick first one for now if (!introducer) introducer = intr; // we pick first one for now
it = m_Sessions.find (ep); auto it = m_Sessions.find (ep);
if (it != m_Sessions.end ()) if (it != m_Sessions.end ())
{ {
introducerSession = it->second; introducerSession = it->second;
@ -541,11 +534,10 @@ namespace transport
introducerSession = std::make_shared<SSUSession> (*this, introducerEndpoint, router); introducerSession = std::make_shared<SSUSession> (*this, introducerEndpoint, router);
m_Sessions[introducerEndpoint] = introducerSession; m_Sessions[introducerEndpoint] = introducerSession;
} }
#if BOOST_VERSION >= 104900
if (!address->host.is_unspecified () && address->port) if (!address->host.is_unspecified () && address->port)
#endif
{ {
// create session // create session
boost::asio::ip::udp::endpoint remoteEndpoint (address->host, address->port);
auto session = std::make_shared<SSUSession> (*this, remoteEndpoint, router, peerTest); auto session = std::make_shared<SSUSession> (*this, remoteEndpoint, router, peerTest);
m_Sessions[remoteEndpoint] = session; m_Sessions[remoteEndpoint] = session;
@ -564,9 +556,6 @@ namespace transport
else else
LogPrint (eLogWarning, "SSU: Can't connect to unreachable router and no introducers present"); LogPrint (eLogWarning, "SSU: Can't connect to unreachable router and no introducers present");
} }
else
LogPrint (eLogWarning, "SSU: Router ", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()), " doesn't have SSU address");
}
} }
void SSUServer::DeleteSession (std::shared_ptr<SSUSession> session) void SSUServer::DeleteSession (std::shared_ptr<SSUSession> session)

11
libi2pd/SSU.h

@ -48,13 +48,12 @@ namespace transport
public: public:
SSUServer (int port); SSUServer (int port);
SSUServer (const boost::asio::ip::address & addr, int port); // ipv6 only constructor
~SSUServer (); ~SSUServer ();
void Start (); void Start ();
void Stop (); 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 peerTest = false, bool v4only = false);
void CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router, void CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router,
const boost::asio::ip::address& addr, int port, bool peerTest = false); 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); 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 (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> FindSession (const boost::asio::ip::udp::endpoint& e) const;
@ -64,7 +63,9 @@ namespace transport
void DeleteAllSessions (); void DeleteAllSessions ();
boost::asio::io_service& GetService () { return m_Service; }; boost::asio::io_service& GetService () { return m_Service; };
const boost::asio::ip::udp::endpoint& GetEndpoint () const { return m_Endpoint; }; uint16_t GetPort () const { return m_Endpoint.port (); };
void SetLocalAddress (const boost::asio::ip::address& localAddress);
void Send (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& to); void Send (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& to);
void AddRelay (uint32_t tag, std::shared_ptr<SSUSession> relay); void AddRelay (uint32_t tag, std::shared_ptr<SSUSession> relay);
void RemoveRelay (uint32_t tag); void RemoveRelay (uint32_t tag);
@ -90,7 +91,8 @@ namespace transport
void HandleReceivedPackets (std::vector<SSUPacket *> packets, void HandleReceivedPackets (std::vector<SSUPacket *> packets,
std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<SSUSession> >* sessions); std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<SSUSession> >* sessions);
void CreateSessionThroughIntroducer (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest = false); void CreateSessionThroughIntroducer (std::shared_ptr<const i2p::data::RouterInfo> router,
std::shared_ptr<const i2p::data::RouterInfo::Address> address, bool peerTest = false);
template<typename Filter> template<typename Filter>
std::shared_ptr<SSUSession> GetRandomV4Session (Filter filter); std::shared_ptr<SSUSession> GetRandomV4Session (Filter filter);
template<typename Filter> template<typename Filter>
@ -118,7 +120,6 @@ namespace transport
std::shared_ptr<SSUSession> session; // for Bob to Alice std::shared_ptr<SSUSession> session; // for Bob to Alice
}; };
bool m_OnlyV6;
volatile bool m_IsRunning; volatile bool m_IsRunning;
std::thread * m_Thread, * m_ReceiversThread, * m_ReceiversThreadV6; std::thread * m_Thread, * m_ReceiversThread, * m_ReceiversThreadV6;
boost::asio::io_service m_Service, m_ReceiversService, m_ReceiversServiceV6; boost::asio::io_service m_Service, m_ReceiversService, m_ReceiversServiceV6;

3
libi2pd/SSUSession.cpp

@ -321,6 +321,7 @@ namespace transport
void SSUSession::ProcessSessionConfirmed (const uint8_t * buf, size_t len) void SSUSession::ProcessSessionConfirmed (const uint8_t * buf, size_t len)
{ {
LogPrint (eLogDebug, "SSU: Session confirmed received"); LogPrint (eLogDebug, "SSU: Session confirmed received");
m_ConnectTimer.cancel ();
auto headerSize = GetSSUHeaderSize (buf); auto headerSize = GetSSUHeaderSize (buf);
if (headerSize >= len) if (headerSize >= len)
{ {
@ -683,6 +684,8 @@ namespace transport
buf += 2; // our port buf += 2; // our port
LogPrint (eLogInfo, "SSU: Our external address is ", ourIP.to_string (), ":", ourPort); LogPrint (eLogInfo, "SSU: Our external address is ", ourIP.to_string (), ":", ourPort);
i2p::context.UpdateAddress (ourIP); i2p::context.UpdateAddress (ourIP);
if (ourPort != m_Server.GetPort ())
i2p::context.SetError (eRouterErrorSymmetricNAT);
uint32_t nonce = bufbe32toh (buf); uint32_t nonce = bufbe32toh (buf);
buf += 4; // nonce buf += 4; // nonce
auto it = m_RelayRequests.find (nonce); auto it = m_RelayRequests.find (nonce);

109
libi2pd/Transports.cpp

@ -189,7 +189,6 @@ namespace transport
proxytype = NTCP2Server::eHTTPProxy; proxytype = NTCP2Server::eHTTPProxy;
m_NTCP2Server->UseProxy(proxytype, proxyurl.host, proxyurl.port); m_NTCP2Server->UseProxy(proxytype, proxyurl.host, proxyurl.port);
m_NTCP2Server->Start();
} }
else else
LogPrint(eLogError, "Transports: unsupported NTCP2 proxy URL ", ntcp2proxy); LogPrint(eLogError, "Transports: unsupported NTCP2 proxy URL ", ntcp2proxy);
@ -199,40 +198,91 @@ namespace transport
return; return;
} }
else else
{
m_NTCP2Server = new NTCP2Server (); m_NTCP2Server = new NTCP2Server ();
m_NTCP2Server->Start ();
}
} }
// create acceptors // create SSU server
int ssuPort = 0;
if (enableSSU)
{
auto& addresses = context.GetRouterInfo ().GetAddresses (); auto& addresses = context.GetRouterInfo ().GetAddresses ();
for (const auto& address: addresses) for (const auto& address: addresses)
{ {
if (!address) continue; if (!address) continue;
if (address->transportStyle == RouterInfo::eTransportSSU) if (address->transportStyle == RouterInfo::eTransportSSU)
{ {
if (m_SSUServer == nullptr && enableSSU) ssuPort = address->port;
{
if (address->host.is_v4())
m_SSUServer = new SSUServer (address->port); m_SSUServer = new SSUServer (address->port);
else break;
m_SSUServer = new SSUServer (address->host, address->port); }
LogPrint (eLogInfo, "Transports: Start listening UDP port ", address->port); }
try { }
// bind to interfaces
bool ipv4; i2p::config::GetOption("ipv4", ipv4);
if (ipv4)
{
std::string address; i2p::config::GetOption("address4", address);
if (!address.empty ())
{
boost::system::error_code ec;
auto addr = boost::asio::ip::address::from_string (address, ec);
if (!ec)
{
if (m_NTCP2Server) m_NTCP2Server->SetLocalAddress (addr);
if (m_SSUServer) m_SSUServer->SetLocalAddress (addr);
}
}
}
bool ipv6; i2p::config::GetOption("ipv6", ipv6);
if (ipv6)
{
std::string address; i2p::config::GetOption("address6", address);
if (!address.empty ())
{
boost::system::error_code ec;
auto addr = boost::asio::ip::address::from_string (address, ec);
if (!ec)
{
if (m_NTCP2Server) m_NTCP2Server->SetLocalAddress (addr);
if (m_SSUServer) m_SSUServer->SetLocalAddress (addr);
}
}
}
bool ygg; i2p::config::GetOption("meshnets.yggdrasil", ygg);
if (ygg)
{
std::string address; i2p::config::GetOption("meshnets.yggaddress", address);
if (!address.empty ())
{
boost::system::error_code ec;
auto addr = boost::asio::ip::address::from_string (address, ec);
if (!ec && m_NTCP2Server && i2p::util::net::IsYggdrasilAddress (addr))
m_NTCP2Server->SetLocalAddress (addr);
}
}
// start servers
if (m_NTCP2Server) m_NTCP2Server->Start ();
if (m_SSUServer)
{
LogPrint (eLogInfo, "Transports: Start listening UDP port ", ssuPort);
try
{
m_SSUServer->Start (); m_SSUServer->Start ();
} catch ( std::exception & ex ) { }
LogPrint(eLogError, "Transports: Failed to bind to UDP port", address->port); catch (std::exception& ex )
{
LogPrint(eLogError, "Transports: Failed to bind to UDP port", ssuPort);
m_SSUServer->Stop ();
delete m_SSUServer; delete m_SSUServer;
m_SSUServer = nullptr; m_SSUServer = nullptr;
continue;
}
DetectExternalIP ();
}
else
LogPrint (eLogError, "Transports: SSU server already exists");
} }
if (m_SSUServer) DetectExternalIP ();
} }
m_PeerCleanupTimer->expires_from_now (boost::posix_time::seconds(5*SESSION_CREATION_TIMEOUT)); m_PeerCleanupTimer->expires_from_now (boost::posix_time::seconds(5*SESSION_CREATION_TIMEOUT));
m_PeerCleanupTimer->async_wait (std::bind (&Transports::HandlePeerCleanupTimer, this, std::placeholders::_1)); m_PeerCleanupTimer->async_wait (std::bind (&Transports::HandlePeerCleanupTimer, this, std::placeholders::_1));
@ -351,7 +401,7 @@ namespace transport
try try
{ {
auto r = netdb.FindRouter (ident); auto r = netdb.FindRouter (ident);
if (!r || !r->IsCompatible (i2p::context.GetRouterInfo ())) return; if (!r || r->IsUnreachable () || !r->IsCompatible (i2p::context.GetRouterInfo ())) return;
{ {
std::unique_lock<std::mutex> l(m_PeersMutex); std::unique_lock<std::mutex> l(m_PeersMutex);
it = m_Peers.insert (std::pair<i2p::data::IdentHash, Peer>(ident, { 0, r, {}, it = m_Peers.insert (std::pair<i2p::data::IdentHash, Peer>(ident, { 0, r, {},
@ -418,19 +468,10 @@ namespace transport
if (address) if (address)
{ {
auto s = std::make_shared<NTCP2Session> (*m_NTCP2Server, peer.router, address); auto s = std::make_shared<NTCP2Session> (*m_NTCP2Server, peer.router, address);
if( m_NTCP2Server->UsingProxy()) if( m_NTCP2Server->UsingProxy())
{ m_NTCP2Server->ConnectWithProxy(s);
NTCP2Server::RemoteAddressType remote = NTCP2Server::eIP4Address;
std::string addr = address->host.to_string();
if(address->host.is_v6())
remote = NTCP2Server::eIP6Address;
m_NTCP2Server->ConnectWithProxy(addr, address->port, remote, s);
}
else else
m_NTCP2Server->Connect (address->host, address->port, s); m_NTCP2Server->Connect (s);
return true; return true;
} }
} }
@ -464,7 +505,7 @@ namespace transport
} }
if (address) if (address)
{ {
m_SSUServer->CreateSession (peer.router, address->host, address->port); m_SSUServer->CreateSession (peer.router, address);
return true; return true;
} }
} }
@ -480,7 +521,7 @@ namespace transport
if (address) if (address)
{ {
auto s = std::make_shared<NTCP2Session> (*m_NTCP2Server, peer.router, address); auto s = std::make_shared<NTCP2Session> (*m_NTCP2Server, peer.router, address);
m_NTCP2Server->Connect (address->host, address->port, s); m_NTCP2Server->Connect (s);
return true; return true;
} }
} }

17
libi2pd_client/BOB.cpp

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

2
libi2pd_client/BOB.h

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

6
libi2pd_client/ClientContext.cpp

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

29
libi2pd_client/I2PTunnel.cpp

@ -107,6 +107,22 @@ namespace client
} }
} }
void I2PTunnelConnection::Connect (const boost::asio::ip::address& localAddress)
{
if (m_Socket)
{
if (m_RemoteEndpoint.address().is_v6 ())
m_Socket->open (boost::asio::ip::tcp::v6 ());
else
m_Socket->open (boost::asio::ip::tcp::v4 ());
boost::system::error_code ec;
m_Socket->bind (boost::asio::ip::tcp::endpoint (localAddress, 0), ec);
if (ec)
LogPrint (eLogError, "I2PTunnel: can't bind to ", localAddress.to_string (), ": ", ec.message ());
}
Connect (false);
}
void I2PTunnelConnection::Terminate () void I2PTunnelConnection::Terminate ()
{ {
if (Kill()) return; if (Kill()) return;
@ -600,6 +616,16 @@ namespace client
m_IsAccessList = true; m_IsAccessList = true;
} }
void I2PServerTunnel::SetLocalAddress (const std::string& localAddress)
{
boost::system::error_code ec;
auto addr = boost::asio::ip::address::from_string(localAddress, ec);
if (!ec)
m_LocalAddress.reset (new boost::asio::ip::address (addr));
else
LogPrint (eLogError, "I2PTunnel: can't set local address ", localAddress);
}
void I2PServerTunnel::Accept () void I2PServerTunnel::Accept ()
{ {
if (m_PortDestination) if (m_PortDestination)
@ -631,6 +657,9 @@ namespace client
// new connection // new connection
auto conn = CreateI2PConnection (stream); auto conn = CreateI2PConnection (stream);
AddHandler (conn); AddHandler (conn);
if (m_LocalAddress)
conn->Connect (*m_LocalAddress);
else
conn->Connect (m_IsUniqueLocal); conn->Connect (m_IsUniqueLocal);
} }
} }

4
libi2pd_client/I2PTunnel.h

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

Loading…
Cancel
Save