Merge pull request #5 from orignal/master

update
This commit is contained in:
mikhail4021 2014-01-29 22:07:53 -08:00
commit 45f91363ab
32 changed files with 1175 additions and 231 deletions

7
.gitignore vendored
View file

@ -1,3 +1,10 @@
# i2pd
*.o
router.info
router.keys
i2p
netDb
#################
## Eclipse
#################

View file

@ -236,8 +236,8 @@ namespace garlic
auto it = m_Sessions.find (destination->GetIdentHash ());
if (it != m_Sessions.end ())
{
m_Sessions.erase (it);
delete it->second;
m_Sessions.erase (it);
}
GarlicRoutingSession * session = new GarlicRoutingSession (destination, 0); // not follow-on messages expected
m_Sessions[destination->GetIdentHash ()] = session;

View file

@ -386,15 +386,13 @@ namespace i2p
TunnelGatewayHeader * header = (TunnelGatewayHeader *)msg->GetPayload ();
uint32_t tunnelID = be32toh(header->tunnelID);
uint16_t len = be16toh(header->length);
LogPrint ("TunnelGateway of ", (int)len, " bytes for tunnel ", (unsigned int)tunnelID);
// we make payload as new I2NP message to send
msg->offset += sizeof (I2NPHeader) + sizeof (TunnelGatewayHeader);
msg->len = msg->offset + len;
LogPrint ("TunnelGateway of ", (int)len, " bytes for tunnel ", (unsigned int)tunnelID, ". Msg type ", (int)msg->GetHeader()->typeID);
i2p::tunnel::TransitTunnel * tunnel = i2p::tunnel::tunnels.GetTransitTunnel (tunnelID);
if (tunnel)
{
// we make payload as new I2NP message to send
msg->offset += sizeof (I2NPHeader) + sizeof (TunnelGatewayHeader);
msg->len = msg->offset + len;
tunnel->SendTunnelDataMsg (nullptr, 0, msg);
}
else
{
LogPrint ("Tunnel ", (unsigned int)tunnelID, " not found");

View file

@ -1,81 +1,81 @@
#include "I2PEndian.h"
// http://habrahabr.ru/post/121811/
// http://codepad.org/2ycmkz2y
#include "LittleBigEndian.h"
uint16_t htobe16(uint16_t int16)
{
BigEndian<uint16_t> u16(int16);
return u16.raw_value;
}
uint32_t htobe32(uint32_t int32)
{
BigEndian<uint32_t> u32(int32);
return u32.raw_value;
}
uint64_t htobe64(uint64_t int64)
{
BigEndian<uint64_t> u64(int64);
return u64.raw_value;
}
uint16_t be16toh(uint16_t big16)
{
LittleEndian<uint16_t> u16(big16);
return u16.raw_value;
}
uint32_t be32toh(uint32_t big32)
{
LittleEndian<uint32_t> u32(big32);
return u32.raw_value;
}
uint64_t be64toh(uint64_t big64)
{
LittleEndian<uint64_t> u64(big64);
return u64.raw_value;
}
/* it can be used in Windows 8
#include <Winsock2.h>
uint16_t htobe16(uint16_t int16)
{
return htons(int16);
}
uint32_t htobe32(uint32_t int32)
{
return htonl(int32);
}
uint64_t htobe64(uint64_t int64)
{
// http://msdn.microsoft.com/en-us/library/windows/desktop/jj710199%28v=vs.85%29.aspx
//return htonll(int64);
return 0;
}
uint16_t be16toh(uint16_t big16)
{
return ntohs(big16);
}
uint32_t be32toh(uint32_t big32)
{
return ntohl(big32);
}
uint64_t be64toh(uint64_t big64)
{
// http://msdn.microsoft.com/en-us/library/windows/desktop/jj710199%28v=vs.85%29.aspx
//return ntohll(big64);
return 0;
}
*/
//#include "I2PEndian.h"
//
//// http://habrahabr.ru/post/121811/
//// http://codepad.org/2ycmkz2y
//
//#include "LittleBigEndian.h"
//
//uint16_t htobe16(uint16_t int16)
//{
// BigEndian<uint16_t> u16(int16);
// return u16.raw_value;
//}
//
//uint32_t htobe32(uint32_t int32)
//{
// BigEndian<uint32_t> u32(int32);
// return u32.raw_value;
//}
//
//uint64_t htobe64(uint64_t int64)
//{
// BigEndian<uint64_t> u64(int64);
// return u64.raw_value;
//}
//
//uint16_t be16toh(uint16_t big16)
//{
// LittleEndian<uint16_t> u16(big16);
// return u16.raw_value;
//}
//
//uint32_t be32toh(uint32_t big32)
//{
// LittleEndian<uint32_t> u32(big32);
// return u32.raw_value;
//}
//
//uint64_t be64toh(uint64_t big64)
//{
// LittleEndian<uint64_t> u64(big64);
// return u64.raw_value;
//}
//
///* it can be used in Windows 8
//#include <Winsock2.h>
//
//uint16_t htobe16(uint16_t int16)
//{
// return htons(int16);
//}
//
//uint32_t htobe32(uint32_t int32)
//{
// return htonl(int32);
//}
//
//uint64_t htobe64(uint64_t int64)
//{
// // http://msdn.microsoft.com/en-us/library/windows/desktop/jj710199%28v=vs.85%29.aspx
// //return htonll(int64);
// return 0;
//}
//
//
//uint16_t be16toh(uint16_t big16)
//{
// return ntohs(big16);
//}
//
//uint32_t be32toh(uint32_t big32)
//{
// return ntohl(big32);
//}
//
//uint64_t be64toh(uint64_t big64)
//{
// // http://msdn.microsoft.com/en-us/library/windows/desktop/jj710199%28v=vs.85%29.aspx
// //return ntohll(big64);
// return 0;
//}
//*/

View file

@ -5,14 +5,16 @@
#include <endian.h>
#else
#include <cstdint>
//
//uint16_t htobe16(uint16_t int16);
//uint32_t htobe32(uint32_t int32);
//uint64_t htobe64(uint64_t int64);
//
//uint16_t be16toh(uint16_t big16);
//uint32_t be32toh(uint32_t big32);
//uint64_t be64toh(uint64_t big64);
uint16_t htobe16(uint16_t int16);
uint32_t htobe32(uint32_t int32);
uint64_t htobe64(uint64_t int64);
uint16_t be16toh(uint16_t big16);
uint32_t be32toh(uint32_t big32);
uint64_t be64toh(uint64_t big64);
#include "portable_endian.h"
#endif

View file

@ -3,6 +3,7 @@
#include "CryptoConst.h"
#include "Log.h"
#include "Timestamp.h"
#include "NetDb.h"
#include "LeaseSet.h"
namespace i2p
@ -28,6 +29,7 @@ namespace data
memcpy (m_EncryptionKey, header->encryptionKey, 256);
LogPrint ("LeaseSet num=", (int)header->num);
// process leases
const uint8_t * leases = buf + sizeof (H);
for (int i = 0; i < header->num; i++)
{
@ -36,8 +38,16 @@ namespace data
lease.endDate = be64toh (lease.endDate);
m_Leases.push_back (lease);
leases += sizeof (Lease);
}
// check if lease's gateway is in our netDb
if (!netdb.FindRouter (lease.tunnelGateway))
{
// if not found request it
LogPrint ("Lease's tunnel gateway not found. Requested");
netdb.RequestDestination (lease.tunnelGateway);
}
}
// verify
CryptoPP::DSA::PublicKey pubKey;
pubKey.Initialize (i2p::crypto::dsap, i2p::crypto::dsaq, i2p::crypto::dsag,

View file

@ -3,9 +3,9 @@ CC = g++
CFLAGS = -g -Wall -std=c++0x
OBJECTS = i2p.o base64.o NTCPSession.o RouterInfo.o Transports.o RouterContext.o \
NetDb.o LeaseSet.o Tunnel.o TunnelEndpoint.o TunnelGateway.o TransitTunnel.o \
I2NPProtocol.o Log.o Garlic.o HTTPServer.o Streaming.o Identity.o
I2NPProtocol.o Log.o Garlic.o HTTPServer.o Streaming.o Identity.o SSU.o util.o
INCFLAGS =
LDFLAGS = -Wl,-rpath,/usr/local/lib -lcryptopp -lboost_system -lboost_filesystem
LDFLAGS = -Wl,-rpath,/usr/local/lib -lcryptopp -lboost_system -lboost_filesystem -lpthread
LIBS =
all: i2p

View file

@ -517,10 +517,10 @@ namespace ntcp
}
NTCPClient::NTCPClient (boost::asio::io_service& service, const char * address,
NTCPClient::NTCPClient (boost::asio::io_service& service, const boost::asio::ip::address& address,
int port, i2p::data::RouterInfo& in_RouterInfo):
NTCPSession (service, in_RouterInfo),
m_Endpoint (boost::asio::ip::address::from_string (address), port)
m_Endpoint (address, port)
{
Connect ();
}

View file

@ -144,7 +144,7 @@ namespace ntcp
{
public:
NTCPClient (boost::asio::io_service& service, const char * address, int port, i2p::data::RouterInfo& in_RouterInfo);
NTCPClient (boost::asio::io_service& service, const boost::asio::ip::address& address, int port, i2p::data::RouterInfo& in_RouterInfo);
private:

252
NetDb.cpp
View file

@ -1,6 +1,7 @@
#include "I2PEndian.h"
#include <fstream>
#include <vector>
#include <boost/asio.hpp>
#include <boost/filesystem.hpp>
#include <cryptopp/gzip.h>
#include "base64.h"
@ -8,6 +9,7 @@
#include "Timestamp.h"
#include "I2NPProtocol.h"
#include "Tunnel.h"
#include "Transports.h"
#include "RouterContext.h"
#include "Garlic.h"
#include "NetDb.h"
@ -29,6 +31,16 @@ namespace data
m_LastReplyTunnel = replyTunnel;
return msg;
}
I2NPMessage * RequestedDestination::CreateRequestMessage (const IdentHash& floodfill)
{
I2NPMessage * msg = i2p::CreateDatabaseLookupMsg (m_Destination,
i2p::context.GetRouterInfo ().GetIdentHash () , 0, false, &m_ExcludedPeers);
m_ExcludedPeers.insert (floodfill);
m_LastRouter = nullptr;
m_LastReplyTunnel = nullptr;
return msg;
}
NetDb netdb;
@ -165,63 +177,106 @@ namespace data
return it->second;
else
return nullptr;
}
}
// TODO: Move to reseed and/or scheduled tasks. (In java version, scheduler fix this as well as sort RIs.)
bool NetDb::CreateNetDb(const char * directory)
{
boost::filesystem::path p (directory);
LogPrint (directory, " doesn't exist, trying to create it.");
if (!boost::filesystem::create_directory (p))
{
LogPrint("Failed to create directory ", directory);
return false;
}
// list of chars might appear in base64 string
const char * chars = GetBase64SubstitutionTable (); // 64 bytes
boost::filesystem::path suffix;
for (int i = 0; i < 64; i++)
{
#ifndef _WIN32
suffix = std::string ("/r") + chars[i];
#else
suffix = std::string ("\\r") + chars[i];
#endif
if (!boost::filesystem::create_directory( boost::filesystem::path (p / suffix) )) return false;
}
return true;
}
void NetDb::Load (const char * directory)
{
boost::filesystem::path p (directory);
if (boost::filesystem::exists (p))
if (!boost::filesystem::exists (p))
{
int numRouters = 0;
boost::filesystem::directory_iterator end;
for (boost::filesystem::directory_iterator it (p); it != end; ++it)
if (!CreateNetDb(directory)) return;
}
// TODO: Reseed if needed.
int numRouters = 0;
boost::filesystem::directory_iterator end;
for (boost::filesystem::directory_iterator it (p); it != end; ++it)
{
if (boost::filesystem::is_directory (it->status()))
{
if (boost::filesystem::is_directory (it->status()))
for (boost::filesystem::directory_iterator it1 (it->path ()); it1 != end; ++it1)
{
for (boost::filesystem::directory_iterator it1 (it->path ()); it1 != end; ++it1)
{
#if BOOST_VERSION > 10500
RouterInfo * r = new RouterInfo (it1->path().string().c_str ());
RouterInfo * r = new RouterInfo (it1->path().string().c_str ());
#else
RouterInfo * r = new RouterInfo(it1->path().c_str());
RouterInfo * r = new RouterInfo(it1->path().c_str());
#endif
m_RouterInfos[r->GetIdentHash ()] = r;
numRouters++;
}
m_RouterInfos[r->GetIdentHash ()] = r;
numRouters++;
}
}
LogPrint (numRouters, " routers loaded");
}
else
LogPrint (directory, " doesn't exist");
LogPrint (numRouters, " routers loaded");
}
void NetDb::SaveUpdated (const char * directory)
{
auto GetFilePath = [](const char * directory, const RouterInfo * routerInfo)
{
#ifndef _WIN32
return std::string (directory) + "/r" +
routerInfo->GetIdentHashBase64 ()[0] + "/routerInfo-" +
routerInfo->GetIdentHashBase64 ()[0] + "/routerInfo-" +
#else
return std::string (directory) + "\\r" +
routerInfo->GetIdentHashBase64 ()[0] + "\\routerInfo-" +
#endif
routerInfo->GetIdentHashBase64 () + ".dat";
};
int count = 0, deletedCount = 0;
auto total = m_RouterInfos.size ();
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch ();
for (auto it: m_RouterInfos)
{
if (it.second->IsUpdated ())
{
std::ofstream r (GetFilePath(directory, it.second));
std::ofstream r (GetFilePath(directory, it.second), std::ofstream::binary);
r.write ((char *)it.second->GetBuffer (), it.second->GetBufferLen ());
it.second->SetUpdated (false);
count++;
}
else if (it.second->IsUnreachable ())
else
{
if (boost::filesystem::exists (GetFilePath (directory, it.second)))
{
boost::filesystem::remove (GetFilePath (directory, it.second));
deletedCount++;
// RouterInfo expires in 72 hours if more than 300
if (total > 300 && ts > it.second->GetTimestamp () + 3*24*3600*1000LL) // 3 days
{
total--;
it.second->SetUnreachable (true);
}
if (it.second->IsUnreachable ())
{
if (boost::filesystem::exists (GetFilePath (directory, it.second)))
{
boost::filesystem::remove (GetFilePath (directory, it.second));
deletedCount++;
}
}
}
}
if (count > 0)
@ -239,32 +294,49 @@ namespace data
void NetDb::RequestDestination (const IdentHash& destination, bool isLeaseSet)
{
auto floodfill= GetRandomNTCPRouter (true);
if (floodfill)
RequestDestination (destination, floodfill, isLeaseSet);
else
LogPrint ("No floodfill routers found");
}
void NetDb::RequestDestination (const IdentHash& destination, const RouterInfo * floodfill, bool isLeaseSet)
{
if (!floodfill) return;
i2p::tunnel::OutboundTunnel * outbound = i2p::tunnel::tunnels.GetNextOutboundTunnel ();
if (outbound)
{
i2p::tunnel::InboundTunnel * inbound = i2p::tunnel::tunnels.GetNextInboundTunnel ();
if (inbound)
if (isLeaseSet) // we request LeaseSet through tunnels
{
i2p::tunnel::OutboundTunnel * outbound = i2p::tunnel::tunnels.GetNextOutboundTunnel ();
if (outbound)
{
RequestedDestination * dest = CreateRequestedDestination (destination, isLeaseSet);
dest->SetLastOutboundTunnel (outbound);
auto msg = dest->CreateRequestMessage (floodfill, inbound);
outbound->SendTunnelDataMsg (floodfill->GetIdentHash (), 0, msg);
}
i2p::tunnel::InboundTunnel * inbound = i2p::tunnel::tunnels.GetNextInboundTunnel ();
if (inbound)
{
RequestedDestination * dest = CreateRequestedDestination (destination, isLeaseSet);
auto floodfill = GetClosestFloodfill (destination, dest->GetExcludedPeers ());
if (floodfill)
{
std::vector<i2p::tunnel::TunnelMessageBlock> msgs;
// DatabaseLookup message
dest->SetLastOutboundTunnel (outbound);
msgs.push_back (i2p::tunnel::TunnelMessageBlock
{
i2p::tunnel::eDeliveryTypeRouter,
floodfill->GetIdentHash (), 0,
dest->CreateRequestMessage (floodfill, inbound)
});
outbound->SendTunnelDataMsg (msgs);
}
else
LogPrint ("No more floodfills found");
}
else
LogPrint ("No inbound tunnels found");
}
else
LogPrint ("No inbound tunnels found");
}
else
LogPrint ("No outbound tunnels found");
LogPrint ("No outbound tunnels found");
}
else // RouterInfo is requested directly
{
RequestedDestination * dest = CreateRequestedDestination (destination, false);
auto floodfill = GetClosestFloodfill (destination, dest->GetExcludedPeers ());
if (floodfill)
{
dest->SetLastOutboundTunnel (nullptr);
i2p::transports.SendMessage (floodfill->GetIdentHash (), dest->CreateRequestMessage (floodfill->GetIdentHash ()));
}
}
}
void NetDb::HandleDatabaseStoreMsg (uint8_t * buf, size_t len)
@ -377,18 +449,25 @@ namespace data
dest->GetLastRouter ()->GetIdentHash (), 0, msg
});
}
}
else // we should send directly
{
if (!dest->IsLeaseSet ()) // if not LeaseSet
i2p::transports.SendMessage (router, dest->CreateRequestMessage (router));
else
LogPrint ("Can't request LeaseSet");
}
}
}
if (msgs.size () > 0)
if (outbound && msgs.size () > 0)
outbound->SendTunnelDataMsg (msgs);
}
else
{
// no more requests for detination possible. delete it
m_RequestedDestinations.erase (it);
delete it->second;
m_RequestedDestinations.erase (it);
}
}
else
@ -402,7 +481,7 @@ namespace data
auto inbound = i2p::tunnel::tunnels.GetNextInboundTunnel ();
if (outbound && inbound)
{
auto floodfill = GetRandomNTCPRouter (true);
auto floodfill = GetRandomRouter (outbound->GetEndpointRouter (), true);
if (floodfill)
{
LogPrint ("Exploring new routers ...");
@ -449,8 +528,8 @@ namespace data
auto it = m_RequestedDestinations.find (dest);
if (it != m_RequestedDestinations.end ())
{
m_RequestedDestinations.erase (it);
delete it->second;
m_RequestedDestinations.erase (it);
}
}
@ -470,16 +549,29 @@ namespace data
return last;
}
const RouterInfo * NetDb::GetRandomRouter () const
const RouterInfo * NetDb::GetRandomRouter (const RouterInfo * compatibleWith, bool floodfillOnly) const
{
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
uint32_t ind = rnd.GenerateWord32 (0, m_RouterInfos.size () - 1), i = 0;
for (auto it: m_RouterInfos)
uint32_t ind = rnd.GenerateWord32 (0, m_RouterInfos.size () - 1);
for (int j = 0; j < 2; j++)
{
if (i >= ind) return it.second;
else i++;
uint32_t i = 0;
for (auto it: m_RouterInfos)
{
if (i >= ind)
{
if (!it.second->IsUnreachable () &&
(!compatibleWith || it.second->IsCompatible (*compatibleWith)) &&
(!floodfillOnly || it.second->IsFloodfill ()))
return it.second;
}
else
i++;
}
// we couldn't find anything, try second pass
ind = 0;
}
return nullptr;
return nullptr; // seem we have too few routers
}
void NetDb::PostI2NPMsg (I2NPMessage * msg)
@ -487,7 +579,8 @@ namespace data
if (msg) m_Queue.Put (msg);
}
const RouterInfo * NetDb::GetClosestFloodfill (const IdentHash& destination) const
const RouterInfo * NetDb::GetClosestFloodfill (const IdentHash& destination,
const std::set<IdentHash>& excluded) const
{
RouterInfo * r = nullptr;
XORMetric minMetric;
@ -495,7 +588,7 @@ namespace data
minMetric.SetMax ();
for (auto it: m_RouterInfos)
{
if (it.second->IsFloodfill () &&! it.second->IsUnreachable ())
if (it.second->IsFloodfill () &&! it.second->IsUnreachable () && !excluded.count (it.first))
{
XORMetric m = destKey ^ it.second->GetRoutingKey ();
if (m < minMetric)
@ -507,5 +600,46 @@ namespace data
}
return r;
}
//TODO: Move to reseed.
//TODO: Implement v1 & v2 reseeding. Lightweight zip library is needed for v2.
// orignal: zip is part of crypto++, see implementation of DatabaseStoreMsg
//TODO: Implement SU3, utils.
void NetDb::DownloadRouterInfo (const std::string& address, const std::string& filename)
{
try
{
boost::asio::ip::tcp::iostream site(address, "http");
if (!site)
{
//site.expires_from_now (boost::posix_time::seconds (10)); // wait for 10 seconds
site << "GET " << filename << "HTTP/1.0\nHost: " << address << "\nAccept: */*\nConnection: close\n\n";
// read response
std::string version, statusMessage;
site >> version; // HTTP version
int status;
site >> status; // status
std::getline (site, statusMessage);
if (status == 200) // OK
{
std::string header;
while (header != "\n")
std::getline (site, header);
// read content
std::stringstream ss;
ss << site.rdbuf();
AddRouterInfo ((uint8_t *)ss.str ().c_str (), ss.str ().size ());
}
else
LogPrint ("HTTP response ", status);
}
else
LogPrint ("Can't connect to ", address);
}
catch (std::exception& ex)
{
LogPrint ("Failed to download ", filename, " : ", ex.what ());
}
}
}
}

