diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml
index c979e43f..83b68d71 100644
--- a/.github/workflows/build-windows.yml
+++ b/.github/workflows/build-windows.yml
@@ -29,3 +29,7 @@ jobs:
run: |
mkdir -p obj/Win32 obj/libi2pd obj/libi2pd_client obj/daemon
make USE_UPNP=yes DEBUG=no -j3
+ - name: Upload artifacts
+ uses: actions/upload-artifact@v2
+ with:
+ path: i2pd.exe
diff --git a/Makefile b/Makefile
index 3e0b23cf..7357f220 100644
--- a/Makefile
+++ b/Makefile
@@ -66,6 +66,7 @@ mk_obj_dir:
@mkdir -p obj/$(DAEMON_SRC_DIR)
api: mk_obj_dir $(SHLIB) $(ARLIB)
+client: mk_obj_dir $(SHLIB_CLIENT) $(ARLIB_CLIENT)
api_client: mk_obj_dir $(SHLIB) $(ARLIB) $(SHLIB_CLIENT) $(ARLIB_CLIENT)
## NOTE: The NEEDED_CXXFLAGS are here so that CXXFLAGS can be specified at build time
@@ -128,6 +129,7 @@ doxygen:
.PHONY: last-dist
.PHONY: api
.PHONY: api_client
+.PHONY: client
.PHONY: mk_obj_dir
.PHONY: install
.PHONY: strip
diff --git a/contrib/i2pd.service b/contrib/i2pd.service
index 8ce851b0..45fe4cc6 100644
--- a/contrib/i2pd.service
+++ b/contrib/i2pd.service
@@ -17,7 +17,12 @@ PIDFile=/run/i2pd/i2pd.pid
### Uncomment, if auto restart needed
#Restart=on-failure
-KillSignal=SIGQUIT
+# Use SIGTERM to stop i2pd immediately.
+# Some cleanup processes can delay stopping, so we set 30 seconds timeout and then SIGKILL i2pd.
+KillSignal=SIGTERM
+TimeoutStopSec=30s
+SendSIGKILL=yes
+
# If you have the patience waiting 10 min on restarting/stopping it, uncomment this.
# i2pd stops accepting new tunnels and waits ~10 min while old ones do not die.
#KillSignal=SIGINT
diff --git a/daemon/HTTPServer.cpp b/daemon/HTTPServer.cpp
index aba30fd7..89960e0c 100644
--- a/daemon/HTTPServer.cpp
+++ b/daemon/HTTPServer.cpp
@@ -263,6 +263,9 @@ namespace http {
case eRouterErrorOffline:
s << " - Offline";
break;
+ case eRouterErrorSymmetricNAT:
+ s << " - Symmetric NAT";
+ break;
default: ;
}
break;
@@ -706,23 +709,23 @@ namespace http {
std::stringstream tmp_s, tmp_s6; uint16_t cnt = 0, cnt6 = 0;
for (const auto& it: sessions )
{
- if (it.second && it.second->IsEstablished () && !it.second->GetSocket ().remote_endpoint ().address ().is_v6 ())
+ if (it.second && it.second->IsEstablished () && !it.second->GetRemoteEndpoint ().address ().is_v6 ())
{
tmp_s << "
\r\n";
if (it.second->IsOutgoing ()) tmp_s << " ⇒ ";
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 << " ⇒ ";
tmp_s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
tmp_s << "
\r\n" << std::endl;
cnt++;
}
- if (it.second && it.second->IsEstablished () && it.second->GetSocket ().remote_endpoint ().address ().is_v6 ())
+ if (it.second && it.second->IsEstablished () && it.second->GetRemoteEndpoint ().address ().is_v6 ())
{
tmp_s6 << "\r\n";
if (it.second->IsOutgoing ()) tmp_s6 << " ⇒ ";
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 << " ⇒ ";
tmp_s6 << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
tmp_s6 << "
\r\n" << std::endl;
diff --git a/daemon/UPnP.cpp b/daemon/UPnP.cpp
index 6ea33c46..25e24bb5 100644
--- a/daemon/UPnP.cpp
+++ b/daemon/UPnP.cpp
@@ -81,10 +81,10 @@ namespace transport
void UPnP::Discover ()
{
bool isError;
- int err;
+ int err;
#if ((MINIUPNPC_API_VERSION >= 8) || defined (UPNPDISCOVER_SUCCESS))
- err = UPNPDISCOVER_SUCCESS;
+ err = UPNPDISCOVER_SUCCESS;
#if (MINIUPNPC_API_VERSION >= 14)
m_Devlist = upnpDiscover (UPNP_RESPONSE_TIMEOUT, NULL, NULL, 0, 0, 2, &err);
@@ -94,8 +94,8 @@ namespace transport
isError = err != UPNPDISCOVER_SUCCESS;
#else // MINIUPNPC_API_VERSION >= 8
- err = 0;
- m_Devlist = upnpDiscover (UPNP_RESPONSE_TIMEOUT, NULL, NULL, 0);
+ err = 0;
+ m_Devlist = upnpDiscover (UPNP_RESPONSE_TIMEOUT, NULL, NULL, 0);
isError = m_Devlist == NULL;
#endif // MINIUPNPC_API_VERSION >= 8
{
@@ -106,15 +106,15 @@ namespace transport
if (isError)
{
- LogPrint (eLogError, "UPnP: unable to discover Internet Gateway Devices: error ", err);
+ LogPrint (eLogError, "UPnP: unable to discover Internet Gateway Devices: error ", err);
return;
}
err = UPNP_GetValidIGD (m_Devlist, &m_upnpUrls, &m_upnpData, m_NetworkAddr, sizeof (m_NetworkAddr));
- m_upnpUrlsInitialized=err!=0;
+ m_upnpUrlsInitialized=err!=0;
if (err == UPNP_IGD_VALID_CONNECTED)
{
- err = UPNP_GetExternalIPAddress (m_upnpUrls.controlURL, m_upnpData.first.servicetype, m_externalIPAddress);
+ err = UPNP_GetExternalIPAddress (m_upnpUrls.controlURL, m_upnpData.first.servicetype, m_externalIPAddress);
if(err != UPNPCOMMAND_SUCCESS)
{
LogPrint (eLogError, "UPnP: unable to get external address: error ", err);
@@ -125,14 +125,14 @@ namespace transport
LogPrint (eLogError, "UPnP: found Internet Gateway Device ", m_upnpUrls.controlURL);
if (!m_externalIPAddress[0])
{
- LogPrint (eLogError, "UPnP: found Internet Gateway Device doesn't know our external address");
+ LogPrint (eLogError, "UPnP: found Internet Gateway Device doesn't know our external address");
return;
}
}
}
else
{
- LogPrint (eLogError, "UPnP: unable to find valid Internet Gateway Device: error ", err);
+ LogPrint (eLogError, "UPnP: unable to find valid Internet Gateway Device: error ", err);
return;
}
@@ -183,7 +183,7 @@ namespace transport
err = CheckMapping (strPort.c_str (), strType.c_str ());
if (err != UPNPCOMMAND_SUCCESS) // if mapping not found
{
- LogPrint (eLogDebug, "UPnP: possibly port ", strPort, " is not forwarded: return code ", err);
+ LogPrint (eLogDebug, "UPnP: possibly port ", strPort, " is not forwarded: return code ", err);
#if ((MINIUPNPC_API_VERSION >= 8) || defined (UPNPDISCOVER_SUCCESS))
err = UPNP_AddPortMapping (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strPort.c_str (), m_NetworkAddr, strDesc.c_str (), strType.c_str (), NULL, NULL);
@@ -203,7 +203,7 @@ namespace transport
}
else
{
- LogPrint (eLogDebug, "UPnP: external forward from ", m_NetworkAddr, ":", strPort, " exists on current Internet Gateway Device");
+ LogPrint (eLogDebug, "UPnP: external forward from ", m_NetworkAddr, ":", strPort, " exists on current Internet Gateway Device");
return;
}
}
@@ -220,14 +220,14 @@ namespace transport
void UPnP::CloseMapping (std::shared_ptr address)
{
- if(!m_upnpUrlsInitialized) {
- return;
- }
+ if(!m_upnpUrlsInitialized) {
+ return;
+ }
std::string strType (GetProto (address)), strPort (std::to_string (address->port));
int err = UPNPCOMMAND_SUCCESS;
err = CheckMapping (strPort.c_str (), strType.c_str ());
- if (err == UPNPCOMMAND_SUCCESS)
+ if (err == UPNPCOMMAND_SUCCESS)
{
err = UPNP_DeletePortMapping (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strType.c_str (), NULL);
LogPrint (eLogError, "UPnP: DeletePortMapping() returned : ", err);
@@ -238,11 +238,11 @@ namespace transport
{
freeUPNPDevlist (m_Devlist);
m_Devlist = 0;
- if(m_upnpUrlsInitialized){
- FreeUPNPUrls (&m_upnpUrls);
- m_upnpUrlsInitialized=false;
- }
- }
+ if(m_upnpUrlsInitialized){
+ FreeUPNPUrls (&m_upnpUrls);
+ m_upnpUrlsInitialized=false;
+ }
+ }
std::string UPnP::GetProto (std::shared_ptr address)
{
diff --git a/libi2pd/Config.cpp b/libi2pd/Config.cpp
index 9da0bbe0..cc79c87c 100644
--- a/libi2pd/Config.cpp
+++ b/libi2pd/Config.cpp
@@ -50,7 +50,9 @@ namespace config {
("nat", bool_switch()->default_value(true), "Should we assume we are behind NAT? (default: enabled)")
("port", value()->default_value(0), "Port to listen for incoming connections (default: auto)")
("ipv4", bool_switch()->default_value(true), "Enable communication through ipv4 (default: enabled)")
+ ("address4", value()->default_value(""), "Local address to bind ipv4 transport sockets to")
("ipv6", bool_switch()->default_value(false), "Enable communication through ipv6 (default: disabled)")
+ ("address6", value()->default_value(""), "Local address to bind ipv6 transport sockets to")
("reservedrange", bool_switch()->default_value(true), "Check remote RI for being in blacklist of reserved IP ranges (default: enabled)")
("netid", value()->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)")
@@ -247,7 +249,7 @@ namespace config {
("ntcp2.enabled", value()->default_value(true), "Enable NTCP2 (default: enabled)")
("ntcp2.published", value()->default_value(true), "Publish NTCP2 (default: enabled)")
("ntcp2.port", value()->default_value(0), "Port to listen for incoming NTCP2 connections (default: auto)")
- ("ntcp2.addressv6", value()->default_value("::"), "Address to bind NTCP2 on")
+ ("ntcp2.addressv6", value()->default_value("::"), "Address to publish NTCP2 with")
("ntcp2.proxy", value()->default_value(""), "Proxy URL for NTCP2 transport")
;
diff --git a/libi2pd/Crypto.cpp b/libi2pd/Crypto.cpp
index 68850a9d..14ef83ae 100644
--- a/libi2pd/Crypto.cpp
+++ b/libi2pd/Crypto.cpp
@@ -353,7 +353,7 @@ namespace crypto
bool X25519Keys::Agree (const uint8_t * pub, uint8_t * shared)
{
- if (pub[31] & 0x80) return false; // not x25519 key
+ if (!pub || (pub[31] & 0x80)) return false; // not x25519 key
#if OPENSSL_X25519
EVP_PKEY_derive_init (m_Ctx);
auto pkey = EVP_PKEY_new_raw_public_key (EVP_PKEY_X25519, NULL, pub, 32);
diff --git a/libi2pd/NTCP2.cpp b/libi2pd/NTCP2.cpp
index dc485d5b..96a52b15 100644
--- a/libi2pd/NTCP2.cpp
+++ b/libi2pd/NTCP2.cpp
@@ -333,15 +333,14 @@ namespace transport
if (in_RemoteRouter) // Alice
{
m_Establisher->m_RemoteIdentHash = GetRemoteIdentity ()->GetIdentHash ();
- if (!addr)
- addr = in_RemoteRouter->GetNTCP2Address (true); // we need a published address
if (addr)
{
memcpy (m_Establisher->m_RemoteStaticKey, addr->ntcp2->staticKey, 32);
memcpy (m_Establisher->m_IV, addr->ntcp2->iv, 16);
+ m_RemoteEndpoint = boost::asio::ip::tcp::endpoint (addr->host, addr->port);
}
else
- LogPrint (eLogWarning, "NTCP2: Missing NTCP2 parameters");
+ LogPrint (eLogWarning, "NTCP2: Missing NTCP2 address");
}
m_NextRouterInfoResendTime = i2p::util::GetSecondsSinceEpoch () + NTCP2_ROUTERINFO_RESEND_INTERVAL +
rand ()%NTCP2_ROUTERINFO_RESEND_INTERVAL_THRESHOLD;
@@ -657,19 +656,13 @@ namespace transport
SendTerminationAndTerminate (eNTCP2Message3Error);
return;
}
- auto addr = ri.GetNTCP2Address (false); // any NTCP2 address
+ auto addr = ri.GetNTCP2AddressWithStaticKey (m_Establisher->m_RemoteStaticKey);
if (!addr)
{
- LogPrint (eLogError, "NTCP2: No NTCP2 address found in SessionConfirmed");
+ LogPrint (eLogError, "NTCP2: No NTCP2 address wth static key found in SessionConfirmed");
Terminate ();
return;
}
- if (memcmp (addr->ntcp2->staticKey, m_Establisher->m_RemoteStaticKey, 32))
- {
- LogPrint (eLogError, "NTCP2: Static key mismatch in SessionConfirmed");
- SendTerminationAndTerminate (eNTCP2IncorrectSParameter);
- return;
- }
i2p::data::netdb.PostI2NPMsg (CreateI2NPMessage (eI2NPDummyMsg, buf.data () + 3, size)); // TODO: should insert ri and not parse it twice
// TODO: process options
@@ -1177,7 +1170,9 @@ namespace transport
{
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 )
{
@@ -1274,10 +1269,15 @@ namespace transport
return nullptr;
}
- void NTCP2Server::Connect(const boost::asio::ip::address & address, uint16_t port, std::shared_ptr conn)
+ void NTCP2Server::Connect(std::shared_ptr conn)
{
- LogPrint (eLogDebug, "NTCP2: Connecting to ", address ,":", port);
- GetService ().post([this, address, port, conn]()
+ if (!conn || conn->GetRemoteEndpoint ().address ().is_unspecified ())
+ {
+ LogPrint (eLogError, "NTCP2: Can't connect to unspecified address");
+ return;
+ }
+ LogPrint (eLogDebug, "NTCP2: Connecting to ", conn->GetRemoteEndpoint ());
+ GetService ().post([this, conn]()
{
if (this->AddNTCP2Session (conn))
{
@@ -1290,12 +1290,32 @@ namespace transport
if (ecode != boost::asio::error::operation_aborted)
{
LogPrint (eLogInfo, "NTCP2: Not connected in ", timeout, " seconds");
- if (conn->GetRemoteIdentity ())
- i2p::data::netdb.SetUnreachable (conn->GetRemoteIdentity ()->GetIdentHash (), true);
conn->Terminate ();
}
});
- conn->GetSocket ().async_connect (boost::asio::ip::tcp::endpoint (address, port), std::bind (&NTCP2Server::HandleConnect, this, std::placeholders::_1, conn, timer));
+ // bind to local address
+ std::shared_ptr localAddress;
+ if (conn->GetRemoteEndpoint ().address ().is_v6 ())
+ {
+ if (i2p::util::net::IsYggdrasilAddress (conn->GetRemoteEndpoint ().address ()))
+ localAddress = m_YggdrasilAddress;
+ else
+ localAddress = m_Address6;
+ conn->GetSocket ().open (boost::asio::ip::tcp::v6 ());
+ }
+ else
+ {
+ localAddress = m_Address4;
+ conn->GetSocket ().open (boost::asio::ip::tcp::v4 ());
+ }
+ if (localAddress)
+ {
+ boost::system::error_code ec;
+ conn->GetSocket ().bind (*localAddress, ec);
+ if (ec)
+ LogPrint (eLogError, "NTCP2: can't bind to ", localAddress->address ().to_string (), ": ", ec.message ());
+ }
+ conn->GetSocket ().async_connect (conn->GetRemoteEndpoint (), std::bind (&NTCP2Server::HandleConnect, this, std::placeholders::_1, conn, timer));
}
else
conn->Terminate ();
@@ -1312,7 +1332,7 @@ namespace transport
}
else
{
- LogPrint (eLogDebug, "NTCP2: Connected to ", conn->GetSocket ().remote_endpoint ());
+ LogPrint (eLogDebug, "NTCP2: Connected to ", conn->GetRemoteEndpoint ());
conn->ClientLogin ();
}
}
@@ -1328,6 +1348,7 @@ namespace transport
LogPrint (eLogDebug, "NTCP2: Connected from ", ep);
if (conn)
{
+ conn->SetRemoteEndpoint (ep);
conn->ServerLogin ();
m_PendingIncomingSessions.push_back (conn);
conn = nullptr;
@@ -1361,6 +1382,7 @@ namespace transport
LogPrint (eLogDebug, "NTCP2: Connected from ", ep);
if (conn)
{
+ conn->SetRemoteEndpoint (ep);
conn->ServerLogin ();
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 conn)
+ void NTCP2Server::ConnectWithProxy (std::shared_ptr conn)
{
if(!m_ProxyEndpoint) return;
- GetService().post([this, host, port, addrtype, conn]() {
+ if (!conn || conn->GetRemoteEndpoint ().address ().is_unspecified ())
+ {
+ LogPrint (eLogError, "NTCP2: Can't connect to unspecified address");
+ return;
+ }
+ GetService().post([this, conn]()
+ {
if (this->AddNTCP2Session (conn))
{
-
auto timer = std::make_shared(GetService());
auto timeout = NTCP2_CONNECT_TIMEOUT * 5;
conn->SetTerminationTimeout(timeout * 2);
@@ -1431,11 +1458,10 @@ namespace transport
if (ecode != boost::asio::error::operation_aborted)
{
LogPrint (eLogInfo, "NTCP2: Not connected in ", timeout, " seconds");
- i2p::data::netdb.SetUnreachable (conn->GetRemoteIdentity ()->GetIdentHash (), true);
conn->Terminate ();
}
});
- conn->GetSocket ().async_connect (*m_ProxyEndpoint, std::bind (&NTCP2Server::HandleProxyConnect, this, std::placeholders::_1, conn, timer, host, port, addrtype));
+ conn->GetSocket ().async_connect (*m_ProxyEndpoint, std::bind (&NTCP2Server::HandleProxyConnect, this, std::placeholders::_1, conn, timer));
}
});
}
@@ -1447,7 +1473,7 @@ namespace transport
m_ProxyPort = port;
}
- void NTCP2Server::HandleProxyConnect(const boost::system::error_code& ecode, std::shared_ptr conn, std::shared_ptr timer, const std::string & host, uint16_t port, RemoteAddressType addrtype)
+ void NTCP2Server::HandleProxyConnect(const boost::system::error_code& ecode, std::shared_ptr conn, std::shared_ptr timer)
{
if (ecode)
{
@@ -1473,7 +1499,7 @@ namespace transport
});
auto readbuff = std::make_shared >(2);
boost::asio::async_read(conn->GetSocket(), boost::asio::buffer(readbuff->data (), 2),
- [this, readbuff, timer, conn, host, port, addrtype](const boost::system::error_code & ec, std::size_t transferred)
+ [this, readbuff, timer, conn](const boost::system::error_code & ec, std::size_t transferred)
{
if(ec)
{
@@ -1486,7 +1512,7 @@ namespace transport
{
if((*readbuff)[1] == 0x00)
{
- AfterSocksHandshake(conn, timer, host, port, addrtype);
+ AfterSocksHandshake(conn, timer);
return;
}
else if ((*readbuff)[1] == 0xff)
@@ -1506,13 +1532,14 @@ namespace transport
}
case eHTTPProxy:
{
+ auto& ep = conn->GetRemoteEndpoint ();
i2p::http::HTTPReq req;
req.method = "CONNECT";
req.version ="HTTP/1.1";
- if(addrtype == eIP6Address)
- req.uri = "[" + host + "]:" + std::to_string(port);
+ if(ep.address ().is_v6 ())
+ req.uri = "[" + ep.address ().to_string() + "]:" + std::to_string(ep.port ());
else
- req.uri = host + ":" + std::to_string(port);
+ req.uri = ep.address ().to_string() + ":" + std::to_string(ep.port ());
boost::asio::streambuf writebuff;
std::ostream out(&writebuff);
@@ -1566,7 +1593,7 @@ namespace transport
}
}
- void NTCP2Server::AfterSocksHandshake(std::shared_ptr conn, std::shared_ptr timer, const std::string & host, uint16_t port, RemoteAddressType addrtype)
+ void NTCP2Server::AfterSocksHandshake(std::shared_ptr conn, std::shared_ptr timer)
{
// build request
size_t sz = 6; // header + port
@@ -1576,27 +1603,28 @@ namespace transport
(*buff)[1] = 0x01;
(*buff)[2] = 0x00;
- if(addrtype == eIP4Address)
+ auto& ep = conn->GetRemoteEndpoint ();
+ if(ep.address ().is_v4 ())
{
(*buff)[3] = 0x01;
- auto addrbytes = boost::asio::ip::address::from_string(host).to_v4().to_bytes();
+ auto addrbytes = ep.address ().to_v4().to_bytes();
sz += 4;
memcpy(buff->data () + 4, addrbytes.data(), 4);
}
- else if (addrtype == eIP6Address)
+ else if (ep.address ().is_v6 ())
{
(*buff)[3] = 0x04;
- auto addrbytes = boost::asio::ip::address::from_string(host).to_v6().to_bytes();
+ auto addrbytes = ep.address ().to_v6().to_bytes();
sz += 16;
memcpy(buff->data () + 4, addrbytes.data(), 16);
}
- else if (addrtype == eHostname)
+ else
{
// We mustn't really fall here because all connections are made to IP addresses
- LogPrint(eLogError, "NTCP2: Tried to connect to domain name via socks proxy");
+ LogPrint(eLogError, "NTCP2: Tried to connect to unexpected address via proxy");
return;
}
- htobe16buf(buff->data () + sz - 2, port);
+ htobe16buf(buff->data () + sz - 2, ep.port ());
boost::asio::async_write(conn->GetSocket(), boost::asio::buffer(buff->data (), sz), boost::asio::transfer_all(),
[buff](const boost::system::error_code & ec, std::size_t written)
{
@@ -1623,11 +1651,23 @@ namespace transport
return;
}
}
- if(!e)
- i2p::data::netdb.SetUnreachable (conn->GetRemoteIdentity ()->GetIdentHash (), true);
timer->cancel();
conn->Terminate();
});
}
+
+ void NTCP2Server::SetLocalAddress (const boost::asio::ip::address& localAddress)
+ {
+ auto addr = std::make_shared(boost::asio::ip::tcp::endpoint(localAddress, 0));
+ if (localAddress.is_v6 ())
+ {
+ if (i2p::util::net::IsYggdrasilAddress (localAddress))
+ m_YggdrasilAddress = addr;
+ else
+ m_Address6 = addr;
+ }
+ else
+ m_Address4 = addr;
+ }
}
}
diff --git a/libi2pd/NTCP2.h b/libi2pd/NTCP2.h
index a7708872..23b619e8 100644
--- a/libi2pd/NTCP2.h
+++ b/libi2pd/NTCP2.h
@@ -134,6 +134,8 @@ namespace transport
void Close () { m_Socket.close (); }; // for accept
boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; };
+ const boost::asio::ip::tcp::endpoint& GetRemoteEndpoint () { return m_RemoteEndpoint; };
+ void SetRemoteEndpoint (const boost::asio::ip::tcp::endpoint& ep) { m_RemoteEndpoint = ep; };
bool IsEstablished () const { return m_IsEstablished; };
bool IsTerminated () const { return m_IsTerminated; };
@@ -189,6 +191,7 @@ namespace transport
NTCP2Server& m_Server;
boost::asio::ip::tcp::socket m_Socket;
+ boost::asio::ip::tcp::endpoint m_RemoteEndpoint;
bool m_IsEstablished, m_IsTerminated;
std::unique_ptr m_Establisher;
@@ -221,13 +224,6 @@ namespace transport
{
public:
- enum RemoteAddressType
- {
- eIP4Address,
- eIP6Address,
- eHostname
- };
-
enum ProxyType
{
eNoProxy,
@@ -246,23 +242,23 @@ namespace transport
void RemoveNTCP2Session (std::shared_ptr session);
std::shared_ptr FindNTCP2Session (const i2p::data::IdentHash& ident);
- void ConnectWithProxy (const std::string& addr, uint16_t port, RemoteAddressType addrtype, std::shared_ptr conn);
- void Connect(const boost::asio::ip::address & address, uint16_t port, std::shared_ptr conn);
-
- void AfterSocksHandshake(std::shared_ptr conn, std::shared_ptr timer, const std::string & host, uint16_t port, RemoteAddressType addrtype);
-
+ void ConnectWithProxy (std::shared_ptr conn);
+ void Connect(std::shared_ptr conn);
bool UsingProxy() const { return m_ProxyType != eNoProxy; };
void UseProxy(ProxyType proxy, const std::string & address, uint16_t port);
+ void SetLocalAddress (const boost::asio::ip::address& localAddress);
+
private:
void HandleAccept (std::shared_ptr conn, const boost::system::error_code& error);
void HandleAcceptV6 (std::shared_ptr conn, const boost::system::error_code& error);
void HandleConnect (const boost::system::error_code& ecode, std::shared_ptr conn, std::shared_ptr timer);
- void HandleProxyConnect(const boost::system::error_code& ecode, std::shared_ptr conn, std::shared_ptr timer, const std::string & host, uint16_t port, RemoteAddressType adddrtype);
-
+ void HandleProxyConnect(const boost::system::error_code& ecode, std::shared_ptr conn, std::shared_ptr timer);
+ void AfterSocksHandshake(std::shared_ptr conn, std::shared_ptr timer);
+
// timer
void ScheduleTermination ();
void HandleTerminationTimer (const boost::system::error_code& ecode);
@@ -279,7 +275,8 @@ namespace transport
uint16_t m_ProxyPort;
boost::asio::ip::tcp::resolver m_Resolver;
std::unique_ptr m_ProxyEndpoint;
-
+ std::shared_ptr m_Address4, m_Address6, m_YggdrasilAddress;
+
public:
// for HTTP/I2PControl
diff --git a/libi2pd/NetDb.cpp b/libi2pd/NetDb.cpp
index 0222ccdf..66d15cae 100644
--- a/libi2pd/NetDb.cpp
+++ b/libi2pd/NetDb.cpp
@@ -1150,7 +1150,7 @@ namespace data
return GetRandomRouter (
[v4only](std::shared_ptr router)->bool
{
- return !router->IsHidden () && router->IsPeerTesting () && router->IsSSU (v4only);
+ return !router->IsHidden () && router->IsPeerTesting (v4only);
});
}
diff --git a/libi2pd/RouterContext.cpp b/libi2pd/RouterContext.cpp
index 6183f2be..a7273376 100644
--- a/libi2pd/RouterContext.cpp
+++ b/libi2pd/RouterContext.cpp
@@ -41,6 +41,7 @@ namespace i2p
if (!Load ())
CreateNewRouter ();
m_Decryptor = m_Keys.CreateDecryptor (nullptr);
+ m_TunnelDecryptor = m_Keys.CreateDecryptor (nullptr);
UpdateRouterInfo ();
if (IsECIES ())
{
@@ -77,6 +78,12 @@ namespace i2p
std::string ifname; i2p::config::GetOption("ifname", ifname);
std::string ifname4; i2p::config::GetOption("ifname4", ifname4);
std::string ifname6; i2p::config::GetOption("ifname6", ifname6);
+
+ if ((ntcp2 || ygg) && !m_NTCP2Keys)
+ NewNTCP2Keys ();
+ bool ntcp2Published = false;
+ if (ntcp2)
+ i2p::config::GetOption("ntcp2.published", ntcp2Published);
uint8_t caps = 0;
if (ipv4)
{
@@ -86,14 +93,20 @@ namespace i2p
else if (!nat && !ifname.empty())
/* bind to interface, we have no NAT so set external address too */
host = i2p::util::net::GetInterfaceAddress(ifname, false).to_string(); // v4
-
if(ifname4.size())
host = i2p::util::net::GetInterfaceAddress(ifname4, false).to_string();
+ 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)
{
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)
@@ -103,16 +116,35 @@ namespace i2p
i2p::config::GetOption("host", host);
else if (!ifname.empty())
host = i2p::util::net::GetInterfaceAddress(ifname, true).to_string(); // v6
-
if(ifname6.size())
host = i2p::util::net::GetInterfaceAddress(ifname6, true).to_string();
-
+
+ if (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)
{
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);
+ }
routerInfo.SetCaps (caps); // caps + L
routerInfo.SetProperty ("netId", std::to_string (m_NetID));
@@ -120,40 +152,6 @@ namespace i2p
routerInfo.CreateBuffer (m_Keys);
m_RouterInfo.SetRouterIdentity (GetIdentity ());
m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ());
-
- if (ntcp2) // we don't store iv in the address if non published so we must update it from keys
- {
- if (!m_NTCP2Keys) NewNTCP2Keys ();
- bool published; i2p::config::GetOption("ntcp2.published", published);
- if (ipv4 || !published) UpdateNTCP2Address (true); // create not published NTCP2 address
- if (published)
- {
- 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 ()
@@ -226,7 +224,7 @@ namespace i2p
if (port == 9150) port = 9151; // Tor browser
}
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->iv = m_NTCP2Keys->iv;
updated = true;
@@ -427,7 +425,6 @@ namespace i2p
caps &= ~i2p::data::RouterInfo::eReachable;
caps |= i2p::data::RouterInfo::eUnreachable;
caps &= ~i2p::data::RouterInfo::eFloodfill; // can't be floodfill
- caps &= ~i2p::data::RouterInfo::eSSUIntroducer; // can't be introducer
m_RouterInfo.SetCaps (caps);
uint16_t port = 0;
// delete previous introducers
@@ -435,6 +432,8 @@ namespace i2p
for (auto& addr : addresses)
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 ();
port = addr->port;
}
@@ -452,7 +451,6 @@ namespace i2p
uint8_t caps = m_RouterInfo.GetCaps ();
caps &= ~i2p::data::RouterInfo::eUnreachable;
caps |= i2p::data::RouterInfo::eReachable;
- caps |= i2p::data::RouterInfo::eSSUIntroducer;
if (m_IsFloodfill)
caps |= i2p::data::RouterInfo::eFloodfill;
m_RouterInfo.SetCaps (caps);
@@ -462,6 +460,8 @@ namespace i2p
for (auto& addr : addresses)
if (addr->ssu)
{
+ addr->cost = i2p::data::COST_SSU_DIRECT;
+ addr->caps |= i2p::data::RouterInfo::eSSUIntroducer;
addr->ssu->introducers.clear ();
port = addr->port;
}
@@ -774,7 +774,7 @@ namespace i2p
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 (!m_InitialNoiseState) return false;
@@ -782,7 +782,7 @@ namespace i2p
m_CurrentNoiseState.reset (new i2p::crypto::NoiseSymmetricState (*m_InitialNoiseState));
m_CurrentNoiseState->MixHash (encrypted, 32); // h = SHA256(h || sepk)
uint8_t sharedSecret[32];
- if (!m_Decryptor->Decrypt (encrypted, sharedSecret, ctx, false))
+ if (!m_TunnelDecryptor->Decrypt (encrypted, sharedSecret, ctx, false))
{
LogPrint (eLogWarning, "Router: Incorrect ephemeral public key");
return false;
@@ -801,7 +801,7 @@ namespace i2p
return true;
}
else
- return m_Decryptor->Decrypt (encrypted, data, ctx, false);
+ return m_TunnelDecryptor->Decrypt (encrypted, data, ctx, false);
}
i2p::crypto::X25519Keys& RouterContext::GetStaticKeys ()
diff --git a/libi2pd/RouterContext.h b/libi2pd/RouterContext.h
index 12b77d65..228da788 100644
--- a/libi2pd/RouterContext.h
+++ b/libi2pd/RouterContext.h
@@ -39,7 +39,8 @@ namespace i2p
{
eRouterErrorNone = 0,
eRouterErrorClockSkew = 1,
- eRouterErrorOffline = 2
+ eRouterErrorOffline = 2,
+ eRouterErrorSymmetricNAT = 3
};
class RouterContext: public i2p::garlic::GarlicDestination
@@ -153,7 +154,7 @@ namespace i2p
i2p::data::RouterInfo m_RouterInfo;
i2p::data::PrivateKeys m_Keys;
- std::shared_ptr m_Decryptor;
+ std::shared_ptr m_Decryptor, m_TunnelDecryptor;
uint64_t m_LastUpdateTime; // in seconds
bool m_AcceptsTunnels, m_IsFloodfill;
std::chrono::time_point m_StartupTime;
diff --git a/libi2pd/RouterInfo.cpp b/libi2pd/RouterInfo.cpp
index 8da49fc6..4ea66add 100644
--- a/libi2pd/RouterInfo.cpp
+++ b/libi2pd/RouterInfo.cpp
@@ -186,6 +186,7 @@ namespace data
void RouterInfo::ReadFromStream (std::istream& s)
{
+ m_Caps = 0;
s.read ((char *)&m_Timestamp, sizeof (m_Timestamp));
m_Timestamp = be64toh (m_Timestamp);
// read addresses
@@ -215,6 +216,7 @@ namespace data
}
else
address->transportStyle = eTransportUnknown;
+ address->caps = 0;
address->port = 0;
uint16_t size, r = 0;
s.read ((char *)&size, sizeof (size)); if (!s) return;
@@ -250,7 +252,7 @@ namespace data
LogPrint (eLogWarning, "RouterInfo: Unexpected field 'key' for NTCP");
}
else if (!strcmp (key, "caps"))
- ExtractCaps (value);
+ address->caps = ExtractAddressCaps (value);
else if (!strcmp (key, "s")) // ntcp2 static key
{
Base64ToByteStream (value, strlen (value), address->ntcp2->staticKey, 32);
@@ -309,7 +311,15 @@ namespace data
supportedTransports |= eNTCP2V4;
}
else if (!address->ntcp2->isPublished)
- supportedTransports |= eNTCP2V4; // most likely, since we don't have host
+ {
+ if (address->caps)
+ {
+ if (address->caps | AddressCaps::eV4) supportedTransports |= eNTCP2V4;
+ if (address->caps | AddressCaps::eV6) supportedTransports |= eNTCP2V6;
+ }
+ else
+ supportedTransports |= eNTCP2V4; // most likely, since we don't have host
+ }
}
}
else if (address->transportStyle == eTransportSSU)
@@ -319,7 +329,7 @@ namespace data
if (isHost)
supportedTransports |= address->host.is_v4 () ? eSSUV4 : eSSUV6;
else
- if (introducers) supportedTransports |= eSSUV4; // in case if host is not presented
+ if (introducers) supportedTransports |= eSSUV4; // in case if host is not presented
}
}
if (supportedTransports)
@@ -430,18 +440,39 @@ namespace data
case CAPS_FLAG_UNREACHABLE:
m_Caps |= Caps::eUnreachable;
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:
- m_Caps |= Caps::eSSUTesting;
+ caps |= AddressCaps::eSSUTesting;
break;
case CAPS_FLAG_SSU_INTRODUCER:
- m_Caps |= Caps::eSSUIntroducer;
+ caps |= AddressCaps::eSSUIntroducer;
break;
default: ;
}
cap++;
}
+ return caps;
}
-
+
void RouterInfo::UpdateCapsProperty ()
{
std::string caps;
@@ -482,10 +513,26 @@ namespace data
s.write ((const char *)&address.cost, sizeof (address.cost));
s.write ((const char *)&address.date, sizeof (address.date));
std::stringstream properties;
+ bool isPublished = false;
if (address.transportStyle == eTransportNTCP)
{
if (address.IsNTCP2 ())
+ {
WriteString ("NTCP2", s);
+ if (address.IsPublishedNTCP2 ())
+ 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
continue; // don't write NTCP address
}
@@ -496,15 +543,19 @@ namespace data
WriteString ("caps", properties);
properties << '=';
std::string caps;
- if (IsPeerTesting ()) caps += CAPS_FLAG_SSU_TESTING;
- if (IsIntroducer ()) caps += CAPS_FLAG_SSU_INTRODUCER;
+ if (address.IsPeerTesting ()) caps += CAPS_FLAG_SSU_TESTING;
+ if (address.IsIntroducer ()) caps += CAPS_FLAG_SSU_INTRODUCER;
+ if (IsReachable ())
+ isPublished = true;
+ else
+ caps += CAPS_FLAG_V4;
WriteString (caps, properties);
properties << ';';
}
else
WriteString ("", s);
- if (!address.IsNTCP2 () || address.IsPublishedNTCP2 ())
+ if (isPublished)
{
WriteString ("host", properties);
properties << '=';
@@ -514,7 +565,7 @@ namespace data
if (address.transportStyle == eTransportSSU)
{
// write introducers if any
- if (address.ssu->introducers.size () > 0)
+ if (!address.ssu->introducers.empty())
{
int i = 0;
for (const auto& introducer: address.ssu->introducers)
@@ -593,7 +644,7 @@ namespace data
WriteString (address.ntcp2->iv.ToBase64 (), properties); properties << ';';
}
- if (!address.IsNTCP2 () || address.IsPublishedNTCP2 ())
+ if (isPublished || address.ssu)
{
WriteString ("port", properties);
properties << '=';
@@ -721,7 +772,8 @@ namespace data
addr->host = boost::asio::ip::address::from_string (host);
addr->port = port;
addr->transportStyle = eTransportSSU;
- addr->cost = 10; // NTCP should have priority over SSU
+ addr->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->ssu.reset (new SSUExt ());
addr->ssu->mtu = mtu;
@@ -744,7 +796,8 @@ namespace data
addr->host = host;
addr->port = port;
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->ntcp2.reset (new NTCP2Ext ());
if (port) addr->ntcp2->isPublished = true;
@@ -962,12 +1015,13 @@ namespace data
return nullptr;
}
- std::shared_ptr RouterInfo::GetNTCP2Address (bool publishedOnly) const
+ std::shared_ptr RouterInfo::GetNTCP2AddressWithStaticKey (const uint8_t * key) const
{
+ if (!key) return nullptr;
return GetAddress (
- [publishedOnly](std::shared_ptr address)->bool
+ [key](std::shared_ptr 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 &&
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 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 address)->bool
+ {
+ return (address->transportStyle == eTransportSSU) && address->IsIntroducer ();
+ }) != nullptr;
+ }
}
}
diff --git a/libi2pd/RouterInfo.h b/libi2pd/RouterInfo.h
index 45670ee5..9e9f00cd 100644
--- a/libi2pd/RouterInfo.h
+++ b/libi2pd/RouterInfo.h
@@ -44,9 +44,16 @@ namespace data
const char CAPS_FLAG_EXTRA_BANDWIDTH1 = 'P'; /* 256-2000 KBps */
const char CAPS_FLAG_EXTRA_BANDWIDTH2 = 'X'; /* > 2000 KBps */
+ const char CAPS_FLAG_V4 = '4';
+ const char CAPS_FLAG_V6 = '6';
const char CAPS_FLAG_SSU_TESTING = 'B';
const char CAPS_FLAG_SSU_INTRODUCER = 'C';
+ const uint8_t COST_NTCP2_PUBLISHED = 3;
+ const uint8_t COST_NTCP2_NON_PUBLISHED = 14;
+ const uint8_t COST_SSU_DIRECT = 9;
+ const uint8_t COST_SSU_THROUGH_INTRODUCERS = 11;
+
const int MAX_RI_BUFFER_SIZE = 2048; // if RouterInfo exceeds 2048 we consider it as malformed, might be changed later
class RouterInfo: public RoutingDestination
{
@@ -67,12 +74,18 @@ namespace data
eHighBandwidth = 0x02,
eExtraBandwidth = 0x04,
eReachable = 0x08,
- eSSUTesting = 0x10,
- eSSUIntroducer = 0x20,
- eHidden = 0x40,
- eUnreachable = 0x80
+ eHidden = 0x10,
+ eUnreachable = 0x20
};
+ enum AddressCaps
+ {
+ eV4 = 0x01,
+ eV6 = 0x02,
+ eSSUTesting = 0x04,
+ eSSUIntroducer = 0x08
+ };
+
enum TransportStyle
{
eTransportUnknown = 0,
@@ -111,7 +124,7 @@ namespace data
boost::asio::ip::address host;
int port;
uint64_t date;
- uint8_t cost;
+ uint8_t cost, caps;
std::unique_ptr ssu; // not null for SSU
std::unique_ptr ntcp2; // not null for NTCP2
@@ -134,6 +147,9 @@ namespace data
bool IsNTCP2 () const { return (bool)ntcp2; };
bool IsPublishedNTCP2 () const { return IsNTCP2 () && ntcp2->isPublished; };
+
+ bool IsIntroducer () const { return caps & eSSUIntroducer; };
+ bool IsPeerTesting () const { return caps & eSSUTesting; };
};
typedef std::list > Addresses;
@@ -150,7 +166,7 @@ namespace data
uint64_t GetTimestamp () const { return m_Timestamp; };
int GetVersion () const { return m_Version; };
Addresses& GetAddresses () { return *m_Addresses; }; // should be called for local RI only, otherwise must return shared_ptr
- std::shared_ptr GetNTCP2Address (bool publishedOnly) const;
+ std::shared_ptr GetNTCP2AddressWithStaticKey (const uint8_t * key) const;
std::shared_ptr GetPublishedNTCP2V4Address () const;
std::shared_ptr GetPublishedNTCP2V6Address () const;
std::shared_ptr GetSSUAddress (bool v4only = true) const;
@@ -183,12 +199,12 @@ namespace data
bool IsCompatible (const RouterInfo& other) const { return m_SupportedTransports & other.m_SupportedTransports; };
bool HasValidAddresses () const { return m_SupportedTransports; };
bool UsesIntroducer () const;
- bool IsIntroducer () const { return m_Caps & eSSUIntroducer; };
- bool IsPeerTesting () const { return m_Caps & eSSUTesting; };
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;
uint8_t GetCaps () const { return m_Caps; };
void SetCaps (uint8_t caps);
@@ -232,6 +248,7 @@ namespace data
size_t ReadString (char* str, size_t len, std::istream& s) const;
void WriteString (const std::string& str, std::ostream& s) const;
void ExtractCaps (const char * value);
+ uint8_t ExtractAddressCaps (const char * value) const;
template
std::shared_ptr GetAddress (Filter filter) const;
void UpdateCapsProperty ();
diff --git a/libi2pd/SSU.cpp b/libi2pd/SSU.cpp
index 79b4f35f..be5ac9c7 100644
--- a/libi2pd/SSU.cpp
+++ b/libi2pd/SSU.cpp
@@ -22,21 +22,8 @@ namespace i2p
{
namespace transport
{
-
- SSUServer::SSUServer (const boost::asio::ip::address & addr, int port):
- m_OnlyV6(true), m_IsRunning(false), m_Thread (nullptr),
- m_ReceiversThread (nullptr), m_ReceiversThreadV6 (nullptr), m_Work (m_Service),
- m_ReceiversWork (m_ReceiversService), m_ReceiversWorkV6 (m_ReceiversServiceV6),
- m_EndpointV6 (addr, port), m_Socket (m_ReceiversService, m_Endpoint),
- m_SocketV6 (m_ReceiversServiceV6), m_IntroducersUpdateTimer (m_Service),
- m_PeerTestsCleanupTimer (m_Service), m_TerminationTimer (m_Service),
- m_TerminationTimerV6 (m_Service)
- {
- OpenSocketV6 ();
- }
-
SSUServer::SSUServer (int port):
- m_OnlyV6(false), m_IsRunning(false), m_Thread (nullptr),
+ m_IsRunning(false), m_Thread (nullptr),
m_ReceiversThread (nullptr), m_ReceiversThreadV6 (nullptr), m_Work (m_Service),
m_ReceiversWork (m_ReceiversService), m_ReceiversWorkV6 (m_ReceiversServiceV6),
m_Endpoint (boost::asio::ip::udp::v4 (), port), m_EndpointV6 (boost::asio::ip::udp::v6 (), port),
@@ -44,9 +31,6 @@ namespace transport
m_IntroducersUpdateTimer (m_Service), m_PeerTestsCleanupTimer (m_Service),
m_TerminationTimer (m_Service), m_TerminationTimerV6 (m_Service)
{
- OpenSocket ();
- if (context.SupportsV6 ())
- OpenSocketV6 ();
}
SSUServer::~SSUServer ()
@@ -91,18 +75,18 @@ namespace transport
void SSUServer::Start ()
{
m_IsRunning = true;
- if (!m_OnlyV6)
+ m_Thread = new std::thread (std::bind (&SSUServer::Run, this));
+ if (context.SupportsV4 ())
{
- m_ReceiversThread = new std::thread (std::bind (&SSUServer::RunReceivers, this));
- m_Thread = new std::thread (std::bind (&SSUServer::Run, this));
+ OpenSocket ();
+ m_ReceiversThread = new std::thread (std::bind (&SSUServer::RunReceivers, this));
m_ReceiversService.post (std::bind (&SSUServer::Receive, this));
ScheduleTermination ();
}
if (context.SupportsV6 ())
{
+ OpenSocketV6 ();
m_ReceiversThreadV6 = new std::thread (std::bind (&SSUServer::RunReceiversV6, this));
- if (!m_Thread)
- m_Thread = new std::thread (std::bind (&SSUServer::Run, this));
m_ReceiversServiceV6.post (std::bind (&SSUServer::ReceiveV6, this));
ScheduleTerminationV6 ();
}
@@ -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 relay)
{
m_Relays[tag] = relay;
@@ -442,21 +434,21 @@ namespace transport
{
auto address = router->GetSSUAddress (v4only || !context.SupportsV6 ());
if (address)
- CreateSession (router, address->host, address->port, peerTest);
+ CreateSession (router, address, peerTest);
else
LogPrint (eLogWarning, "SSU: Router ", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()), " doesn't have SSU address");
}
void SSUServer::CreateSession (std::shared_ptr router,
- const boost::asio::ip::address& addr, int port, bool peerTest)
+ std::shared_ptr address, bool peerTest)
{
- if (router)
+ if (router && address)
{
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
{
- 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));
}
}
@@ -485,13 +477,13 @@ namespace transport
}
}
- void SSUServer::CreateSessionThroughIntroducer (std::shared_ptr router, bool peerTest)
+ void SSUServer::CreateSessionThroughIntroducer (std::shared_ptr router,
+ std::shared_ptr address, bool peerTest)
{
- if (router && router->UsesIntroducer ())
- {
- auto address = router->GetSSUAddress (true); // v4 only for now
- if (address)
- {
+ if (router && router->UsesIntroducer () && address)
+ {
+ if (!address->host.is_unspecified () && address->port)
+ {
boost::asio::ip::udp::endpoint remoteEndpoint (address->host, address->port);
auto it = m_Sessions.find (remoteEndpoint);
// check if session is presented already
@@ -502,70 +494,67 @@ namespace transport
session->SendPeerTest ();
return;
}
- // create new session
- int numIntroducers = address->ssu->introducers.size ();
- if (numIntroducers > 0)
+ }
+ // create new session
+ int numIntroducers = address->ssu->introducers.size ();
+ if (numIntroducers > 0)
+ {
+ uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
+ std::shared_ptr introducerSession;
+ const i2p::data::RouterInfo::Introducer * introducer = nullptr;
+ // we might have a session to introducer already
+ for (int i = 0; i < numIntroducers; i++)
{
- uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
- std::shared_ptr introducerSession;
- const i2p::data::RouterInfo::Introducer * introducer = nullptr;
- // we might have a session to introducer already
- for (int i = 0; i < numIntroducers; i++)
+ auto intr = &(address->ssu->introducers[i]);
+ if (intr->iExp > 0 && ts > intr->iExp) continue; // skip expired introducer
+ boost::asio::ip::udp::endpoint ep (intr->iHost, intr->iPort);
+ if (ep.address ().is_v4 ()) // ipv4 only
{
- auto intr = &(address->ssu->introducers[i]);
- if (intr->iExp > 0 && ts > intr->iExp) continue; // skip expired introducer
- boost::asio::ip::udp::endpoint ep (intr->iHost, intr->iPort);
- if (ep.address ().is_v4 ()) // ipv4 only
+ if (!introducer) introducer = intr; // we pick first one for now
+ auto it = m_Sessions.find (ep);
+ if (it != m_Sessions.end ())
{
- if (!introducer) introducer = intr; // we pick first one for now
- it = m_Sessions.find (ep);
- if (it != m_Sessions.end ())
- {
- introducerSession = it->second;
- break;
- }
+ introducerSession = it->second;
+ break;
}
}
- if (!introducer)
- {
- LogPrint (eLogWarning, "SSU: Can't connect to unreachable router and no ipv4 non-expired introducers presented");
- return;
- }
+ }
+ if (!introducer)
+ {
+ LogPrint (eLogWarning, "SSU: Can't connect to unreachable router and no ipv4 non-expired introducers presented");
+ return;
+ }
- if (introducerSession) // session found
- LogPrint (eLogWarning, "SSU: Session to introducer already exists");
- else // create new
- {
- LogPrint (eLogDebug, "SSU: Creating new session to introducer ", introducer->iHost);
- boost::asio::ip::udp::endpoint introducerEndpoint (introducer->iHost, introducer->iPort);
- introducerSession = std::make_shared (*this, introducerEndpoint, router);
- m_Sessions[introducerEndpoint] = introducerSession;
- }
-#if BOOST_VERSION >= 104900
- if (!address->host.is_unspecified () && address->port)
-#endif
+ if (introducerSession) // session found
+ LogPrint (eLogWarning, "SSU: Session to introducer already exists");
+ else // create new
+ {
+ LogPrint (eLogDebug, "SSU: Creating new session to introducer ", introducer->iHost);
+ boost::asio::ip::udp::endpoint introducerEndpoint (introducer->iHost, introducer->iPort);
+ introducerSession = std::make_shared (*this, introducerEndpoint, router);
+ m_Sessions[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 (*this, remoteEndpoint, router, peerTest);
+ m_Sessions[remoteEndpoint] = session;
+
+ // introduce
+ LogPrint (eLogInfo, "SSU: Introduce new session to [", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()),
+ "] through introducer ", introducer->iHost, ":", introducer->iPort);
+ session->WaitForIntroduction ();
+ if (i2p::context.GetRouterInfo ().UsesIntroducer ()) // if we are unreachable
{
- // create session
- auto session = std::make_shared (*this, remoteEndpoint, router, peerTest);
- m_Sessions[remoteEndpoint] = session;
-
- // introduce
- LogPrint (eLogInfo, "SSU: Introduce new session to [", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()),
- "] through introducer ", introducer->iHost, ":", introducer->iPort);
- session->WaitForIntroduction ();
- if (i2p::context.GetRouterInfo ().UsesIntroducer ()) // if we are unreachable
- {
- uint8_t buf[1];
- Send (buf, 0, remoteEndpoint); // send HolePunch
- }
+ uint8_t buf[1];
+ Send (buf, 0, remoteEndpoint); // send HolePunch
}
- introducerSession->Introduce (*introducer, router);
}
- else
- LogPrint (eLogWarning, "SSU: Can't connect to unreachable router and no introducers present");
+ introducerSession->Introduce (*introducer, router);
}
else
- LogPrint (eLogWarning, "SSU: Router ", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()), " doesn't have SSU address");
+ LogPrint (eLogWarning, "SSU: Can't connect to unreachable router and no introducers present");
}
}
diff --git a/libi2pd/SSU.h b/libi2pd/SSU.h
index d20f1c9d..bfcfed4e 100644
--- a/libi2pd/SSU.h
+++ b/libi2pd/SSU.h
@@ -48,13 +48,12 @@ namespace transport
public:
SSUServer (int port);
- SSUServer (const boost::asio::ip::address & addr, int port); // ipv6 only constructor
~SSUServer ();
void Start ();
void Stop ();
void CreateSession (std::shared_ptr router, bool peerTest = false, bool v4only = false);
void CreateSession (std::shared_ptr router,
- const boost::asio::ip::address& addr, int port, bool peerTest = false);
+ std::shared_ptr address, bool peerTest = false);
void CreateDirectSession (std::shared_ptr router, boost::asio::ip::udp::endpoint remoteEndpoint, bool peerTest);
std::shared_ptr FindSession (std::shared_ptr router) const;
std::shared_ptr FindSession (const boost::asio::ip::udp::endpoint& e) const;
@@ -64,7 +63,9 @@ namespace transport
void DeleteAllSessions ();
boost::asio::io_service& GetService () { return m_Service; };
- const boost::asio::ip::udp::endpoint& GetEndpoint () const { return m_Endpoint; };
+ uint16_t GetPort () const { return m_Endpoint.port (); };
+ void SetLocalAddress (const boost::asio::ip::address& localAddress);
+
void Send (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& to);
void AddRelay (uint32_t tag, std::shared_ptr relay);
void RemoveRelay (uint32_t tag);
@@ -90,7 +91,8 @@ namespace transport
void HandleReceivedPackets (std::vector packets,
std::map >* sessions);
- void CreateSessionThroughIntroducer (std::shared_ptr router, bool peerTest = false);
+ void CreateSessionThroughIntroducer (std::shared_ptr router,
+ std::shared_ptr address, bool peerTest = false);
template
std::shared_ptr GetRandomV4Session (Filter filter);
template
@@ -118,7 +120,6 @@ namespace transport
std::shared_ptr session; // for Bob to Alice
};
- bool m_OnlyV6;
volatile bool m_IsRunning;
std::thread * m_Thread, * m_ReceiversThread, * m_ReceiversThreadV6;
boost::asio::io_service m_Service, m_ReceiversService, m_ReceiversServiceV6;
diff --git a/libi2pd/SSUSession.cpp b/libi2pd/SSUSession.cpp
index b4f2fcdb..e1e48b75 100644
--- a/libi2pd/SSUSession.cpp
+++ b/libi2pd/SSUSession.cpp
@@ -321,6 +321,7 @@ namespace transport
void SSUSession::ProcessSessionConfirmed (const uint8_t * buf, size_t len)
{
LogPrint (eLogDebug, "SSU: Session confirmed received");
+ m_ConnectTimer.cancel ();
auto headerSize = GetSSUHeaderSize (buf);
if (headerSize >= len)
{
@@ -683,6 +684,8 @@ namespace transport
buf += 2; // our port
LogPrint (eLogInfo, "SSU: Our external address is ", ourIP.to_string (), ":", ourPort);
i2p::context.UpdateAddress (ourIP);
+ if (ourPort != m_Server.GetPort ())
+ i2p::context.SetError (eRouterErrorSymmetricNAT);
uint32_t nonce = bufbe32toh (buf);
buf += 4; // nonce
auto it = m_RelayRequests.find (nonce);
diff --git a/libi2pd/Transports.cpp b/libi2pd/Transports.cpp
index 2eadda85..705754f0 100644
--- a/libi2pd/Transports.cpp
+++ b/libi2pd/Transports.cpp
@@ -189,7 +189,6 @@ namespace transport
proxytype = NTCP2Server::eHTTPProxy;
m_NTCP2Server->UseProxy(proxytype, proxyurl.host, proxyurl.port);
- m_NTCP2Server->Start();
}
else
LogPrint(eLogError, "Transports: unsupported NTCP2 proxy URL ", ntcp2proxy);
@@ -199,40 +198,91 @@ namespace transport
return;
}
else
- {
m_NTCP2Server = new NTCP2Server ();
- m_NTCP2Server->Start ();
- }
}
- // create acceptors
- auto& addresses = context.GetRouterInfo ().GetAddresses ();
- for (const auto& address : addresses)
- {
- if (!address) continue;
- if (address->transportStyle == RouterInfo::eTransportSSU)
+ // create SSU server
+ int ssuPort = 0;
+ if (enableSSU)
+ {
+ auto& addresses = context.GetRouterInfo ().GetAddresses ();
+ for (const auto& address: addresses)
{
- if (m_SSUServer == nullptr && enableSSU)
+ if (!address) continue;
+ if (address->transportStyle == RouterInfo::eTransportSSU)
{
- if (address->host.is_v4())
- m_SSUServer = new SSUServer (address->port);
- else
- m_SSUServer = new SSUServer (address->host, address->port);
- LogPrint (eLogInfo, "Transports: Start listening UDP port ", address->port);
- try {
- m_SSUServer->Start ();
- } catch ( std::exception & ex ) {
- LogPrint(eLogError, "Transports: Failed to bind to UDP port", address->port);
- delete m_SSUServer;
- m_SSUServer = nullptr;
- continue;
- }
- DetectExternalIP ();
+ ssuPort = address->port;
+ m_SSUServer = new SSUServer (address->port);
+ break;
}
- else
- LogPrint (eLogError, "Transports: SSU server already exists");
}
+ }
+
+ // bind to interfaces
+ bool ipv4; i2p::config::GetOption("ipv4", ipv4);
+ if (ipv4)
+ {
+ std::string address; i2p::config::GetOption("address4", address);
+ if (!address.empty ())
+ {
+ boost::system::error_code ec;
+ auto addr = boost::asio::ip::address::from_string (address, ec);
+ if (!ec)
+ {
+ if (m_NTCP2Server) m_NTCP2Server->SetLocalAddress (addr);
+ if (m_SSUServer) m_SSUServer->SetLocalAddress (addr);
+ }
+ }
+ }
+
+ bool ipv6; i2p::config::GetOption("ipv6", ipv6);
+ if (ipv6)
+ {
+ std::string address; i2p::config::GetOption("address6", address);
+ if (!address.empty ())
+ {
+ boost::system::error_code ec;
+ auto addr = boost::asio::ip::address::from_string (address, ec);
+ if (!ec)
+ {
+ if (m_NTCP2Server) m_NTCP2Server->SetLocalAddress (addr);
+ if (m_SSUServer) m_SSUServer->SetLocalAddress (addr);
+ }
+ }
+ }
+
+ bool ygg; i2p::config::GetOption("meshnets.yggdrasil", ygg);
+ if (ygg)
+ {
+ std::string address; i2p::config::GetOption("meshnets.yggaddress", address);
+ if (!address.empty ())
+ {
+ boost::system::error_code ec;
+ auto addr = boost::asio::ip::address::from_string (address, ec);
+ if (!ec && m_NTCP2Server && i2p::util::net::IsYggdrasilAddress (addr))
+ m_NTCP2Server->SetLocalAddress (addr);
+ }
}
+
+ // start servers
+ if (m_NTCP2Server) m_NTCP2Server->Start ();
+ if (m_SSUServer)
+ {
+ LogPrint (eLogInfo, "Transports: Start listening UDP port ", ssuPort);
+ try
+ {
+ m_SSUServer->Start ();
+ }
+ catch (std::exception& ex )
+ {
+ LogPrint(eLogError, "Transports: Failed to bind to UDP port", ssuPort);
+ m_SSUServer->Stop ();
+ delete m_SSUServer;
+ m_SSUServer = nullptr;
+ }
+ if (m_SSUServer) DetectExternalIP ();
+ }
+
m_PeerCleanupTimer->expires_from_now (boost::posix_time::seconds(5*SESSION_CREATION_TIMEOUT));
m_PeerCleanupTimer->async_wait (std::bind (&Transports::HandlePeerCleanupTimer, this, std::placeholders::_1));
@@ -351,7 +401,7 @@ namespace transport
try
{
auto r = netdb.FindRouter (ident);
- if (!r || !r->IsCompatible (i2p::context.GetRouterInfo ())) return;
+ if (!r || r->IsUnreachable () || !r->IsCompatible (i2p::context.GetRouterInfo ())) return;
{
std::unique_lock l(m_PeersMutex);
it = m_Peers.insert (std::pair(ident, { 0, r, {},
@@ -418,19 +468,10 @@ namespace transport
if (address)
{
auto s = std::make_shared (*m_NTCP2Server, peer.router, address);
-
- if(m_NTCP2Server->UsingProxy())
- {
- NTCP2Server::RemoteAddressType remote = NTCP2Server::eIP4Address;
- std::string addr = address->host.to_string();
-
- if(address->host.is_v6())
- remote = NTCP2Server::eIP6Address;
-
- m_NTCP2Server->ConnectWithProxy(addr, address->port, remote, s);
- }
+ if( m_NTCP2Server->UsingProxy())
+ m_NTCP2Server->ConnectWithProxy(s);
else
- m_NTCP2Server->Connect (address->host, address->port, s);
+ m_NTCP2Server->Connect (s);
return true;
}
}
@@ -464,7 +505,7 @@ namespace transport
}
if (address)
{
- m_SSUServer->CreateSession (peer.router, address->host, address->port);
+ m_SSUServer->CreateSession (peer.router, address);
return true;
}
}
@@ -480,7 +521,7 @@ namespace transport
if (address)
{
auto s = std::make_shared (*m_NTCP2Server, peer.router, address);
- m_NTCP2Server->Connect (address->host, address->port, s);
+ m_NTCP2Server->Connect (s);
return true;
}
}
diff --git a/libi2pd_client/BOB.cpp b/libi2pd_client/BOB.cpp
index c4447808..69c516d2 100644
--- a/libi2pd_client/BOB.cpp
+++ b/libi2pd_client/BOB.cpp
@@ -645,6 +645,22 @@ namespace client
localDestination->RequestDestinationWithEncryptedLeaseSet (addr->blindedPublicKey, requstCallback);
}
+ void BOBCommandSession::LookupLocalCommandHandler (const char * operand, size_t len)
+ {
+ LogPrint (eLogDebug, "BOB: lookup local ", operand);
+ auto addr = context.GetAddressBook ().GetAddress (operand);
+ if (!addr)
+ {
+ SendReplyError ("Address Not found");
+ return;
+ }
+ auto ls = i2p::data::netdb.FindLeaseSet (addr->identHash);
+ if (ls)
+ SendReplyOK (ls->GetIdentity ()->ToBase64 ().c_str ());
+ else
+ SendReplyError ("Local LeaseSet Not found");
+ }
+
void BOBCommandSession::ClearCommandHandler (const char * operand, size_t len)
{
LogPrint (eLogDebug, "BOB: clear");
@@ -770,6 +786,7 @@ namespace client
m_CommandHandlers[BOB_COMMAND_INPORT] = &BOBCommandSession::InportCommandHandler;
m_CommandHandlers[BOB_COMMAND_QUIET] = &BOBCommandSession::QuietCommandHandler;
m_CommandHandlers[BOB_COMMAND_LOOKUP] = &BOBCommandSession::LookupCommandHandler;
+ m_CommandHandlers[BOB_COMMAND_LOOKUP_LOCAL] = &BOBCommandSession::LookupLocalCommandHandler;
m_CommandHandlers[BOB_COMMAND_CLEAR] = &BOBCommandSession::ClearCommandHandler;
m_CommandHandlers[BOB_COMMAND_LIST] = &BOBCommandSession::ListCommandHandler;
m_CommandHandlers[BOB_COMMAND_OPTION] = &BOBCommandSession::OptionCommandHandler;
diff --git a/libi2pd_client/BOB.h b/libi2pd_client/BOB.h
index 74418011..f5f0c8ee 100644
--- a/libi2pd_client/BOB.h
+++ b/libi2pd_client/BOB.h
@@ -42,6 +42,7 @@ namespace client
const char BOB_COMMAND_INPORT[] = "inport";
const char BOB_COMMAND_QUIET[] = "quiet";
const char BOB_COMMAND_LOOKUP[] = "lookup";
+ const char BOB_COMMAND_LOOKUP_LOCAL[] = "lookuplocal";
const char BOB_COMMAND_CLEAR[] = "clear";
const char BOB_COMMAND_LIST[] = "list";
const char BOB_COMMAND_OPTION[] = "option";
@@ -206,6 +207,7 @@ namespace client
void InportCommandHandler (const char * operand, size_t len);
void QuietCommandHandler (const char * operand, size_t len);
void LookupCommandHandler (const char * operand, size_t len);
+ void LookupLocalCommandHandler (const char * operand, size_t len);
void ClearCommandHandler (const char * operand, size_t len);
void ListCommandHandler (const char * operand, size_t len);
void OptionCommandHandler (const char * operand, size_t len);
diff --git a/libi2pd_client/ClientContext.cpp b/libi2pd_client/ClientContext.cpp
index cac472f9..2f3d4d48 100644
--- a/libi2pd_client/ClientContext.cpp
+++ b/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::CryptoKeyType cryptoType = section.second.get (I2P_CLIENT_TUNNEL_CRYPTO_TYPE, i2p::data::CRYPTO_KEY_TYPE_ELGAMAL);
- std::string address = section.second.get (I2P_SERVER_TUNNEL_ADDRESS, "127.0.0.1");
+ std::string address = section.second.get (I2P_SERVER_TUNNEL_ADDRESS, "");
bool isUniqueLocal = section.second.get(I2P_SERVER_TUNNEL_ENABLE_UNIQUE_LOCAL, true);
// I2CP
@@ -718,6 +718,7 @@ namespace client
{
// udp server tunnel
// TODO: hostnames
+ if (address.empty ()) address = "127.0.0.1";
auto localAddress = boost::asio::ip::address::from_string(address);
boost::asio::ip::udp::endpoint endpoint(boost::asio::ip::address::from_string(host), port);
auto serverTunnel = std::make_shared(name, localDestination, localAddress, endpoint, port, gzip);
@@ -750,12 +751,13 @@ namespace client
else // regular server tunnel by default
serverTunnel = std::make_shared (name, host, port, localDestination, inPort, gzip);
+ if (!address.empty ())
+ serverTunnel->SetLocalAddress (address);
if(!isUniqueLocal)
{
LogPrint(eLogInfo, "Clients: disabling loopback address mapping");
serverTunnel->SetUniqueLocal(isUniqueLocal);
}
-
if (accessList.length () > 0)
{
std::set idents;
diff --git a/libi2pd_client/I2PTunnel.cpp b/libi2pd_client/I2PTunnel.cpp
index 61c42b24..b15b47b0 100644
--- a/libi2pd_client/I2PTunnel.cpp
+++ b/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 ()
{
if (Kill()) return;
@@ -600,6 +616,16 @@ namespace client
m_IsAccessList = true;
}
+ void I2PServerTunnel::SetLocalAddress (const std::string& localAddress)
+ {
+ boost::system::error_code ec;
+ auto addr = boost::asio::ip::address::from_string(localAddress, ec);
+ if (!ec)
+ m_LocalAddress.reset (new boost::asio::ip::address (addr));
+ else
+ LogPrint (eLogError, "I2PTunnel: can't set local address ", localAddress);
+ }
+
void I2PServerTunnel::Accept ()
{
if (m_PortDestination)
@@ -631,7 +657,10 @@ namespace client
// new connection
auto conn = CreateI2PConnection (stream);
AddHandler (conn);
- conn->Connect (m_IsUniqueLocal);
+ if (m_LocalAddress)
+ conn->Connect (*m_LocalAddress);
+ else
+ conn->Connect (m_IsUniqueLocal);
}
}
diff --git a/libi2pd_client/I2PTunnel.h b/libi2pd_client/I2PTunnel.h
index f7185dfd..3b52ea1a 100644
--- a/libi2pd_client/I2PTunnel.h
+++ b/libi2pd_client/I2PTunnel.h
@@ -48,7 +48,8 @@ namespace client
~I2PTunnelConnection ();
void I2PConnect (const uint8_t * msg = nullptr, size_t len = 0);
void Connect (bool isUniqueLocal = true);
-
+ void Connect (const boost::asio::ip::address& localAddress);
+
protected:
void Terminate ();
@@ -314,6 +315,8 @@ namespace client
void SetUniqueLocal (bool isUniqueLocal) { m_IsUniqueLocal = isUniqueLocal; }
bool IsUniqueLocal () const { return m_IsUniqueLocal; }
+ void SetLocalAddress (const std::string& localAddress);
+
const std::string& GetAddress() const { return m_Address; }
int GetPort () const { return m_Port; };
uint16_t GetLocalPort () const { return m_PortDestination->GetLocalPort (); };
@@ -339,6 +342,7 @@ namespace client
std::shared_ptr m_PortDestination;
std::set m_AccessList;
bool m_IsAccessList;
+ std::unique_ptr m_LocalAddress;
};
class I2PServerTunnelHTTP: public I2PServerTunnel