Browse Source

Merge branch 'master' into websocks

pull/771/head
Jeff Becker 8 years ago
parent
commit
68f3c877ee
  1. 2
      AddressBook.cpp
  2. 5
      Config.cpp
  3. 31
      Crypto.cpp
  4. 13
      Crypto.h
  5. 352
      Datagram.cpp
  6. 74
      Datagram.h
  7. 9
      Destination.cpp
  8. 35
      Event.cpp
  9. 18
      Event.h
  10. 10
      Garlic.cpp
  11. 2
      Garlic.h
  12. 17
      HTTP.cpp
  13. 5
      HTTP.h
  14. 239
      HTTPProxy.cpp
  15. 42
      HTTPServer.cpp
  16. 20
      HTTPServer.h
  17. 22
      I2PService.cpp
  18. 6
      I2PService.h
  19. 132
      I2PTunnel.cpp
  20. 20
      I2PTunnel.h
  21. 2
      Identity.h
  22. 2
      LeaseSet.cpp
  23. 8
      Makefile.mingw
  24. 13
      Makefile.osx
  25. 2
      NTCPSession.cpp
  26. 4
      NetDb.cpp
  27. 44
      RouterInfo.cpp
  28. 2
      SSUData.cpp
  29. 2
      SSUSession.cpp
  30. 2
      Transports.cpp
  31. 6
      Tunnel.cpp
  32. 10
      TunnelPool.cpp
  33. 11
      TunnelPool.h
  34. 72
      Websocket.cpp
  35. 2
      version.h

2
AddressBook.cpp

@ -842,7 +842,7 @@ namespace client
else else
memset (response + 8, 0, 32); // not found memset (response + 8, 0, 32); // not found
memset (response + 40, 0, 4); // set expiration time to zero memset (response + 40, 0, 4); // set expiration time to zero
m_LocalDestination->GetDatagramDestination ()->SendDatagramTo (response, 44, from.GetIdentHash (), toPort, fromPort); m_LocalDestination->GetDatagramDestination ()->SendDatagramTo (response, 44, from.GetIdentHash(), toPort, fromPort);
} }
void AddressResolver::AddAddress (const std::string& name, const i2p::data::IdentHash& ident) void AddressResolver::AddAddress (const std::string& name, const i2p::data::IdentHash& ident)

5
Config.cpp