16
NetDb.h
View file

@ -26,12 +26,15 @@ namespace data
const IdentHash& GetDestination () const { return m_Destination; };
int GetNumExcludedPeers () const { return m_ExcludedPeers.size (); };
const RouterInfo * GetLastRouter () const { return m_LastRouter; };
const std::set<IdentHash>& GetExcludedPeers () { return m_ExcludedPeers; };
const RouterInfo * GetLastRouter () const { return m_LastRouter; };
const i2p::tunnel::InboundTunnel * GetLastReplyTunnel () const { return m_LastReplyTunnel; };
bool IsExploratory () const { return m_IsExploratory; };
bool IsLeaseSet () const { return m_IsLeaseSet; };
bool IsExcluded (const IdentHash& ident) const { return m_ExcludedPeers.count (ident); };
I2NPMessage * CreateRequestMessage (const RouterInfo * router, const i2p::tunnel::InboundTunnel * replyTunnel);
I2NPMessage * CreateRequestMessage (const IdentHash& floodfill);
i2p::tunnel::OutboundTunnel * GetLastOutboundTunnel () const { return m_LastOutboundTunnel; };
void SetLastOutboundTunnel (i2p::tunnel::OutboundTunnel * tunnel) { m_LastOutboundTunnel = tunnel; };
@ -62,23 +65,24 @@ namespace data
void RequestDestination (const char * b32); // in base32
void RequestDestination (const IdentHash& destination, bool isLeaseSet = false);
void RequestDestination (const IdentHash& destination, const RouterInfo * floodfill, bool isLeaseSet = false);
void HandleDatabaseStoreMsg (uint8_t * buf, size_t len);
void HandleDatabaseSearchReplyMsg (I2NPMessage * msg);
const RouterInfo * GetRandomNTCPRouter (bool floodfillOnly = false) const;
const RouterInfo * GetRandomRouter () const;
const RouterInfo * GetRandomRouter (const RouterInfo * compatibleWith = nullptr, bool floodfillOnly = false) const;
void PostI2NPMsg (I2NPMessage * msg);
private:
bool CreateNetDb(const char * directory);
void Load (const char * directory);
void SaveUpdated (const char * directory);
void DownloadRouterInfo (const std::string& address, const std::string& filename); // for reseed
void Run (); // exploratory thread
void Explore ();
const RouterInfo * GetClosestFloodfill (const IdentHash& destination) const;
const RouterInfo * GetClosestFloodfill (const IdentHash& destination, const std::set<IdentHash>& excluded) const;
RequestedDestination * CreateRequestedDestination (const IdentHash& dest,
bool isLeaseSet, bool isExploratory = false);

