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 ## Eclipse
################# #################

View file

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

View file

@ -386,15 +386,13 @@ namespace i2p
TunnelGatewayHeader * header = (TunnelGatewayHeader *)msg->GetPayload (); TunnelGatewayHeader * header = (TunnelGatewayHeader *)msg->GetPayload ();
uint32_t tunnelID = be32toh(header->tunnelID); uint32_t tunnelID = be32toh(header->tunnelID);
uint16_t len = be16toh(header->length); 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); i2p::tunnel::TransitTunnel * tunnel = i2p::tunnel::tunnels.GetTransitTunnel (tunnelID);
if (tunnel) 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); tunnel->SendTunnelDataMsg (nullptr, 0, msg);
}
else else
{ {
LogPrint ("Tunnel ", (unsigned int)tunnelID, " not found"); LogPrint ("Tunnel ", (unsigned int)tunnelID, " not found");

View file

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

View file

@ -5,14 +5,16 @@
#include <endian.h> #include <endian.h>
#else #else
#include <cstdint> #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); #include "portable_endian.h"
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);
#endif #endif

View file

@ -3,6 +3,7 @@
#include "CryptoConst.h" #include "CryptoConst.h"
#include "Log.h" #include "Log.h"
#include "Timestamp.h" #include "Timestamp.h"
#include "NetDb.h"
#include "LeaseSet.h" #include "LeaseSet.h"
namespace i2p namespace i2p
@ -28,6 +29,7 @@ namespace data
memcpy (m_EncryptionKey, header->encryptionKey, 256); memcpy (m_EncryptionKey, header->encryptionKey, 256);
LogPrint ("LeaseSet num=", (int)header->num); LogPrint ("LeaseSet num=", (int)header->num);
// process leases
const uint8_t * leases = buf + sizeof (H); const uint8_t * leases = buf + sizeof (H);
for (int i = 0; i < header->num; i++) for (int i = 0; i < header->num; i++)
{ {
@ -36,8 +38,16 @@ namespace data
lease.endDate = be64toh (lease.endDate); lease.endDate = be64toh (lease.endDate);
m_Leases.push_back (lease); m_Leases.push_back (lease);
leases += sizeof (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 // verify
CryptoPP::DSA::PublicKey pubKey; CryptoPP::DSA::PublicKey pubKey;
pubKey.Initialize (i2p::crypto::dsap, i2p::crypto::dsaq, i2p::crypto::dsag, 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 CFLAGS = -g -Wall -std=c++0x
OBJECTS = i2p.o base64.o NTCPSession.o RouterInfo.o Transports.o RouterContext.o \ 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 \ 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 = 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 = LIBS =
all: i2p 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): int port, i2p::data::RouterInfo& in_RouterInfo):
NTCPSession (service, in_RouterInfo), NTCPSession (service, in_RouterInfo),
m_Endpoint (boost::asio::ip::address::from_string (address), port) m_Endpoint (address, port)
{ {
Connect (); Connect ();
} }

View file

@ -144,7 +144,7 @@ namespace ntcp
{ {
public: 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: private:

252
NetDb.cpp
View file

@ -1,6 +1,7 @@
#include "I2PEndian.h" #include "I2PEndian.h"
#include <fstream> #include <fstream>
#include <vector> #include <vector>
#include <boost/asio.hpp>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <cryptopp/gzip.h> #include <cryptopp/gzip.h>
#include "base64.h" #include "base64.h"
@ -8,6 +9,7 @@
#include "Timestamp.h" #include "Timestamp.h"
#include "I2NPProtocol.h" #include "I2NPProtocol.h"
#include "Tunnel.h" #include "Tunnel.h"
#include "Transports.h"
#include "RouterContext.h" #include "RouterContext.h"
#include "Garlic.h" #include "Garlic.h"
#include "NetDb.h" #include "NetDb.h"
@ -29,6 +31,16 @@ namespace data
m_LastReplyTunnel = replyTunnel; m_LastReplyTunnel = replyTunnel;
return msg; 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; NetDb netdb;
@ -165,63 +177,106 @@ namespace data
return it->second; return it->second;
else else
return nullptr; 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) void NetDb::Load (const char * directory)
{ {
boost::filesystem::path p (directory); boost::filesystem::path p (directory);
if (boost::filesystem::exists (p)) if (!boost::filesystem::exists (p))
{ {
int numRouters = 0; if (!CreateNetDb(directory)) return;
boost::filesystem::directory_iterator end; }
for (boost::filesystem::directory_iterator it (p); it != end; ++it) // 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 #if BOOST_VERSION > 10500
RouterInfo * r = new RouterInfo (it1->path().string().c_str ()); RouterInfo * r = new RouterInfo (it1->path().string().c_str ());
#else #else
RouterInfo * r = new RouterInfo(it1->path().c_str()); RouterInfo * r = new RouterInfo(it1->path().c_str());
#endif #endif
m_RouterInfos[r->GetIdentHash ()] = r; m_RouterInfos[r->GetIdentHash ()] = r;
numRouters++; numRouters++;
}
} }
} }
LogPrint (numRouters, " routers loaded");
} }
else LogPrint (numRouters, " routers loaded");
LogPrint (directory, " doesn't exist");
} }
void NetDb::SaveUpdated (const char * directory) void NetDb::SaveUpdated (const char * directory)
{ {
auto GetFilePath = [](const char * directory, const RouterInfo * routerInfo) auto GetFilePath = [](const char * directory, const RouterInfo * routerInfo)
{ {
#ifndef _WIN32
return std::string (directory) + "/r" + 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"; routerInfo->GetIdentHashBase64 () + ".dat";
}; };
int count = 0, deletedCount = 0; int count = 0, deletedCount = 0;
auto total = m_RouterInfos.size ();
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch ();
for (auto it: m_RouterInfos) for (auto it: m_RouterInfos)
{ {
if (it.second->IsUpdated ()) 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 ()); r.write ((char *)it.second->GetBuffer (), it.second->GetBufferLen ());
it.second->SetUpdated (false); it.second->SetUpdated (false);
count++; count++;
} }
else if (it.second->IsUnreachable ()) else
{ {
if (boost::filesystem::exists (GetFilePath (directory, it.second))) // RouterInfo expires in 72 hours if more than 300
{ if (total > 300 && ts > it.second->GetTimestamp () + 3*24*3600*1000LL) // 3 days
boost::filesystem::remove (GetFilePath (directory, it.second)); {
deletedCount++; 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) if (count > 0)
@ -239,32 +294,49 @@ namespace data
void NetDb::RequestDestination (const IdentHash& destination, bool isLeaseSet) void NetDb::RequestDestination (const IdentHash& destination, bool isLeaseSet)
{ {
auto floodfill= GetRandomNTCPRouter (true); if (isLeaseSet) // we request LeaseSet through tunnels
if (floodfill) {
RequestDestination (destination, floodfill, isLeaseSet); i2p::tunnel::OutboundTunnel * outbound = i2p::tunnel::tunnels.GetNextOutboundTunnel ();
else if (outbound)
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)
{ {
RequestedDestination * dest = CreateRequestedDestination (destination, isLeaseSet); i2p::tunnel::InboundTunnel * inbound = i2p::tunnel::tunnels.GetNextInboundTunnel ();
dest->SetLastOutboundTunnel (outbound); if (inbound)
auto msg = dest->CreateRequestMessage (floodfill, inbound); {
outbound->SendTunnelDataMsg (floodfill->GetIdentHash (), 0, msg); 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 else
LogPrint ("No inbound tunnels found"); LogPrint ("No outbound tunnels found");
} }
else else // RouterInfo is requested directly
LogPrint ("No outbound tunnels found"); {
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) void NetDb::HandleDatabaseStoreMsg (uint8_t * buf, size_t len)
@ -377,18 +449,25 @@ namespace data
dest->GetLastRouter ()->GetIdentHash (), 0, msg 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); outbound->SendTunnelDataMsg (msgs);
} }
else else
{ {
// no more requests for detination possible. delete it // no more requests for detination possible. delete it
m_RequestedDestinations.erase (it);
delete it->second; delete it->second;
m_RequestedDestinations.erase (it);
} }
} }
else else
@ -402,7 +481,7 @@ namespace data
auto inbound = i2p::tunnel::tunnels.GetNextInboundTunnel (); auto inbound = i2p::tunnel::tunnels.GetNextInboundTunnel ();
if (outbound && inbound) if (outbound && inbound)
{ {
auto floodfill = GetRandomNTCPRouter (true); auto floodfill = GetRandomRouter (outbound->GetEndpointRouter (), true);
if (floodfill) if (floodfill)
{ {
LogPrint ("Exploring new routers ..."); LogPrint ("Exploring new routers ...");
@ -449,8 +528,8 @@ namespace data
auto it = m_RequestedDestinations.find (dest); auto it = m_RequestedDestinations.find (dest);
if (it != m_RequestedDestinations.end ()) if (it != m_RequestedDestinations.end ())
{ {
m_RequestedDestinations.erase (it);
delete it->second; delete it->second;
m_RequestedDestinations.erase (it);
} }
} }
@ -470,16 +549,29 @@ namespace data
return last; return last;
} }
const RouterInfo * NetDb::GetRandomRouter () const const RouterInfo * NetDb::GetRandomRouter (const RouterInfo * compatibleWith, bool floodfillOnly) const
{ {
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
uint32_t ind = rnd.GenerateWord32 (0, m_RouterInfos.size () - 1), i = 0; uint32_t ind = rnd.GenerateWord32 (0, m_RouterInfos.size () - 1);
for (auto it: m_RouterInfos) for (int j = 0; j < 2; j++)
{ {
if (i >= ind) return it.second; uint32_t i = 0;
else i++; 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) void NetDb::PostI2NPMsg (I2NPMessage * msg)
@ -487,7 +579,8 @@ namespace data
if (msg) m_Queue.Put (msg); 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; RouterInfo * r = nullptr;
XORMetric minMetric; XORMetric minMetric;
@ -495,7 +588,7 @@ namespace data
minMetric.SetMax (); minMetric.SetMax ();
for (auto it: m_RouterInfos) 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 (); XORMetric m = destKey ^ it.second->GetRoutingKey ();
if (m < minMetric) if (m < minMetric)
@ -507,5 +600,46 @@ namespace data
} }
return r; 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; }; const IdentHash& GetDestination () const { return m_Destination; };
int GetNumExcludedPeers () const { return m_ExcludedPeers.size (); }; 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; }; const i2p::tunnel::InboundTunnel * GetLastReplyTunnel () const { return m_LastReplyTunnel; };
bool IsExploratory () const { return m_IsExploratory; }; bool IsExploratory () const { return m_IsExploratory; };
bool IsLeaseSet () const { return m_IsLeaseSet; };
bool IsExcluded (const IdentHash& ident) const { return m_ExcludedPeers.count (ident); }; bool IsExcluded (const IdentHash& ident) const { return m_ExcludedPeers.count (ident); };
I2NPMessage * CreateRequestMessage (const RouterInfo * router, const i2p::tunnel::InboundTunnel * replyTunnel); I2NPMessage * CreateRequestMessage (const RouterInfo * router, const i2p::tunnel::InboundTunnel * replyTunnel);
I2NPMessage * CreateRequestMessage (const IdentHash& floodfill);
i2p::tunnel::OutboundTunnel * GetLastOutboundTunnel () const { return m_LastOutboundTunnel; }; i2p::tunnel::OutboundTunnel * GetLastOutboundTunnel () const { return m_LastOutboundTunnel; };
void SetLastOutboundTunnel (i2p::tunnel::OutboundTunnel * tunnel) { m_LastOutboundTunnel = tunnel; }; 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 char * b32); // in base32
void RequestDestination (const IdentHash& destination, bool isLeaseSet = false); 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 HandleDatabaseStoreMsg (uint8_t * buf, size_t len);
void HandleDatabaseSearchReplyMsg (I2NPMessage * msg); void HandleDatabaseSearchReplyMsg (I2NPMessage * msg);
const RouterInfo * GetRandomNTCPRouter (bool floodfillOnly = false) const; 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); void PostI2NPMsg (I2NPMessage * msg);
private: private:
bool CreateNetDb(const char * directory);
void Load (const char * directory); void Load (const char * directory);
void SaveUpdated (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 Run (); // exploratory thread
void Explore (); 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, RequestedDestination * CreateRequestedDestination (const IdentHash& dest,
bool isLeaseSet, bool isExploratory = false); bool isLeaseSet, bool isExploratory = false);