@ -90,7 +90,8 @@ namespace config {
("httpproxy.inbound.quantity", value<std::string>()->default_value("5"), "HTTP proxy inbound tunnels quantity") ("httpproxy.inbound.quantity", value<std::string>()->default_value("5"), "HTTP proxy inbound tunnels quantity")
("httpproxy.outbound.quantity", value<std::string>()->default_value("5"), "HTTP proxy outbound tunnels quantity") ("httpproxy.outbound.quantity", value<std::string>()->default_value("5"), "HTTP proxy outbound tunnels quantity")
("httpproxy.latency.min", value<std::string>()->default_value("0"), "HTTP proxy min latency for tunnels") ("httpproxy.latency.min", value<std::string>()->default_value("0"), "HTTP proxy min latency for tunnels")
("httpproxy.latency.max", value<std::string>()->default_value("0"), "HTTP proxy max latency for tunnels") ("httpproxy.latency.max", value<std::string>()->default_value("0"), "HTTP proxy max latency for tunnels")
("httpproxy.outproxy", value<std::string>()->default_value(""), "HTTP proxy upstream out proxy url")
; ;
options_description socksproxy("SOCKS Proxy options"); options_description socksproxy("SOCKS Proxy options");
@ -171,7 +172,7 @@ namespace config {
"https://i2p.mooo.com/netDb/," "https://i2p.mooo.com/netDb/,"
"https://netdb.i2p2.no/," "https://netdb.i2p2.no/,"
"https://us.reseed.i2p2.no:444/," "https://us.reseed.i2p2.no:444/,"
"https://uk.reseed.i2p2.no:444/," // "https://uk.reseed.i2p2.no:444/," // mamoth's shit
"https://i2p-0.manas.ca:8443/," "https://i2p-0.manas.ca:8443/,"
"https://reseed.i2p.vzaws.com:8443/," "https://reseed.i2p.vzaws.com:8443/,"
"https://download.xxlspeed.com/," "https://download.xxlspeed.com/,"

31
Crypto.cpp

@ -386,9 +386,11 @@ namespace crypto
// HMAC // HMAC
const uint64_t IPAD = 0x3636363636363636; const uint64_t IPAD = 0x3636363636363636;
const uint64_t OPAD = 0x5C5C5C5C5C5C5C5C; const uint64_t OPAD = 0x5C5C5C5C5C5C5C5C;
#if defined(__AVX__)
static const uint64_t ipads[] = { IPAD, IPAD, IPAD, IPAD }; static const uint64_t ipads[] = { IPAD, IPAD, IPAD, IPAD };
static const uint64_t opads[] = { OPAD, OPAD, OPAD, OPAD }; static const uint64_t opads[] = { OPAD, OPAD, OPAD, OPAD };
#endif
void HMACMD5Digest (uint8_t * msg, size_t len, const MACKey& key, uint8_t * digest) void HMACMD5Digest (uint8_t * msg, size_t len, const MACKey& key, uint8_t * digest)
// key is 32 bytes // key is 32 bytes
@ -402,18 +404,19 @@ namespace crypto
( (
"vmovups %[key], %%ymm0 \n" "vmovups %[key], %%ymm0 \n"
"vmovups %[ipad], %%ymm1 \n" "vmovups %[ipad], %%ymm1 \n"
"vmovups %%ymm1, 32%[buf] \n" "vmovups %%ymm1, 32(%[buf]) \n"
"vxorps %%ymm0, %%ymm1, %%ymm1 \n" "vxorps %%ymm0, %%ymm1, %%ymm1 \n"
"vmovups %%ymm1, %[buf] \n" "vmovups %%ymm1, (%[buf]) \n"
"vmovups %[opad], %%ymm1 \n" "vmovups %[opad], %%ymm1 \n"
"vmovups %%ymm1, 32%[hash] \n" "vmovups %%ymm1, 32(%[hash]) \n"
"vxorps %%ymm0, %%ymm1, %%ymm1 \n" "vxorps %%ymm0, %%ymm1, %%ymm1 \n"
"vmovups %%ymm1, %[hash] \n" "vmovups %%ymm1, (%[hash]) \n"
"vzeroall \n" // end of AVX "vzeroall \n" // end of AVX
"movups %%xmm0, 80%[hash] \n" // zero last 16 bytes "movups %%xmm0, 80(%[hash]) \n" // zero last 16 bytes
: [buf]"=m"(*buf), [hash]"=m"(*hash) :
: [key]"m"(*(const uint8_t *)key), [ipad]"m"(*ipads), [opad]"m"(*opads) : [key]"m"(*(const uint8_t *)key), [ipad]"m"(*ipads), [opad]"m"(*opads),
: "memory" [buf]"r"(buf), [hash]"r"(hash)
: "memory", "%xmm0" // TODO: change to %ymm0 later
); );
#else #else
// ikeypad // ikeypad
@ -421,13 +424,19 @@ namespace crypto
buf[1] = key.GetLL ()[1] ^ IPAD; buf[1] = key.GetLL ()[1] ^ IPAD;
buf[2] = key.GetLL ()[2] ^ IPAD; buf[2] = key.GetLL ()[2] ^ IPAD;
buf[3] = key.GetLL ()[3] ^ IPAD; buf[3] = key.GetLL ()[3] ^ IPAD;
memcpy (buf + 4, ipads, 32); buf[4] = IPAD;
buf[5] = IPAD;
buf[6] = IPAD;
buf[7] = IPAD;
// okeypad // okeypad
hash[0] = key.GetLL ()[0] ^ OPAD; hash[0] = key.GetLL ()[0] ^ OPAD;
hash[1] = key.GetLL ()[1] ^ OPAD; hash[1] = key.GetLL ()[1] ^ OPAD;
hash[2] = key.GetLL ()[2] ^ OPAD; hash[2] = key.GetLL ()[2] ^ OPAD;
hash[3] = key.GetLL ()[3] ^ OPAD; hash[3] = key.GetLL ()[3] ^ OPAD;
memcpy (hash + 4, opads, 32); hash[4] = OPAD;
hash[5] = OPAD;
hash[6] = OPAD;
hash[7] = OPAD;
// fill last 16 bytes with zeros (first hash size assumed 32 bytes in I2P) // fill last 16 bytes with zeros (first hash size assumed 32 bytes in I2P)
memset (hash + 10, 0, 16); memset (hash + 10, 0, 16);
#endif #endif

13
Crypto.h

@ -76,7 +76,18 @@ namespace crypto
void operator^=(const ChipherBlock& other) // XOR void operator^=(const ChipherBlock& other) // XOR
{ {
#if defined(__x86_64__) || defined(__SSE__) // for Intel x84 or with SSE #if defined(__AVX__) // AVX
__asm__
(
"vmovups (%[buf]), %%xmm0 \n"
"vmovups (%[other]), %%xmm1 \n"
"vxorps %%xmm0, %%xmm1, %%xmm0 \n"
"vmovups %%xmm0, (%[buf]) \n"
:
: [buf]"r"(buf), [other]"r"(other.buf)
: "%xmm0", "%xmm1", "memory"
);
#elif defined(__SSE__) // SSE
__asm__ __asm__
( (
"movups (%[buf]), %%xmm0 \n" "movups (%[buf]), %%xmm0 \n"

352
Datagram.cpp

@ -22,9 +22,9 @@ namespace datagram
{ {
m_Sessions.clear(); m_Sessions.clear();
} }
void DatagramDestination::SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint16_t fromPort, uint16_t toPort) void DatagramDestination::SendDatagramTo(const uint8_t * payload, size_t len, const i2p::data::IdentHash & identity, uint16_t fromPort, uint16_t toPort)
{ {
auto owner = m_Owner; auto owner = m_Owner;
std::vector<uint8_t> v(MAX_DATAGRAM_SIZE); std::vector<uint8_t> v(MAX_DATAGRAM_SIZE);
uint8_t * buf = v.data(); uint8_t * buf = v.data();
@ -45,8 +45,7 @@ namespace datagram
owner->Sign (buf1, len, signature); owner->Sign (buf1, len, signature);
auto msg = CreateDataMessage (buf, len + headerLen, fromPort, toPort); auto msg = CreateDataMessage (buf, len + headerLen, fromPort, toPort);
auto session = ObtainSession(ident); ObtainSession(identity)->SendMsg(msg);
session->SendMsg(msg);
} }
@ -69,6 +68,8 @@ namespace datagram
if (verified) if (verified)
{ {
auto h = identity.GetIdentHash();
ObtainSession(h)->Ack();
auto r = FindReceiver(toPort); auto r = FindReceiver(toPort);
if(r) if(r)
r(identity, fromPort, toPort, buf + headerLen, len -headerLen); r(identity, fromPort, toPort, buf + headerLen, len -headerLen);
@ -138,15 +139,15 @@ namespace datagram
} }
} }
std::shared_ptr<DatagramSession> DatagramDestination::ObtainSession(const i2p::data::IdentHash & ident) std::shared_ptr<DatagramSession> DatagramDestination::ObtainSession(const i2p::data::IdentHash & identity)
{ {
std::shared_ptr<DatagramSession> session = nullptr; std::shared_ptr<DatagramSession> session = nullptr;
std::lock_guard<std::mutex> lock(m_SessionsMutex); std::lock_guard<std::mutex> lock(m_SessionsMutex);
auto itr = m_Sessions.find(ident); auto itr = m_Sessions.find(identity);
if (itr == m_Sessions.end()) { if (itr == m_Sessions.end()) {
// not found, create new session // not found, create new session
session = std::make_shared<DatagramSession>(m_Owner, ident); session = std::make_shared<DatagramSession>(m_Owner, identity);
m_Sessions[ident] = session; m_Sessions[identity] = session;
} else { } else {
session = itr->second; session = itr->second;
} }
@ -164,13 +165,13 @@ namespace datagram
} }
DatagramSession::DatagramSession(i2p::client::ClientDestination * localDestination, DatagramSession::DatagramSession(i2p::client::ClientDestination * localDestination,
const i2p::data::IdentHash & remoteIdent) : const i2p::data::IdentHash & remoteIdent) :
m_LocalDestination(localDestination), m_LocalDestination(localDestination),
m_RemoteIdentity(remoteIdent), m_RemoteIdent(remoteIdent),
m_LastUse(i2p::util::GetMillisecondsSinceEpoch ()), m_SendQueueTimer(localDestination->GetService())
m_LastPathChange(0),
m_LastSuccess(0)
{ {
m_LastUse = i2p::util::GetMillisecondsSinceEpoch ();
ScheduleFlushSendQueue();
} }
void DatagramSession::SendMsg(std::shared_ptr<I2NPMessage> msg) void DatagramSession::SendMsg(std::shared_ptr<I2NPMessage> msg)
@ -184,262 +185,149 @@ namespace datagram
DatagramSession::Info DatagramSession::GetSessionInfo() const DatagramSession::Info DatagramSession::GetSessionInfo() const
{ {
if(!m_RoutingSession) if(!m_RoutingSession)
return DatagramSession::Info(nullptr, nullptr, m_LastUse, m_LastSuccess); return DatagramSession::Info(nullptr, nullptr, m_LastUse);
auto routingPath = m_RoutingSession->GetSharedRoutingPath(); auto routingPath = m_RoutingSession->GetSharedRoutingPath();
if (!routingPath) if (!routingPath)
return DatagramSession::Info(nullptr, nullptr, m_LastUse, m_LastSuccess); return DatagramSession::Info(nullptr, nullptr, m_LastUse);
auto lease = routingPath->remoteLease; auto lease = routingPath->remoteLease;
auto tunnel = routingPath->outboundTunnel; auto tunnel = routingPath->outboundTunnel;
if(lease) if(lease)
{ {
if(tunnel) if(tunnel)
return DatagramSession::Info(lease->tunnelGateway, tunnel->GetEndpointIdentHash(), m_LastUse, m_LastSuccess); return DatagramSession::Info(lease->tunnelGateway, tunnel->GetEndpointIdentHash(), m_LastUse);
else else
return DatagramSession::Info(lease->tunnelGateway, nullptr, m_LastUse, m_LastSuccess); return DatagramSession::Info(lease->tunnelGateway, nullptr, m_LastUse);
} }
else if(tunnel) else if(tunnel)
return DatagramSession::Info(nullptr, tunnel->GetEndpointIdentHash(), m_LastUse, m_LastSuccess); return DatagramSession::Info(nullptr, tunnel->GetEndpointIdentHash(), m_LastUse);
else else
return DatagramSession::Info(nullptr, nullptr, m_LastUse, m_LastSuccess); return DatagramSession::Info(nullptr, nullptr, m_LastUse);
}
void DatagramSession::HandleSend(std::shared_ptr<I2NPMessage> msg)
{
if(!m_RoutingSession)
{
// try to get one
if(m_RemoteLeaseSet) m_RoutingSession = m_LocalDestination->GetRoutingSession(m_RemoteLeaseSet, true);
else
{
UpdateLeaseSet(msg);
return;
}
}
// do we have a routing session?
if(m_RoutingSession)
{
// should we switch paths?
if(ShouldUpdateRoutingPath ())
{
LogPrint(eLogDebug, "DatagramSession: try getting new routing path");
// try switching paths
auto path = GetNextRoutingPath();
if(path)
UpdateRoutingPath (path);
else
ResetRoutingPath();
}
auto routingPath = m_RoutingSession->GetSharedRoutingPath ();
// make sure we have a routing path
if (routingPath)
{
auto outboundTunnel = routingPath->outboundTunnel;
if (outboundTunnel)
{
if(outboundTunnel->IsEstablished())
{
m_LastSuccess = i2p::util::GetMillisecondsSinceEpoch ();
// we have a routing path and routing session and the outbound tunnel we are using is good
// wrap message with routing session and send down routing path's outbound tunnel wrapped for the IBGW
auto m = m_RoutingSession->WrapSingleMessage(msg);
routingPath->outboundTunnel->SendTunnelDataMsg({i2p::tunnel::TunnelMessageBlock{
i2p::tunnel::eDeliveryTypeTunnel,
routingPath->remoteLease->tunnelGateway, routingPath->remoteLease->tunnelID,
m
}});
return;
}
}
}
}
auto now = i2p::util::GetMillisecondsSinceEpoch ();
// if this path looks dead reset the routing path since we didn't seem to be able to get a path in time
if (m_LastPathChange && now - m_LastPathChange >= DATAGRAM_SESSION_PATH_TIMEOUT ) ResetRoutingPath();
UpdateLeaseSet(msg);
}
void DatagramSession::UpdateRoutingPath(const std::shared_ptr<i2p::garlic::GarlicRoutingPath> & path)
{
if(m_RoutingSession == nullptr && m_RemoteLeaseSet)
m_RoutingSession = m_LocalDestination->GetRoutingSession(m_RemoteLeaseSet, true);
if(!m_RoutingSession) return;
// set routing path and update time we last updated the routing path
m_RoutingSession->SetSharedRoutingPath (path);
m_LastPathChange = i2p::util::GetMillisecondsSinceEpoch ();
} }
bool DatagramSession::ShouldUpdateRoutingPath() const void DatagramSession::Ack()
{ {
bool dead = m_RoutingSession == nullptr || m_RoutingSession->GetSharedRoutingPath () == nullptr; m_LastUse = i2p::util::GetMillisecondsSinceEpoch();
auto now = i2p::util::GetMillisecondsSinceEpoch (); auto path = GetSharedRoutingPath();
// we need to rotate paths becuase the routing path is too old if(path)
// if (now - m_LastPathChange >= DATAGRAM_SESSION_PATH_SWITCH_INTERVAL) return true; path->updateTime = i2p::util::GetSecondsSinceEpoch ();
// too fast switching paths
if (now - m_LastPathChange < DATAGRAM_SESSION_PATH_MIN_LIFETIME ) return false;
// our path looks dead so we need to rotate paths
if (now - m_LastSuccess >= DATAGRAM_SESSION_PATH_TIMEOUT) return !dead;
// if we have a routing session and routing path we don't need to switch paths
return dead;
} }
std::shared_ptr<i2p::garlic::GarlicRoutingPath> DatagramSession::GetSharedRoutingPath ()
bool DatagramSession::ShouldSwitchLease() const
{
std::shared_ptr<i2p::garlic::GarlicRoutingPath> routingPath = nullptr;
std::shared_ptr<const i2p::data::Lease> currentLease = nullptr;
if(m_RoutingSession)
routingPath = m_RoutingSession->GetSharedRoutingPath ();
if(routingPath)
currentLease = routingPath->remoteLease;
if(currentLease) // if we have a lease return true if it's about to expire otherwise return false
return currentLease->ExpiresWithin( DATAGRAM_SESSION_LEASE_HANDOVER_WINDOW, DATAGRAM_SESSION_LEASE_HANDOVER_FUDGE );
// we have no current lease, we should switch
return currentLease == nullptr;
}
std::shared_ptr<i2p::garlic::GarlicRoutingPath> DatagramSession::GetNextRoutingPath()
{ {
std::shared_ptr<i2p::tunnel::OutboundTunnel> outboundTunnel = nullptr; if(!m_RoutingSession) {
std::shared_ptr<i2p::garlic::GarlicRoutingPath> routingPath = nullptr; if(!m_RemoteLeaseSet) {
// get existing routing path if we have one m_RemoteLeaseSet = m_LocalDestination->FindLeaseSet(m_RemoteIdent);
if(m_RoutingSession)
routingPath = m_RoutingSession->GetSharedRoutingPath();
// do we have an existing outbound tunnel and routing path?
if(routingPath && routingPath->outboundTunnel)
{
// is the outbound tunnel we are using good?
if (routingPath->outboundTunnel->IsEstablished())
{
// ya so let's stick with it
outboundTunnel = routingPath->outboundTunnel;
} }
else if(!m_RemoteLeaseSet) {
outboundTunnel = m_LocalDestination->GetTunnelPool()->GetNextOutboundTunnel(routingPath->outboundTunnel); // no so we'll switch outbound tunnels // no remote lease set
m_LocalDestination->RequestDestination(m_RemoteIdent, std::bind(&DatagramSession::HandleLeaseSetUpdated, this, std::placeholders::_1));
return nullptr;
}
m_RoutingSession = m_LocalDestination->GetRoutingSession(m_RemoteLeaseSet, true);
} }
// do we have an outbound tunnel that works already ? auto path = m_RoutingSession->GetSharedRoutingPath();
if(!outboundTunnel) if(path) {
outboundTunnel = m_LocalDestination->GetTunnelPool()->GetNextOutboundTunnel(); // no, let's get a new outbound tunnel as we probably just started if (m_CurrentOutboundTunnel && !m_CurrentOutboundTunnel->IsEstablished()) {
// bad outbound tunnel, switch outbound tunnel
if(outboundTunnel) m_CurrentOutboundTunnel = m_LocalDestination->GetTunnelPool()->GetNextOutboundTunnel(m_CurrentOutboundTunnel);
{ path->outboundTunnel = m_CurrentOutboundTunnel;
std::shared_ptr<const i2p::data::Lease> lease = nullptr;
// should we switch leases ?
if (ShouldSwitchLease ())
{
// yes, get next available lease
lease = GetNextLease();
} }
else if (routingPath) if(m_CurrentRemoteLease && m_CurrentRemoteLease->ExpiresWithin(DATAGRAM_SESSION_LEASE_HANDOVER_WINDOW)) {
{ // bad lease, switch to next one
if(routingPath->remoteLease) if(m_RemoteLeaseSet) {
{ auto ls = m_RemoteLeaseSet->GetNonExpiredLeasesExcluding([&](const i2p::data::Lease& l) -> bool {
if(routingPath->remoteLease->ExpiresWithin(DATAGRAM_SESSION_LEASE_HANDOVER_WINDOW, DATAGRAM_SESSION_LEASE_HANDOVER_FUDGE)) return l.tunnelGateway == m_CurrentRemoteLease->tunnelGateway;
lease = GetNextLease(); });
else auto sz = ls.size();
lease = routingPath->remoteLease; if (sz) {
auto idx = rand() % sz;
m_CurrentRemoteLease = ls[idx];
}
} else {
// no remote lease set?
LogPrint(eLogWarning, "DatagramSession: no cached remote lease set for ", m_RemoteIdent.ToBase32());
} }
path->remoteLease = m_CurrentRemoteLease;
} }
else } else {
lease = GetNextLease(); // no current path, make one
if(lease) path = std::make_shared<i2p::garlic::GarlicRoutingPath>();
{ // switch outbound tunnel if bad
// we have a valid lease to use and an outbound tunnel if(m_CurrentOutboundTunnel == nullptr || ! m_CurrentOutboundTunnel->IsEstablished()) {
// create new routing path m_CurrentOutboundTunnel = m_LocalDestination->GetTunnelPool()->GetNextOutboundTunnel(m_CurrentOutboundTunnel);
uint32_t now = i2p::util::GetSecondsSinceEpoch(); }
routingPath = std::make_shared<i2p::garlic::GarlicRoutingPath>(i2p::garlic::GarlicRoutingPath{ // switch lease if bad
outboundTunnel, if(m_CurrentRemoteLease == nullptr || m_CurrentRemoteLease->ExpiresWithin(DATAGRAM_SESSION_LEASE_HANDOVER_WINDOW)) {
lease, if(!m_RemoteLeaseSet) {
0, m_RemoteLeaseSet = m_LocalDestination->FindLeaseSet(m_RemoteIdent);
now, }
0 if(m_RemoteLeaseSet) {
}); // pick random next good lease
auto ls = m_RemoteLeaseSet->GetNonExpiredLeasesExcluding([&] (const i2p::data::Lease & l) -> bool {
if(m_CurrentRemoteLease)
return l.tunnelGateway == m_CurrentRemoteLease->tunnelGateway;
return false;
});
auto sz = ls.size();
if(sz) {
auto idx = rand() % sz;
m_CurrentRemoteLease = ls[idx];
}
} else {
// no remote lease set currently, bail
LogPrint(eLogWarning, "DatagramSession: no remote lease set found for ", m_RemoteIdent.ToBase32());
return nullptr;
}
} }
else // we don't have a new routing path to give path->outboundTunnel = m_CurrentOutboundTunnel;
routingPath = nullptr; path->remoteLease = m_CurrentRemoteLease;
m_RoutingSession->SetSharedRoutingPath(path);
} }
return routingPath; return path;
} }
void DatagramSession::ResetRoutingPath() void DatagramSession::HandleLeaseSetUpdated(std::shared_ptr<i2p::data::LeaseSet> ls)
{ {
if(m_RoutingSession) if(!ls) return;
{ // only update lease set if found and newer than previous lease set
auto routingPath = m_RoutingSession->GetSharedRoutingPath(); uint64_t oldExpire = 0;
if(routingPath && routingPath->remoteLease) // we have a remote lease already specified and a routing path if(m_RemoteLeaseSet) oldExpire = m_RemoteLeaseSet->GetExpirationTime();
{ if(ls && ls->GetExpirationTime() > oldExpire) m_RemoteLeaseSet = ls;
// get outbound tunnel on this path
auto outboundTunnel = routingPath->outboundTunnel;
// is this outbound tunnel there and established
if (outboundTunnel && outboundTunnel->IsEstablished())
m_InvalidIBGW.push_back(routingPath->remoteLease->tunnelGateway); // yes, let's mark remote lease as dead because the outbound tunnel seems fine
}
// reset the routing path
UpdateRoutingPath(nullptr);
}
} }
std::shared_ptr<const i2p::data::Lease> DatagramSession::GetNextLease() void DatagramSession::HandleSend(std::shared_ptr<I2NPMessage> msg)
{ {
auto now = i2p::util::GetMillisecondsSinceEpoch (); m_SendQueue.push_back(msg);
std::shared_ptr<const i2p::data::Lease> next = nullptr; // flush queue right away if full
if(m_RemoteLeaseSet) if(m_SendQueue.size() >= DATAGRAM_SEND_QUEUE_MAX_SIZE) FlushSendQueue();
}
void DatagramSession::FlushSendQueue ()
{
std::vector<i2p::tunnel::TunnelMessageBlock> send;
auto routingPath = GetSharedRoutingPath();
// if we don't have a routing path we will drop all queued messages
if(routingPath)
{ {
std::vector<i2p::data::IdentHash> exclude; for (const auto & msg : m_SendQueue)
for(const auto & ident : m_InvalidIBGW)
exclude.push_back(ident);
// find get all leases that are not in our ban list and are not going to expire within our lease set handover window + fudge
auto leases = m_RemoteLeaseSet->GetNonExpiredLeasesExcluding( [&exclude, now] (const i2p::data::Lease & l) -> bool {
if(exclude.size())
{
auto end = std::end(exclude);
return std::find_if(exclude.begin(), end, [l, now] ( const i2p::data::IdentHash & ident) -> bool {
return ident == l.tunnelGateway;
}) != end;
}
else
return false;
});
if(leases.size())
{ {
// pick random valid next lease auto m = m_RoutingSession->WrapSingleMessage(msg);
uint32_t idx = rand() % leases.size(); send.push_back(i2p::tunnel::TunnelMessageBlock{i2p::tunnel::eDeliveryTypeTunnel,routingPath->remoteLease->tunnelGateway, routingPath->remoteLease->tunnelID, m});
next = leases[idx];
} }
else routingPath->outboundTunnel->SendTunnelDataMsg(send);
LogPrint(eLogWarning, "DatagramDestination: no leases to use");
} }
return next; m_SendQueue.clear();
} ScheduleFlushSendQueue();
void DatagramSession::UpdateLeaseSet(std::shared_ptr<I2NPMessage> msg)
{
LogPrint(eLogInfo, "DatagramSession: updating lease set");
m_LocalDestination->RequestDestination(m_RemoteIdentity, std::bind(&DatagramSession::HandleGotLeaseSet, this, std::placeholders::_1, msg));
} }
void DatagramSession::HandleGotLeaseSet(std::shared_ptr<const i2p::data::LeaseSet> remoteIdent, std::shared_ptr<I2NPMessage> msg) void DatagramSession::ScheduleFlushSendQueue()
{ {
if(remoteIdent) boost::posix_time::milliseconds dlt(100);
{ m_SendQueueTimer.expires_from_now(dlt);
// update routing session m_SendQueueTimer.async_wait([&](const boost::system::error_code & ec) { if(ec) return; FlushSendQueue(); });
if(m_RoutingSession)
m_RoutingSession = nullptr;
m_RoutingSession = m_LocalDestination->GetRoutingSession(remoteIdent, true);
// clear invalid IBGW as we have a new lease set
m_InvalidIBGW.clear();
m_RemoteLeaseSet = remoteIdent;
// update routing path
auto path = GetNextRoutingPath();
if (path)
UpdateRoutingPath(path);
else
ResetRoutingPath();
// send the message that was queued if it was provided
if(msg)
HandleSend(msg);
}
} }
} }
} }