View file

@ -3,6 +3,7 @@
#include <cryptopp/dsa.h>
#include "CryptoConst.h"
#include "RouterContext.h"
#include "util.h"
namespace i2p
{
@ -45,7 +46,7 @@ namespace i2p
auto address = m_RouterInfo.GetNTCPAddress ();
if (address)
{
address->host = host;
address->host = boost::asio::ip::address::from_string (host);
address->port = port;
}
@ -60,7 +61,7 @@ namespace i2p
bool RouterContext::Load ()
{
std::ifstream fk (ROUTER_KEYS);
std::ifstream fk (ROUTER_KEYS, std::ifstream::binary | std::ofstream::in);
if (!fk.is_open ()) return false;
fk.read ((char *)&m_Keys, sizeof (m_Keys));
@ -74,10 +75,10 @@ namespace i2p
void RouterContext::Save ()
{
std::ofstream fk (ROUTER_KEYS);
std::ofstream fk (ROUTER_KEYS, std::ofstream::binary | std::ofstream::out);
fk.write ((char *)&m_Keys, sizeof (m_Keys));
std::ofstream fi (ROUTER_INFO);
std::ofstream fi (ROUTER_INFO, std::ofstream::binary | std::ofstream::out);
fi.write ((char *)m_RouterInfo.GetBuffer (), m_RouterInfo.GetBufferLen ());
}
}
}