View file

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

View file

@ -12,18 +12,19 @@
#include "RouterInfo.h" #include "RouterInfo.h"
#include "RouterContext.h" #include "RouterContext.h"
namespace i2p namespace i2p
{ {
namespace data namespace data
{ {
RouterInfo::RouterInfo (const char * filename): RouterInfo::RouterInfo (const char * filename):
m_IsUpdated (false), m_IsUnreachable (false) m_IsUpdated (false), m_IsUnreachable (false), m_SupportedTransports (0)
{ {
ReadFromFile (filename); ReadFromFile (filename);
} }
RouterInfo::RouterInfo (const uint8_t * buf, int len): 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); memcpy (m_Buffer, buf, len);
m_BufferLen = len; m_BufferLen = len;
@ -41,7 +42,7 @@ namespace data
void RouterInfo::ReadFromFile (const char * filename) void RouterInfo::ReadFromFile (const char * filename)
{ {
std::ifstream s(filename); std::ifstream s(filename, std::ifstream::binary);
if (s.is_open ()) if (s.is_open ())
{ {
s.seekg (0,std::ios::end); s.seekg (0,std::ios::end);
@ -95,15 +96,34 @@ namespace data
size = be16toh (size); size = be16toh (size);
while (r < size) while (r < size)
{ {
char key[50], value[50]; char key[500], value[500];
r += ReadString (key, s); r += ReadString (key, s);
s.seekg (1, std::ios_base::cur); r++; // = s.seekg (1, std::ios_base::cur); r++; // =
r += ReadString (value, s); r += ReadString (value, s);
s.seekg (1, std::ios_base::cur); r++; // ; s.seekg (1, std::ios_base::cur); r++; // ;
if (!strcmp (key, "host")) 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")) else if (!strcmp (key, "port"))
address.port = boost::lexical_cast<int>(value); address.port = boost::lexical_cast<int>(value);
else if (!strcmp (key, "key"))
Base64ToByteStream (value, strlen (value), address.key, 32);
} }
m_Addresses.push_back(address); m_Addresses.push_back(address);
} }
@ -117,7 +137,13 @@ namespace data
size = be16toh (size); size = be16toh (size);
while (r < 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]; char key[50], value[50];
#endif
r += ReadString (key, s); r += ReadString (key, s);
s.seekg (1, std::ios_base::cur); r++; // = s.seekg (1, std::ios_base::cur); r++; // =
r += ReadString (value, s); r += ReadString (value, s);
@ -166,7 +192,7 @@ namespace data
std::stringstream properties; std::stringstream properties;
WriteString ("host", properties); WriteString ("host", properties);
properties << '='; properties << '=';
WriteString (address.host, properties); WriteString (address.host.to_string (), properties);
properties << ';'; properties << ';';
WriteString ("port", properties); WriteString ("port", properties);
properties << '='; properties << '=';
@ -227,7 +253,7 @@ namespace data
void RouterInfo::AddNTCPAddress (const char * host, int port) void RouterInfo::AddNTCPAddress (const char * host, int port)
{ {
Address addr; Address addr;
addr.host = host; addr.host = boost::asio::ip::address::from_string (host);
addr.port = port; addr.port = port;
addr.transportStyle = eTransportNTCP; addr.transportStyle = eTransportNTCP;
addr.cost = 2; addr.cost = 2;
@ -256,22 +282,33 @@ namespace data
return false; return false;
} }
bool RouterInfo::IsNTCP () const bool RouterInfo::IsNTCP (bool v4only) const
{ {
for (auto& address : m_Addresses) if (v4only)
{ return m_SupportedTransports & eNTCPV4;
if (address.transportStyle == eTransportNTCP) else
return true; return m_SupportedTransports & (eNTCPV4 | eNTCPV6);
} }
return false;
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) for (auto& address : m_Addresses)
{ {
if (address.transportStyle == eTransportNTCP) if (address.transportStyle == s)
return &address; {
if (!v4only || address.host.is_v4 ())
return &address;
}
} }
return nullptr; return nullptr;
} }

View file

@ -17,6 +17,14 @@ namespace data
{ {
public: public:
enum SupportedTranports
{
eNTCPV4 = 0x01,
eNTCPV6 = 0x20,
eSSUV4 = 0x40,
eSSUV6 = 0x80
};
enum TransportStyle enum TransportStyle
{ {
eTransportUnknown = 0, eTransportUnknown = 0,
@ -27,10 +35,11 @@ namespace data
struct Address struct Address
{ {
TransportStyle transportStyle; TransportStyle transportStyle;
std::string host; boost::asio::ip::address host;
int port; int port;
uint64_t date; uint64_t date;
uint8_t cost; uint8_t cost;
uint8_t key[32]; // into key for SSU
}; };
RouterInfo (const char * filename); RouterInfo (const char * filename);
@ -45,14 +54,17 @@ namespace data
const char * GetIdentHashAbbreviation () const { return m_IdentHashAbbreviation; }; const char * GetIdentHashAbbreviation () const { return m_IdentHashAbbreviation; };
uint64_t GetTimestamp () const { return m_Timestamp; }; uint64_t GetTimestamp () const { return m_Timestamp; };
const std::vector<Address>& GetAddresses () const { return m_Addresses; }; 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; }; const RoutingKey& GetRoutingKey () const { return m_RoutingKey; };
void AddNTCPAddress (const char * host, int port); void AddNTCPAddress (const char * host, int port);
void SetProperty (const char * key, const char * value); void SetProperty (const char * key, const char * value);
const char * GetProperty (const char * key) const; const char * GetProperty (const char * key) const;
bool IsFloodfill () 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; }; void SetUnreachable (bool unreachable) { m_IsUnreachable = unreachable; };
bool IsUnreachable () const { return m_IsUnreachable; }; bool IsUnreachable () const { return m_IsUnreachable; };
@ -78,6 +90,7 @@ namespace data
size_t ReadString (char * str, std::istream& s); size_t ReadString (char * str, std::istream& s);
void WriteString (const std::string& str, std::ostream& s); void WriteString (const std::string& str, std::ostream& s);
void UpdateIdentHashBase64 (); void UpdateIdentHashBase64 ();
Address * GetAddress (TransportStyle s, bool v4only);
private: private:
@ -91,6 +104,7 @@ namespace data
std::vector<Address> m_Addresses; std::vector<Address> m_Addresses;
std::map<std::string, std::string> m_Properties; std::map<std::string, std::string> m_Properties;
bool m_IsUpdated, m_IsUnreachable; 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 <string>
#include <algorithm> #include <algorithm>
#include <cryptopp/gzip.h> #include <cryptopp/gzip.h>
@ -26,30 +25,18 @@ namespace stream
{ {
while (auto packet = m_ReceiveQueue.Get ()) while (auto packet = m_ReceiveQueue.Get ())
delete packet; delete packet;
for (auto it: m_SavedPackets)
delete it;
} }
void Stream::HandleNextPacket (Packet * packet) void Stream::HandleNextPacket (Packet * packet)
{ {
const uint8_t * buf = packet->buf;
buf += 4; // sendStreamID
if (!m_SendStreamID) if (!m_SendStreamID)
m_SendStreamID = be32toh (*(uint32_t *)buf); m_SendStreamID = packet->GetReceiveStreamID ();
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;
// process flags // process flags
uint16_t flags = packet->GetFlags ();
const uint8_t * optionData = packet->GetOptionData ();
if (flags & PACKET_FLAG_SYNCHRONIZE) if (flags & PACKET_FLAG_SYNCHRONIZE)
{ {
LogPrint ("Synchronize"); LogPrint ("Synchronize");
@ -58,21 +45,21 @@ namespace stream
if (flags & PACKET_FLAG_SIGNATURE_INCLUDED) if (flags & PACKET_FLAG_SIGNATURE_INCLUDED)
{ {
LogPrint ("Signature"); LogPrint ("Signature");
optionalData += 40; optionData += 40;
} }
if (flags & PACKET_FLAG_FROM_INCLUDED) if (flags & PACKET_FLAG_FROM_INCLUDED)
{ {
LogPrint ("From identity"); 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); LogPrint ("seqn=", receivedSeqn, ", flags=", flags);
if (!receivedSeqn || receivedSeqn == m_LastReceivedSequenceNumber + 1) if (!receivedSeqn || receivedSeqn == m_LastReceivedSequenceNumber + 1)
{ {
// we have received next message // we have received next message
packet->offset = buf - packet->buf; packet->offset = packet->GetPayload () - packet->buf;
if (packet->GetLength () > 0) if (packet->GetLength () > 0)
m_ReceiveQueue.Put (packet); m_ReceiveQueue.Put (packet);
else else
@ -80,6 +67,26 @@ namespace stream
m_LastReceivedSequenceNumber = receivedSeqn; m_LastReceivedSequenceNumber = receivedSeqn;
SendQuickAck (); 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 else
{ {
@ -90,13 +97,14 @@ namespace stream
m_OutboundTunnel = i2p::tunnel::tunnels.GetNextOutboundTunnel (); // pick another tunnel m_OutboundTunnel = i2p::tunnel::tunnels.GetNextOutboundTunnel (); // pick another tunnel
if (m_OutboundTunnel) if (m_OutboundTunnel)
SendQuickAck (); // resend ack for previous message again SendQuickAck (); // resend ack for previous message again
delete packet; // packet dropped
} }
else else
{ {
LogPrint ("Missing messages from ", m_LastReceivedSequenceNumber + 1, " to ", receivedSeqn - 1); 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) 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) size_t Stream::Send (uint8_t * buf, size_t len, int timeout)
{ {
if (!m_IsOpen) if (!m_IsOpen)

View file

@ -3,7 +3,9 @@
#include <inttypes.h> #include <inttypes.h>
#include <map> #include <map>
#include <set>
#include <cryptopp/dsa.h> #include <cryptopp/dsa.h>
#include "I2PEndian.h"
#include "Queue.h" #include "Queue.h"
#include "Identity.h" #include "Identity.h"
#include "LeaseSet.h" #include "LeaseSet.h"
@ -37,6 +39,25 @@ namespace stream
Packet (): len (0), offset (0) {}; Packet (): len (0), offset (0) {};
uint8_t * GetBuffer () { return buf + offset; }; uint8_t * GetBuffer () { return buf + offset; };
size_t GetLength () const { return len - 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; class StreamingDestination;
@ -61,6 +82,8 @@ namespace stream
void ConnectAndSend (uint8_t * buf, size_t len); void ConnectAndSend (uint8_t * buf, size_t len);
void SendQuickAck (); void SendQuickAck ();
void SavePacket (Packet * packet);
private: private:
@ -69,6 +92,7 @@ namespace stream
StreamingDestination * m_LocalDestination; StreamingDestination * m_LocalDestination;
const i2p::data::LeaseSet * m_RemoteLeaseSet; const i2p::data::LeaseSet * m_RemoteLeaseSet;
i2p::util::Queue<Packet> m_ReceiveQueue; i2p::util::Queue<Packet> m_ReceiveQueue;
std::set<Packet *, PacketCmp> m_SavedPackets;
i2p::tunnel::OutboundTunnel * m_OutboundTunnel; i2p::tunnel::OutboundTunnel * m_OutboundTunnel;
}; };

View file

@ -12,7 +12,7 @@ namespace i2p
Transports transports; Transports transports;
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, m_NTCPAcceptor = new boost::asio::ip::tcp::acceptor (m_Service,
boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), address.port)); 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); auto conn = new i2p::ntcp::NTCPServerConnection (m_Service);
m_NTCPAcceptor->async_accept(conn->GetSocket (), boost::bind (&Transports::HandleAccept, this, m_NTCPAcceptor->async_accept(conn->GetSocket (), boost::bind (&Transports::HandleAccept, this,
conn, boost::asio::placeholders::error)); 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 (); m_NTCPSessions.clear ();
delete m_NTCPAcceptor; delete m_NTCPAcceptor;
if (m_SSUServer)
{
m_SSUServer->Stop ();
delete m_SSUServer;
}
m_IsRunning = false; m_IsRunning = false;
m_Service.stop (); m_Service.stop ();
if (m_Thread) if (m_Thread)
@ -139,7 +156,7 @@ namespace i2p
auto address = r->GetNTCPAddress (); auto address = r->GetNTCPAddress ();
if (address) 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); AddNTCPSession (session);
} }
else else

View file

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

View file

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

View file

@ -67,7 +67,8 @@ namespace tunnel
void SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, i2p::I2NPMessage * msg); void SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, i2p::I2NPMessage * msg);
void SendTunnelDataMsg (std::vector<TunnelMessageBlock> msgs); // multiple messages 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 (); }; size_t GetNumSentBytes () const { return m_Gateway.GetNumSentBytes (); };
// implements TunnelBase // 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="..\NTCPSession.cpp" />
<ClCompile Include="..\RouterContext.cpp" /> <ClCompile Include="..\RouterContext.cpp" />
<ClCompile Include="..\RouterInfo.cpp" /> <ClCompile Include="..\RouterInfo.cpp" />
<ClCompile Include="..\SSU.cpp" />
<ClCompile Include="..\Streaming.cpp" /> <ClCompile Include="..\Streaming.cpp" />
<ClCompile Include="..\TransitTunnel.cpp" /> <ClCompile Include="..\TransitTunnel.cpp" />
<ClCompile Include="..\Transports.cpp" /> <ClCompile Include="..\Transports.cpp" />
@ -48,6 +49,7 @@
<ClInclude Include="..\Queue.h" /> <ClInclude Include="..\Queue.h" />
<ClInclude Include="..\RouterContext.h" /> <ClInclude Include="..\RouterContext.h" />
<ClInclude Include="..\RouterInfo.h" /> <ClInclude Include="..\RouterInfo.h" />
<ClInclude Include="..\SSU.h" />
<ClInclude Include="..\Streaming.h" /> <ClInclude Include="..\Streaming.h" />
<ClInclude Include="..\Timestamp.h" /> <ClInclude Include="..\Timestamp.h" />
<ClInclude Include="..\TransitTunnel.h" /> <ClInclude Include="..\TransitTunnel.h" />

View file

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

View file

@ -27,6 +27,11 @@ namespace data
'4', '5', '6', '7', '8', '9', '-', '~' '4', '5', '6', '7', '8', '9', '-', '~'
}; };
const char * GetBase64SubstitutionTable ()
{
return T64;
}
/* /*
* Reverse Substitution Table (built in run time) * 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 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 ); 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); 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 "Tunnel.h"
#include "NetDb.h" #include "NetDb.h"
#include "HTTPServer.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 (); httpServer.Start ();
i2p::data::netdb.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