Browse Source

Merge branch 'master' of https://github.com/orignal/i2pd

pull/40/head
Meeh 11 years ago
parent
commit
370324d119
  1. 38
      NetDb.cpp
  2. 1
      NetDb.h
  3. 138
      SSU.cpp
  4. 3
      SSU.h
  5. 18
      Transports.cpp

38
NetDb.cpp

@ -448,10 +448,18 @@ namespace data
{ {
auto r = FindRouter (router); auto r = FindRouter (router);
// do we have that floodfill router in our database? // do we have that floodfill router in our database?
if (r) if (r)
{ {
// we do
if (!dest->IsExcluded (r->GetIdentHash ()) && dest->GetNumExcludedPeers () < 30) // TODO: fix TunnelGateway first if (!dest->IsExcluded (r->GetIdentHash ()) && dest->GetNumExcludedPeers () < 30) // TODO: fix TunnelGateway first
{ {
// tell floodfill about us
msgs.push_back (i2p::tunnel::TunnelMessageBlock
{
i2p::tunnel::eDeliveryTypeRouter,
r->GetIdentHash (), 0,
CreateDatabaseStoreMsg ()
});
// request destination // request destination
auto msg = dest->CreateRequestMessage (r, dest->GetLastReplyTunnel ()); auto msg = dest->CreateRequestMessage (r, dest->GetLastReplyTunnel ());
msgs.push_back (i2p::tunnel::TunnelMessageBlock msgs.push_back (i2p::tunnel::TunnelMessageBlock
@ -506,16 +514,15 @@ namespace data
auto inbound = i2p::tunnel::tunnels.GetNextInboundTunnel (); auto inbound = i2p::tunnel::tunnels.GetNextInboundTunnel ();
if (outbound && inbound) if (outbound && inbound)
{ {
auto floodfill = GetRandomRouter (outbound->GetEndpointRouter (), true); CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
uint8_t randomHash[32];
rnd.GenerateBlock (randomHash, 32);
RequestedDestination * dest = CreateRequestedDestination (IdentHash (randomHash), false, true);
dest->SetLastOutboundTunnel (outbound);
auto floodfill = GetClosestFloodfill (randomHash, dest->GetExcludedPeers ());
if (floodfill) if (floodfill)
{ {
LogPrint ("Exploring new routers ..."); LogPrint ("Exploring new routers ...");
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
uint8_t randomHash[32];
rnd.GenerateBlock (randomHash, 32);
RequestedDestination * dest = CreateRequestedDestination (IdentHash (randomHash), false, true);
dest->SetLastOutboundTunnel (outbound);
std::vector<i2p::tunnel::TunnelMessageBlock> msgs; std::vector<i2p::tunnel::TunnelMessageBlock> msgs;
msgs.push_back (i2p::tunnel::TunnelMessageBlock msgs.push_back (i2p::tunnel::TunnelMessageBlock
{ {
@ -529,8 +536,10 @@ namespace data
floodfill->GetIdentHash (), 0, floodfill->GetIdentHash (), 0,
dest->CreateRequestMessage (floodfill, inbound) // explore dest->CreateRequestMessage (floodfill, inbound) // explore
}); });
outbound->SendTunnelDataMsg (msgs); outbound->SendTunnelDataMsg (msgs);
} }
else
DeleteRequestedDestination (dest);
} }
} }
@ -557,6 +566,15 @@ namespace data
m_RequestedDestinations.erase (it); m_RequestedDestinations.erase (it);
} }
} }
void NetDb::DeleteRequestedDestination (RequestedDestination * dest)
{
if (dest)
{
m_RequestedDestinations.erase (dest->GetDestination ());
delete dest;
}
}
const RouterInfo * NetDb::GetRandomNTCPRouter (bool floodfillOnly) const const RouterInfo * NetDb::GetRandomNTCPRouter (bool floodfillOnly) const
{ {

1
NetDb.h

@ -87,6 +87,7 @@ namespace data
RequestedDestination * CreateRequestedDestination (const IdentHash& dest, RequestedDestination * CreateRequestedDestination (const IdentHash& dest,
bool isLeaseSet, bool isExploratory = false); bool isLeaseSet, bool isExploratory = false);
void DeleteRequestedDestination (const IdentHash& dest); void DeleteRequestedDestination (const IdentHash& dest);
void DeleteRequestedDestination (RequestedDestination * dest);
private: private:

138
SSU.cpp

@ -47,6 +47,7 @@ namespace ssu
{ {
switch (m_State) switch (m_State)
{ {
case eSessionStateConfirmedSent:
case eSessionStateEstablished: case eSessionStateEstablished:
// most common case // most common case
ProcessMessage (buf, len); ProcessMessage (buf, len);
@ -86,23 +87,38 @@ namespace ssu
LogPrint ("SSU test received"); LogPrint ("SSU test received");
break; break;
case PAYLOAD_TYPE_SESSION_DESTROYED: case PAYLOAD_TYPE_SESSION_DESTROYED:
{
LogPrint ("SSU session destroy received"); LogPrint ("SSU session destroy received");
if (m_Server)
m_Server->DeleteSession (this); // delete this
}
break; break;
default: default:
LogPrint ("Unexpected SSU payload type ", (int)payloadType); LogPrint ("Unexpected SSU payload type ", (int)payloadType);
} }
} }
// TODO: try intro key as well
else else
LogPrint ("MAC verifcation failed"); {
LogPrint ("MAC key failed. Trying intro key");
auto introKey = GetIntroKey ();
if (introKey && Validate (buf, len, introKey))
{
Decrypt (buf, len, introKey);
SSUHeader * header = (SSUHeader *)buf;
LogPrint ("Unexpected SSU payload type ", (int)(header->flag >> 4));
// TODO:
}
else
LogPrint ("MAC verifcation failed");
m_State = eSessionStateUnknown;
}
} }
void SSUSession::ProcessSessionRequest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint) void SSUSession::ProcessSessionRequest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint)
{ {
LogPrint ("Process session request"); LogPrint ("Process session request");
// use our intro key // use our intro key
if (ProcessIntroKeyEncryptedMessage (PAYLOAD_TYPE_SESSION_REQUEST, if (ProcessIntroKeyEncryptedMessage (PAYLOAD_TYPE_SESSION_REQUEST, buf, len))
i2p::context.GetRouterInfo (), buf, len))
{ {
m_State = eSessionStateRequestReceived; m_State = eSessionStateRequestReceived;
LogPrint ("Session request received"); LogPrint ("Session request received");
@ -121,7 +137,7 @@ namespace ssu
} }
// use remote intro key // use remote intro key
if (ProcessIntroKeyEncryptedMessage (PAYLOAD_TYPE_SESSION_CREATED, *m_RemoteRouter, buf, len)) if (ProcessIntroKeyEncryptedMessage (PAYLOAD_TYPE_SESSION_CREATED, buf, len))
{ {
m_State = eSessionStateCreatedReceived; m_State = eSessionStateCreatedReceived;
LogPrint ("Session created received"); LogPrint ("Session created received");
@ -132,8 +148,6 @@ namespace ssu
i2p::context.UpdateAddress (ourIP.to_string ().c_str ()); i2p::context.UpdateAddress (ourIP.to_string ().c_str ());
uint32_t relayTag = be32toh (*(uint32_t *)(buf + sizeof (SSUHeader) + 263)); uint32_t relayTag = be32toh (*(uint32_t *)(buf + sizeof (SSUHeader) + 263));
SendSessionConfirmed (buf + sizeof (SSUHeader), ourAddress, relayTag); SendSessionConfirmed (buf + sizeof (SSUHeader), ourAddress, relayTag);
m_State = eSessionStateEstablished;
Established ();
} }
} }
@ -147,9 +161,9 @@ namespace ssu
if ((header->flag >> 4) == PAYLOAD_TYPE_SESSION_CONFIRMED) if ((header->flag >> 4) == PAYLOAD_TYPE_SESSION_CONFIRMED)
{ {
m_State = eSessionStateConfirmedReceived; m_State = eSessionStateConfirmedReceived;
LogPrint ("Session confirmed received"); LogPrint ("Session confirmed received");
// TODO:
m_State = eSessionStateEstablished; m_State = eSessionStateEstablished;
// TODO: send DeliverStatus
Established (); Established ();
} }
else else
@ -161,10 +175,10 @@ namespace ssu
void SSUSession::SendSessionRequest () void SSUSession::SendSessionRequest ()
{ {
auto address = m_RemoteRouter ? m_RemoteRouter->GetSSUAddress () : nullptr; auto introKey = GetIntroKey ();
if (!address) if (!introKey)
{ {
LogPrint ("Missing remote SSU address"); LogPrint ("SSU is not supported");
return; return;
} }
@ -177,7 +191,7 @@ namespace ssu
uint8_t iv[16]; uint8_t iv[16];
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
rnd.GenerateBlock (iv, 16); // random iv rnd.GenerateBlock (iv, 16); // random iv
FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_REQUEST, buf, 304, address->key, iv, address->key); FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_REQUEST, buf, 304, introKey, iv, introKey);
m_State = eSessionStateRequestSent; m_State = eSessionStateRequestSent;
m_Server->Send (buf, 304, m_RemoteEndpoint); m_Server->Send (buf, 304, m_RemoteEndpoint);
@ -185,10 +199,10 @@ namespace ssu
void SSUSession::SendSessionCreated (const uint8_t * x) void SSUSession::SendSessionCreated (const uint8_t * x)
{ {
auto address = m_RemoteRouter ? m_RemoteRouter->GetSSUAddress () : nullptr; auto introKey = GetIntroKey ();
if (!address) if (!introKey)
{ {
LogPrint ("Missing remote SSU address"); LogPrint ("SSU is not supported");
return; return;
} }
uint8_t signedData[532]; // x,y, remote IP, remote port, our IP, our port, relayTag, signed on time uint8_t signedData[532]; // x,y, remote IP, remote port, our IP, our port, relayTag, signed on time
@ -224,20 +238,13 @@ namespace ssu
m_Encryption.ProcessData (payload, payload, 48); m_Encryption.ProcessData (payload, payload, 48);
// encrypt message with intro key // encrypt message with intro key
FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_CREATED, buf, 368, address->key, iv, address->key); FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_CREATED, buf, 368, introKey, iv, introKey);
m_State = eSessionStateRequestSent; m_State = eSessionStateRequestSent;
m_Server->Send (buf, 368, m_RemoteEndpoint); m_Server->Send (buf, 368, m_RemoteEndpoint);
} }
void SSUSession::SendSessionConfirmed (const uint8_t * y, const uint8_t * ourAddress, uint32_t relayTag) void SSUSession::SendSessionConfirmed (const uint8_t * y, const uint8_t * ourAddress, uint32_t relayTag)
{ {
auto address = m_RemoteRouter ? m_RemoteRouter->GetSSUAddress () : nullptr;
if (!address)
{
LogPrint ("Missing remote SSU address");
return;
}
uint8_t buf[480 + 18]; uint8_t buf[480 + 18];
uint8_t * payload = buf + sizeof (SSUHeader); uint8_t * payload = buf + sizeof (SSUHeader);
*payload = 1; // 1 fragment *payload = 1; // 1 fragment
@ -275,15 +282,15 @@ namespace ssu
m_Server->Send (buf, 480, m_RemoteEndpoint); m_Server->Send (buf, 480, m_RemoteEndpoint);
} }
bool SSUSession::ProcessIntroKeyEncryptedMessage (uint8_t expectedPayloadType, const i2p::data::RouterInfo& r, uint8_t * buf, size_t len) bool SSUSession::ProcessIntroKeyEncryptedMessage (uint8_t expectedPayloadType, uint8_t * buf, size_t len)
{ {
auto address = r.GetSSUAddress (); auto introKey = GetIntroKey ();
if (address) if (introKey)
{ {
// use intro key for verification and decryption // use intro key for verification and decryption
if (Validate (buf, len, address->key)) if (Validate (buf, len, introKey))
{ {
Decrypt (buf, len, address->key); Decrypt (buf, len, introKey);
SSUHeader * header = (SSUHeader *)buf; SSUHeader * header = (SSUHeader *)buf;
if ((header->flag >> 4) == expectedPayloadType) if ((header->flag >> 4) == expectedPayloadType)
{ {
@ -297,7 +304,7 @@ namespace ssu
LogPrint ("MAC verifcation failed"); LogPrint ("MAC verifcation failed");
} }
else else
LogPrint ("SSU is not supported by ", r.GetIdentHashAbbreviation ()); LogPrint ("SSU is not supported");
return false; return false;
} }
@ -374,6 +381,7 @@ namespace ssu
void SSUSession::Established () void SSUSession::Established ()
{ {
SendI2NPMessage (CreateDatabaseStoreMsg ());
if (!m_DelayedMessages.empty ()) if (!m_DelayedMessages.empty ())
{ {
for (auto it :m_DelayedMessages) for (auto it :m_DelayedMessages)
@ -382,6 +390,22 @@ namespace ssu
} }
} }
const uint8_t * SSUSession::GetIntroKey () const
{
if (m_RemoteRouter)
{
// we are client
auto address = m_RemoteRouter->GetSSUAddress ();
return address ? address->key : nullptr;
}
else
{
// we are server
auto address = i2p::context.GetRouterInfo ().GetSSUAddress ();
return address ? address->key : nullptr;
}
}
void SSUSession::SendI2NPMessage (I2NPMessage * msg) void SSUSession::SendI2NPMessage (I2NPMessage * msg)
{ {
if (msg) if (msg)
@ -463,11 +487,25 @@ namespace ssu
m_IncomleteMessages[msgID] = msg; m_IncomleteMessages[msgID] = msg;
if (isLast) if (isLast)
{ {
SendMsgAck (msgID);
if (fragmentNum > 0) if (fragmentNum > 0)
m_IncomleteMessages.erase (msgID); m_IncomleteMessages.erase (msgID);
msg->FromSSU (msgID); msg->FromSSU (msgID);
i2p::HandleI2NPMessage (msg, false); if (m_State == eSessionStateEstablished)
SendMsgAck (msgID); i2p::HandleI2NPMessage (msg, false);
else
{
// we expect DeliveryStatus
if (msg->GetHeader ()->typeID == eI2NPDeliveryStatus)
{
LogPrint ("SSU session established");
m_State = eSessionStateEstablished;
Established ();
}
else
LogPrint ("SSU unexpected message ", (int)msg->GetHeader ()->typeID);
DeleteI2NPMessage (msg);
}
} }
} }
buf += fragmentSize; buf += fragmentSize;
@ -499,8 +537,21 @@ namespace ssu
uint8_t buf[48 + 18], iv[16]; uint8_t buf[48 + 18], iv[16];
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
rnd.GenerateBlock (iv, 16); // random iv rnd.GenerateBlock (iv, 16); // random iv
// encrypt message with session key if (m_State == eSessionStateEstablished)
FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_DESTROYED, buf, 48, m_SessionKey, iv, m_MacKey); // encrypt message with session key
FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_DESTROYED, buf, 48, m_SessionKey, iv, m_MacKey);
else
{
auto introKey = GetIntroKey ();
if (introKey)
// encrypt message with intro key
FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_DESTROYED, buf, 48, introKey, iv, introKey);
else
{
LogPrint ("SSU: can't send SessionDestroyed message");
return;
}
}
m_Server->Send (buf, 48, m_RemoteEndpoint); m_Server->Send (buf, 48, m_RemoteEndpoint);
} }
@ -514,10 +565,13 @@ namespace ssu
uint32_t fragmentNum = 0; uint32_t fragmentNum = 0;
while (len > 0) while (len > 0)
{ {
uint8_t buf[SSU_MTU + 18], iv[16]; uint8_t buf[SSU_MTU + 18], iv[16], * payload = buf + sizeof (SSUHeader);
buf[0] = DATA_FLAG_WANT_REPLY; // for compatibility *payload = DATA_FLAG_WANT_REPLY; // for compatibility
buf[1] = 1; // always 1 message fragment per message payload++;
*(uint32_t *)(buf + 2) = msgID; *payload = 1; // always 1 message fragment per message
payload++;
*(uint32_t *)payload = msgID;
payload += 4;
bool isLast = (len <= payloadSize); bool isLast = (len <= payloadSize);
size_t size = isLast ? len : payloadSize; size_t size = isLast ? len : payloadSize;
uint32_t fragmentInfo = (fragmentNum << 17); uint32_t fragmentInfo = (fragmentNum << 17);
@ -526,10 +580,11 @@ namespace ssu
fragmentInfo |= size; fragmentInfo |= size;
fragmentInfo = htobe32 (fragmentInfo); fragmentInfo = htobe32 (fragmentInfo);
memcpy (buf + 6, (uint8_t *)(&fragmentInfo) + 1, 3); memcpy (payload, (uint8_t *)(&fragmentInfo) + 1, 3);
memcpy (buf + 9, msgBuf, size); payload += 3;
memcpy (payload, msgBuf, size);
size += sizeof (SSUHeader) + 9; size += payload - buf;
if (size % 16) // make sure 16 bytes boundary if (size % 16) // make sure 16 bytes boundary
size = (size/16 + 1)*16; size = (size/16 + 1)*16;
@ -570,6 +625,7 @@ namespace ssu
void SSUServer::Stop () void SSUServer::Stop ()
{ {
DeleteAllSessions ();
m_Socket.close (); m_Socket.close ();
} }

3
SSU.h

@ -89,10 +89,11 @@ namespace ssu
void SendSesionDestroyed (); void SendSesionDestroyed ();
void Send (i2p::I2NPMessage * msg); void Send (i2p::I2NPMessage * msg);
bool ProcessIntroKeyEncryptedMessage (uint8_t expectedPayloadType, const i2p::data::RouterInfo& r, uint8_t * buf, size_t len); bool ProcessIntroKeyEncryptedMessage (uint8_t expectedPayloadType, uint8_t * buf, size_t len);
void FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len, const uint8_t * aesKey, const uint8_t * iv, const uint8_t * macKey); void FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len, const uint8_t * aesKey, const uint8_t * iv, const uint8_t * macKey);
void Decrypt (uint8_t * buf, size_t len, const uint8_t * aesKey); void Decrypt (uint8_t * buf, size_t len, const uint8_t * aesKey);
bool Validate (uint8_t * buf, size_t len, const uint8_t * macKey); bool Validate (uint8_t * buf, size_t len, const uint8_t * macKey);
const uint8_t * GetIntroKey () const;
private: private:

18
Transports.cpp

@ -168,7 +168,23 @@ namespace i2p
AddNTCPSession (session); AddNTCPSession (session);
} }
else else
LogPrint ("No NTCP addresses available"); {
// SSU always have lower prioprity than NTCP
// TODO: it shouldn't
LogPrint ("No NTCP addresses available. Trying SSU");
address = r->GetSSUAddress ();
if (address && m_SSUServer)
{
auto s = m_SSUServer->GetSession (r);
if (s)
{
s->SendI2NPMessage (msg);
return;
}
}
else
LogPrint ("No SSU addresses available");
}
} }
else else
{ {

Loading…
Cancel
Save