View file

@ -12,18 +12,19 @@
#include "RouterInfo.h"
#include "RouterContext.h"
namespace i2p
{
namespace data
{
RouterInfo::RouterInfo (const char * filename):
m_IsUpdated (false), m_IsUnreachable (false)
m_IsUpdated (false), m_IsUnreachable (false), m_SupportedTransports (0)
{
ReadFromFile (filename);
}
RouterInfo::RouterInfo (const uint8_t * buf, int len):
m_IsUpdated (true)
m_IsUpdated (true), m_IsUnreachable (false), m_SupportedTransports (0)
{
memcpy (m_Buffer, buf, len);
m_BufferLen = len;
@ -41,7 +42,7 @@ namespace data
void RouterInfo::ReadFromFile (const char * filename)
{
std::ifstream s(filename);
std::ifstream s(filename, std::ifstream::binary);
if (s.is_open ())
{
s.seekg (0,std::ios::end);
@ -95,15 +96,34 @@ namespace data
size = be16toh (size);
while (r < size)
{
char key[50], value[50];
char key[500], value[500];
r += ReadString (key, s);
s.seekg (1, std::ios_base::cur); r++; // =
r += ReadString (value, s);
s.seekg (1, std::ios_base::cur); r++; // ;
if (!strcmp (key, "host"))
address.host = value;
{
boost::system::error_code ecode;
address.host = boost::asio::ip::address::from_string (value, ecode);
if (ecode)
{
// TODO: we should try to resolve address here
LogPrint ("Unexpected address ", value);
SetUnreachable (true);
}
else
{
// add supported protocol
if (address.host.is_v4 ())
m_SupportedTransports |= (address.transportStyle == eTransportNTCP) ? eNTCPV4 : eSSUV4;
else
m_SupportedTransports |= (address.transportStyle == eTransportNTCP) ? eNTCPV6 : eSSUV6;
}
}
else if (!strcmp (key, "port"))
address.port = boost::lexical_cast<int>(value);
else if (!strcmp (key, "key"))
Base64ToByteStream (value, strlen (value), address.key, 32);
}
m_Addresses.push_back(address);
}
@ -117,7 +137,13 @@ namespace data
size = be16toh (size);
while (r < size)
{
#ifdef _WIN32
char key[500], value[500];
// TODO: investigate why properties get read as one long string under Windows
// length should not be more than 44
#else
char key[50], value[50];
#endif
r += ReadString (key, s);
s.seekg (1, std::ios_base::cur); r++; // =
r += ReadString (value, s);
@ -166,7 +192,7 @@ namespace data
std::stringstream properties;
WriteString ("host", properties);
properties << '=';
WriteString (address.host, properties);
WriteString (address.host.to_string (), properties);
properties << ';';
WriteString ("port", properties);
properties << '=';
@ -227,7 +253,7 @@ namespace data
void RouterInfo::AddNTCPAddress (const char * host, int port)
{
Address addr;
addr.host = host;
addr.host = boost::asio::ip::address::from_string (host);
addr.port = port;
addr.transportStyle = eTransportNTCP;
addr.cost = 2;
@ -256,22 +282,33 @@ namespace data
return false;
}
bool RouterInfo::IsNTCP () const
bool RouterInfo::IsNTCP (bool v4only) const
{
for (auto& address : m_Addresses)
{
if (address.transportStyle == eTransportNTCP)
return true;
}
return false;
if (v4only)
return m_SupportedTransports & eNTCPV4;
else
return m_SupportedTransports & (eNTCPV4 | eNTCPV6);
}
RouterInfo::Address * RouterInfo::GetNTCPAddress (bool v4only)
{
return GetAddress (eTransportNTCP, v4only);
}
RouterInfo::Address * RouterInfo::GetNTCPAddress ()
RouterInfo::Address * RouterInfo::GetSSUAddress (bool v4only)
{
return GetAddress (eTransportSSU, v4only);
}
RouterInfo::Address * RouterInfo::GetAddress (TransportStyle s, bool v4only)
{
for (auto& address : m_Addresses)
{
if (address.transportStyle == eTransportNTCP)
return &address;
if (address.transportStyle == s)
{
if (!v4only || address.host.is_v4 ())
return &address;
}
}
return nullptr;
}

View file

@ -17,6 +17,14 @@ namespace data
{
public:
enum SupportedTranports
{
eNTCPV4 = 0x01,
eNTCPV6 = 0x20,
eSSUV4 = 0x40,
eSSUV6 = 0x80
};
enum TransportStyle
{
eTransportUnknown = 0,
@ -27,10 +35,11 @@ namespace data
struct Address
{
TransportStyle transportStyle;
std::string host;
boost::asio::ip::address host;
int port;
uint64_t date;
uint8_t cost;
uint8_t key[32]; // into key for SSU
};
RouterInfo (const char * filename);
@ -45,14 +54,17 @@ namespace data
const char * GetIdentHashAbbreviation () const { return m_IdentHashAbbreviation; };
uint64_t GetTimestamp () const { return m_Timestamp; };
const std::vector<Address>& GetAddresses () const { return m_Addresses; };
Address * GetNTCPAddress ();
Address * GetNTCPAddress (bool v4only = true);
Address * GetSSUAddress (bool v4only = true);
const RoutingKey& GetRoutingKey () const { return m_RoutingKey; };
void AddNTCPAddress (const char * host, int port);
void SetProperty (const char * key, const char * value);
const char * GetProperty (const char * key) const;
bool IsFloodfill () const;
bool IsNTCP () const;
bool IsNTCP (bool v4only = true) const;
bool IsCompatible (const RouterInfo& other) const { return m_SupportedTransports & other.m_SupportedTransports; };
void SetUnreachable (bool unreachable) { m_IsUnreachable = unreachable; };
bool IsUnreachable () const { return m_IsUnreachable; };
@ -78,6 +90,7 @@ namespace data
size_t ReadString (char * str, std::istream& s);
void WriteString (const std::string& str, std::ostream& s);
void UpdateIdentHashBase64 ();
Address * GetAddress (TransportStyle s, bool v4only);
private:
@ -91,6 +104,7 @@ namespace data
std::vector<Address> m_Addresses;
std::map<std::string, std::string> m_Properties;
bool m_IsUpdated, m_IsUnreachable;
uint8_t m_SupportedTransports;
};
}
}

294
SSU.cpp Normal file
View file

@ -0,0 +1,294 @@
#include <string.h>
#include <boost/bind.hpp>
#include <cryptopp/dh.h>
#include <cryptopp/secblock.h>
#include "CryptoConst.h"
#include "Log.h"
#include "Timestamp.h"
#include "RouterContext.h"
#include "hmac.h"
#include "SSU.h"
namespace i2p
{
namespace ssu
{
SSUSession::SSUSession (SSUServer * server, const boost::asio::ip::udp::endpoint& remoteEndpoint,
i2p::data::RouterInfo * router): m_Server (server), m_RemoteEndpoint (remoteEndpoint),
m_RemoteRouter (router), m_State (eSessionStateUnknown)
{
}
void SSUSession::CreateAESKey (uint8_t * pubKey, uint8_t * aesKey) // TODO: move it to base class for NTCP and SSU
{
CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg);
CryptoPP::SecByteBlock secretKey(dh.AgreedValueLength());
if (!dh.Agree (secretKey, i2p::context.GetPrivateKey (), pubKey))
{
LogPrint ("Couldn't create shared key");
return;
};
if (secretKey[0] & 0x80)
{
aesKey[0] = 0;
memcpy (aesKey + 1, secretKey, 31);
}
else
memcpy (aesKey, secretKey, 32);
}
void SSUSession::ProcessNextMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint)
{
switch (m_State)
{
case eSessionStateUnknown:
// session request
ProcessSessionRequest (buf, len, senderEndpoint);
break;
case eSessionStateRequestSent:
// session created
ProcessSessionCreated (buf, len);
break;
default:
LogPrint ("SSU state not implemented yet");
}
}
void SSUSession::ProcessSessionRequest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint)
{
LogPrint ("Process session request");
if (ProcessIntroKeyEncryptedMessage (PAYLOAD_TYPE_SESSION_REQUEST, buf, len))
{
m_State = eSessionStateRequestReceived;
LogPrint ("Session request received");
SendSessionCreated (senderEndpoint);
}
}
void SSUSession::ProcessSessionCreated (uint8_t * buf, size_t len)
{
LogPrint ("Process session created");
if (ProcessIntroKeyEncryptedMessage (PAYLOAD_TYPE_SESSION_CREATED, buf, len))
{
m_State = eSessionStateCreatedReceived;
LogPrint ("Session request received");
// TODO:
}
}
void SSUSession::SendSessionRequest ()
{
auto address = m_RemoteRouter ? m_RemoteRouter->GetSSUAddress () : nullptr;
if (!address)
{
LogPrint ("Missing remote SSU address");
return;
}
uint8_t buf[304 + 18]; // 304 bytes for ipv4 (320 for ipv6)
uint8_t * payload = buf + sizeof (SSUHeader);
memcpy (payload, i2p::context.GetRouterIdentity ().publicKey, 256);
payload[256] = 4; // we assume ipv4
*(uint32_t *)(payload + 257) = address->host.to_v4 ().to_ulong (); // network bytes order already
uint8_t iv[16];
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
rnd.GenerateBlock (iv, 16); // random iv
FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_REQUEST, buf, 304, address->key, iv, address->key);
m_State = eSessionStateRequestSent;
m_Server->Send (buf, 304, m_RemoteEndpoint);
}
void SSUSession::SendSessionCreated (const boost::asio::ip::udp::endpoint& senderEndpoint)
{
auto address = m_RemoteRouter ? m_RemoteRouter->GetSSUAddress () : nullptr;
if (!address)
{
LogPrint ("Missing remote SSU address");
return;
}
uint8_t buf[368 + 18];
uint8_t * payload = buf + sizeof (SSUHeader);
memcpy (payload, i2p::context.GetRouterIdentity ().publicKey, 256);
m_State = eSessionStateRequestSent;
m_Server->Send (buf, 368, m_RemoteEndpoint);
}
bool SSUSession::ProcessIntroKeyEncryptedMessage (uint8_t expectedPayloadType, uint8_t * buf, size_t len)
{
auto address = i2p::context.GetRouterInfo ().GetSSUAddress ();
if (address)
{
// use intro key for verification and decryption
if (Validate (buf, len, address->key))
{
Decrypt (buf, len, address->key);
SSUHeader * header = (SSUHeader *)buf;
if ((header->flag >> 4) == expectedPayloadType)
{
CreateAESKey (buf + sizeof (SSUHeader), m_SessionKey);
return true;
}
else
LogPrint ("Unexpected payload type ", (int)(header->flag >> 4));
}
else
LogPrint ("MAC verifcation failed");
}
else
LogPrint ("SSU is not supported");
return false;
}
void SSUSession::FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len, uint8_t * aesKey, uint8_t * iv, uint8_t * macKey)
{
if (len < sizeof (SSUHeader))
{
LogPrint ("Unexpected SSU packet length ", len);
return;
}
SSUHeader * header = (SSUHeader *)buf;
memcpy (header->iv, iv, 16);
header->flag = payloadType << 4; // MSB is 0
header->time = htobe32 (i2p::util::GetSecondsSinceEpoch ());
uint8_t * encrypted = &header->flag;
uint16_t encryptedLen = len - (encrypted - buf);
m_Encryption.SetKeyWithIV (aesKey, 32, iv);
m_Encryption.ProcessData (encrypted, encrypted, encryptedLen);
// assume actual buffer size is 18 (16 + 2) bytes more
memcpy (buf + len, iv, 16);
*(uint16_t *)(buf + len + 16) = htobe16 (encryptedLen);
i2p::crypto::HMACMD5Digest (encrypted, encryptedLen + 18, macKey, header->mac);
}
void SSUSession::Decrypt (uint8_t * buf, size_t len, uint8_t * aesKey)
{
if (len < sizeof (SSUHeader))
{
LogPrint ("Unexpected SSU packet length ", len);
return;
}
SSUHeader * header = (SSUHeader *)buf;
uint8_t * encrypted = &header->flag;
uint16_t encryptedLen = len - (encrypted - buf);
m_Decryption.SetKeyWithIV (aesKey, 32, header->iv);
m_Decryption.ProcessData (encrypted, encrypted, encryptedLen);
}
bool SSUSession::Validate (uint8_t * buf, size_t len, uint8_t * macKey)
{
if (len < sizeof (SSUHeader))
{
LogPrint ("Unexpected SSU packet length ", len);
return false;
}
SSUHeader * header = (SSUHeader *)buf;
uint8_t * encrypted = &header->flag;
uint16_t encryptedLen = len - (encrypted - buf);
// assume actual buffer size is 18 (16 + 2) bytes more
memcpy (buf + len, header->iv, 16);
*(uint16_t *)(buf + len + 16) = htobe16 (encryptedLen);
uint8_t digest[16];
i2p::crypto::HMACMD5Digest (encrypted, encryptedLen + 18, macKey, digest);
return !memcmp (header->mac, digest, 16);
}
void SSUSession::Connect ()
{
SendSessionRequest ();
}
void SSUSession::SendI2NPMessage (I2NPMessage * msg)
{
// TODO:
}
SSUServer::SSUServer (boost::asio::io_service& service, int port):
m_Socket (service, boost::asio::ip::udp::endpoint (boost::asio::ip::udp::v4 (), port))
{
}
SSUServer::~SSUServer ()
{
for (auto it: m_Sessions)
delete it.second;
}
void SSUServer::Start ()
{
Receive ();
}
void SSUServer::Stop ()
{
m_Socket.close ();
}
void SSUServer::Send (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& to)
{
m_Socket.send_to (boost::asio::buffer (buf, len), to);
}
void SSUServer::Receive ()
{
m_Socket.async_receive_from (boost::asio::buffer (m_ReceiveBuffer, SSU_MTU), m_SenderEndpoint,
boost::bind (&SSUServer::HandleReceivedFrom, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
}
void SSUServer::HandleReceivedFrom (const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
if (!ecode)
{
LogPrint ("SSU received ", bytes_transferred, " bytes");
SSUSession * session = nullptr;
auto it = m_Sessions.find (m_SenderEndpoint);
if (it != m_Sessions.end ())
session = it->second;
if (!session)
{
session = new SSUSession (this, m_SenderEndpoint);
m_Sessions[m_SenderEndpoint] = session;
LogPrint ("New SSU session from ", m_SenderEndpoint.address ().to_string (), ":", m_SenderEndpoint.port (), " created");
}
session->ProcessNextMessage (m_ReceiveBuffer, bytes_transferred, m_SenderEndpoint);
Receive ();
}
else
LogPrint ("SSU receive error: ", ecode.message ());
}
SSUSession * SSUServer::GetSession (i2p::data::RouterInfo * router)
{
SSUSession * session = nullptr;
if (router)
{
auto address = router->GetSSUAddress ();
if (address)
{
boost::asio::ip::udp::endpoint remoteEndpoint (address->host, address->port);
auto it = m_Sessions.find (remoteEndpoint);
if (it != m_Sessions.end ())
session = it->second;
else
{
// otherwise create new session
session = new SSUSession (this, remoteEndpoint, router);
m_Sessions[remoteEndpoint] = session;
LogPrint ("New SSU session to [", router->GetIdentHashAbbreviation (), "] ",
remoteEndpoint.address ().to_string (), ":", remoteEndpoint.port (), " created");
session->Connect ();
}
}
else
LogPrint ("Router ", router->GetIdentHashAbbreviation (), " doesn't have SSU address");
}
return session;
}
}
}

116
SSU.h Normal file
View file

@ -0,0 +1,116 @@
#ifndef SSU_H__
#define SSU_H__
#include <inttypes.h>
#include <map>
#include <boost/asio.hpp>
#include <cryptopp/modes.h>
#include <cryptopp/aes.h>
#include "I2PEndian.h"
#include "RouterInfo.h"
#include "I2NPProtocol.h"
namespace i2p
{
namespace ssu
{
#pragma pack(1)
struct SSUHeader
{
uint8_t mac[16];
uint8_t iv[16];
uint8_t flag;
uint32_t time;
};
#pragma pack()
const int SSU_MTU = 1484;
// payload types (3 bits)
const uint8_t PAYLOAD_TYPE_SESSION_REQUEST = 0;
const uint8_t PAYLOAD_TYPE_SESSION_CREATED = 1;
const uint8_t PAYLOAD_TYPE_SESSION_CONFIRMED = 2;
const uint8_t PAYLOAD_TYPE_RELAY_REQUEST = 3;
const uint8_t PAYLOAD_TYPE_RELAY_RESPONSE = 4;
const uint8_t PAYLOAD_TYPE_RELAY_INTRO = 5;
const uint8_t PAYLOAD_TYPE_DATA = 6;
const uint8_t PAYLOAD_TYPE_TEST = 7;
enum SessionState
{
eSessionStateUnknown,
eSessionStateRequestSent,
eSessionStateRequestReceived,
eSessionStateCreatedSent,
eSessionStateCreatedReceived,
eSessionStateConfirmedSent,
eSessionStateConfirmedReceived,
eSessionStateEstablised
};
class SSUServer;
class SSUSession
{
public:
SSUSession (SSUServer * server, const boost::asio::ip::udp::endpoint& remoteEndpoint,
i2p::data::RouterInfo * router = nullptr);
void ProcessNextMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint);
void Connect ();
void SendI2NPMessage (I2NPMessage * msg);
private:
void CreateAESKey (uint8_t * pubKey, uint8_t * aesKey); // TODO: shouldn't be here
void ProcessSessionRequest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint);
void SendSessionRequest ();
void ProcessSessionCreated (uint8_t * buf, size_t len);
void SendSessionCreated (const boost::asio::ip::udp::endpoint& senderEndpoint);
bool ProcessIntroKeyEncryptedMessage (uint8_t expectedPayloadType, uint8_t * buf, size_t len);
void FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len, uint8_t * aesKey, uint8_t * iv, uint8_t * macKey);
void Decrypt (uint8_t * buf, size_t len, uint8_t * aesKey);
bool Validate (uint8_t * buf, size_t len, uint8_t * macKey);
private:
SSUServer * m_Server;
boost::asio::ip::udp::endpoint m_RemoteEndpoint;
i2p::data::RouterInfo * m_RemoteRouter;
SessionState m_State;
CryptoPP::CBC_Mode<CryptoPP::AES>::Encryption m_Encryption;
CryptoPP::CBC_Mode<CryptoPP::AES>::Decryption m_Decryption;
uint8_t m_SessionKey[32];
};
class SSUServer
{
public:
SSUServer (boost::asio::io_service& service, int port);
~SSUServer ();
void Start ();
void Stop ();
SSUSession * GetSession (i2p::data::RouterInfo * router);
void Send (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& to);
private:
void Receive ();
void HandleReceivedFrom (const boost::system::error_code& ecode, std::size_t bytes_transferred);
private:
boost::asio::ip::udp::socket m_Socket;
boost::asio::ip::udp::endpoint m_SenderEndpoint;
uint8_t m_ReceiveBuffer[2*SSU_MTU];
std::map<boost::asio::ip::udp::endpoint, SSUSession *> m_Sessions;
};
}
}
#endif