74
Datagram.h

@ -31,29 +31,33 @@ namespace datagram
const uint64_t DATAGRAM_SESSION_LEASE_HANDOVER_FUDGE = 1000; const uint64_t DATAGRAM_SESSION_LEASE_HANDOVER_FUDGE = 1000;
// milliseconds minimum time between path switches // milliseconds minimum time between path switches
const uint64_t DATAGRAM_SESSION_PATH_MIN_LIFETIME = 5 * 1000; const uint64_t DATAGRAM_SESSION_PATH_MIN_LIFETIME = 5 * 1000;
// max 64 messages buffered in send queue for each datagram session
const size_t DATAGRAM_SEND_QUEUE_MAX_SIZE = 64;
class DatagramSession class DatagramSession
{ {
public: public:
DatagramSession(i2p::client::ClientDestination * localDestination, DatagramSession(i2p::client::ClientDestination * localDestination,
const i2p::data::IdentHash & remoteIdent); const i2p::data::IdentHash & remoteIdent);
/** @brief ack the garlic routing path */
void Ack();
/** send an i2np message to remote endpoint for this session */ /** send an i2np message to remote endpoint for this session */
void SendMsg(std::shared_ptr<I2NPMessage> msg); void SendMsg(std::shared_ptr<I2NPMessage> msg);
/** get the last time in milliseconds for when we used this datagram session */ /** get the last time in milliseconds for when we used this datagram session */
uint64_t LastActivity() const { return m_LastUse; } uint64_t LastActivity() const { return m_LastUse; }
/** get the last time in milliseconds when we successfully sent data */
uint64_t LastSuccess() const { return m_LastSuccess; }
struct Info struct Info
{ {
std::shared_ptr<const i2p::data::IdentHash> IBGW; std::shared_ptr<const i2p::data::IdentHash> IBGW;
std::shared_ptr<const i2p::data::IdentHash> OBEP; std::shared_ptr<const i2p::data::IdentHash> OBEP;
const uint64_t activity; const uint64_t activity;
const uint64_t success;
Info() : IBGW(nullptr), OBEP(nullptr), activity(0), success(0) {} Info() : IBGW(nullptr), OBEP(nullptr), activity(0) {}
Info(const uint8_t * ibgw, const uint8_t * obep, const uint64_t a, const uint64_t s) : Info(const uint8_t * ibgw, const uint8_t * obep, const uint64_t a) :
activity(a), activity(a) {
success(s) {
if(ibgw) IBGW = std::make_shared<i2p::data::IdentHash>(ibgw); if(ibgw) IBGW = std::make_shared<i2p::data::IdentHash>(ibgw);
else IBGW = nullptr; else IBGW = nullptr;
if(obep) OBEP = std::make_shared<i2p::data::IdentHash>(obep); if(obep) OBEP = std::make_shared<i2p::data::IdentHash>(obep);
@ -63,44 +67,28 @@ namespace datagram
Info GetSessionInfo() const; Info GetSessionInfo() const;
private: private:
/** update our routing path we are using, mark that we have changed paths */ void FlushSendQueue();
void UpdateRoutingPath(const std::shared_ptr<i2p::garlic::GarlicRoutingPath> & path); void ScheduleFlushSendQueue();
/** return true if we should switch routing paths because of path lifetime or timeout otherwise false */ void HandleSend(std::shared_ptr<I2NPMessage> msg);
bool ShouldUpdateRoutingPath() const;
std::shared_ptr<i2p::garlic::GarlicRoutingPath> GetSharedRoutingPath();
void HandleLeaseSetUpdated(std::shared_ptr<i2p::data::LeaseSet> ls);
/** return true if we should switch the lease for out routing path otherwise return false */
bool ShouldSwitchLease() const;
/** get next usable routing path, try reusing outbound tunnels */
std::shared_ptr<i2p::garlic::GarlicRoutingPath> GetNextRoutingPath();
/**
* mark current routing path as invalid and clear it
* if the outbound tunnel we were using was okay don't use the IBGW in the routing path's lease next time
*/
void ResetRoutingPath();
/** get next usable lease, does not fetch or update if expired or have no lease set */
std::shared_ptr<const i2p::data::Lease> GetNextLease();
void HandleSend(std::shared_ptr<I2NPMessage> msg);
void HandleGotLeaseSet(std::shared_ptr<const i2p::data::LeaseSet> remoteIdent,
std::shared_ptr<I2NPMessage> msg);
void UpdateLeaseSet(std::shared_ptr<I2NPMessage> msg=nullptr);
private: private:
i2p::client::ClientDestination * m_LocalDestination; i2p::client::ClientDestination * m_LocalDestination;
i2p::data::IdentHash m_RemoteIdentity; i2p::data::IdentHash m_RemoteIdent;
std::shared_ptr<i2p::garlic::GarlicRoutingSession> m_RoutingSession; std::shared_ptr<const i2p::data::LeaseSet> m_RemoteLeaseSet;
// Ident hash of IBGW that are invalid std::shared_ptr<i2p::garlic::GarlicRoutingSession> m_RoutingSession;
std::vector<i2p::data::IdentHash> m_InvalidIBGW; std::shared_ptr<const i2p::data::Lease> m_CurrentRemoteLease;
std::shared_ptr<const i2p::data::LeaseSet> m_RemoteLeaseSet; std::shared_ptr<i2p::tunnel::OutboundTunnel> m_CurrentOutboundTunnel;
uint64_t m_LastUse; boost::asio::deadline_timer m_SendQueueTimer;
uint64_t m_LastPathChange; std::vector<std::shared_ptr<I2NPMessage> > m_SendQueue;
uint64_t m_LastSuccess; uint64_t m_LastUse;
}; };
const size_t MAX_DATAGRAM_SIZE = 32768; const size_t MAX_DATAGRAM_SIZE = 32768;
@ -112,9 +100,9 @@ namespace datagram
DatagramDestination (std::shared_ptr<i2p::client::ClientDestination> owner); DatagramDestination (std::shared_ptr<i2p::client::ClientDestination> owner);
~DatagramDestination (); ~DatagramDestination ();
void SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint16_t fromPort = 0, uint16_t toPort = 0); void SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash & ident, uint16_t fromPort = 0, uint16_t toPort = 0);
void HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); void HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
void SetReceiver (const Receiver& receiver) { m_Receiver = receiver; }; void SetReceiver (const Receiver& receiver) { m_Receiver = receiver; };
@ -130,7 +118,7 @@ namespace datagram
private: private:
std::shared_ptr<DatagramSession> ObtainSession(const i2p::data::IdentHash & ident); std::shared_ptr<DatagramSession> ObtainSession(const i2p::data::IdentHash & ident);
std::shared_ptr<I2NPMessage> CreateDataMessage (const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort); std::shared_ptr<I2NPMessage> CreateDataMessage (const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort);

9
Destination.cpp

@ -544,11 +544,14 @@ namespace client
else // duplicate else // duplicate
{ {
LogPrint (eLogInfo, "Destination: Request of LeaseSet ", dest.ToBase64 (), " is pending already"); LogPrint (eLogInfo, "Destination: Request of LeaseSet ", dest.ToBase64 (), " is pending already");
// TODO: implement it properly
//ret.first->second->requestComplete.push_back (requestComplete);
if (ts > ret.first->second->requestTime + MAX_LEASESET_REQUEST_TIMEOUT) if (ts > ret.first->second->requestTime + MAX_LEASESET_REQUEST_TIMEOUT)
{
// something went wrong
m_LeaseSetRequests.erase (ret.first); m_LeaseSetRequests.erase (ret.first);
if (requestComplete) requestComplete (nullptr); if (requestComplete) requestComplete (nullptr);
}
else if (requestComplete)
ret.first->second->requestComplete.push_back (requestComplete);
} }
} }
else else

35
Event.cpp

@ -17,15 +17,44 @@ namespace i2p
void EventCore::QueueEvent(const EventType & ev) void EventCore::QueueEvent(const EventType & ev)
{ {
if(m_listener) if(m_listener) m_listener->HandleEvent(ev);
m_listener->HandleEvent(ev); }
void EventCore::CollectEvent(const std::string & type, const std::string & ident, uint64_t val)
{
std::unique_lock<std::mutex> lock(m_collect_mutex);
std::string key = type + "." + ident;
if (m_collected.find(key) == m_collected.end())
{
m_collected[key] = {type, key, 0};
}
m_collected[key].Val += val;
}
void EventCore::PumpCollected(EventListener * listener)
{
std::unique_lock<std::mutex> lock(m_collect_mutex);
if(listener)
{
for(const auto & ev : m_collected) {
listener->HandlePumpEvent({{"type", ev.second.Key}, {"ident", ev.second.Ident}}, ev.second.Val);
}
}
m_collected.clear();
} }
} }
} }
void EmitEvent(const EventType & e) void QueueIntEvent(const std::string & type, const std::string & ident, uint64_t val)
{ {
#ifdef WITH_EVENTS #ifdef WITH_EVENTS
i2p::event::core.CollectEvent(type, ident, val);
#endif
}
void EmitEvent(const EventType & e)
{
#if WITH_EVENTS
i2p::event::core.QueueEvent(e); i2p::event::core.QueueEvent(e);
#endif #endif
} }

18
Event.h

@ -3,6 +3,8 @@
#include <map> #include <map>
#include <string> #include <string>
#include <memory> #include <memory>
#include <mutex>
#include <tuple>
#include <boost/asio.hpp> #include <boost/asio.hpp>
@ -16,15 +18,27 @@ namespace i2p
public: public:
virtual ~EventListener() {}; virtual ~EventListener() {};
virtual void HandleEvent(const EventType & ev) = 0; virtual void HandleEvent(const EventType & ev) = 0;
/** @brief handle collected event when pumped */
virtual void HandlePumpEvent(const EventType & ev, const uint64_t & val) = 0;
}; };
class EventCore class EventCore
{ {
public: public:
void QueueEvent(const EventType & ev); void QueueEvent(const EventType & ev);
void CollectEvent(const std::string & type, const std::string & ident, uint64_t val);
void SetListener(EventListener * l); void SetListener(EventListener * l);
void PumpCollected(EventListener * l);
private: private:
std::mutex m_collect_mutex;
struct CollectedEvent
{
std::string Key;
std::string Ident;
uint64_t Val;
};
std::map<std::string, CollectedEvent> m_collected;
EventListener * m_listener = nullptr; EventListener * m_listener = nullptr;
}; };
#ifdef WITH_EVENTS #ifdef WITH_EVENTS
@ -32,6 +46,8 @@ namespace i2p
#endif #endif
} }
} }
void QueueIntEvent(const std::string & type, const std::string & ident, uint64_t val);
void EmitEvent(const EventType & ev); void EmitEvent(const EventType & ev);
#endif #endif

10
Garlic.cpp

