|
|
@ -23,7 +23,7 @@ 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); |
|
|
@ -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,151 @@ 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) |
|
|
|
void DatagramSession::Ack() |
|
|
|
{ |
|
|
|
{ |
|
|
|
if(!m_RoutingSession) |
|
|
|
m_LastUse = i2p::util::GetMillisecondsSinceEpoch(); |
|
|
|
{ |
|
|
|
auto path = GetSharedRoutingPath(); |
|
|
|
// try to get one
|
|
|
|
if(path) |
|
|
|
if(m_RemoteLeaseSet) m_RoutingSession = m_LocalDestination->GetRoutingSession(m_RemoteLeaseSet, true); |
|
|
|
path->updateTime = i2p::util::GetSecondsSinceEpoch (); |
|
|
|
else |
|
|
|
} |
|
|
|
{ |
|
|
|
|
|
|
|
UpdateLeaseSet(msg); |
|
|
|
std::shared_ptr<i2p::garlic::GarlicRoutingPath> DatagramSession::GetSharedRoutingPath () |
|
|
|
return; |
|
|
|
{ |
|
|
|
|
|
|
|
if(!m_RoutingSession) { |
|
|
|
|
|
|
|
if(!m_RemoteLeaseSet) { |
|
|
|
|
|
|
|
m_RemoteLeaseSet = m_LocalDestination->FindLeaseSet(m_RemoteIdent); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if(!m_RemoteLeaseSet) { |
|
|
|
|
|
|
|
// 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 a routing session?
|
|
|
|
auto path = m_RoutingSession->GetSharedRoutingPath(); |
|
|
|
if(m_RoutingSession) |
|
|
|
if(path) { |
|
|
|
{ |
|
|
|
if (m_CurrentOutboundTunnel && !m_CurrentOutboundTunnel->IsEstablished()) { |
|
|
|
// should we switch paths?
|
|
|
|
// bad outbound tunnel, switch outbound tunnel
|
|
|
|
if(ShouldUpdateRoutingPath ()) |
|
|
|
m_CurrentOutboundTunnel = m_LocalDestination->GetTunnelPool()->GetNextOutboundTunnel(m_CurrentOutboundTunnel); |
|
|
|
{ |
|
|
|
path->outboundTunnel = m_CurrentOutboundTunnel; |
|
|
|
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 (); |
|
|
|
if(m_CurrentRemoteLease && ! m_CurrentRemoteLease->ExpiresWithin(DATAGRAM_SESSION_LEASE_HANDOVER_WINDOW)) { |
|
|
|
// make sure we have a routing path
|
|
|
|
// bad lease, switch to next one
|
|
|
|
if (routingPath) |
|
|
|
if(m_RemoteLeaseSet) { |
|
|
|
{ |
|
|
|
auto ls = m_RemoteLeaseSet->GetNonExpiredLeasesExcluding([&](const i2p::data::Lease& l) -> bool { |
|
|
|
auto outboundTunnel = routingPath->outboundTunnel; |
|
|
|
return l.tunnelGateway == m_CurrentRemoteLease->tunnelGateway || l.endDate <= m_CurrentRemoteLease->endDate; |
|
|
|
if (outboundTunnel) |
|
|
|
}); |
|
|
|
{ |
|
|
|
auto sz = ls.size(); |
|
|
|
if(outboundTunnel->IsEstablished()) |
|
|
|
if (sz) { |
|
|
|
{ |
|
|
|
auto idx = rand() % sz; |
|
|
|
m_LastSuccess = i2p::util::GetMillisecondsSinceEpoch (); |
|
|
|
m_CurrentRemoteLease = ls[idx]; |
|
|
|
// we have a routing path and routing session and the outbound tunnel we are using is good
|
|
|
|
} else { |
|
|
|
// wrap message with routing session and send down routing path's outbound tunnel wrapped for the IBGW
|
|
|
|
// no more leases, bail
|
|
|
|
auto m = m_RoutingSession->WrapSingleMessage(msg); |
|
|
|
LogPrint(eLogWarning, "DatagramSession: no more valid remote leases to ", m_RemoteIdent.ToBase32()); |
|
|
|
routingPath->outboundTunnel->SendTunnelDataMsg({i2p::tunnel::TunnelMessageBlock{ |
|
|
|
|
|
|
|
i2p::tunnel::eDeliveryTypeTunnel, |
|
|
|
|
|
|
|
routingPath->remoteLease->tunnelGateway, routingPath->remoteLease->tunnelID, |
|
|
|
|
|
|
|
m |
|
|
|
|
|
|
|
}}); |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
// no remote lease set?
|
|
|
|
|
|
|
|
LogPrint(eLogWarning, "DatagramSession: no cached remote lease set for ", m_RemoteIdent.ToBase32()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
path->remoteLease = m_CurrentRemoteLease; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
// no current path, make one
|
|
|
|
|
|
|
|
path = std::make_shared<i2p::garlic::GarlicRoutingPath>(); |
|
|
|
|
|
|
|
// switch outbound tunnel if bad
|
|
|
|
|
|
|
|
if(m_CurrentOutboundTunnel == nullptr || ! m_CurrentOutboundTunnel->IsEstablished()) { |
|
|
|
|
|
|
|
m_CurrentOutboundTunnel = m_LocalDestination->GetTunnelPool()->GetNextOutboundTunnel(m_CurrentOutboundTunnel); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// switch lease if bad
|
|
|
|
|
|
|
|
if(m_CurrentRemoteLease == nullptr || m_CurrentRemoteLease->ExpiresWithin(DATAGRAM_SESSION_LEASE_HANDOVER_WINDOW)) { |
|
|
|
|
|
|
|
if(!m_RemoteLeaseSet) { |
|
|
|
|
|
|
|
m_RemoteLeaseSet = m_LocalDestination->FindLeaseSet(m_RemoteIdent); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
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; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
path->outboundTunnel = m_CurrentOutboundTunnel; |
|
|
|
|
|
|
|
path->remoteLease = m_CurrentRemoteLease; |
|
|
|
|
|
|
|
m_RoutingSession->SetSharedRoutingPath(path); |
|
|
|
} |
|
|
|
} |
|
|
|
auto now = i2p::util::GetMillisecondsSinceEpoch (); |
|
|
|
return path; |
|
|
|
// 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) |
|
|
|
void DatagramSession::HandleLeaseSetUpdated(std::shared_ptr<i2p::data::LeaseSet> ls) |
|
|
|
{ |
|
|
|
{ |
|
|
|
if(m_RoutingSession == nullptr && m_RemoteLeaseSet) |
|
|
|
// only update lease set if found and newer than previous lease set
|
|
|
|
m_RoutingSession = m_LocalDestination->GetRoutingSession(m_RemoteLeaseSet, true); |
|
|
|
uint64_t oldExpire = 0; |
|
|
|
if(!m_RoutingSession) return; |
|
|
|
if(m_RemoteLeaseSet) oldExpire = m_RemoteLeaseSet->GetExpirationTime(); |
|
|
|
// set routing path and update time we last updated the routing path
|
|
|
|
if(ls && ls->GetExpirationTime() > oldExpire) m_RemoteLeaseSet = ls; |
|
|
|
m_RoutingSession->SetSharedRoutingPath (path); |
|
|
|
|
|
|
|
m_LastPathChange = i2p::util::GetMillisecondsSinceEpoch (); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
bool DatagramSession::ShouldUpdateRoutingPath() const |
|
|
|
void DatagramSession::HandleSend(std::shared_ptr<I2NPMessage> msg) |
|
|
|
{ |
|
|
|
{ |
|
|
|
bool dead = m_RoutingSession == nullptr || m_RoutingSession->GetSharedRoutingPath () == nullptr; |
|
|
|
m_SendQueue.push_back(msg); |
|
|
|
auto now = i2p::util::GetMillisecondsSinceEpoch (); |
|
|
|
// flush queue right away if full
|
|
|
|
// we need to rotate paths becuase the routing path is too old
|
|
|
|
if(m_SendQueue.size() >= DATAGRAM_SEND_QUEUE_MAX_SIZE) FlushSendQueue(); |
|
|
|
// if (now - m_LastPathChange >= DATAGRAM_SESSION_PATH_SWITCH_INTERVAL) return true;
|
|
|
|
|
|
|
|
// 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; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void DatagramSession::FlushSendQueue () |
|
|
|
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::vector<i2p::tunnel::TunnelMessageBlock> send; |
|
|
|
{ |
|
|
|
auto routingPath = GetSharedRoutingPath(); |
|
|
|
std::shared_ptr<i2p::tunnel::OutboundTunnel> outboundTunnel = nullptr; |
|
|
|
// if we don't have a routing path we will drop all queued messages
|
|
|
|
std::shared_ptr<i2p::garlic::GarlicRoutingPath> routingPath = nullptr; |
|
|
|
if(routingPath) |
|
|
|
// get existing routing path if we have one
|
|
|
|
|
|
|
|
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 |
|
|
|
|
|
|
|
outboundTunnel = m_LocalDestination->GetTunnelPool()->GetNextOutboundTunnel(routingPath->outboundTunnel); // no so we'll switch outbound tunnels
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// do we have an outbound tunnel that works already ?
|
|
|
|
|
|
|
|
if(!outboundTunnel) |
|
|
|
|
|
|
|
outboundTunnel = m_LocalDestination->GetTunnelPool()->GetNextOutboundTunnel(); // no, let's get a new outbound tunnel as we probably just started
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(outboundTunnel) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
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(routingPath->remoteLease) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if(routingPath->remoteLease->ExpiresWithin(DATAGRAM_SESSION_LEASE_HANDOVER_WINDOW, DATAGRAM_SESSION_LEASE_HANDOVER_FUDGE)) |
|
|
|
|
|
|
|
lease = GetNextLease(); |
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
lease = routingPath->remoteLease; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
lease = GetNextLease(); |
|
|
|
|
|
|
|
if(lease) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// we have a valid lease to use and an outbound tunnel
|
|
|
|
|
|
|
|
// create new routing path
|
|
|
|
|
|
|
|
uint32_t now = i2p::util::GetSecondsSinceEpoch(); |
|
|
|
|
|
|
|
routingPath = std::make_shared<i2p::garlic::GarlicRoutingPath>(i2p::garlic::GarlicRoutingPath{ |
|
|
|
|
|
|
|
outboundTunnel, |
|
|
|
|
|
|
|
lease, |
|
|
|
|
|
|
|
0, |
|
|
|
|
|
|
|
now, |
|
|
|
|
|
|
|
0 |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
else // we don't have a new routing path to give
|
|
|
|
|
|
|
|
routingPath = nullptr; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return routingPath; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void DatagramSession::ResetRoutingPath() |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if(m_RoutingSession) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
auto routingPath = m_RoutingSession->GetSharedRoutingPath(); |
|
|
|
|
|
|
|
if(routingPath && routingPath->remoteLease) // we have a remote lease already specified and a routing path
|
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// 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() |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
auto now = i2p::util::GetMillisecondsSinceEpoch (); |
|
|
|
|
|
|
|
std::shared_ptr<const i2p::data::Lease> next = nullptr; |
|
|
|
|
|
|
|
if(m_RemoteLeaseSet) |
|
|
|
|
|
|
|
{ |
|
|
|
{ |
|
|
|
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); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|