View file

@ -1,4 +1,3 @@
#include "I2PEndian.h"
#include <string>
#include <algorithm>
#include <cryptopp/gzip.h>
@ -26,30 +25,18 @@ namespace stream
{
while (auto packet = m_ReceiveQueue.Get ())
delete packet;
for (auto it: m_SavedPackets)
delete it;
}
void Stream::HandleNextPacket (Packet * packet)
{
const uint8_t * buf = packet->buf;
buf += 4; // sendStreamID
if (!m_SendStreamID)
m_SendStreamID = be32toh (*(uint32_t *)buf);
buf += 4; // receiveStreamID
uint32_t receivedSeqn = be32toh (*(uint32_t *)buf);
buf += 4; // sequenceNum
buf += 4; // ackThrough
int nackCount = buf[0];
buf++; // NACK count
buf += 4*nackCount; // NACKs
buf++; // resendDelay
uint16_t flags = be16toh (*(uint16_t *)buf);
buf += 2; // flags
uint16_t optionalSize = be16toh (*(uint16_t *)buf);
buf += 2; // optional size
const uint8_t * optionalData = buf;
buf += optionalSize;
m_SendStreamID = packet->GetReceiveStreamID ();
// process flags
uint16_t flags = packet->GetFlags ();
const uint8_t * optionData = packet->GetOptionData ();
if (flags & PACKET_FLAG_SYNCHRONIZE)
{
LogPrint ("Synchronize");
@ -58,21 +45,21 @@ namespace stream
if (flags & PACKET_FLAG_SIGNATURE_INCLUDED)
{
LogPrint ("Signature");
optionalData += 40;
optionData += 40;
}
if (flags & PACKET_FLAG_FROM_INCLUDED)
{
LogPrint ("From identity");
optionalData += sizeof (i2p::data::Identity);
optionData += sizeof (i2p::data::Identity);
}
// we have reached payload section
uint32_t receivedSeqn = packet->GetSeqn ();
LogPrint ("seqn=", receivedSeqn, ", flags=", flags);
if (!receivedSeqn || receivedSeqn == m_LastReceivedSequenceNumber + 1)
{
// we have received next message
packet->offset = buf - packet->buf;
packet->offset = packet->GetPayload () - packet->buf;
if (packet->GetLength () > 0)
m_ReceiveQueue.Put (packet);
else
@ -80,6 +67,26 @@ namespace stream
m_LastReceivedSequenceNumber = receivedSeqn;
SendQuickAck ();
// we should also try stored messages if any
for (auto it = m_SavedPackets.begin (); it != m_SavedPackets.end ();)
{
if ((*it)->GetSeqn () == m_LastReceivedSequenceNumber + 1)
{
Packet * packet = *it;
m_SavedPackets.erase (it++);
LogPrint ("Process saved packet seqn=", packet->GetSeqn ());
if (packet->GetLength () > 0)
m_ReceiveQueue.Put (packet);
else
delete packet;
m_LastReceivedSequenceNumber++;
SendQuickAck ();
}
else
break;
}
}
else
{
@ -90,13 +97,14 @@ namespace stream
m_OutboundTunnel = i2p::tunnel::tunnels.GetNextOutboundTunnel (); // pick another tunnel
if (m_OutboundTunnel)
SendQuickAck (); // resend ack for previous message again
delete packet; // packet dropped
}
else
{
LogPrint ("Missing messages from ", m_LastReceivedSequenceNumber + 1, " to ", receivedSeqn - 1);
// actually do nothing. just wait for missing message again
// save message and wait for missing message again
SavePacket (packet);
}
delete packet; // packet dropped
}
if (flags & PACKET_FLAG_CLOSE)
@ -107,6 +115,11 @@ namespace stream
}
}
void Stream::SavePacket (Packet * packet)
{
m_SavedPackets.insert (packet);
}
size_t Stream::Send (uint8_t * buf, size_t len, int timeout)
{
if (!m_IsOpen)

View file

@ -3,7 +3,9 @@
#include <inttypes.h>
#include <map>
#include <set>
#include <cryptopp/dsa.h>
#include "I2PEndian.h"
#include "Queue.h"
#include "Identity.h"
#include "LeaseSet.h"
@ -37,6 +39,25 @@ namespace stream
Packet (): len (0), offset (0) {};
uint8_t * GetBuffer () { return buf + offset; };
size_t GetLength () const { return len - offset; };
uint32_t GetSendStreamID () const { return be32toh (*(uint32_t *)buf); };
uint32_t GetReceiveStreamID () const { return be32toh (*(uint32_t *)(buf + 4)); };
uint32_t GetSeqn () const { return be32toh (*(uint32_t *)(buf + 8)); };
uint32_t GetAckThrough () const { return be32toh (*(uint32_t *)(buf + 12)); };
uint8_t GetNACKCount () const { return buf[16]; };
const uint8_t * GetOption () const { return buf + 17 + GetNACKCount ()*4 + 3; }; // 3 = resendDelay + flags
uint16_t GetFlags () const { return be16toh (*(uint16_t *)(GetOption () - 2)); };
uint16_t GetOptionSize () const { return be16toh (*(uint16_t *)GetOption ()); };
const uint8_t * GetOptionData () const { return GetOption () + 2; };
const uint8_t * GetPayload () const { return GetOptionData () + GetOptionSize (); };
};
struct PacketCmp
{
bool operator() (const Packet * p1, const Packet * p2) const
{
return p1->GetSeqn () < p2->GetSeqn ();
};
};
class StreamingDestination;
@ -61,6 +82,8 @@ namespace stream
void ConnectAndSend (uint8_t * buf, size_t len);
void SendQuickAck ();
void SavePacket (Packet * packet);
private:
@ -69,6 +92,7 @@ namespace stream
StreamingDestination * m_LocalDestination;
const i2p::data::LeaseSet * m_RemoteLeaseSet;
i2p::util::Queue<Packet> m_ReceiveQueue;
std::set<Packet *, PacketCmp> m_SavedPackets;
i2p::tunnel::OutboundTunnel * m_OutboundTunnel;
};