@ -20,8 +20,7 @@ namespace garlic
std::shared_ptr<const i2p::data::RoutingDestination> destination, int numTags, bool attachLeaseSet): std::shared_ptr<const i2p::data::RoutingDestination> destination, int numTags, bool attachLeaseSet):
m_Owner (owner), m_Destination (destination), m_NumTags (numTags), m_Owner (owner), m_Destination (destination), m_NumTags (numTags),
m_LeaseSetUpdateStatus (attachLeaseSet ? eLeaseSetUpdated : eLeaseSetDoNotSend), m_LeaseSetUpdateStatus (attachLeaseSet ? eLeaseSetUpdated : eLeaseSetDoNotSend),
m_LeaseSetUpdateMsgID (0), m_LeaseSetUpdateMsgID (0)
m_ElGamalEncryption (new i2p::crypto::ElGamalEncryption (destination->GetEncryptionPublicKey ()))
{ {
// create new session tags and session key // create new session tags and session key
RAND_bytes (m_SessionKey, 32); RAND_bytes (m_SessionKey, 32);
@ -29,7 +28,7 @@ namespace garlic
} }
GarlicRoutingSession::GarlicRoutingSession (const uint8_t * sessionKey, const SessionTag& sessionTag): GarlicRoutingSession::GarlicRoutingSession (const uint8_t * sessionKey, const SessionTag& sessionTag):
m_Owner (nullptr), m_Destination (nullptr), m_NumTags (1), m_LeaseSetUpdateStatus (eLeaseSetDoNotSend), m_LeaseSetUpdateMsgID (0) m_Owner (nullptr), m_NumTags (1), m_LeaseSetUpdateStatus (eLeaseSetDoNotSend), m_LeaseSetUpdateMsgID (0)
{ {
memcpy (m_SessionKey, sessionKey, 32); memcpy (m_SessionKey, sessionKey, 32);
m_Encryption.SetKey (m_SessionKey); m_Encryption.SetKey (m_SessionKey);
@ -188,7 +187,8 @@ namespace garlic
RAND_bytes (elGamal.preIV, 32); // Pre-IV RAND_bytes (elGamal.preIV, 32); // Pre-IV
uint8_t iv[32]; // IV is first 16 bytes uint8_t iv[32]; // IV is first 16 bytes
SHA256(elGamal.preIV, 32, iv); SHA256(elGamal.preIV, 32, iv);
m_ElGamalEncryption->Encrypt ((uint8_t *)&elGamal, buf, true); i2p::crypto::ElGamalEncryption elGamalEncryption (m_Destination->GetEncryptionPublicKey ());
elGamalEncryption.Encrypt ((uint8_t *)&elGamal, buf, true);
m_Encryption.SetIV (iv); m_Encryption.SetIV (iv);
buf += 514; buf += 514;
len += 514; len += 514;
@ -315,7 +315,7 @@ namespace garlic
{ {
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 8000; // 8 sec uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 8000; // 8 sec
size_t size = 0; size_t size = 0;
if (isDestination && m_Destination) if (isDestination)
{ {
buf[size] = eGarlicDeliveryTypeDestination << 5;// delivery instructions flag destination buf[size] = eGarlicDeliveryTypeDestination << 5;// delivery instructions flag destination
size++; size++;

2
Garlic.h

@ -128,6 +128,7 @@ namespace garlic
GarlicDestination * m_Owner; GarlicDestination * m_Owner;
std::shared_ptr<const i2p::data::RoutingDestination> m_Destination; std::shared_ptr<const i2p::data::RoutingDestination> m_Destination;
i2p::crypto::AESKey m_SessionKey; i2p::crypto::AESKey m_SessionKey;
std::list<SessionTag> m_SessionTags; std::list<SessionTag> m_SessionTags;
int m_NumTags; int m_NumTags;
@ -138,7 +139,6 @@ namespace garlic
uint64_t m_LeaseSetSubmissionTime; // in milliseconds uint64_t m_LeaseSetSubmissionTime; // in milliseconds
i2p::crypto::CBCEncryption m_Encryption; i2p::crypto::CBCEncryption m_Encryption;
std::unique_ptr<const i2p::crypto::ElGamalEncryption> m_ElGamalEncryption;
std::shared_ptr<GarlicRoutingPath> m_SharedRoutingPath; std::shared_ptr<GarlicRoutingPath> m_SharedRoutingPath;

17
HTTP.cpp

@ -259,16 +259,21 @@ namespace http {
return eoh + strlen(HTTP_EOH); return eoh + strlen(HTTP_EOH);
} }
std::string HTTPReq::to_string() { void HTTPReq::write(std::ostream & o) {
std::stringstream ss; o << method << " " << uri << " " << version << CRLF;
ss << method << " " << uri << " " << version << CRLF;
for (auto & h : headers) { for (auto & h : headers) {
ss << h.first << ": " << h.second << CRLF; o << h.first << ": " << h.second << CRLF;
} }
ss << CRLF; o << CRLF;
return ss.str();
} }
std::string HTTPReq::to_string()
{
std::stringstream ss;
write(ss);
return ss.str();
}
bool HTTPRes::is_chunked() { bool HTTPRes::is_chunked() {
auto it = headers.find("Transfer-Encoding"); auto it = headers.find("Transfer-Encoding");
if (it == headers.end()) if (it == headers.end())

5
HTTP.h

@ -82,6 +82,9 @@ namespace http {
/** @brief Serialize HTTP request to string */ /** @brief Serialize HTTP request to string */
std::string to_string(); std::string to_string();
void write(std::ostream & o);
}; };
struct HTTPRes : HTTPMsg { struct HTTPRes : HTTPMsg {
@ -116,6 +119,8 @@ namespace http {
*/ */
std::string to_string(); std::string to_string();
void write(std::ostream & o);
/** @brief Checks that response declared as chunked data */ /** @brief Checks that response declared as chunked data */
bool is_chunked(); bool is_chunked();

239
HTTPProxy.cpp

@ -64,15 +64,40 @@ namespace proxy {
void HostNotFound(std::string & host); void HostNotFound(std::string & host);
void SendProxyError(std::string & content); void SendProxyError(std::string & content);
void ForwardToUpstreamProxy();
void HandleUpstreamHTTPProxyConnect(const boost::system::error_code & ec);
void HandleUpstreamSocksProxyConnect(const boost::system::error_code & ec);
void HandleSocksProxySendHandshake(const boost::system::error_code & ec, std::size_t bytes_transfered);
void HandleSocksProxyReply(const boost::system::error_code & ec, std::size_t bytes_transfered);
typedef std::function<void(boost::asio::ip::tcp::endpoint)> ProxyResolvedHandler;
void HandleUpstreamProxyResolved(const boost::system::error_code & ecode, boost::asio::ip::tcp::resolver::iterator itr, ProxyResolvedHandler handler);
void SocksProxySuccess();
void HandoverToUpstreamProxy();
uint8_t m_recv_chunk[8192]; uint8_t m_recv_chunk[8192];
std::string m_recv_buf; // from client std::string m_recv_buf; // from client
std::string m_send_buf; // to upstream std::string m_send_buf; // to upstream
std::shared_ptr<boost::asio::ip::tcp::socket> m_sock; std::shared_ptr<boost::asio::ip::tcp::socket> m_sock;
std::shared_ptr<boost::asio::ip::tcp::socket> m_proxysock;
boost::asio::ip::tcp::resolver m_proxy_resolver;
i2p::http::URL m_ProxyURL;
i2p::http::URL m_RequestURL;
uint8_t m_socks_buf[255+8]; // for socks request/response
ssize_t m_req_len;
i2p::http::URL m_ClientRequestURL;
i2p::http::HTTPReq m_ClientRequest;
i2p::http::HTTPRes m_ClientResponse;
std::stringstream m_ClientRequestBuffer;
public: public:
HTTPReqHandler(HTTPProxy * parent, std::shared_ptr<boost::asio::ip::tcp::socket> sock) : HTTPReqHandler(HTTPProxy * parent, std::shared_ptr<boost::asio::ip::tcp::socket> sock) :
I2PServiceHandler(parent), m_sock(sock) {} I2PServiceHandler(parent), m_sock(sock),
m_proxysock(std::make_shared<boost::asio::ip::tcp::socket>(parent->GetService())),
m_proxy_resolver(parent->GetService()) {}
~HTTPReqHandler() { Terminate(); } ~HTTPReqHandler() { Terminate(); }
void Handle () { AsyncSockRead(); } /* overload */ void Handle () { AsyncSockRead(); } /* overload */
}; };
@ -97,6 +122,13 @@ namespace proxy {
m_sock->close(); m_sock->close();
m_sock = nullptr; m_sock = nullptr;
} }
if(m_proxysock)
{
LogPrint(eLogDebug, "HTTPProxy: close proxysock");
if(m_proxysock->is_open())
m_proxysock->close();
m_proxysock = nullptr;
}
Done(shared_from_this()); Done(shared_from_this());
} }
@ -142,7 +174,7 @@ namespace proxy {
<< "</html>\r\n"; << "</html>\r\n";
res.body = ss.str(); res.body = ss.str();
std::string response = res.to_string(); std::string response = res.to_string();
boost::asio::async_write(*m_sock, boost::asio::buffer(response), boost::asio::async_write(*m_sock, boost::asio::buffer(response), boost::asio::transfer_all(),
std::bind(&HTTPReqHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1)); std::bind(&HTTPReqHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1));
} }
@ -198,54 +230,51 @@ namespace proxy {
*/ */
bool HTTPReqHandler::HandleRequest() bool HTTPReqHandler::HandleRequest()
{ {
i2p::http::HTTPReq req;
i2p::http::URL url;
std::string b64; std::string b64;
int req_len = 0;
req_len = req.parse(m_recv_buf); m_req_len = m_ClientRequest.parse(m_recv_buf);
if (req_len == 0) if (m_req_len == 0)
return false; /* need more data */ return false; /* need more data */
if (req_len < 0) { if (m_req_len < 0) {
LogPrint(eLogError, "HTTPProxy: unable to parse request"); LogPrint(eLogError, "HTTPProxy: unable to parse request");
GenericProxyError("Invalid request", "Proxy unable to parse your request"); GenericProxyError("Invalid request", "Proxy unable to parse your request");
return true; /* parse error */ return true; /* parse error */
} }
/* parsing success, now let's look inside request */ /* parsing success, now let's look inside request */
LogPrint(eLogDebug, "HTTPProxy: requested: ", req.uri); LogPrint(eLogDebug, "HTTPProxy: requested: ", m_ClientRequest.uri);
url.parse(req.uri); m_RequestURL.parse(m_ClientRequest.uri);
if (ExtractAddressHelper(url, b64)) { if (ExtractAddressHelper(m_RequestURL, b64)) {
i2p::client::context.GetAddressBook ().InsertAddress (url.host, b64); i2p::client::context.GetAddressBook ().InsertAddress (m_RequestURL.host, b64);
LogPrint (eLogInfo, "HTTPProxy: added b64 from addresshelper for ", url.host); LogPrint (eLogInfo, "HTTPProxy: added b64 from addresshelper for ", m_RequestURL.host);
std::string full_url = url.to_string(); std::string full_url = m_RequestURL.to_string();
std::stringstream ss; std::stringstream ss;
ss << "Host " << url.host << " added to router's addressbook from helper. " ss << "Host " << m_RequestURL.host << " added to router's addressbook from helper. "
<< "Click <a href=\"" << full_url << "\">here</a> to proceed."; << "Click <a href=\"" << full_url << "\">here</a> to proceed.";
GenericProxyInfo("Addresshelper found", ss.str().c_str()); GenericProxyInfo("Addresshelper found", ss.str().c_str());
return true; /* request processed */ return true; /* request processed */
} }
SanitizeHTTPRequest(req); SanitizeHTTPRequest(m_ClientRequest);
std::string dest_host = url.host; std::string dest_host = m_RequestURL.host;
uint16_t dest_port = url.port; uint16_t dest_port = m_RequestURL.port;
/* always set port, even if missing in request */ /* always set port, even if missing in request */
if (!dest_port) { if (!dest_port) {
dest_port = (url.schema == "https") ? 443 : 80; dest_port = (m_RequestURL.schema == "https") ? 443 : 80;
} }
/* detect dest_host, set proper 'Host' header in upstream request */ /* detect dest_host, set proper 'Host' header in upstream request */
auto h = req.headers.find("Host"); auto h = m_ClientRequest.headers.find("Host");
if (dest_host != "") { if (dest_host != "") {
/* absolute url, replace 'Host' header */ /* absolute url, replace 'Host' header */
std::string h = dest_host; std::string h = dest_host;
if (dest_port != 0 && dest_port != 80) if (dest_port != 0 && dest_port != 80)
h += ":" + std::to_string(dest_port); h += ":" + std::to_string(dest_port);
req.add_header("Host", h, true); m_ClientRequest.add_header("Host", h, true);
} else if (h != req.headers.end()) { } else if (h != m_ClientRequest.headers.end()) {
/* relative url and 'Host' header provided. transparent proxy mode? */ /* relative url and 'Host' header provided. transparent proxy mode? */
i2p::http::URL u; i2p::http::URL u;
std::string t = "http://" + h->second; std::string t = "http://" + h->second;
@ -265,23 +294,31 @@ namespace proxy {
HostNotFound(dest_host); HostNotFound(dest_host);
return true; /* request processed */ return true; /* request processed */
} }
/* TODO: outproxy handler here */
} else { } else {
LogPrint (eLogWarning, "HTTPProxy: outproxy failure for ", dest_host, ": not implemented yet"); std::string outproxyUrl; i2p::config::GetOption("httpproxy.outproxy", outproxyUrl);
std::string message = "Host" + dest_host + "not inside I2P network, but outproxy support not implemented yet"; if(outproxyUrl.size()) {
GenericProxyError("Outproxy failure", message.c_str()); LogPrint (eLogDebug, "HTTPProxy: use outproxy ", outproxyUrl);
if(m_ProxyURL.parse(outproxyUrl))
ForwardToUpstreamProxy();
else
GenericProxyError("Outproxy failure", "bad outproxy settings");
} else {
LogPrint (eLogWarning, "HTTPProxy: outproxy failure for ", dest_host, ": no outprxy enabled");
std::string message = "Host" + dest_host + "not inside I2P network, but outproxy is not enabled";
GenericProxyError("Outproxy failure", message.c_str());
}
return true; return true;
} }
/* make relative url */ /* make relative url */
url.schema = ""; m_RequestURL.schema = "";
url.host = ""; m_RequestURL.host = "";
req.uri = url.to_string(); m_ClientRequest.uri = m_RequestURL.to_string();
/* drop original request from recv buffer */ /* drop original request from recv buffer */
m_recv_buf.erase(0, req_len); m_recv_buf.erase(0, m_req_len);
/* build new buffer from modified request and data from original request */ /* build new buffer from modified request and data from original request */
m_send_buf = req.to_string(); m_send_buf = m_ClientRequest.to_string();
m_send_buf.append(m_recv_buf); m_send_buf.append(m_recv_buf);
/* connect to destination */ /* connect to destination */
LogPrint(eLogDebug, "HTTPProxy: connecting to host ", dest_host, ":", dest_port); LogPrint(eLogDebug, "HTTPProxy: connecting to host ", dest_host, ":", dest_port);
@ -290,6 +327,144 @@ namespace proxy {
return true; return true;
} }
void HTTPReqHandler::ForwardToUpstreamProxy()
{
LogPrint(eLogDebug, "HTTPProxy: forward to upstream");
// build http requset
m_ClientRequestURL = m_RequestURL;
LogPrint(eLogDebug, "HTTPProxy: ", m_ClientRequestURL.host);
m_ClientRequestURL.schema = "";
m_ClientRequestURL.host = "";
m_ClientRequest.uri = m_ClientRequestURL.to_string();
m_ClientRequest.write(m_ClientRequestBuffer);
m_ClientRequestBuffer << m_recv_buf.substr(m_req_len);
// assume http if empty schema
if (m_ProxyURL.schema == "" || m_ProxyURL.schema == "http") {
// handle upstream http proxy
if (!m_ProxyURL.port) m_ProxyURL.port = 80;
boost::asio::ip::tcp::resolver::query q(m_ProxyURL.host, std::to_string(m_ProxyURL.port));
m_proxy_resolver.async_resolve(q, std::bind(&HTTPReqHandler::HandleUpstreamProxyResolved, this, std::placeholders::_1, std::placeholders::_2, [&](boost::asio::ip::tcp::endpoint ep) {
m_proxysock->async_connect(ep, std::bind(&HTTPReqHandler::HandleUpstreamHTTPProxyConnect, this, std::placeholders::_1));
}));
} else if (m_ProxyURL.schema == "socks") {
// handle upstream socks proxy
if (!m_ProxyURL.port) m_ProxyURL.port = 9050; // default to tor default if not specified
boost::asio::ip::tcp::resolver::query q(m_ProxyURL.host, std::to_string(m_ProxyURL.port));
m_proxy_resolver.async_resolve(q, std::bind(&HTTPReqHandler::HandleUpstreamProxyResolved, this, std::placeholders::_1, std::placeholders::_2, [&](boost::asio::ip::tcp::endpoint ep) {
m_proxysock->async_connect(ep, std::bind(&HTTPReqHandler::HandleUpstreamSocksProxyConnect, this, std::placeholders::_1));
}));
} else {
// unknown type, complain
GenericProxyError("unknown outproxy url", m_ProxyURL.to_string().c_str());
}
}
void HTTPReqHandler::HandleUpstreamProxyResolved(const boost::system::error_code & ec, boost::asio::ip::tcp::resolver::iterator it, ProxyResolvedHandler handler)
{
if(ec) GenericProxyError("cannot resolve upstream proxy", ec.message().c_str());
else handler(*it);
}
void HTTPReqHandler::HandleUpstreamSocksProxyConnect(const boost::system::error_code & ec)
{
if(!ec) {
if(m_RequestURL.host.size() > 255) {
GenericProxyError("hostname too long", m_RequestURL.host.c_str());
return;
}
uint16_t port = m_RequestURL.port;
if(!port) port = 80;
LogPrint(eLogDebug, "HTTPProxy: connected to socks upstream");
std::string host = m_RequestURL.host;
std::size_t reqsize = 0;
m_socks_buf[0] = '\x04';
m_socks_buf[1] = 1;
htobe16buf(m_socks_buf+2, port);
m_socks_buf[4] = 0;
m_socks_buf[5] = 0;
m_socks_buf[6] = 0;
m_socks_buf[7] = 1;
// user id
m_socks_buf[8] = 'i';
m_socks_buf[9] = '2';
m_socks_buf[10] = 'p';
m_socks_buf[11] = 'd';
m_socks_buf[12] = 0;
reqsize += 13;
memcpy(m_socks_buf+ reqsize, host.c_str(), host.size());
reqsize += host.size();
m_socks_buf[++reqsize] = 0;
boost::asio::async_write(*m_proxysock, boost::asio::buffer(m_socks_buf, reqsize), boost::asio::transfer_all(), std::bind(&HTTPReqHandler::HandleSocksProxySendHandshake, this, std::placeholders::_1, std::placeholders::_2));
} else GenericProxyError("cannot connect to upstream socks proxy", ec.message().c_str());
}
void HTTPReqHandler::HandleSocksProxySendHandshake(const boost::system::error_code & ec, std::size_t bytes_transferred)
{
LogPrint(eLogDebug, "HTTPProxy: upstream socks handshake sent");
if(ec) GenericProxyError("Cannot negotiate with socks proxy", ec.message().c_str());
else m_proxysock->async_read_some(boost::asio::buffer(m_socks_buf, 8), std::bind(&HTTPReqHandler::HandleSocksProxyReply, this, std::placeholders::_1, std::placeholders::_2));
}
void HTTPReqHandler::HandoverToUpstreamProxy()
{
LogPrint(eLogDebug, "HTTPProxy: handover to socks proxy");
auto connection = std::make_shared<i2p::client::TCPIPPipe>(GetOwner(), m_proxysock, m_sock);
m_sock = nullptr;
m_proxysock = nullptr;
GetOwner()->AddHandler(connection);
connection->Start();
Terminate();
}
void HTTPReqHandler::SocksProxySuccess()
{
if(m_ClientRequest.method == "CONNECT") {
m_ClientResponse.code = 200;
m_send_buf = m_ClientResponse.to_string();
boost::asio::async_write(*m_sock, boost::asio::buffer(m_send_buf), boost::asio::transfer_all(), [&] (const boost::system::error_code & ec, std::size_t transferred) {
if(ec) GenericProxyError("socks proxy error", ec.message().c_str());
else HandoverToUpstreamProxy();
});
} else {
m_send_buf = m_ClientRequestBuffer.str();
LogPrint(eLogDebug, "HTTPProxy: send ", m_send_buf.size(), " bytes");
boost::asio::async_write(*m_proxysock, boost::asio::buffer(m_send_buf), boost::asio::transfer_all(), [&](const boost::system::error_code & ec, std::size_t transferred) {
if(ec) GenericProxyError("failed to send request to upstream", ec.message().c_str());
else HandoverToUpstreamProxy();
});
}
}
void HTTPReqHandler::HandleSocksProxyReply(const boost::system::error_code & ec, std::size_t bytes_transferred)
{
if(!ec)
{
if(m_socks_buf[1] == 90) {
// success
SocksProxySuccess();
} else {
std::stringstream ss;
ss << "error code: ";
ss << (int) m_socks_buf[1];
std::string msg = ss.str();
GenericProxyError("Socks Proxy error", msg.c_str());
}
}
else GenericProxyError("No Reply From socks proxy", ec.message().c_str());
}
void HTTPReqHandler::HandleUpstreamHTTPProxyConnect(const boost::system::error_code & ec)
{
if(!ec) {
LogPrint(eLogDebug, "HTTPProxy: connected to http upstream");
GenericProxyError("cannot connect", "http out proxy not implemented");
} else GenericProxyError("cannot connect to upstream http proxy", ec.message().c_str());
}
/* will be called after some data received from client */ /* will be called after some data received from client */
void HTTPReqHandler::HandleSockRecv(const boost::system::error_code & ecode, std::size_t len) void HTTPReqHandler::HandleSockRecv(const boost::system::error_code & ecode, std::size_t len)
{ {

42
HTTPServer.cpp

@ -451,26 +451,26 @@ namespace http {
s << "<br>\r\n"; s << "<br>\r\n";
} }
static void ShowCommands (std::stringstream& s) static void ShowCommands (std::stringstream& s, uint32_t token)
{ {
/* commands */ /* commands */
s << "<b>Router Commands</b><br>\r\n"; s << "<b>Router Commands</b><br>\r\n";
s << " <a href=\"/?cmd=" << HTTP_COMMAND_RUN_PEER_TEST << "\">Run peer test</a><br>\r\n"; s << " <a href=\"/?cmd=" << HTTP_COMMAND_RUN_PEER_TEST << "&token=" << token << "\">Run peer test</a><br>\r\n";
//s << " <a href=\"/?cmd=" << HTTP_COMMAND_RELOAD_CONFIG << "\">Reload config</a><br>\r\n"; //s << " <a href=\"/?cmd=" << HTTP_COMMAND_RELOAD_CONFIG << "\">Reload config</a><br>\r\n";
if (i2p::context.AcceptsTunnels ()) if (i2p::context.AcceptsTunnels ())
s << " <a href=\"/?cmd=" << HTTP_COMMAND_DISABLE_TRANSIT << "\">Decline transit tunnels</a><br>\r\n"; s << " <a href=\"/?cmd=" << HTTP_COMMAND_DISABLE_TRANSIT << "&token=" << token << "\">Decline transit tunnels</a><br>\r\n";
else else
s << " <a href=\"/?cmd=" << HTTP_COMMAND_ENABLE_TRANSIT << "\">Accept transit tunnels</a><br>\r\n"; s << " <a href=\"/?cmd=" << HTTP_COMMAND_ENABLE_TRANSIT << "&token=" << token << "\">Accept transit tunnels</a><br>\r\n";
#if (!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) #if (!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID))
if (Daemon.gracefulShutdownInterval) if (Daemon.gracefulShutdownInterval)
s << " <a href=\"/?cmd=" << HTTP_COMMAND_SHUTDOWN_CANCEL << "\">Cancel graceful shutdown</a><br>"; s << " <a href=\"/?cmd=" << HTTP_COMMAND_SHUTDOWN_CANCEL << "&token=" << token << "\">Cancel graceful shutdown</a><br>";
else else
s << " <a href=\"/?cmd=" << HTTP_COMMAND_SHUTDOWN_START << "\">Start graceful shutdown</a><br>\r\n"; s << " <a href=\"/?cmd=" << HTTP_COMMAND_SHUTDOWN_START << "&token=" << token << "\">Start graceful shutdown</a><br>\r\n";
#endif #endif
#ifdef WIN32_APP #ifdef WIN32_APP
s << " <a href=\"/?cmd=" << HTTP_COMMAND_SHUTDOWN_START << "\">Graceful shutdown</a><br>\r\n"; s << " <a href=\"/?cmd=" << HTTP_COMMAND_SHUTDOWN_START << "&token=" << token << "\">Graceful shutdown</a><br>\r\n";
#endif #endif
s << " <a href=\"/?cmd=" << HTTP_COMMAND_SHUTDOWN_NOW << "\">Force shutdown</a><br>\r\n"; s << " <a href=\"/?cmd=" << HTTP_COMMAND_SHUTDOWN_NOW << "&token=" << token << "\">Force shutdown</a><br>\r\n";
} }
static void ShowTransitTunnels (std::stringstream& s) static void ShowTransitTunnels (std::stringstream& s)
@ -756,6 +756,7 @@ namespace http {
SendReply (res, content); SendReply (res, content);
} }
std::map<uint32_t, uint32_t> HTTPConnection::m_Tokens;
void HTTPConnection::HandlePage (const HTTPReq& req, HTTPRes& res, std::stringstream& s) void HTTPConnection::HandlePage (const HTTPReq& req, HTTPRes& res, std::stringstream& s)
{ {
std::map<std::string, std::string> params; std::map<std::string, std::string> params;
@ -771,7 +772,20 @@ namespace http {
else if (page == HTTP_PAGE_TUNNELS) else if (page == HTTP_PAGE_TUNNELS)
ShowTunnels (s); ShowTunnels (s);
else if (page == HTTP_PAGE_COMMANDS) else if (page == HTTP_PAGE_COMMANDS)
ShowCommands (s); {
uint32_t token;
RAND_bytes ((uint8_t *)&token, 4);
auto ts = i2p::util::GetSecondsSinceEpoch ();
for (auto it = m_Tokens.begin (); it != m_Tokens.end (); )
{
if (ts > it->second + TOKEN_EXPIRATION_TIMEOUT)
it = m_Tokens.erase (it);
else
++it;
}
m_Tokens[token] = ts;
ShowCommands (s, token);
}
else if (page == HTTP_PAGE_TRANSIT_TUNNELS) else if (page == HTTP_PAGE_TRANSIT_TUNNELS)
ShowTransitTunnels (s); ShowTransitTunnels (s);
else if (page == HTTP_PAGE_LOCAL_DESTINATIONS) else if (page == HTTP_PAGE_LOCAL_DESTINATIONS)
@ -798,13 +812,19 @@ namespace http {
void HTTPConnection::HandleCommand (const HTTPReq& req, HTTPRes& res, std::stringstream& s) void HTTPConnection::HandleCommand (const HTTPReq& req, HTTPRes& res, std::stringstream& s)
{ {
std::map<std::string, std::string> params; std::map<std::string, std::string> params;
std::string cmd("");
URL url; URL url;
url.parse(req.uri); url.parse(req.uri);
url.parse_query(params); url.parse_query(params);
cmd = params["cmd"];
std::string token = params["token"];
if (token.empty () || m_Tokens.find (std::stoi (token)) == m_Tokens.end ())
{
ShowError(s, "Invalid token");
return;
}
std::string cmd = params["cmd"];
if (cmd == HTTP_COMMAND_RUN_PEER_TEST) if (cmd == HTTP_COMMAND_RUN_PEER_TEST)
i2p::transport::transports.PeerTest (); i2p::transport::transports.PeerTest ();
else if (cmd == HTTP_COMMAND_RELOAD_CONFIG) else if (cmd == HTTP_COMMAND_RELOAD_CONFIG)

20
HTTPServer.h

@ -1,10 +1,20 @@
#ifndef HTTP_SERVER_H__ #ifndef HTTP_SERVER_H__
#define HTTP_SERVER_H__ #define HTTP_SERVER_H__
namespace i2p { #include <inttypes.h>
namespace http { #include <string>
extern const char *itoopieFavicon; #include <memory>
const size_t HTTP_CONNECTION_BUFFER_SIZE = 8192; #include <map>
#include <thread>
#include <boost/asio.hpp>
#include "HTTP.h"
namespace i2p
{
namespace http
{
const size_t HTTP_CONNECTION_BUFFER_SIZE = 8192;
const int TOKEN_EXPIRATION_TIMEOUT = 30; // in seconds
class HTTPConnection: public std::enable_shared_from_this<HTTPConnection> class HTTPConnection: public std::enable_shared_from_this<HTTPConnection>
{ {
@ -35,6 +45,8 @@ namespace http {
bool needAuth; bool needAuth;
std::string user; std::string user;
std::string pass; std::string pass;
static std::map<uint32_t, uint32_t> m_Tokens; // token->timestamp in seconds
}; };
class HTTPServer class HTTPServer

22
I2PService.cpp

@ -53,7 +53,6 @@ namespace client
void TCPIPPipe::Terminate() void TCPIPPipe::Terminate()
{ {
if(Kill()) return; if(Kill()) return;
Done(shared_from_this());
if (m_up) { if (m_up) {
if (m_up->is_open()) { if (m_up->is_open()) {
m_up->close(); m_up->close();
@ -66,6 +65,7 @@ namespace client
} }
m_down = nullptr; m_down = nullptr;
} }
Done(shared_from_this());
} }
void TCPIPPipe::AsyncReceiveUpstream() void TCPIPPipe::AsyncReceiveUpstream()
@ -90,11 +90,11 @@ namespace client
} }
} }
void TCPIPPipe::UpstreamWrite(const uint8_t * buf, size_t len) void TCPIPPipe::UpstreamWrite(size_t len)
{ {
if (m_up) { if (m_up) {
LogPrint(eLogDebug, "TCPIPPipe: upstream: ", (int) len, " bytes written"); LogPrint(eLogDebug, "TCPIPPipe: upstream: ", (int) len, " bytes written");
boost::asio::async_write(*m_up, boost::asio::buffer(buf, len), boost::asio::async_write(*m_up, boost::asio::buffer(m_upstream_buf, len),
boost::asio::transfer_all(), boost::asio::transfer_all(),
std::bind(&TCPIPPipe::HandleUpstreamWrite, std::bind(&TCPIPPipe::HandleUpstreamWrite,
shared_from_this(), shared_from_this(),
@ -105,11 +105,11 @@ namespace client
} }
} }
void TCPIPPipe::DownstreamWrite(const uint8_t * buf, size_t len) void TCPIPPipe::DownstreamWrite(size_t len)
{ {
if (m_down) { if (m_down) {
LogPrint(eLogDebug, "TCPIPPipe: downstream: ", (int) len, " bytes written"); LogPrint(eLogDebug, "TCPIPPipe: downstream: ", (int) len, " bytes written");
boost::asio::async_write(*m_down, boost::asio::buffer(buf, len), boost::asio::async_write(*m_down, boost::asio::buffer(m_downstream_buf, len),
boost::asio::transfer_all(), boost::asio::transfer_all(),
std::bind(&TCPIPPipe::HandleDownstreamWrite, std::bind(&TCPIPPipe::HandleDownstreamWrite,
shared_from_this(), shared_from_this(),
@ -131,9 +131,8 @@ namespace client
} else { } else {
if (bytes_transfered > 0 ) { if (bytes_transfered > 0 ) {
memcpy(m_upstream_buf, m_downstream_to_up_buf, bytes_transfered); memcpy(m_upstream_buf, m_downstream_to_up_buf, bytes_transfered);
UpstreamWrite(m_upstream_buf, bytes_transfered);
} }
AsyncReceiveDownstream(); UpstreamWrite(bytes_transfered);
} }
} }
@ -142,6 +141,8 @@ namespace client
LogPrint(eLogError, "TCPIPPipe: downstream write error:" , ecode.message()); LogPrint(eLogError, "TCPIPPipe: downstream write error:" , ecode.message());
if (ecode != boost::asio::error::operation_aborted) if (ecode != boost::asio::error::operation_aborted)
Terminate(); Terminate();
} else {
AsyncReceiveUpstream();
} }
} }
@ -150,6 +151,8 @@ namespace client
LogPrint(eLogError, "TCPIPPipe: upstream write error:" , ecode.message()); LogPrint(eLogError, "TCPIPPipe: upstream write error:" , ecode.message());
if (ecode != boost::asio::error::operation_aborted) if (ecode != boost::asio::error::operation_aborted)
Terminate(); Terminate();
} else {
AsyncReceiveDownstream();
} }
} }
@ -162,10 +165,9 @@ namespace client
Terminate(); Terminate();
} else { } else {
if (bytes_transfered > 0 ) { if (bytes_transfered > 0 ) {
memcpy(m_upstream_buf, m_upstream_to_down_buf, bytes_transfered); memcpy(m_downstream_buf, m_upstream_to_down_buf, bytes_transfered);
DownstreamWrite(m_upstream_buf, bytes_transfered);
} }
AsyncReceiveUpstream(); DownstreamWrite(bytes_transfered);
} }
} }

6
I2PService.h

@ -77,7 +77,7 @@ namespace client
std::atomic<bool> m_Dead; //To avoid cleaning up multiple times std::atomic<bool> m_Dead; //To avoid cleaning up multiple times
}; };
const size_t TCP_IP_PIPE_BUFFER_SIZE = 8192; const size_t TCP_IP_PIPE_BUFFER_SIZE = 8192 * 8;
// bidirectional pipe for 2 tcp/ip sockets // bidirectional pipe for 2 tcp/ip sockets
class TCPIPPipe: public I2PServiceHandler, public std::enable_shared_from_this<TCPIPPipe> { class TCPIPPipe: public I2PServiceHandler, public std::enable_shared_from_this<TCPIPPipe> {
@ -93,8 +93,8 @@ namespace client
void HandleDownstreamReceived(const boost::system::error_code & ecode, std::size_t bytes_transferred); void HandleDownstreamReceived(const boost::system::error_code & ecode, std::size_t bytes_transferred);
void HandleUpstreamWrite(const boost::system::error_code & ecode); void HandleUpstreamWrite(const boost::system::error_code & ecode);
void HandleDownstreamWrite(const boost::system::error_code & ecode); void HandleDownstreamWrite(const boost::system::error_code & ecode);
void UpstreamWrite(const uint8_t * buf, size_t len); void UpstreamWrite(size_t len);
void DownstreamWrite(const uint8_t * buf, size_t len); void DownstreamWrite(size_t len);
private: private:
uint8_t m_upstream_to_down_buf[TCP_IP_PIPE_BUFFER_SIZE], m_downstream_to_up_buf[TCP_IP_PIPE_BUFFER_SIZE]; uint8_t m_upstream_to_down_buf[TCP_IP_PIPE_BUFFER_SIZE], m_downstream_to_up_buf[TCP_IP_PIPE_BUFFER_SIZE];
uint8_t m_upstream_buf[TCP_IP_PIPE_BUFFER_SIZE], m_downstream_buf[TCP_IP_PIPE_BUFFER_SIZE]; uint8_t m_upstream_buf[TCP_IP_PIPE_BUFFER_SIZE], m_downstream_buf[TCP_IP_PIPE_BUFFER_SIZE];

132
I2PTunnel.cpp

@ -92,7 +92,9 @@ namespace client
m_Stream->Close (); m_Stream->Close ();
m_Stream.reset (); m_Stream.reset ();
} }
m_Socket->shutdown(boost::asio::ip::tcp::socket::shutdown_send); // avoid RST
m_Socket->close (); m_Socket->close ();
Done(shared_from_this ()); Done(shared_from_this ());
} }
@ -107,9 +109,11 @@ namespace client
{ {
if (ecode) if (ecode)
{ {
LogPrint (eLogError, "I2PTunnel: read error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted) if (ecode != boost::asio::error::operation_aborted)
{
LogPrint (eLogError, "I2PTunnel: read error: ", ecode.message ());
Terminate (); Terminate ();
}
} }
else else
{ {
@ -540,15 +544,32 @@ namespace client
void I2PUDPServerTunnel::ExpireStale(const uint64_t delta) { void I2PUDPServerTunnel::ExpireStale(const uint64_t delta) {
std::lock_guard<std::mutex> lock(m_SessionsMutex); std::lock_guard<std::mutex> lock(m_SessionsMutex);
uint64_t now = i2p::util::GetMillisecondsSinceEpoch(); uint64_t now = i2p::util::GetMillisecondsSinceEpoch();
std::remove_if(m_Sessions.begin(), m_Sessions.end(), [now, delta](const UDPSession * u) -> bool { auto itr = m_Sessions.begin();
return now - u->LastActivity >= delta; while(itr != m_Sessions.end()) {
}); if(now - (*itr)->LastActivity >= delta )
itr = m_Sessions.erase(itr);
else
++itr;
}
} }
UDPSession * I2PUDPServerTunnel::ObtainUDPSession(const i2p::data::IdentityEx& from, uint16_t localPort, uint16_t remotePort) void I2PUDPClientTunnel::ExpireStale(const uint64_t delta) {
std::lock_guard<std::mutex> lock(m_SessionsMutex);
uint64_t now = i2p::util::GetMillisecondsSinceEpoch();
std::vector<uint16_t> removePorts;
for (const auto & s : m_Sessions) {
if (now - s.second.second >= delta)
removePorts.push_back(s.first);
}
for(auto port : removePorts) {
m_Sessions.erase(port);
}
}
std::shared_ptr<UDPSession> I2PUDPServerTunnel::ObtainUDPSession(const i2p::data::IdentityEx& from, uint16_t localPort, uint16_t remotePort)
{ {
auto ih = from.GetIdentHash(); auto ih = from.GetIdentHash();
for ( UDPSession * s : m_Sessions ) for (auto & s : m_Sessions )
{ {
if ( s->Identity == ih) if ( s->Identity == ih)
{ {
@ -559,8 +580,9 @@ namespace client
} }
/** create new udp session */ /** create new udp session */
boost::asio::ip::udp::endpoint ep(m_LocalAddress, 0); boost::asio::ip::udp::endpoint ep(m_LocalAddress, 0);
m_Sessions.push_back(new UDPSession(ep, m_LocalDest, m_RemoteEndpoint, &ih, localPort, remotePort)); m_Sessions.push_back(std::make_shared<UDPSession>(ep, m_LocalDest, m_RemoteEndpoint, &ih, localPort, remotePort));
return m_Sessions.back(); auto & back = m_Sessions.back();
return back;
} }
UDPSession::UDPSession(boost::asio::ip::udp::endpoint localEndpoint, UDPSession::UDPSession(boost::asio::ip::udp::endpoint localEndpoint,
@ -568,7 +590,6 @@ namespace client
boost::asio::ip::udp::endpoint endpoint, const i2p::data::IdentHash * to, boost::asio::ip::udp::endpoint endpoint, const i2p::data::IdentHash * to,
uint16_t ourPort, uint16_t theirPort) : uint16_t ourPort, uint16_t theirPort) :
m_Destination(localDestination->GetDatagramDestination()), m_Destination(localDestination->GetDatagramDestination()),
m_Service(localDestination->GetService()),
IPSocket(localDestination->GetService(), localEndpoint), IPSocket(localDestination->GetService(), localEndpoint),
SendEndpoint(endpoint), SendEndpoint(endpoint),
LastActivity(i2p::util::GetMillisecondsSinceEpoch()), LastActivity(i2p::util::GetMillisecondsSinceEpoch()),
@ -592,7 +613,7 @@ namespace client
{ {
LogPrint(eLogDebug, "UDPSession: forward ", len, "B from ", FromEndpoint); LogPrint(eLogDebug, "UDPSession: forward ", len, "B from ", FromEndpoint);
LastActivity = i2p::util::GetMillisecondsSinceEpoch(); LastActivity = i2p::util::GetMillisecondsSinceEpoch();
m_Destination->SendDatagramTo(m_Buffer, len, Identity, 0, 0); m_Destination->SendDatagramTo(m_Buffer, len, Identity, LocalPort, RemotePort);
Receive(); Receive();
} else { } else {
LogPrint(eLogError, "UDPSession: ", ecode.message()); LogPrint(eLogError, "UDPSession: ", ecode.message());
@ -602,9 +623,8 @@ namespace client
I2PUDPServerTunnel::I2PUDPServerTunnel(const std::string & name, std::shared_ptr<i2p::client::ClientDestination> localDestination, I2PUDPServerTunnel::I2PUDPServerTunnel(const std::string & name, std::shared_ptr<i2p::client::ClientDestination> localDestination,
const boost::asio::ip::address& localAddress, boost::asio::ip::udp::endpoint forwardTo, uint16_t port) : boost::asio::ip::address localAddress, boost::asio::ip::udp::endpoint forwardTo, uint16_t port) :
m_Name(name), m_Name(name),
LocalPort(port),
m_LocalAddress(localAddress), m_LocalAddress(localAddress),
m_RemoteEndpoint(forwardTo) m_RemoteEndpoint(forwardTo)
{ {
@ -630,7 +650,7 @@ namespace client
{ {
std::vector<std::shared_ptr<DatagramSessionInfo> > sessions; std::vector<std::shared_ptr<DatagramSessionInfo> > sessions;
std::lock_guard<std::mutex> lock(m_SessionsMutex); std::lock_guard<std::mutex> lock(m_SessionsMutex);
for ( UDPSession * s : m_Sessions ) for (auto & s : m_Sessions )
{ {
if (!s->m_Destination) continue; if (!s->m_Destination) continue;
auto info = s->m_Destination->GetInfoForRemote(s->Identity); auto info = s->m_Destination->GetInfoForRemote(s->Identity);
@ -652,13 +672,12 @@ namespace client
std::shared_ptr<i2p::client::ClientDestination> localDestination, std::shared_ptr<i2p::client::ClientDestination> localDestination,
uint16_t remotePort) : uint16_t remotePort) :
m_Name(name), m_Name(name),
m_Session(nullptr),
m_RemoteDest(remoteDest), m_RemoteDest(remoteDest),
m_LocalDest(localDestination), m_LocalDest(localDestination),
m_LocalEndpoint(localEndpoint), m_LocalEndpoint(localEndpoint),
m_RemoteIdent(nullptr), m_RemoteIdent(nullptr),
m_ResolveThread(nullptr), m_ResolveThread(nullptr),
LocalPort(localEndpoint.port()), m_LocalSocket(localDestination->GetService(), localEndpoint),
RemotePort(remotePort), RemotePort(remotePort),
m_cancel_resolve(false) m_cancel_resolve(false)
{ {
@ -675,29 +694,44 @@ namespace client
m_LocalDest->Start(); m_LocalDest->Start();
if (m_ResolveThread == nullptr) if (m_ResolveThread == nullptr)
m_ResolveThread = new std::thread(std::bind(&I2PUDPClientTunnel::TryResolving, this)); m_ResolveThread = new std::thread(std::bind(&I2PUDPClientTunnel::TryResolving, this));
RecvFromLocal();
} }
void I2PUDPClientTunnel::RecvFromLocal()
{
m_LocalSocket.async_receive_from(boost::asio::buffer(m_RecvBuff, I2P_UDP_MAX_MTU),
m_RecvEndpoint, std::bind(&I2PUDPClientTunnel::HandleRecvFromLocal, this, std::placeholders::_1, std::placeholders::_2));
}
void I2PUDPClientTunnel::HandleRecvFromLocal(const boost::system::error_code & ec, std::size_t transferred)
{
if(ec) {
LogPrint(eLogError, "UDP Client: ", ec.message());
return;
}
if(!m_RemoteIdent) {
LogPrint(eLogWarning, "UDP Client: remote endpoint not resolved yet");
RecvFromLocal();
return; // drop, remote not resolved
}
auto remotePort = m_RecvEndpoint.port();
auto itr = m_Sessions.find(remotePort);
if (itr == m_Sessions.end()) {
// track new udp convo
m_Sessions[remotePort] = {boost::asio::ip::udp::endpoint(m_RecvEndpoint), 0};
}
// send off to remote i2p destination
LogPrint(eLogDebug, "UDP Client: send ", transferred, " to ", m_RemoteIdent->ToBase32(), ":", RemotePort);
m_LocalDest->GetDatagramDestination()->SendDatagramTo(m_RecvBuff, transferred, *m_RemoteIdent, remotePort, RemotePort);
// mark convo as active
m_Sessions[remotePort].second = i2p::util::GetMillisecondsSinceEpoch();
RecvFromLocal();
}
std::vector<std::shared_ptr<DatagramSessionInfo> > I2PUDPClientTunnel::GetSessions() std::vector<std::shared_ptr<DatagramSessionInfo> > I2PUDPClientTunnel::GetSessions()
{ {
// TODO: implement
std::vector<std::shared_ptr<DatagramSessionInfo> > infos; std::vector<std::shared_ptr<DatagramSessionInfo> > infos;
if(m_Session && m_LocalDest)
{
auto s = m_Session;
if (s->m_Destination)
{
auto info = m_Session->m_Destination->GetInfoForRemote(s->Identity);
if(info)
{
auto sinfo = std::make_shared<DatagramSessionInfo>();
sinfo->Name = m_Name;
sinfo->LocalIdent = std::make_shared<i2p::data::IdentHash>(m_LocalDest->GetIdentHash().data());
sinfo->RemoteIdent = std::make_shared<i2p::data::IdentHash>(s->Identity.data());
sinfo->CurrentIBGW = info->IBGW;
sinfo->CurrentOBEP = info->OBEP;
infos.push_back(sinfo);
}
}
}
return infos; return infos;
} }
@ -717,26 +751,28 @@ namespace client
return; return;
} }
LogPrint(eLogInfo, "UDP Tunnel: resolved ", m_RemoteDest, " to ", m_RemoteIdent->ToBase32()); LogPrint(eLogInfo, "UDP Tunnel: resolved ", m_RemoteDest, " to ", m_RemoteIdent->ToBase32());
// delete existing session
if(m_Session) delete m_Session;
boost::asio::ip::udp::endpoint ep(boost::asio::ip::address::from_string("127.0.0.1"), 0);
m_Session = new UDPSession(m_LocalEndpoint, m_LocalDest, ep, m_RemoteIdent, LocalPort, RemotePort);
} }
void I2PUDPClientTunnel::HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len) void I2PUDPClientTunnel::HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
{ {
if(m_RemoteIdent && from.GetIdentHash() == *m_RemoteIdent) if(m_RemoteIdent && from.GetIdentHash() == *m_RemoteIdent)
{ {
// address match auto itr = m_Sessions.find(toPort);
if(m_Session) // found convo ?
if(itr != m_Sessions.end())
{ {
// tell session // found convo
LogPrint(eLogDebug, "UDP Client: got ", len, "B from ", from.GetIdentHash().ToBase32()); if (len > 0) {
m_Session->IPSocket.send_to(boost::asio::buffer(buf, len), m_Session->FromEndpoint); LogPrint(eLogDebug, "UDP Client: got ", len, "B from ", from.GetIdentHash().ToBase32());
uint8_t sendbuf[len];
memcpy(sendbuf, buf, len);
m_LocalSocket.send_to(boost::asio::buffer(buf, len), itr->second.first);
// mark convo as active
itr->second.second = i2p::util::GetMillisecondsSinceEpoch();
}
} }
else else
LogPrint(eLogWarning, "UDP Client: no session"); LogPrint(eLogWarning, "UDP Client: not tracking udp session using port ", (int) toPort);
} }
else else
LogPrint(eLogWarning, "UDP Client: unwarrented traffic from ", from.GetIdentHash().ToBase32()); LogPrint(eLogWarning, "UDP Client: unwarrented traffic from ", from.GetIdentHash().ToBase32());
@ -747,7 +783,11 @@ namespace client
auto dgram = m_LocalDest->GetDatagramDestination(); auto dgram = m_LocalDest->GetDatagramDestination();
if (dgram) dgram->ResetReceiver(); if (dgram) dgram->ResetReceiver();
if (m_Session) delete m_Session; m_Sessions.clear();
if(m_LocalSocket.is_open())
m_LocalSocket.close();
m_cancel_resolve = true; m_cancel_resolve = true;
if(m_ResolveThread) if(m_ResolveThread)

20
I2PTunnel.h

@ -4,6 +4,7 @@
#include <inttypes.h> #include <inttypes.h>
#include <string> #include <string>
#include <set> #include <set>
#include <tuple>
#include <memory> #include <memory>
#include <sstream> #include <sstream>
#include <boost/asio.hpp> #include <boost/asio.hpp>
@ -141,7 +142,6 @@ namespace client
struct UDPSession struct UDPSession
{ {
i2p::datagram::DatagramDestination * m_Destination; i2p::datagram::DatagramDestination * m_Destination;
boost::asio::io_service & m_Service;
boost::asio::ip::udp::socket IPSocket; boost::asio::ip::udp::socket IPSocket;
i2p::data::IdentHash Identity; i2p::data::IdentHash Identity;
boost::asio::ip::udp::endpoint FromEndpoint; boost::asio::ip::udp::endpoint FromEndpoint;
@ -189,7 +189,7 @@ namespace client
public: public:
I2PUDPServerTunnel(const std::string & name, I2PUDPServerTunnel(const std::string & name,
std::shared_ptr<i2p::client::ClientDestination> localDestination, std::shared_ptr<i2p::client::ClientDestination> localDestination,
const boost::asio::ip::address & localAddress, boost::asio::ip::address localAddress,
boost::asio::ip::udp::endpoint forwardTo, uint16_t port); boost::asio::ip::udp::endpoint forwardTo, uint16_t port);
~I2PUDPServerTunnel(); ~I2PUDPServerTunnel();
/** expire stale udp conversations */ /** expire stale udp conversations */
@ -202,15 +202,14 @@ namespace client
private: private:
void HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); void HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
UDPSession * ObtainUDPSession(const i2p::data::IdentityEx& from, uint16_t localPort, uint16_t remotePort); std::shared_ptr<UDPSession> ObtainUDPSession(const i2p::data::IdentityEx& from, uint16_t localPort, uint16_t remotePort);
private: private:
const std::string m_Name; const std::string m_Name;
const uint16_t LocalPort;
boost::asio::ip::address m_LocalAddress; boost::asio::ip::address m_LocalAddress;
boost::asio::ip::udp::endpoint m_RemoteEndpoint; boost::asio::ip::udp::endpoint m_RemoteEndpoint;
std::mutex m_SessionsMutex; std::mutex m_SessionsMutex;
std::vector<UDPSession*> m_Sessions; std::vector<std::shared_ptr<UDPSession> > m_Sessions;
std::shared_ptr<i2p::client::ClientDestination> m_LocalDest; std::shared_ptr<i2p::client::ClientDestination> m_LocalDest;
}; };
@ -228,18 +227,25 @@ namespace client
bool IsLocalDestination(const i2p::data::IdentHash & destination) const { return destination == m_LocalDest->GetIdentHash(); } bool IsLocalDestination(const i2p::data::IdentHash & destination) const { return destination == m_LocalDest->GetIdentHash(); }
std::shared_ptr<ClientDestination> GetLocalDestination () const { return m_LocalDest; } std::shared_ptr<ClientDestination> GetLocalDestination () const { return m_LocalDest; }
void ExpireStale(const uint64_t delta=I2P_UDP_SESSION_TIMEOUT);
private: private:
typedef std::pair<boost::asio::ip::udp::endpoint, uint64_t> UDPConvo;
void RecvFromLocal();
void HandleRecvFromLocal(const boost::system::error_code & e, std::size_t transferred);
void HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); void HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
void TryResolving(); void TryResolving();
const std::string m_Name; const std::string m_Name;
UDPSession * m_Session; std::mutex m_SessionsMutex;
std::map<uint16_t, UDPConvo > m_Sessions; // maps i2p port -> local udp convo
const std::string m_RemoteDest; const std::string m_RemoteDest;
std::shared_ptr<i2p::client::ClientDestination> m_LocalDest; std::shared_ptr<i2p::client::ClientDestination> m_LocalDest;
const boost::asio::ip::udp::endpoint m_LocalEndpoint; const boost::asio::ip::udp::endpoint m_LocalEndpoint;
i2p::data::IdentHash * m_RemoteIdent; i2p::data::IdentHash * m_RemoteIdent;
std::thread * m_ResolveThread; std::thread * m_ResolveThread;
uint16_t LocalPort; boost::asio::ip::udp::socket m_LocalSocket;
boost::asio::ip::udp::endpoint m_RecvEndpoint;
uint8_t m_RecvBuff[I2P_UDP_MAX_MTU];
uint16_t RemotePort; uint16_t RemotePort;
bool m_cancel_resolve; bool m_cancel_resolve;
}; };

2
Identity.h

@ -133,7 +133,7 @@ namespace data
size_t FromBase64(const std::string& s); size_t FromBase64(const std::string& s);
std::string ToBase64 () const; std::string ToBase64 () const;
static PrivateKeys CreateRandomKeys (SigningKeyType type = SIGNING_KEY_TYPE_DSA_SHA1); static PrivateKeys CreateRandomKeys (SigningKeyType type = SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519);
private: private:

2
LeaseSet.cpp

@ -91,7 +91,7 @@ namespace data
if (m_StoreLeases) if (m_StoreLeases)
{ {
auto ret = m_Leases.insert (std::make_shared<Lease>(lease)); auto ret = m_Leases.insert (std::make_shared<Lease>(lease));
if (!ret.second) *(*ret.first) = lease; // update existing if (!ret.second) (*ret.first)->endDate = lease.endDate; // update existing
(*ret.first)->isUpdated = true; (*ret.first)->isUpdated = true;
// check if lease's gateway is in our netDb // check if lease's gateway is in our netDb
if (!netdb.FindRouter (lease.tunnelGateway)) if (!netdb.FindRouter (lease.tunnelGateway))

8
Makefile.mingw

@ -39,9 +39,13 @@ endif
# don't change following line to ifeq ($(USE_AESNI),yes) !!! # don't change following line to ifeq ($(USE_AESNI),yes) !!!
ifeq ($(USE_AESNI),1) ifeq ($(USE_AESNI),1)
CPU_FLAGS = -maes -DAESNI CPU_FLAGS += -maes -DAESNI
else else
CPU_FLAGS = -msse CPU_FLAGS += -msse
endif
ifeq ($(USE_AVX),1)
CPU_FLAGS += -mavx
endif endif
ifeq ($(USE_ASLR),yes) ifeq ($(USE_ASLR),yes)

13
Makefile.osx vendored

@ -3,21 +3,26 @@ CXXFLAGS = -g -Wall -std=c++11 -DMAC_OSX
#CXXFLAGS = -g -O2 -Wall -std=c++11 #CXXFLAGS = -g -O2 -Wall -std=c++11
INCFLAGS = -I/usr/local/include -I/usr/local/ssl/include INCFLAGS = -I/usr/local/include -I/usr/local/ssl/include
LDFLAGS = -Wl,-rpath,/usr/local/lib -L/usr/local/lib -L/usr/local/ssl/lib LDFLAGS = -Wl,-rpath,/usr/local/lib -L/usr/local/lib -L/usr/local/ssl/lib
ifeq ($(USE_STATIC),yes)
LDLIBS = -lz -lcrypto -lssl /usr/local/lib/libboost_system.a /usr/local/lib/libboost_date_time.a /usr/local/lib/libboost_filesystem.a /usr/local/lib/libboost_program_options.a -lpthread
else
LDLIBS = -lz -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread LDLIBS = -lz -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread
endif
ifeq ($(USE_UPNP),yes) ifeq ($(USE_UPNP),yes)
LDFLAGS += -ldl LDFLAGS += -ldl
CXXFLAGS += -DUSE_UPNP CXXFLAGS += -DUSE_UPNP
endif endif
# OSX Notes
# http://www.hutsby.net/2011/08/macs-with-aes-ni.html
# Seems like all recent Mac's have AES-NI, after firmware upgrade 2.2
# Found no good way to detect it from command line. TODO: Might be some osx sysinfo magic
ifeq ($(USE_AESNI),yes) ifeq ($(USE_AESNI),yes)
CXXFLAGS += -maes -DAESNI CXXFLAGS += -maes -DAESNI
endif endif
ifeq ($(USE_AVX),yes)
CXXFLAGS += -mavx
endif
# Disabled, since it will be the default make rule. I think its better # Disabled, since it will be the default make rule. I think its better
# to define the default rule in Makefile and not Makefile.<ostype> - torkel # to define the default rule in Makefile and not Makefile.<ostype> - torkel
#install: all #install: all

2
NTCPSession.cpp

@ -610,7 +610,7 @@ namespace transport
if (!m_NextMessage->IsExpired ()) if (!m_NextMessage->IsExpired ())
{ {
#ifdef WITH_EVENTS #ifdef WITH_EVENTS
EmitEvent({{"type", "transport.recvmsg"} , {"ident", GetIdentHashBase64()}, {"number", "1"}}); QueueIntEvent("transport.recvmsg", GetIdentHashBase64(), 1);
#endif #endif
m_Handler.PutNextMessage (m_NextMessage); m_Handler.PutNextMessage (m_NextMessage);
} }

4
NetDb.cpp

@ -912,7 +912,6 @@ namespace data
uint8_t randomHash[32]; uint8_t randomHash[32];
std::vector<i2p::tunnel::TunnelMessageBlock> msgs; std::vector<i2p::tunnel::TunnelMessageBlock> msgs;
std::set<const RouterInfo *> floodfills;
LogPrint (eLogInfo, "NetDb: exploring new ", numDestinations, " routers ..."); LogPrint (eLogInfo, "NetDb: exploring new ", numDestinations, " routers ...");
for (int i = 0; i < numDestinations; i++) for (int i = 0; i < numDestinations; i++)
{ {
@ -924,9 +923,8 @@ namespace data
return; return;
} }
auto floodfill = GetClosestFloodfill (randomHash, dest->GetExcludedPeers ()); auto floodfill = GetClosestFloodfill (randomHash, dest->GetExcludedPeers ());
if (floodfill && !floodfills.count (floodfill.get ())) // request floodfill only once if (floodfill)
{ {
floodfills.insert (floodfill.get ());
if (i2p::transport::transports.IsConnected (floodfill->GetIdentHash ())) if (i2p::transport::transports.IsConnected (floodfill->GetIdentHash ()))
throughTunnels = false; throughTunnels = false;
if (throughTunnels) if (throughTunnels)

44
RouterInfo.cpp

@ -167,19 +167,19 @@ namespace data
{ {
uint8_t supportedTransports = 0; uint8_t supportedTransports = 0;
bool isValidAddress = true; bool isValidAddress = true;
Address address; auto address = std::make_shared<Address>();
s.read ((char *)&address.cost, sizeof (address.cost)); s.read ((char *)&address->cost, sizeof (address->cost));
s.read ((char *)&address.date, sizeof (address.date)); s.read ((char *)&address->date, sizeof (address->date));
char transportStyle[5]; char transportStyle[5];
ReadString (transportStyle, 5, s); ReadString (transportStyle, 5, s);
if (!strcmp (transportStyle, "NTCP")) if (!strcmp (transportStyle, "NTCP"))
address.transportStyle = eTransportNTCP; address->transportStyle = eTransportNTCP;
else if (!strcmp (transportStyle, "SSU")) else if (!strcmp (transportStyle, "SSU"))
address.transportStyle = eTransportSSU; address->transportStyle = eTransportSSU;
else else
address.transportStyle = eTransportUnknown; address->transportStyle = eTransportUnknown;
address.port = 0; address->port = 0;
address.mtu = 0; address->mtu = 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;
size = be16toh (size); size = be16toh (size);
@ -194,35 +194,35 @@ namespace data
if (!strcmp (key, "host")) if (!strcmp (key, "host"))
{ {
boost::system::error_code ecode; boost::system::error_code ecode;
address.host = boost::asio::ip::address::from_string (value, ecode); address->host = boost::asio::ip::address::from_string (value, ecode);
if (ecode) if (ecode)
{ {
if (address.transportStyle == eTransportNTCP) if (address->transportStyle == eTransportNTCP)
{ {
supportedTransports |= eNTCPV4; // TODO: supportedTransports |= eNTCPV4; // TODO:
address.addressString = value; address->addressString = value;
} }
else else
{ {
supportedTransports |= eSSUV4; // TODO: supportedTransports |= eSSUV4; // TODO:
address.addressString = value; address->addressString = value;
} }
} }
else else
{ {
// add supported protocol // add supported protocol
if (address.host.is_v4 ()) if (address->host.is_v4 ())
supportedTransports |= (address.transportStyle == eTransportNTCP) ? eNTCPV4 : eSSUV4; supportedTransports |= (address->transportStyle == eTransportNTCP) ? eNTCPV4 : eSSUV4;
else else
supportedTransports |= (address.transportStyle == eTransportNTCP) ? eNTCPV6 : eSSUV6; supportedTransports |= (address->transportStyle == eTransportNTCP) ? eNTCPV6 : eSSUV6;
} }
} }
else if (!strcmp (key, "port")) else if (!strcmp (key, "port"))
address.port = boost::lexical_cast<int>(value); address->port = boost::lexical_cast<int>(value);
else if (!strcmp (key, "mtu")) else if (!strcmp (key, "mtu"))
address.mtu = boost::lexical_cast<int>(value); address->mtu = boost::lexical_cast<int>(value);
else if (!strcmp (key, "key")) else if (!strcmp (key, "key"))
Base64ToByteStream (value, strlen (value), address.key, 32); Base64ToByteStream (value, strlen (value), address->key, 32);
else if (!strcmp (key, "caps")) else if (!strcmp (key, "caps"))
ExtractCaps (value); ExtractCaps (value);
else if (key[0] == 'i') else if (key[0] == 'i')
@ -237,9 +237,9 @@ namespace data
LogPrint (eLogError, "RouterInfo: Unexpected introducer's index ", index, " skipped"); LogPrint (eLogError, "RouterInfo: Unexpected introducer's index ", index, " skipped");
if (s) continue; else return; if (s) continue; else return;
} }
if (index >= address.introducers.size ()) if (index >= address->introducers.size ())
address.introducers.resize (index + 1); address->introducers.resize (index + 1);
Introducer& introducer = address.introducers.at (index); Introducer& introducer = address->introducers.at (index);
if (!strcmp (key, "ihost")) if (!strcmp (key, "ihost"))
{ {
boost::system::error_code ecode; boost::system::error_code ecode;
@ -256,7 +256,7 @@ namespace data
} }
if (isValidAddress) if (isValidAddress)
{ {
addresses->push_back(std::make_shared<Address>(address)); addresses->push_back(address);
m_SupportedTransports |= supportedTransports; m_SupportedTransports |= supportedTransports;
} }
} }

2
SSUData.cpp

@ -240,7 +240,7 @@ namespace transport
if (!msg->IsExpired ()) if (!msg->IsExpired ())
{ {
#ifdef WITH_EVENTS #ifdef WITH_EVENTS
EmitEvent({{"type", "transport.recvmsg"} , {"ident", m_Session.GetIdentHashBase64()}, {"number", "1"}}); QueueIntEvent("transport.recvmsg", m_Session.GetIdentHashBase64(), 1);
#endif #endif
m_Handler.PutNextMessage (msg); m_Handler.PutNextMessage (msg);
} }

2
SSUSession.cpp

@ -1101,7 +1101,7 @@ namespace transport
{ {
// we are Alice // we are Alice
LogPrint (eLogDebug, "SSU: sending peer test"); LogPrint (eLogDebug, "SSU: sending peer test");
auto address = i2p::context.GetRouterInfo ().GetSSUAddress (false); auto address = i2p::context.GetRouterInfo ().GetSSUAddress (i2p::context.SupportsV4 ());
if (!address) if (!address)
{ {
LogPrint (eLogInfo, "SSU is not supported. Can't send peer test"); LogPrint (eLogInfo, "SSU is not supported. Can't send peer test");

2
Transports.cpp

@ -249,7 +249,7 @@ namespace transport
void Transports::SendMessages (const i2p::data::IdentHash& ident, const std::vector<std::shared_ptr<i2p::I2NPMessage> >& msgs) void Transports::SendMessages (const i2p::data::IdentHash& ident, const std::vector<std::shared_ptr<i2p::I2NPMessage> >& msgs)
{ {
#ifdef WITH_EVENTS #ifdef WITH_EVENTS
EmitEvent({{"type" , "transport.sendmsg"}, {"ident", ident.ToBase64()}, {"number", std::to_string(msgs.size())}}); QueueIntEvent("transport.send", ident.ToBase64(), msgs.size());
#endif #endif
m_Service.post (std::bind (&Transports::PostMessages, this, ident, msgs)); m_Service.post (std::bind (&Transports::PostMessages, this, ident, msgs));
} }

6
Tunnel.cpp

@ -612,6 +612,7 @@ namespace tunnel
for (auto it = pendingTunnels.begin (); it != pendingTunnels.end ();) for (auto it = pendingTunnels.begin (); it != pendingTunnels.end ();)
{ {
auto tunnel = it->second; auto tunnel = it->second;
auto pool = tunnel->GetTunnelPool();
switch (tunnel->GetState ()) switch (tunnel->GetState ())
{ {
case eTunnelStatePending: case eTunnelStatePending:
@ -637,6 +638,8 @@ namespace tunnel
#ifdef WITH_EVENTS #ifdef WITH_EVENTS
EmitTunnelEvent("tunnel.state", tunnel.get(), eTunnelStateBuildFailed); EmitTunnelEvent("tunnel.state", tunnel.get(), eTunnelStateBuildFailed);
#endif #endif
// for i2lua
if(pool) pool->OnTunnelBuildResult(tunnel, eBuildResultTimeout);
// delete // delete
it = pendingTunnels.erase (it); it = pendingTunnels.erase (it);
m_NumFailedTunnelCreations++; m_NumFailedTunnelCreations++;
@ -649,6 +652,9 @@ namespace tunnel
#ifdef WITH_EVENTS #ifdef WITH_EVENTS
EmitTunnelEvent("tunnel.state", tunnel.get(), eTunnelStateBuildFailed); EmitTunnelEvent("tunnel.state", tunnel.get(), eTunnelStateBuildFailed);
#endif #endif
// for i2lua
if(pool) pool->OnTunnelBuildResult(tunnel, eBuildResultRejected);
it = pendingTunnels.erase (it); it = pendingTunnels.erase (it);
m_NumFailedTunnelCreations++; m_NumFailedTunnelCreations++;
break; break;

10
TunnelPool.cpp

@ -81,6 +81,8 @@ namespace tunnel
} }
if (m_LocalDestination) if (m_LocalDestination)
m_LocalDestination->SetLeaseSetUpdated (); m_LocalDestination->SetLeaseSetUpdated ();
OnTunnelBuildResult(createdTunnel, eBuildResultOkay);
} }
void TunnelPool::TunnelExpired (std::shared_ptr<InboundTunnel> expiredTunnel) void TunnelPool::TunnelExpired (std::shared_ptr<InboundTunnel> expiredTunnel)
@ -109,6 +111,8 @@ namespace tunnel
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex); std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
m_OutboundTunnels.insert (createdTunnel); m_OutboundTunnels.insert (createdTunnel);
} }
OnTunnelBuildResult(createdTunnel, eBuildResultOkay);
//CreatePairedInboundTunnel (createdTunnel); //CreatePairedInboundTunnel (createdTunnel);
} }
@ -579,5 +583,11 @@ namespace tunnel
} }
return tun; return tun;
} }
void TunnelPool::OnTunnelBuildResult(std::shared_ptr<Tunnel> tunnel, TunnelBuildResult result)
{
auto peers = tunnel->GetPeers();
if(m_CustomPeerSelector) m_CustomPeerSelector->OnBuildResult(peers, tunnel->IsInbound(), result);
}
} }
} }

11
TunnelPool.h

@ -23,12 +23,21 @@ namespace tunnel
class InboundTunnel; class InboundTunnel;
class OutboundTunnel; class OutboundTunnel;
enum TunnelBuildResult {
eBuildResultOkay, // tunnel was built okay
eBuildResultRejected, // tunnel build was explicitly rejected
eBuildResultTimeout // tunnel build timed out
};
/** interface for custom tunnel peer selection algorithm */ /** interface for custom tunnel peer selection algorithm */
struct ITunnelPeerSelector struct ITunnelPeerSelector
{ {
typedef std::shared_ptr<const i2p::data::IdentityEx> Peer; typedef std::shared_ptr<const i2p::data::IdentityEx> Peer;
typedef std::vector<Peer> TunnelPath; typedef std::vector<Peer> TunnelPath;
virtual bool SelectPeers(TunnelPath & peers, int hops, bool isInbound) = 0; virtual bool SelectPeers(TunnelPath & peers, int hops, bool isInbound) = 0;
virtual bool OnBuildResult(TunnelPath & peers, bool isInbound, TunnelBuildResult result) = 0;
}; };
typedef std::shared_ptr<ITunnelPeerSelector> TunnelPeerSelector; typedef std::shared_ptr<ITunnelPeerSelector> TunnelPeerSelector;
@ -79,6 +88,8 @@ namespace tunnel
/** @brief get the lowest latency tunnel in this tunnel pool regardless of latency requirements */ /** @brief get the lowest latency tunnel in this tunnel pool regardless of latency requirements */
std::shared_ptr<InboundTunnel> GetLowestLatencyInboundTunnel(std::shared_ptr<InboundTunnel> exclude=nullptr) const; std::shared_ptr<InboundTunnel> GetLowestLatencyInboundTunnel(std::shared_ptr<InboundTunnel> exclude=nullptr) const;
std::shared_ptr<OutboundTunnel> GetLowestLatencyOutboundTunnel(std::shared_ptr<OutboundTunnel> exclude=nullptr) const; std::shared_ptr<OutboundTunnel> GetLowestLatencyOutboundTunnel(std::shared_ptr<OutboundTunnel> exclude=nullptr) const;
void OnTunnelBuildResult(std::shared_ptr<Tunnel> tunnel, TunnelBuildResult result);
private: private:

72
Websocket.cpp

@ -2,6 +2,7 @@
#include "Log.h" #include "Log.h"
#include <set> #include <set>
#include <functional>
#include <websocketpp/config/asio_no_tls.hpp> #include <websocketpp/config/asio_no_tls.hpp>
#include <websocketpp/server.hpp> #include <websocketpp/server.hpp>
@ -27,7 +28,11 @@ namespace i2p
typedef ServerImpl::message_ptr MessagePtr; typedef ServerImpl::message_ptr MessagePtr;
public: public:
WebsocketServerImpl(const std::string & addr, int port) : m_run(false), m_thread(nullptr) WebsocketServerImpl(const std::string & addr, int port) :
m_run(false),
m_ws_thread(nullptr),
m_ev_thread(nullptr),
m_WebsocketTicker(m_Service)
{ {
m_server.init_asio(); m_server.init_asio();
m_server.set_open_handler(std::bind(&WebsocketServerImpl::ConnOpened, this, std::placeholders::_1)); m_server.set_open_handler(std::bind(&WebsocketServerImpl::ConnOpened, this, std::placeholders::_1));
@ -44,7 +49,7 @@ namespace i2p
void Start() { void Start() {
m_run = true; m_run = true;
m_server.start_accept(); m_server.start_accept();
m_thread = new std::thread([&] () { m_ws_thread = new std::thread([&] () {
while(m_run) { while(m_run) {
try { try {
m_server.run(); m_server.run();
@ -53,16 +58,35 @@ namespace i2p
} }
} }
}); });
m_ev_thread = new std::thread([&] () {
while(m_run) {
try {
m_Service.run();
break;
} catch (std::exception & e ) {
LogPrint(eLogError, "Websocket service: ", e.what());
}
}
});
ScheduleTick();
} }
void Stop() { void Stop() {
m_run = false; m_run = false;
m_Service.stop();
m_server.stop(); m_server.stop();
if(m_thread) {
m_thread->join(); if(m_ev_thread) {
delete m_thread; m_ev_thread->join();
delete m_ev_thread;
} }
m_thread = nullptr; m_ev_thread = nullptr;
if(m_ws_thread) {
m_ws_thread->join();
delete m_ws_thread;
}
m_ws_thread = nullptr;
} }
void ConnOpened(ServerConn c) void ConnOpened(ServerConn c)
@ -82,11 +106,40 @@ namespace i2p
(void) conn; (void) conn;
(void) msg; (void) msg;
} }
void HandleTick(const boost::system::error_code & ec)
{
if(ec != boost::asio::error::operation_aborted)
LogPrint(eLogError, "Websocket ticker: ", ec.message());
// pump collected events to us
i2p::event::core.PumpCollected(this);
ScheduleTick();
}
void ScheduleTick()
{
LogPrint(eLogDebug, "Websocket schedule tick");
boost::posix_time::seconds dlt(1);
m_WebsocketTicker.expires_from_now(dlt);
m_WebsocketTicker.async_wait(std::bind(&WebsocketServerImpl::HandleTick, this, std::placeholders::_1));
}
/** @brief called from m_ev_thread */
void HandlePumpEvent(const EventType & ev, const uint64_t & val)
{
EventType e;
for (const auto & i : ev)
e[i.first] = i.second;
e["number"] = std::to_string(val);
HandleEvent(e);
}
/** @brief called from m_ws_thread */
void HandleEvent(const EventType & ev) void HandleEvent(const EventType & ev)
{ {
std::lock_guard<std::mutex> lock(m_connsMutex); std::lock_guard<std::mutex> lock(m_connsMutex);
LogPrint(eLogDebug, "websocket event");
boost::property_tree::ptree event; boost::property_tree::ptree event;
for (const auto & item : ev) { for (const auto & item : ev) {
event.put(item.first, item.second); event.put(item.first, item.second);
@ -105,10 +158,13 @@ namespace i2p
private: private:
typedef std::set<ServerConn, std::owner_less<ServerConn> > ConnList; typedef std::set<ServerConn, std::owner_less<ServerConn> > ConnList;
bool m_run; bool m_run;
std::thread * m_thread; std::thread * m_ws_thread;
std::thread * m_ev_thread;
std::mutex m_connsMutex; std::mutex m_connsMutex;
ConnList m_conns; ConnList m_conns;
ServerImpl m_server; ServerImpl m_server;
boost::asio::io_service m_Service;
boost::asio::deadline_timer m_WebsocketTicker;
}; };

2
version.h

@ -21,7 +21,7 @@
#define I2P_VERSION_MAJOR 0 #define I2P_VERSION_MAJOR 0
#define I2P_VERSION_MINOR 9 #define I2P_VERSION_MINOR 9
#define I2P_VERSION_MICRO 27 #define I2P_VERSION_MICRO 28
#define I2P_VERSION_PATCH 0 #define I2P_VERSION_PATCH 0
#define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO) #define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO)

Loading…
Cancel
Save