#include "Crypto.h" #include "Log.h" #include "RouterInfo.h" #include "RouterContext.h" #include "Tunnel.h" #include "Timestamp.h" #include "Destination.h" #include "Streaming.h" namespace i2p { namespace stream { Stream::Stream (boost::asio::io_service& service, StreamingDestination& local, std::shared_ptr remote, int port): m_Service (service), m_SendStreamID (0), m_SequenceNumber (0), m_LastReceivedSequenceNumber (-1), m_Status (eStreamStatusNew), m_IsAckSendScheduled (false), m_LocalDestination (local), m_RemoteLeaseSet (remote), m_ReceiveTimer (m_Service), m_ResendTimer (m_Service), m_AckSendTimer (m_Service), m_NumSentBytes (0), m_NumReceivedBytes (0), m_Port (port), m_WindowSize (MIN_WINDOW_SIZE), m_RTT (INITIAL_RTT), m_RTO (INITIAL_RTO), m_LastWindowSizeIncreaseTime (0), m_NumResendAttempts (0) { RAND_bytes ((uint8_t *)&m_RecvStreamID, 4); m_RemoteIdentity = remote->GetIdentity (); } Stream::Stream (boost::asio::io_service& service, StreamingDestination& local): m_Service (service), m_SendStreamID (0), m_SequenceNumber (0), m_LastReceivedSequenceNumber (-1), m_Status (eStreamStatusNew), m_IsAckSendScheduled (false), m_LocalDestination (local), m_ReceiveTimer (m_Service), m_ResendTimer (m_Service), m_AckSendTimer (m_Service), m_NumSentBytes (0), m_NumReceivedBytes (0), m_Port (0), m_WindowSize (MIN_WINDOW_SIZE), m_RTT (INITIAL_RTT), m_RTO (INITIAL_RTO), m_LastWindowSizeIncreaseTime (0), m_NumResendAttempts (0) { RAND_bytes ((uint8_t *)&m_RecvStreamID, 4); } Stream::~Stream () { while (!m_ReceiveQueue.empty ()) { auto packet = m_ReceiveQueue.front (); m_ReceiveQueue.pop (); delete packet; } for (auto it: m_SentPackets) delete it; m_SentPackets.clear (); for (auto it: m_SavedPackets) delete it; m_SavedPackets.clear (); LogPrint (eLogDebug, "Streaming: Stream deleted"); } void Stream::Terminate () { m_AckSendTimer.cancel (); m_ReceiveTimer.cancel (); m_ResendTimer.cancel (); if (m_SendHandler) { auto handler = m_SendHandler; m_SendHandler = nullptr; handler (boost::asio::error::make_error_code (boost::asio::error::operation_aborted)); } m_LocalDestination.DeleteStream (shared_from_this ()); } void Stream::HandleNextPacket (Packet * packet) { m_NumReceivedBytes += packet->GetLength (); if (!m_SendStreamID) m_SendStreamID = packet->GetReceiveStreamID (); if (!packet->IsNoAck ()) // ack received ProcessAck (packet); int32_t receivedSeqn = packet->GetSeqn (); bool isSyn = packet->IsSYN (); if (!receivedSeqn && !isSyn) { // plain ack LogPrint (eLogDebug, "Streaming: Plain ACK received"); delete packet; return; } LogPrint (eLogDebug, "Streaming: Received seqn=", receivedSeqn, " on sSID=", m_SendStreamID); if (receivedSeqn == m_LastReceivedSequenceNumber + 1) { // we have received next in sequence message ProcessPacket (packet); // we should also try stored messages if any for (auto it = m_SavedPackets.begin (); it != m_SavedPackets.end ();) { if ((*it)->GetSeqn () == (uint32_t)(m_LastReceivedSequenceNumber + 1)) { Packet * savedPacket = *it; m_SavedPackets.erase (it++); ProcessPacket (savedPacket); } else break; } // schedule ack for last message if (m_Status == eStreamStatusOpen) { if (!m_IsAckSendScheduled) { m_IsAckSendScheduled = true; auto ackTimeout = m_RTT/10; if (ackTimeout > ACK_SEND_TIMEOUT) ackTimeout = ACK_SEND_TIMEOUT; m_AckSendTimer.expires_from_now (boost::posix_time::milliseconds(ackTimeout)); m_AckSendTimer.async_wait (std::bind (&Stream::HandleAckSendTimer, shared_from_this (), std::placeholders::_1)); } } else if (isSyn) // we have to send SYN back to incoming connection SendBuffer (); // also sets m_IsOpen } else { if (receivedSeqn <= m_LastReceivedSequenceNumber) { // we have received duplicate LogPrint (eLogWarning, "Streaming: Duplicate message ", receivedSeqn, " on sSID=", m_SendStreamID); SendQuickAck (); // resend ack for previous message again delete packet; // packet dropped } else { LogPrint (eLogWarning, "Streaming: Missing messages on sSID=", m_SendStreamID, ": from ", m_LastReceivedSequenceNumber + 1, " to ", receivedSeqn - 1); // save message and wait for missing message again SavePacket (packet); if (m_LastReceivedSequenceNumber >= 0) { // send NACKs for missing messages ASAP if (m_IsAckSendScheduled) { m_IsAckSendScheduled = false; m_AckSendTimer.cancel (); } SendQuickAck (); } else { // wait for SYN m_IsAckSendScheduled = true; m_AckSendTimer.expires_from_now (boost::posix_time::milliseconds(ACK_SEND_TIMEOUT)); m_AckSendTimer.async_wait (std::bind (&Stream::HandleAckSendTimer, shared_from_this (), std::placeholders::_1)); } } } } void Stream::SavePacket (Packet * packet) { if (!m_SavedPackets.insert (packet).second) delete packet; } void Stream::ProcessPacket (Packet * packet) { // process flags uint32_t receivedSeqn = packet->GetSeqn (); uint16_t flags = packet->GetFlags (); LogPrint (eLogDebug, "Streaming: Process seqn=", receivedSeqn, ", flags=", flags); const uint8_t * optionData = packet->GetOptionData (); if (flags & PACKET_FLAG_DELAY_REQUESTED) optionData += 2; if (flags & PACKET_FLAG_FROM_INCLUDED) { m_RemoteIdentity = std::make_shared(optionData, packet->GetOptionSize ()); optionData += m_RemoteIdentity->GetFullLen (); if (!m_RemoteLeaseSet) LogPrint (eLogDebug, "Streaming: Incoming stream from ", m_RemoteIdentity->GetIdentHash ().ToBase64 (), ", sSID=", m_SendStreamID, ", rSID=", m_RecvStreamID); } if (flags & PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED) { uint16_t maxPacketSize = bufbe16toh (optionData); LogPrint (eLogDebug, "Streaming: Max packet size ", maxPacketSize); optionData += 2; } if (flags & PACKET_FLAG_SIGNATURE_INCLUDED) { uint8_t signature[256]; auto signatureLen = m_RemoteIdentity->GetSignatureLen (); memcpy (signature, optionData, signatureLen); memset (const_cast(optionData), 0, signatureLen); if (!m_RemoteIdentity->Verify (packet->GetBuffer (), packet->GetLength (), signature)) { LogPrint (eLogError, "Streaming: Signature verification failed, sSID=", m_SendStreamID, ", rSID=", m_RecvStreamID); Close (); flags |= PACKET_FLAG_CLOSE; } memcpy (const_cast(optionData), signature, signatureLen); optionData += signatureLen; } packet->offset = packet->GetPayload () - packet->buf; if (packet->GetLength () > 0) { m_ReceiveQueue.push (packet); m_ReceiveTimer.cancel (); } else delete packet; m_LastReceivedSequenceNumber = receivedSeqn; if (flags & PACKET_FLAG_RESET) { LogPrint (eLogDebug, "Streaming: closing stream sSID=", m_SendStreamID, ", rSID=", m_RecvStreamID, ": reset flag received in packet #", receivedSeqn); m_Status = eStreamStatusReset; Close (); } else if (flags & PACKET_FLAG_CLOSE) { if (m_Status != eStreamStatusClosed) SendClose (); m_Status = eStreamStatusClosed; Terminate (); } } void Stream::ProcessAck (Packet * packet) { bool acknowledged = false; auto ts = i2p::util::GetMillisecondsSinceEpoch (); uint32_t ackThrough = packet->GetAckThrough (); if (ackThrough > m_SequenceNumber) { LogPrint (eLogError, "Streaming: Unexpected ackThrough=", ackThrough, " > seqn=", m_SequenceNumber); return; } int nackCount = packet->GetNACKCount (); for (auto it = m_SentPackets.begin (); it != m_SentPackets.end ();) { auto seqn = (*it)->GetSeqn (); if (seqn <= ackThrough) { if (nackCount > 0) { bool nacked = false; for (int i = 0; i < nackCount; i++) if (seqn == packet->GetNACK (i)) { nacked = true; break; } if (nacked) { LogPrint (eLogDebug, "Streaming: Packet ", seqn, " NACK"); ++it; continue; } } auto sentPacket = *it; uint64_t rtt = ts - sentPacket->sendTime; if(ts < sentPacket->sendTime) { LogPrint(eLogError, "Streaming: Packet ", seqn, "sent from the future, sendTime=", sentPacket->sendTime); rtt = 1; } m_RTT = (m_RTT*seqn + rtt)/(seqn + 1); m_RTO = m_RTT*1.5; // TODO: implement it better LogPrint (eLogDebug, "Streaming: Packet ", seqn, " acknowledged rtt=", rtt, " sentTime=", sentPacket->sendTime); m_SentPackets.erase (it++); delete sentPacket; acknowledged = true; if (m_WindowSize < WINDOW_SIZE) m_WindowSize++; // slow start else { // linear growth if (ts > m_LastWindowSizeIncreaseTime + m_RTT) { m_WindowSize++; if (m_WindowSize > MAX_WINDOW_SIZE) m_WindowSize = MAX_WINDOW_SIZE; m_LastWindowSizeIncreaseTime = ts; } } if (!seqn && m_RoutingSession) // first message confirmed m_RoutingSession->SetSharedRoutingPath ( std::make_shared ( i2p::garlic::GarlicRoutingPath{m_CurrentOutboundTunnel, m_CurrentRemoteLease, m_RTT, 0, 0})); } else break; } if (m_SentPackets.empty ()) m_ResendTimer.cancel (); if (acknowledged) { m_NumResendAttempts = 0; SendBuffer (); } if (m_Status == eStreamStatusClosed) Terminate (); else if (m_Status == eStreamStatusClosing) Close (); // check is all outgoing messages have been sent and we can send close } size_t Stream::Send (const uint8_t * buf, size_t len) { if (len > 0 && buf) { std::unique_lock l(m_SendBufferMutex); m_SendBuffer.clear (); m_SendBuffer.write ((const char *)buf, len); } m_Service.post (std::bind (&Stream::SendBuffer, shared_from_this ())); return len; } void Stream::AsyncSend (const uint8_t * buf, size_t len, SendHandler handler) { if (m_SendHandler) handler (boost::asio::error::make_error_code (boost::asio::error::in_progress)); else m_SendHandler = handler; Send (buf, len); } void Stream::SendBuffer () { int numMsgs = m_WindowSize - m_SentPackets.size (); if (numMsgs <= 0) return; // window is full bool isNoAck = m_LastReceivedSequenceNumber < 0; // first packet std::vector packets; { std::unique_lock l(m_SendBufferMutex); while ((m_Status == eStreamStatusNew) || (IsEstablished () && !m_SendBuffer.eof () && numMsgs > 0)) { Packet * p = new Packet (); uint8_t * packet = p->GetBuffer (); // TODO: implement setters size_t size = 0; htobe32buf (packet + size, m_SendStreamID); size += 4; // sendStreamID htobe32buf (packet + size, m_RecvStreamID); size += 4; // receiveStreamID htobe32buf (packet + size, m_SequenceNumber++); size += 4; // sequenceNum if (isNoAck) htobuf32 (packet + size, 0); else htobe32buf (packet + size, m_LastReceivedSequenceNumber); size += 4; // ack Through packet[size] = 0; size++; // NACK count packet[size] = m_RTO/1000; size++; // resend delay if (m_Status == eStreamStatusNew) { // initial packet m_Status = eStreamStatusOpen; uint16_t flags = PACKET_FLAG_SYNCHRONIZE | PACKET_FLAG_FROM_INCLUDED | PACKET_FLAG_SIGNATURE_INCLUDED | PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED; if (isNoAck) flags |= PACKET_FLAG_NO_ACK; htobe16buf (packet + size, flags); size += 2; // flags size_t identityLen = m_LocalDestination.GetOwner ()->GetIdentity ()->GetFullLen (); size_t signatureLen = m_LocalDestination.GetOwner ()->GetIdentity ()->GetSignatureLen (); htobe16buf (packet + size, identityLen + signatureLen + 2); // identity + signature + packet size size += 2; // options size m_LocalDestination.GetOwner ()->GetIdentity ()->ToBuffer (packet + size, identityLen); size += identityLen; // from htobe16buf (packet + size, STREAMING_MTU); size += 2; // max packet size uint8_t * signature = packet + size; // set it later memset (signature, 0, signatureLen); // zeroes for now size += signatureLen; // signature m_SendBuffer.read ((char *)(packet + size), STREAMING_MTU - size); size += m_SendBuffer.gcount (); // payload m_LocalDestination.GetOwner ()->Sign (packet, size, signature); } else { // follow on packet htobuf16 (packet + size, 0); size += 2; // flags htobuf16 (packet + size, 0); // no options size += 2; // options size m_SendBuffer.read((char *)(packet + size), STREAMING_MTU - size); size += m_SendBuffer.gcount (); // payload } p->len = size; packets.push_back (p); numMsgs--; } if (m_SendBuffer.eof () && m_SendHandler) { m_SendHandler (boost::system::error_code ()); m_SendHandler = nullptr; } } if (packets.size () > 0) { if (m_SavedPackets.empty ()) // no NACKS { m_IsAckSendScheduled = false; m_AckSendTimer.cancel (); } bool isEmpty = m_SentPackets.empty (); auto ts = i2p::util::GetMillisecondsSinceEpoch (); for (auto& it: packets) { it->sendTime = ts; m_SentPackets.insert (it); } SendPackets (packets); if (m_Status == eStreamStatusClosing && m_SendBuffer.eof ()) SendClose (); if (isEmpty) ScheduleResend (); } } void Stream::SendQuickAck () { int32_t lastReceivedSeqn = m_LastReceivedSequenceNumber; if (!m_SavedPackets.empty ()) { int32_t seqn = (*m_SavedPackets.rbegin ())->GetSeqn (); if (seqn > lastReceivedSeqn) lastReceivedSeqn = seqn; } if (lastReceivedSeqn < 0) { LogPrint (eLogError, "Streaming: No packets have been received yet"); return; } Packet p; uint8_t * packet = p.GetBuffer (); size_t size = 0; htobe32buf (packet + size, m_SendStreamID); size += 4; // sendStreamID htobe32buf (packet + size, m_RecvStreamID); size += 4; // receiveStreamID htobuf32 (packet + size, 0); // this is plain Ack message size += 4; // sequenceNum htobe32buf (packet + size, lastReceivedSeqn); size += 4; // ack Through uint8_t numNacks = 0; if (lastReceivedSeqn > m_LastReceivedSequenceNumber) { // fill NACKs uint8_t * nacks = packet + size + 1; auto nextSeqn = m_LastReceivedSequenceNumber + 1; for (auto it: m_SavedPackets) { auto seqn = it->GetSeqn (); if (numNacks + (seqn - nextSeqn) >= 256) { LogPrint (eLogError, "Streaming: Number of NACKs exceeds 256. seqn=", seqn, " nextSeqn=", nextSeqn); htobe32buf (packet + 12, nextSeqn); // change ack Through break; } for (uint32_t i = nextSeqn; i < seqn; i++) { htobe32buf (nacks, i); nacks += 4; numNacks++; } nextSeqn = seqn + 1; } packet[size] = numNacks; size++; // NACK count size += numNacks*4; // NACKs } else { // No NACKs packet[size] = 0; size++; // NACK count } size++; // resend delay htobuf16 (packet + size, 0); // nof flags set size += 2; // flags htobuf16 (packet + size, 0); // no options size += 2; // options size p.len = size; SendPackets (std::vector { &p }); LogPrint (eLogDebug, "Streaming: Quick Ack sent. ", (int)numNacks, " NACKs"); } void Stream::Close () { LogPrint(eLogDebug, "Streaming: closing stream with sSID=", m_SendStreamID, ", rSID=", m_RecvStreamID, ", status=", m_Status); switch (m_Status) { case eStreamStatusOpen: m_Status = eStreamStatusClosing; Close (); // recursion if (m_Status == eStreamStatusClosing) //still closing LogPrint (eLogDebug, "Streaming: Trying to send stream data before closing, sSID=", m_SendStreamID); break; case eStreamStatusReset: // TODO: send reset Terminate (); break; case eStreamStatusClosing: if (m_SentPackets.empty () && m_SendBuffer.eof ()) // nothing to send { m_Status = eStreamStatusClosed; SendClose (); } break; case eStreamStatusClosed: // already closed Terminate (); break; default: LogPrint (eLogWarning, "Streaming: Unexpected stream status ", (int)m_Status, "sSID=", m_SendStreamID); }; } void Stream::SendClose () { Packet * p = new Packet (); uint8_t * packet = p->GetBuffer (); size_t size = 0; htobe32buf (packet + size, m_SendStreamID); size += 4; // sendStreamID htobe32buf (packet + size, m_RecvStreamID); size += 4; // receiveStreamID htobe32buf (packet + size, m_SequenceNumber++); size += 4; // sequenceNum htobe32buf (packet + size, m_LastReceivedSequenceNumber >= 0 ? m_LastReceivedSequenceNumber : 0); size += 4; // ack Through packet[size] = 0; size++; // NACK count size++; // resend delay htobe16buf (packet + size, PACKET_FLAG_CLOSE | PACKET_FLAG_SIGNATURE_INCLUDED); size += 2; // flags size_t signatureLen = m_LocalDestination.GetOwner ()->GetIdentity ()->GetSignatureLen (); htobe16buf (packet + size, signatureLen); // signature only size += 2; // options size uint8_t * signature = packet + size; memset (packet + size, 0, signatureLen); size += signatureLen; // signature m_LocalDestination.GetOwner ()->Sign (packet, size, signature); p->len = size; m_Service.post (std::bind (&Stream::SendPacket, shared_from_this (), p)); LogPrint (eLogDebug, "Streaming: FIN sent, sSID=", m_SendStreamID); } size_t Stream::ConcatenatePackets (uint8_t * buf, size_t len) { size_t pos = 0; while (pos < len && !m_ReceiveQueue.empty ()) { Packet * packet = m_ReceiveQueue.front (); size_t l = std::min (packet->GetLength (), len - pos); memcpy (buf + pos, packet->GetBuffer (), l); pos += l; packet->offset += l; if (!packet->GetLength ()) { m_ReceiveQueue.pop (); delete packet; } } return pos; } bool Stream::SendPacket (Packet * packet) { if (packet) { if (m_IsAckSendScheduled) { m_IsAckSendScheduled = false; m_AckSendTimer.cancel (); } SendPackets (std::vector { packet }); bool isEmpty = m_SentPackets.empty (); m_SentPackets.insert (packet); if (isEmpty) ScheduleResend (); return true; } else return false; } void Stream::SendPackets (const std::vector& packets) { if (!m_RemoteLeaseSet) { UpdateCurrentRemoteLease (); if (!m_RemoteLeaseSet) { LogPrint (eLogError, "Streaming: Can't send packets, missing remote LeaseSet, sSID=", m_SendStreamID); return; } } if (!m_CurrentOutboundTunnel) // first message to send { // try to get shared path first if (!m_RoutingSession) m_RoutingSession = m_LocalDestination.GetOwner ()->GetRoutingSession (m_RemoteLeaseSet, true); if (m_RoutingSession) { auto routingPath = m_RoutingSession->GetSharedRoutingPath (); if (routingPath) { m_CurrentOutboundTunnel = routingPath->outboundTunnel; m_CurrentRemoteLease = routingPath->remoteLease; m_RTT = routingPath->rtt; m_RTO = m_RTT*1.5; // TODO: implement it better } } } if (!m_CurrentOutboundTunnel || !m_CurrentOutboundTunnel->IsEstablished ()) m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ()->GetTunnelPool ()->GetNewOutboundTunnel (m_CurrentOutboundTunnel); if (!m_CurrentOutboundTunnel) { LogPrint (eLogError, "Streaming: No outbound tunnels in the pool, sSID=", m_SendStreamID); return; } auto ts = i2p::util::GetMillisecondsSinceEpoch (); if (!m_CurrentRemoteLease || !m_CurrentRemoteLease->endDate || // excluded from LeaseSet ts >= m_CurrentRemoteLease->endDate - i2p::data::LEASE_ENDDATE_THRESHOLD) UpdateCurrentRemoteLease (true); if (m_CurrentRemoteLease && ts < m_CurrentRemoteLease->endDate + i2p::data::LEASE_ENDDATE_THRESHOLD) { std::vector msgs; for (auto it: packets) { auto msg = m_RoutingSession->WrapSingleMessage (m_LocalDestination.CreateDataMessage (it->GetBuffer (), it->GetLength (), m_Port)); msgs.push_back (i2p::tunnel::TunnelMessageBlock { i2p::tunnel::eDeliveryTypeTunnel, m_CurrentRemoteLease->tunnelGateway, m_CurrentRemoteLease->tunnelID, msg }); m_NumSentBytes += it->GetLength (); } m_CurrentOutboundTunnel->SendTunnelDataMsg (msgs); } else { LogPrint (eLogWarning, "Streaming: Remote lease is not available, sSID=", m_SendStreamID); if (m_RoutingSession) m_RoutingSession->SetSharedRoutingPath (nullptr); // invalidate routing path } } void Stream::SendUpdatedLeaseSet () { if (m_RoutingSession) { if (m_RoutingSession->IsLeaseSetNonConfirmed ()) { auto ts = i2p::util::GetMillisecondsSinceEpoch (); if (ts > m_RoutingSession->GetLeaseSetSubmissionTime () + i2p::garlic::LEASET_CONFIRMATION_TIMEOUT) { // LeaseSet was not confirmed, should try other tunnels LogPrint (eLogWarning, "Streaming: LeaseSet was not confrimed in ", i2p::garlic::LEASET_CONFIRMATION_TIMEOUT, " milliseconds. Trying to resubmit"); m_RoutingSession->SetSharedRoutingPath (nullptr); m_CurrentOutboundTunnel = nullptr; m_CurrentRemoteLease = nullptr; SendQuickAck (); } } else if (m_RoutingSession->IsLeaseSetUpdated ()) { LogPrint (eLogDebug, "Streaming: sending updated LeaseSet"); SendQuickAck (); } } } void Stream::ScheduleResend () { m_ResendTimer.cancel (); // check for invalid value if (m_RTO <= 0) m_RTO = INITIAL_RTO; m_ResendTimer.expires_from_now (boost::posix_time::milliseconds(m_RTO)); m_ResendTimer.async_wait (std::bind (&Stream::HandleResendTimer, shared_from_this (), std::placeholders::_1)); } void Stream::HandleResendTimer (const boost::system::error_code& ecode) { if (ecode != boost::asio::error::operation_aborted) { // check for resend attempts if (m_NumResendAttempts >= MAX_NUM_RESEND_ATTEMPTS) { LogPrint (eLogWarning, "Streaming: packet was not ACKed after ", MAX_NUM_RESEND_ATTEMPTS, " attempts, terminate, rSID=", m_RecvStreamID, ", sSID=", m_SendStreamID); m_Status = eStreamStatusReset; Close (); return; } // collect packets to resend auto ts = i2p::util::GetMillisecondsSinceEpoch (); std::vector packets; for (auto it : m_SentPackets) { if (ts >= it->sendTime + m_RTO) { it->sendTime = ts; packets.push_back (it); } } // select tunnels if necessary and send if (packets.size () > 0) { m_NumResendAttempts++; m_RTO *= 2; switch (m_NumResendAttempts) { case 1: // congesion avoidance m_WindowSize /= 2; if (m_WindowSize < MIN_WINDOW_SIZE) m_WindowSize = MIN_WINDOW_SIZE; break; case 2: m_RTO = INITIAL_RTO; // drop RTO to initial upon tunnels pair change first time // no break here case 4: if (m_RoutingSession) m_RoutingSession->SetSharedRoutingPath (nullptr); UpdateCurrentRemoteLease (); // pick another lease LogPrint (eLogWarning, "Streaming: Another remote lease has been selected for stream with rSID=", m_RecvStreamID, ", sSID=", m_SendStreamID); break; case 3: // pick another outbound tunnel if (m_RoutingSession) m_RoutingSession->SetSharedRoutingPath (nullptr); m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ()->GetTunnelPool ()->GetNextOutboundTunnel (m_CurrentOutboundTunnel); LogPrint (eLogWarning, "Streaming: Another outbound tunnel has been selected for stream with sSID=", m_SendStreamID); break; default: ; } SendPackets (packets); } ScheduleResend (); } } void Stream::HandleAckSendTimer (const boost::system::error_code& ecode) { if (m_IsAckSendScheduled) { if (m_LastReceivedSequenceNumber < 0) { LogPrint (eLogWarning, "Streaming: SYN has not been received after ", ACK_SEND_TIMEOUT, " milliseconds after follow on, terminate rSID=", m_RecvStreamID, ", sSID=", m_SendStreamID); m_Status = eStreamStatusReset; Close (); return; } if (m_Status == eStreamStatusOpen) { if (m_RoutingSession && m_RoutingSession->IsLeaseSetNonConfirmed ()) { // seems something went wrong and we should re-select tunnels m_CurrentOutboundTunnel = nullptr; m_CurrentRemoteLease = nullptr; } SendQuickAck (); } m_IsAckSendScheduled = false; } } void Stream::UpdateCurrentRemoteLease (bool expired) { if (!m_RemoteLeaseSet || m_RemoteLeaseSet->IsExpired ()) { m_RemoteLeaseSet = m_LocalDestination.GetOwner ()->FindLeaseSet (m_RemoteIdentity->GetIdentHash ()); if (!m_RemoteLeaseSet) { LogPrint (eLogWarning, "Streaming: LeaseSet ", m_RemoteIdentity->GetIdentHash ().ToBase64 (), " not found"); m_LocalDestination.GetOwner ()->RequestDestination (m_RemoteIdentity->GetIdentHash ()); // try to request for a next attempt } } if (m_RemoteLeaseSet) { if (!m_RoutingSession) m_RoutingSession = m_LocalDestination.GetOwner ()->GetRoutingSession (m_RemoteLeaseSet, true); auto leases = m_RemoteLeaseSet->GetNonExpiredLeases (false); // try without threshold first if (leases.empty ()) { expired = false; m_LocalDestination.GetOwner ()->RequestDestination (m_RemoteIdentity->GetIdentHash ()); // time to request leases = m_RemoteLeaseSet->GetNonExpiredLeases (true); // then with threshold } if (!leases.empty ()) { bool updated = false; if (expired && m_CurrentRemoteLease) { for (const auto& it: leases) if ((it->tunnelGateway == m_CurrentRemoteLease->tunnelGateway) && (it->tunnelID != m_CurrentRemoteLease->tunnelID)) { m_CurrentRemoteLease = it; updated = true; break; } } if (!updated) { uint32_t i = rand () % leases.size (); if (m_CurrentRemoteLease && leases[i]->tunnelID == m_CurrentRemoteLease->tunnelID) // make sure we don't select previous i = (i + 1) % leases.size (); // if so, pick next m_CurrentRemoteLease = leases[i]; } } else { LogPrint (eLogWarning, "Streaming: All remote leases are expired"); m_RemoteLeaseSet = nullptr; m_CurrentRemoteLease = nullptr; // we have requested expired before, no need to do it twice } } else { LogPrint (eLogWarning, "Streaming: Remote LeaseSet not found"); m_CurrentRemoteLease = nullptr; } } StreamingDestination::StreamingDestination (std::shared_ptr owner, uint16_t localPort, bool gzip): m_Owner (owner), m_LocalPort (localPort), m_Gzip (gzip), m_LastIncomingReceiveStreamID (0), m_PendingIncomingTimer (m_Owner->GetService ()), m_ConnTrackTimer(m_Owner->GetService()), m_ConnsPerMinute(DEFAULT_MAX_CONNS_PER_MIN), m_LastBanClear(i2p::util::GetMillisecondsSinceEpoch()) { } StreamingDestination::~StreamingDestination () { for (auto& it: m_SavedPackets) { for (auto it1: it.second) delete it1; it.second.clear (); } m_SavedPackets.clear (); } void StreamingDestination::Start () { ScheduleConnTrack(); } void StreamingDestination::Stop () { ResetAcceptor (); m_PendingIncomingTimer.cancel (); m_ConnTrackTimer.cancel(); { std::unique_lock l(m_StreamsMutex); m_Streams.clear (); } { std::unique_lock l(m_ConnsMutex); m_Conns.clear (); } } void StreamingDestination::HandleNextPacket (Packet * packet) { uint32_t sendStreamID = packet->GetSendStreamID (); if (sendStreamID) { auto it = m_Streams.find (sendStreamID); if (it != m_Streams.end ()) it->second->HandleNextPacket (packet); else { LogPrint (eLogError, "Streaming: Unknown stream sSID=", sendStreamID); delete packet; } } else { if (packet->IsSYN () && !packet->GetSeqn ()) // new incoming stream { uint32_t receiveStreamID = packet->GetReceiveStreamID (); /* if (receiveStreamID == m_LastIncomingReceiveStreamID) { // already pending LogPrint(eLogWarning, "Streaming: Incoming streaming with rSID=", receiveStreamID, " already exists"); delete packet; // drop it, because previous should be connected return; } */ auto incomingStream = CreateNewIncomingStream (); incomingStream->HandleNextPacket (packet); // SYN auto ident = incomingStream->GetRemoteIdentity(); if(ident) { auto ih = ident->GetIdentHash(); if(DropNewStream(ih)) { // drop LogPrint(eLogWarning, "Streaming: Dropping connection, too many inbound streams from ", ih.ToBase32()); incomingStream->Terminate(); return; } } m_LastIncomingReceiveStreamID = receiveStreamID; // handle saved packets if any { auto it = m_SavedPackets.find (receiveStreamID); if (it != m_SavedPackets.end ()) { LogPrint (eLogDebug, "Streaming: Processing ", it->second.size (), " saved packets for rSID=", receiveStreamID); for (auto it1: it->second) incomingStream->HandleNextPacket (it1); m_SavedPackets.erase (it); } } // accept if (m_Acceptor != nullptr) m_Acceptor (incomingStream); else { LogPrint (eLogWarning, "Streaming: Acceptor for incoming stream is not set"); if (m_PendingIncomingStreams.size () < MAX_PENDING_INCOMING_BACKLOG) { m_PendingIncomingStreams.push_back (incomingStream); m_PendingIncomingTimer.cancel (); m_PendingIncomingTimer.expires_from_now (boost::posix_time::seconds(PENDING_INCOMING_TIMEOUT)); m_PendingIncomingTimer.async_wait (std::bind (&StreamingDestination::HandlePendingIncomingTimer, shared_from_this (), std::placeholders::_1)); LogPrint (eLogDebug, "Streaming: Pending incoming stream added, rSID=", receiveStreamID); } else { LogPrint (eLogWarning, "Streaming: Pending incoming streams backlog exceeds ", MAX_PENDING_INCOMING_BACKLOG); incomingStream->Close (); } } } else // follow on packet without SYN { uint32_t receiveStreamID = packet->GetReceiveStreamID (); for (auto& it: m_Streams) if (it.second->GetSendStreamID () == receiveStreamID) { // found it.second->HandleNextPacket (packet); return; } // save follow on packet auto it = m_SavedPackets.find (receiveStreamID); if (it != m_SavedPackets.end ()) it->second.push_back (packet); else { m_SavedPackets[receiveStreamID] = std::list{ packet }; auto timer = std::make_shared (m_Owner->GetService ()); timer->expires_from_now (boost::posix_time::seconds(PENDING_INCOMING_TIMEOUT)); auto s = shared_from_this (); timer->async_wait ([s,timer,receiveStreamID](const boost::system::error_code& ecode) { if (ecode != boost::asio::error::operation_aborted) { auto it = s->m_SavedPackets.find (receiveStreamID); if (it != s->m_SavedPackets.end ()) { for (auto it1: it->second) delete it1; it->second.clear (); s->m_SavedPackets.erase (it); } } }); } } } } std::shared_ptr StreamingDestination::CreateNewOutgoingStream (std::shared_ptr remote, int port) { auto s = std::make_shared (m_Owner->GetService (), *this, remote, port); std::unique_lock l(m_StreamsMutex); m_Streams[s->GetRecvStreamID ()] = s; return s; } std::shared_ptr StreamingDestination::CreateNewIncomingStream () { auto s = std::make_shared (m_Owner->GetService (), *this); std::unique_lock l(m_StreamsMutex); m_Streams[s->GetRecvStreamID ()] = s; return s; } void StreamingDestination::DeleteStream (std::shared_ptr stream) { if (stream) { std::unique_lock l(m_StreamsMutex); auto it = m_Streams.find (stream->GetRecvStreamID ()); if (it != m_Streams.end ()) m_Streams.erase (it); } } void StreamingDestination::SetAcceptor (const Acceptor& acceptor) { m_Owner->GetService ().post([acceptor, this](void) { m_Acceptor = acceptor; for (auto& it: m_PendingIncomingStreams) if (it->GetStatus () == eStreamStatusOpen) // still open? m_Acceptor (it); m_PendingIncomingStreams.clear (); m_PendingIncomingTimer.cancel (); }); } void StreamingDestination::ResetAcceptor () { if (m_Acceptor) m_Acceptor (nullptr); m_Acceptor = nullptr; } void StreamingDestination::HandlePendingIncomingTimer (const boost::system::error_code& ecode) { if (ecode != boost::asio::error::operation_aborted) { LogPrint (eLogWarning, "Streaming: Pending incoming timeout expired"); for (auto& it: m_PendingIncomingStreams) it->Close (); m_PendingIncomingStreams.clear (); } } void StreamingDestination::HandleDataMessagePayload (const uint8_t * buf, size_t len) { // unzip it Packet * uncompressed = new Packet; uncompressed->offset = 0; uncompressed->len = m_Inflator.Inflate (buf, len, uncompressed->buf, MAX_PACKET_SIZE); if (uncompressed->len) HandleNextPacket (uncompressed); else delete uncompressed; } std::shared_ptr StreamingDestination::CreateDataMessage (const uint8_t * payload, size_t len, uint16_t toPort) { auto msg = NewI2NPShortMessage (); if (!m_Gzip || len <= i2p::stream::COMPRESSION_THRESHOLD_SIZE) m_Deflator.SetCompressionLevel (Z_NO_COMPRESSION); else m_Deflator.SetCompressionLevel (Z_DEFAULT_COMPRESSION); uint8_t * buf = msg->GetPayload (); buf += 4; // reserve for lengthlength msg->len += 4; size_t size = m_Deflator.Deflate (payload, len, buf, msg->maxLen - msg->len); if (size) { htobe32buf (msg->GetPayload (), size); // length htobe16buf (buf + 4, m_LocalPort); // source port htobe16buf (buf + 6, toPort); // destination port buf[9] = i2p::client::PROTOCOL_TYPE_STREAMING; // streaming protocol msg->len += size; msg->FillI2NPMessageHeader (eI2NPData); } else msg = nullptr; return msg; } void StreamingDestination::SetMaxConnsPerMinute(const uint32_t conns) { m_ConnsPerMinute = conns; LogPrint(eLogDebug, "Streaming: Set max conns per minute per destination to ", conns); } bool StreamingDestination::DropNewStream(const i2p::data::IdentHash & ih) { std::lock_guard lock(m_ConnsMutex); if (m_Banned.size() > MAX_BANNED_CONNS) return true; // overload auto end = std::end(m_Banned); if ( std::find(std::begin(m_Banned), end, ih) != end) return true; // already banned auto itr = m_Conns.find(ih); if (itr == m_Conns.end()) m_Conns[ih] = 0; m_Conns[ih] += 1; bool ban = m_Conns[ih] >= m_ConnsPerMinute; if (ban) { m_Banned.push_back(ih); m_Conns.erase(ih); LogPrint(eLogWarning, "Streaming: ban ", ih.ToBase32()); } return ban; } void StreamingDestination::HandleConnTrack(const boost::system::error_code& ecode) { if (ecode != boost::asio::error::operation_aborted) { { // acquire lock std::lock_guard lock(m_ConnsMutex); // clear conn tracking m_Conns.clear(); // check for ban clear auto ts = i2p::util::GetMillisecondsSinceEpoch(); if (ts - m_LastBanClear >= DEFAULT_BAN_INTERVAL) { // clear bans m_Banned.clear(); m_LastBanClear = ts; } } // reschedule timer ScheduleConnTrack(); } } void StreamingDestination::ScheduleConnTrack() { m_ConnTrackTimer.expires_from_now (boost::posix_time::seconds(60)); m_ConnTrackTimer.async_wait ( std::bind (&StreamingDestination::HandleConnTrack, shared_from_this (), std::placeholders::_1)); } } }