View file

@ -12,7 +12,7 @@ namespace i2p
Transports transports;
Transports::Transports ():
m_Thread (0), m_Work (m_Service),m_NTCPAcceptor (0)
m_Thread (nullptr), m_Work (m_Service),m_NTCPAcceptor (nullptr), m_SSUServer (nullptr)
{
}
@ -34,11 +34,22 @@ namespace i2p
m_NTCPAcceptor = new boost::asio::ip::tcp::acceptor (m_Service,
boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), address.port));
LogPrint ("Start listening port ", address.port);
LogPrint ("Start listening TCP port ", address.port);
auto conn = new i2p::ntcp::NTCPServerConnection (m_Service);
m_NTCPAcceptor->async_accept(conn->GetSocket (), boost::bind (&Transports::HandleAccept, this,
conn, boost::asio::placeholders::error));
}
else if (address.transportStyle == RouterInfo::eTransportSSU)
{
if (!m_SSUServer)
{
m_SSUServer = new i2p::ssu::SSUServer (m_Service, address.port);
LogPrint ("Start listening UDP port ", address.port);
m_SSUServer->Start ();
}
else
LogPrint ("SSU server already exists");
}
}
}
@ -49,6 +60,12 @@ namespace i2p
m_NTCPSessions.clear ();
delete m_NTCPAcceptor;
if (m_SSUServer)
{
m_SSUServer->Stop ();
delete m_SSUServer;
}
m_IsRunning = false;
m_Service.stop ();
if (m_Thread)
@ -139,7 +156,7 @@ namespace i2p
auto address = r->GetNTCPAddress ();
if (address)
{
session = new i2p::ntcp::NTCPClient (m_Service, address->host.c_str (), address->port, *r);
session = new i2p::ntcp::NTCPClient (m_Service, address->host, address->port, *r);
AddNTCPSession (session);
}
else

View file

@ -7,6 +7,7 @@
#include <string>
#include <boost/asio.hpp>
#include "NTCPSession.h"
#include "SSU.h"
#include "RouterInfo.h"
#include "I2NPProtocol.h"
@ -47,6 +48,7 @@ namespace i2p
boost::asio::ip::tcp::acceptor * m_NTCPAcceptor;
std::map<i2p::data::IdentHash, i2p::ntcp::NTCPSession *> m_NTCPSessions;
i2p::ssu::SSUServer * m_SSUServer;
public:

View file

@ -395,16 +395,16 @@ namespace tunnel
}
else
{
//OutboundTunnel * outboundTunnel = GetNextOutboundTunnel ();
LogPrint ("Creating two hops outbound tunnel...");
auto firstHop = i2p::data::netdb.GetRandomNTCPRouter (); // first hop must be NTCP
CreateTunnel<OutboundTunnel> (
new TunnelConfig (std::vector<const i2p::data::RouterInfo *>
{
i2p::data::netdb.GetRandomNTCPRouter (),
i2p::data::netdb.GetRandomNTCPRouter ()
firstHop,
i2p::data::netdb.GetRandomRouter (firstHop)
},
inboundTunnel->GetTunnelConfig ())/*,
outboundTunnel*/);
inboundTunnel->GetTunnelConfig ()));
}
}
}
@ -450,8 +450,8 @@ namespace tunnel
CreateTunnel<InboundTunnel> (
new TunnelConfig (std::vector<const i2p::data::RouterInfo *>
{
i2p::data::netdb.GetRandomNTCPRouter (),
router != &i2p::context.GetRouterInfo () ? router : i2p::data::netdb.GetRandomNTCPRouter ()
i2p::data::netdb.GetRandomRouter (outboundTunnel->GetEndpointRouter ()),
router != &i2p::context.GetRouterInfo () ? router : i2p::data::netdb.GetRandomNTCPRouter () // last hop must be NTCP
}),
outboundTunnel);
}

View file

@ -67,7 +67,8 @@ namespace tunnel
void SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, i2p::I2NPMessage * msg);
void SendTunnelDataMsg (std::vector<TunnelMessageBlock> msgs); // multiple messages
const i2p::data::RouterInfo * GetEndpointRouter () const
{ return GetTunnelConfig ()->GetLastHop ()->router; };
size_t GetNumSentBytes () const { return m_Gateway.GetNumSentBytes (); };
// implements TunnelBase

8
Win32/.gitignore vendored Normal file
View file

@ -0,0 +1,8 @@
*
!*/
!*.sln
!*.vcproj
!*.vcxproj
!*.vcxproj.filters
!.gitignore

View file

@ -24,6 +24,7 @@
<ClCompile Include="..\NTCPSession.cpp" />
<ClCompile Include="..\RouterContext.cpp" />
<ClCompile Include="..\RouterInfo.cpp" />
<ClCompile Include="..\SSU.cpp" />
<ClCompile Include="..\Streaming.cpp" />
<ClCompile Include="..\TransitTunnel.cpp" />
<ClCompile Include="..\Transports.cpp" />
@ -48,6 +49,7 @@
<ClInclude Include="..\Queue.h" />
<ClInclude Include="..\RouterContext.h" />
<ClInclude Include="..\RouterInfo.h" />
<ClInclude Include="..\SSU.h" />
<ClInclude Include="..\Streaming.h" />
<ClInclude Include="..\Timestamp.h" />
<ClInclude Include="..\TransitTunnel.h" />

View file

@ -72,6 +72,9 @@
<ClCompile Include="..\I2PEndian.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\SSU.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\Identity.h">
@ -149,5 +152,8 @@
<ClInclude Include="..\I2PEndian.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\SSU.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

View file

@ -27,6 +27,11 @@ namespace data
'4', '5', '6', '7', '8', '9', '-', '~'
};
const char * GetBase64SubstitutionTable ()
{
return T64;
}
/*
* Reverse Substitution Table (built in run time)
*/

View file

@ -11,9 +11,9 @@ namespace data
size_t ByteStreamToBase64 (const uint8_t * InBuffer, size_t InCount, char * OutBuffer, size_t len);
size_t Base64ToByteStream (const char * InBuffer, size_t InCount, uint8_t * OutBuffer, size_t len );
const char * GetBase64SubstitutionTable ();
size_t Base32ToByteStream (const char * inBuf, size_t len, uint8_t * outBuf, size_t outLen);
}
}

59
hmac.h Normal file
View file

@ -0,0 +1,59 @@
#ifndef HMAC_H__
#define HMAC_H__
#include <inttypes.h>
#include <string.h>
#define CRYPTOPP_ENABLE_NAMESPACE_WEAK 1
#include <cryptopp/md5.h>
namespace i2p
{
namespace crypto
{
const uint64_t IPAD = 0x3636363636363636;
const uint64_t OPAD = 0x5C5C5C5C5C5C5C5C;
inline void HMACMD5Digest (uint8_t * msg, size_t len, uint8_t * key, uint8_t * digest)
// key is 32 bytes
// digest is 16 bytes
// block size is 64 bytes
{
size_t totalLen = len + 64 + 32;
uint8_t buf[2048];
// ikeypad
((uint64_t *)buf)[0] = ((uint64_t *)key)[0] ^ IPAD;
((uint64_t *)buf)[1] = ((uint64_t *)key)[1] ^ IPAD;
((uint64_t *)buf)[2] = ((uint64_t *)key)[2] ^ IPAD;
((uint64_t *)buf)[3] = ((uint64_t *)key)[3] ^ IPAD;
((uint64_t *)buf)[4] = IPAD;
((uint64_t *)buf)[5] = IPAD;
((uint64_t *)buf)[6] = IPAD;
((uint64_t *)buf)[7] = IPAD;
// concatenate with msg
memcpy (buf + 64, msg, len);
// calculate first hash
uint8_t hash[16]; // MD5
CryptoPP::Weak1::MD5().CalculateDigest (hash, buf, len + 64);
// okeypad
((uint64_t *)buf)[0] = ((uint64_t *)key)[0] ^ OPAD;
((uint64_t *)buf)[1] = ((uint64_t *)key)[1] ^ OPAD;
((uint64_t *)buf)[2] = ((uint64_t *)key)[2] ^ OPAD;
((uint64_t *)buf)[3] = ((uint64_t *)key)[3] ^ OPAD;
((uint64_t *)buf)[4] = OPAD;
((uint64_t *)buf)[5] = OPAD;
((uint64_t *)buf)[6] = OPAD;
((uint64_t *)buf)[7] = OPAD;
// copy first hash after okeypad
memcpy (buf + 64, hash, 16);
// fill next 16 bytes with zeros (first hash size assumed 32 bytes in I2P)
memset (buf + 72, 0, 16);
// calculate digest
CryptoPP::Weak1::MD5().CalculateDigest (digest, buf, totalLen);
}
}
}
#endif

15
i2p.cpp
View file

@ -10,10 +10,21 @@
#include "Tunnel.h"
#include "NetDb.h"
#include "HTTPServer.h"
#include "util.h"
int main( int, char** )
int main( int argc, char* argv[] )
{
i2p::util::HTTPServer httpServer (7070);
i2p::util::ParseArguments(argc,argv);
#ifdef _WIN32
setlocale(LC_CTYPE, "");
SetConsoleCP(1251);
SetConsoleOutputCP(1251);
setlocale(LC_ALL, "Russian");
#endif
int httpport = i2p::util::GetIntArg("--httpport", 7070);
i2p::util::HTTPServer httpServer (httpport);
httpServer.Start ();
i2p::data::netdb.Start ();

115
portable_endian.h Normal file
View file

@ -0,0 +1,115 @@
#ifndef PORTABLE_ENDIAN_H__
#define PORTABLE_ENDIAN_H__
#if (defined(_WIN16) || defined(_WIN32) || defined(_WIN64)) && !defined(__WINDOWS__)
# define __WINDOWS__
#endif
#if defined(__linux__) || defined(__CYGWIN__)
# include <endian.h>
#elif defined(__APPLE__)
# include <libkern/OSByteOrder.h>
# define htobe16 OSSwapHostToBigInt16
# define htole16 OSSwapHostToLittleInt16
# define be16toh OSSwapBigToHostInt16
# define le16toh OSSwapLittleToHostInt16
# define htobe32 OSSwapHostToBigInt32
# define htole32 OSSwapHostToLittleInt32
# define be32toh OSSwapBigToHostInt32
# define le32toh OSSwapLittleToHostInt32
# define htobe64 OSSwapHostToBigInt64
# define htole64 OSSwapHostToLittleInt64
# define be64toh OSSwapBigToHostInt64
# define le64toh OSSwapLittleToHostInt64
# define __BYTE_ORDER BYTE_ORDER
# define __BIG_ENDIAN BIG_ENDIAN
# define __LITTLE_ENDIAN LITTLE_ENDIAN
# define __PDP_ENDIAN PDP_ENDIAN
#elif defined(__OpenBSD__)
# include <sys/endian.h>
#elif defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__)
# include <sys/endian.h>
# define be16toh betoh16
# define le16toh letoh16
# define be32toh betoh32
# define le32toh letoh32
# define be64toh betoh64
# define le64toh letoh64
#elif defined(__WINDOWS__)
#define INCL_EXTRA_HTON_FUNCTIONS
#define NOMINMAX
# include <winsock2.h>
#undef NOMINMAX
//# include <sys/param.h>
# if BYTE_ORDER == LITTLE_ENDIAN
# define htobe16 htons
# define htole16(x) (x)
# define be16toh ntohs
# define le16toh(x) (x)
# define htobe32 htonl
# define htole32(x) (x)
# define be32toh ntohl
# define le32toh(x) (x)
# define htobe64 htonll
# define htole64(x) (x)
# define be64toh ntohll
# define le64toh(x) (x)
# elif BYTE_ORDER == BIG_ENDIAN
/* that would be xbox 360 */
# define htobe16(x) (x)
# define htole16(x) __builtin_bswap16(x)
# define be16toh(x) (x)
# define le16toh(x) __builtin_bswap16(x)
# define htobe32(x) (x)
# define htole32(x) __builtin_bswap32(x)
# define be32toh(x) (x)
# define le32toh(x) __builtin_bswap32(x)
# define htobe64(x) (x)
# define htole64(x) __builtin_bswap64(x)
# define be64toh(x) (x)
# define le64toh(x) __builtin_bswap64(x)
# else
# error byte order not supported
# endif
# define __BYTE_ORDER BYTE_ORDER
# define __BIG_ENDIAN BIG_ENDIAN
# define __LITTLE_ENDIAN LITTLE_ENDIAN
# define __PDP_ENDIAN PDP_ENDIAN
#else
# error platform not supported
#endif
#endif

45
util.cpp Normal file
View file

@ -0,0 +1,45 @@
#include "util.h"
namespace i2p
{
namespace util
{
std::map<std::string, std::string> mapArgs;
void ParseArguments(int argc, const char* const argv[])
{
mapArgs.clear();
for (int i = 1; i < argc; i++)
{
std::string strKey (argv[i]);
std::string strValue;
size_t has_data = strKey.find('=');
if (has_data != std::string::npos)
{
strValue = strKey.substr(has_data+1);
strKey = strKey.substr(0, has_data);
}
if (strKey[0] != '-')
break;
mapArgs[strKey] = strValue;
}
}
int GetIntArg(const std::string& strArg, int nDefault)
{
if (mapArgs.count(strArg))
return atoi(mapArgs[strArg].c_str());
return nDefault;
}
std::string GetStringArg(const std::string& strArg, std::string nDefault)
{
if (mapArgs.count(strArg))
return mapArgs[strArg];
return nDefault;
}
} // Namespace end
}

19
util.h Normal file
View file

@ -0,0 +1,19 @@
#ifndef UTIL_H
#define UTIL_H
#include <map>
#include <string>
namespace i2p
{
namespace util
{
extern std::map<std::string, std::string> mapArgs;
void ParseArguments(int argc, const char* const argv[]);
int GetIntArg(const std::string& strArg, int nDefault);
}
}
#endif