mirror of
https://github.com/PurpleI2P/i2pd
synced 2024-11-10 08:00:38 +03:00
Merge pull request #7 from orignal/master
Merge pull request from orignal/master
This commit is contained in:
commit
5337f4c9a4
7
.gitignore
vendored
7
.gitignore
vendored
@ -1,3 +1,10 @@
|
||||
# i2pd
|
||||
obj/*.o
|
||||
router.info
|
||||
router.keys
|
||||
i2p
|
||||
netDb
|
||||
|
||||
#################
|
||||
## Eclipse
|
||||
#################
|
||||
|
@ -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");
|
||||
|
10
LeaseSet.cpp
10
LeaseSet.cpp
@ -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,6 +38,14 @@ 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
|
||||
|
25
Makefile
25
Makefile
@ -1,26 +1,31 @@
|
||||
|
||||
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 SSU.o
|
||||
OBJECTS = obj/i2p.o obj/base64.o obj/NTCPSession.o obj/RouterInfo.o obj/Transports.o \
|
||||
obj/RouterContext.o obj/NetDb.o obj/LeaseSet.o obj/Tunnel.o obj/TunnelEndpoint.o \
|
||||
obj/TunnelGateway.o obj/TransitTunnel.o obj/I2NPProtocol.o obj/Log.o obj/Garlic.o \
|
||||
obj/HTTPServer.o obj/Streaming.o obj/Identity.o obj/SSU.o obj/util.o obj/Reseed.o
|
||||
INCFLAGS =
|
||||
LDFLAGS = -Wl,-rpath,/usr/local/lib -lcryptopp -lboost_system -lboost_filesystem
|
||||
LDFLAGS = -Wl,-rpath,/usr/local/lib -lcryptopp -lboost_system -lboost_filesystem -lboost_regex -lboost_program_options -lpthread
|
||||
LIBS =
|
||||
|
||||
all: i2p
|
||||
all: obj i2p
|
||||
|
||||
i2p: $(OBJECTS)
|
||||
$(CC) -o i2p $(OBJECTS) $(LDFLAGS) $(LIBS)
|
||||
i2p: $(OBJECTS:obj/%=obj/%)
|
||||
$(CC) -o $@ $^ $(LDFLAGS) $(LIBS)
|
||||
|
||||
.SUFFIXES:
|
||||
.SUFFIXES: .c .cc .C .cpp .o
|
||||
|
||||
.cpp.o :
|
||||
$(CC) -o $@ -c $(CFLAGS) $< $(INCFLAGS)
|
||||
obj/%.o : %.cpp
|
||||
$(CC) -o $@ $< -c $(CFLAGS) $(INCFLAGS)
|
||||
|
||||
obj:
|
||||
mkdir -p obj
|
||||
|
||||
clean:
|
||||
rm -f *.o
|
||||
rm -fr obj i2p
|
||||
|
||||
.PHONY: all
|
||||
.PHONY: clean
|
||||
|
||||
|
253
NetDb.cpp
253
NetDb.cpp
@ -2,22 +2,23 @@
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <cryptopp/gzip.h>
|
||||
#include "base64.h"
|
||||
#include "Log.h"
|
||||
#include "Timestamp.h"
|
||||
#include "I2NPProtocol.h"
|
||||
#include "Tunnel.h"
|
||||
#include "Transports.h"
|
||||
#include "RouterContext.h"
|
||||
#include "Garlic.h"
|
||||
#include "NetDb.h"
|
||||
#include "Reseed.h"
|
||||
#include "util.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace data
|
||||
{
|
||||
|
||||
I2NPMessage * RequestedDestination::CreateRequestMessage (const RouterInfo * router,
|
||||
const i2p::tunnel::InboundTunnel * replyTunnel)
|
||||
{
|
||||
@ -31,9 +32,24 @@ namespace data
|
||||
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;
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
const char NetDb::m_NetDbPath[] = "/netDb";
|
||||
#else
|
||||
const char NetDb::m_NetDbPath[] = "\\netDb";
|
||||
#endif
|
||||
NetDb netdb;
|
||||
|
||||
NetDb::NetDb (): m_IsRunning (false), m_Thread (0)
|
||||
NetDb::NetDb (): m_IsRunning (false), m_ReseedRetries (0), m_Thread (0)
|
||||
{
|
||||
}
|
||||
|
||||
@ -50,7 +66,14 @@ namespace data
|
||||
|
||||
void NetDb::Start ()
|
||||
{
|
||||
Load ("netDb");
|
||||
Load (m_NetDbPath);
|
||||
while (m_RouterInfos.size () < 100 && m_ReseedRetries < 10)
|
||||
{
|
||||
Reseeder reseeder;
|
||||
reseeder.reseedNow();
|
||||
m_ReseedRetries++;
|
||||
Load (m_NetDbPath);
|
||||
}
|
||||
m_Thread = new std::thread (std::bind (&NetDb::Run, this));
|
||||
}
|
||||
|
||||
@ -100,7 +123,7 @@ namespace data
|
||||
if (ts - lastTs >= 60) // save routers every minute
|
||||
{
|
||||
if (lastTs)
|
||||
SaveUpdated ("netDb");
|
||||
SaveUpdated (m_NetDbPath);
|
||||
lastTs = ts;
|
||||
}
|
||||
}
|
||||
@ -168,44 +191,88 @@ namespace data
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// TODO: Move to reseed and/or scheduled tasks. (In java version, scheduler fix this as well as sort RIs.)
|
||||
bool NetDb::CreateNetDb(boost::filesystem::path directory)
|
||||
{
|
||||
LogPrint (directory.string(), " doesn't exist, trying to create it.");
|
||||
if (!boost::filesystem::create_directory (directory))
|
||||
{
|
||||
LogPrint("Failed to create directory ", directory.string());
|
||||
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 (directory / suffix) )) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void NetDb::Load (const char * directory)
|
||||
{
|
||||
boost::filesystem::path p (directory);
|
||||
if (boost::filesystem::exists (p))
|
||||
boost::filesystem::path p (i2p::util::filesystem::GetDataDir());
|
||||
p /= (directory);
|
||||
if (!boost::filesystem::exists (p))
|
||||
{
|
||||
int numRouters = 0;
|
||||
boost::filesystem::directory_iterator end;
|
||||
for (boost::filesystem::directory_iterator it (p); it != end; ++it)
|
||||
// seems netDb doesn't exist yet
|
||||
if (!CreateNetDb(p)) return;
|
||||
}
|
||||
// make sure we cleanup netDb from previous attempts
|
||||
for (auto r: m_RouterInfos)
|
||||
delete r.second;
|
||||
m_RouterInfos.clear ();
|
||||
|
||||
// load routers now
|
||||
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-" +
|
||||
#else
|
||||
return std::string (directory) + "\\r" +
|
||||
routerInfo->GetIdentHashBase64 ()[0] + "\\routerInfo-" +
|
||||
#endif
|
||||
routerInfo->GetIdentHashBase64 () + ".dat";
|
||||
};
|
||||
|
||||
boost::filesystem::path p (i2p::util::filesystem::GetDataDir());
|
||||
p /= (directory);
|
||||
#if BOOST_VERSION > 10500
|
||||
const char * fullDirectory = p.string().c_str ();
|
||||
#else
|
||||
const char * fullDirectory = p.c_str ();
|
||||
#endif
|
||||
int count = 0, deletedCount = 0;
|
||||
auto total = m_RouterInfos.size ();
|
||||
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
@ -213,7 +280,7 @@ namespace data
|
||||
{
|
||||
if (it.second->IsUpdated ())
|
||||
{
|
||||
std::ofstream r (GetFilePath(directory, it.second), std::ofstream::binary);
|
||||
std::ofstream r (GetFilePath(fullDirectory, it.second), std::ofstream::binary);
|
||||
r.write ((char *)it.second->GetBuffer (), it.second->GetBufferLen ());
|
||||
it.second->SetUpdated (false);
|
||||
count++;
|
||||
@ -229,9 +296,9 @@ namespace data
|
||||
|
||||
if (it.second->IsUnreachable ())
|
||||
{
|
||||
if (boost::filesystem::exists (GetFilePath (directory, it.second)))
|
||||
if (boost::filesystem::exists (GetFilePath (fullDirectory, it.second)))
|
||||
{
|
||||
boost::filesystem::remove (GetFilePath (directory, it.second));
|
||||
boost::filesystem::remove (GetFilePath (fullDirectory, it.second));
|
||||
deletedCount++;
|
||||
}
|
||||
}
|
||||
@ -252,44 +319,49 @@ namespace data
|
||||
|
||||
void NetDb::RequestDestination (const IdentHash& destination, bool isLeaseSet)
|
||||
{
|
||||
i2p::tunnel::OutboundTunnel * outbound = i2p::tunnel::tunnels.GetNextOutboundTunnel ();
|
||||
if (outbound)
|
||||
if (isLeaseSet) // we request LeaseSet through tunnels
|
||||
{
|
||||
i2p::tunnel::InboundTunnel * inbound = i2p::tunnel::tunnels.GetNextInboundTunnel ();
|
||||
if (inbound)
|
||||
i2p::tunnel::OutboundTunnel * outbound = i2p::tunnel::tunnels.GetNextOutboundTunnel ();
|
||||
if (outbound)
|
||||
{
|
||||
RequestedDestination * dest = CreateRequestedDestination (destination, isLeaseSet);
|
||||
auto floodfill = GetClosestFloodfill (destination, dest->GetExcludedPeers ());
|
||||
if (floodfill)
|
||||
i2p::tunnel::InboundTunnel * inbound = i2p::tunnel::tunnels.GetNextInboundTunnel ();
|
||||
if (inbound)
|
||||
{
|
||||
std::vector<i2p::tunnel::TunnelMessageBlock> msgs;
|
||||
// our RouterInfo
|
||||
msgs.push_back (i2p::tunnel::TunnelMessageBlock
|
||||
{
|
||||
i2p::tunnel::eDeliveryTypeRouter,
|
||||
floodfill->GetIdentHash (), 0,
|
||||
CreateDatabaseStoreMsg ()
|
||||
});
|
||||
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)
|
||||
});
|
||||
|
||||
// DatabaseLookup message
|
||||
dest->SetLastOutboundTunnel (outbound);
|
||||
msgs.push_back (i2p::tunnel::TunnelMessageBlock
|
||||
{
|
||||
i2p::tunnel::eDeliveryTypeRouter,
|
||||
floodfill->GetIdentHash (), 0,
|
||||
dest->CreateRequestMessage (floodfill, inbound)
|
||||
});
|
||||
|
||||
outbound->SendTunnelDataMsg (msgs);
|
||||
outbound->SendTunnelDataMsg (msgs);
|
||||
}
|
||||
else
|
||||
LogPrint ("No more floodfills found");
|
||||
}
|
||||
else
|
||||
LogPrint ("No more floodfills found");
|
||||
LogPrint ("No inbound tunnels found");
|
||||
}
|
||||
else
|
||||
LogPrint ("No inbound 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 ()));
|
||||
}
|
||||
}
|
||||
else
|
||||
LogPrint ("No outbound tunnels found");
|
||||
}
|
||||
|
||||
void NetDb::HandleDatabaseStoreMsg (uint8_t * buf, size_t len)
|
||||
@ -403,10 +475,17 @@ namespace data
|
||||
});
|
||||
}
|
||||
}
|
||||
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
|
||||
@ -427,7 +506,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 ...");
|
||||
@ -495,19 +574,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;
|
||||
RouterInfo * last = nullptr;
|
||||
for (auto it: m_RouterInfos)
|
||||
uint32_t ind = rnd.GenerateWord32 (0, m_RouterInfos.size () - 1);
|
||||
for (int j = 0; j < 2; j++)
|
||||
{
|
||||
if (!it.second->IsUnreachable ())
|
||||
last = it.second;
|
||||
if (i >= ind) break;
|
||||
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 last;
|
||||
return nullptr; // seem we have too few routers
|
||||
}
|
||||
|
||||
void NetDb::PostI2NPMsg (I2NPMessage * msg)
|
||||
@ -537,41 +626,5 @@ namespace data
|
||||
return r;
|
||||
}
|
||||
|
||||
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 ());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
10
NetDb.h
10
NetDb.h
@ -6,6 +6,7 @@
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include "Queue.h"
|
||||
#include "I2NPProtocol.h"
|
||||
#include "RouterInfo.h"
|
||||
@ -30,8 +31,10 @@ namespace data
|
||||
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; };
|
||||
@ -68,15 +71,15 @@ namespace data
|
||||
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(boost::filesystem::path 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 std::set<IdentHash>& excluded) const;
|
||||
@ -92,8 +95,11 @@ namespace data
|
||||
std::map<IdentHash, RequestedDestination *> m_RequestedDestinations;
|
||||
|
||||
bool m_IsRunning;
|
||||
int m_ReseedRetries;
|
||||
std::thread * m_Thread;
|
||||
i2p::util::Queue<I2NPMessage> m_Queue; // of I2NPDatabaseStoreMsg
|
||||
|
||||
static const char m_NetDbPath[];
|
||||
};
|
||||
|
||||
extern NetDb netdb;
|
||||
|
29
README.md
29
README.md
@ -8,3 +8,32 @@ Requires gcc 4.6 and higher, boost 1.46 and higher, crypto++
|
||||
on Windows
|
||||
|
||||
Requires msvs2013, boost 1.46 and higher, crypto++
|
||||
|
||||
|
||||
Testing
|
||||
-------
|
||||
|
||||
First, build it.
|
||||
|
||||
* $ cd i2pd
|
||||
* $ make
|
||||
|
||||
Next, find out your public ip. (find it for example at http://www.whatismyip.com/)
|
||||
|
||||
Then, run it with:
|
||||
|
||||
$ ./i2p --host=YOUR_PUBLIC_IP
|
||||
|
||||
The client should now reseed by itself.
|
||||
|
||||
Other options:
|
||||
* --port= - The port to listen on
|
||||
* --httpport= - The http port to listen on
|
||||
|
||||
|
||||
To visit an I2P page, you need to find the b32 address of your destination.
|
||||
After that, go to the webconsole and add it behind the url. (Remove http:// and b32.i2p from the address)
|
||||
|
||||
This should resulting in for example:
|
||||
http://localhost:7070/4oes3rlgrpbkmzv4lqcfili23h3cvpwslqcfjlk6vvguxyggspwa
|
||||
|
||||
|
101
Reseed.cpp
Normal file
101
Reseed.cpp
Normal file
@ -0,0 +1,101 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <boost/regex.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include "Reseed.h"
|
||||
#include "Log.h"
|
||||
#include "util.h"
|
||||
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace data
|
||||
{
|
||||
|
||||
static std::vector<std::string> httpReseedHostList = {
|
||||
"http://193.150.121.66/netDb/",
|
||||
"http://netdb.i2p2.no/",
|
||||
"http://reseed.i2p-projekt.de/",
|
||||
"http://cowpuncher.drollette.com/netdb/",
|
||||
"http://i2p.mooo.com/netDb/",
|
||||
"http://reseed.info/",
|
||||
"http://reseed.pkol.de/",
|
||||
"http://uk.reseed.i2p2.no/",
|
||||
"http://i2p-netdb.innovatio.no/",
|
||||
"http://ieb9oopo.mooo.com"
|
||||
};
|
||||
|
||||
//TODO: Implement v2 reseeding. Lightweight zip library is needed.
|
||||
//TODO: Implement SU3, utils.
|
||||
Reseeder::Reseeder()
|
||||
{
|
||||
}
|
||||
|
||||
Reseeder::~Reseeder()
|
||||
{
|
||||
}
|
||||
|
||||
bool Reseeder::reseedNow()
|
||||
{
|
||||
try
|
||||
{
|
||||
std::string reseedHost = httpReseedHostList[(rand() % httpReseedHostList.size())];
|
||||
LogPrint("Reseeding from ", reseedHost);
|
||||
std::string content = i2p::util::http::httpRequest(reseedHost);
|
||||
if (content == "")
|
||||
{
|
||||
LogPrint("Reseed failed");
|
||||
return false;
|
||||
}
|
||||
boost::regex e("<\\s*A\\s+[^>]*href\\s*=\\s*\"([^\"]*)\"", boost::regex::normal | boost::regbase::icase);
|
||||
boost::sregex_token_iterator i(content.begin(), content.end(), e, 1);
|
||||
boost::sregex_token_iterator j;
|
||||
//TODO: Ugly code, try to clean up.
|
||||
//TODO: Try to reduce N number of variables
|
||||
std::string name;
|
||||
std::string routerInfo;
|
||||
std::string tmpUrl;
|
||||
std::string filename;
|
||||
std::string ignoreFileSuffix = ".zip";
|
||||
boost::filesystem::path root = i2p::util::filesystem::GetDataDir();
|
||||
while (i != j)
|
||||
{
|
||||
name = *i++;
|
||||
if (name.find(ignoreFileSuffix)!=std::string::npos)
|
||||
continue;
|
||||
LogPrint("Downloading ", name);
|
||||
tmpUrl = reseedHost;
|
||||
tmpUrl.append(name);
|
||||
routerInfo = i2p::util::http::httpRequest(tmpUrl);
|
||||
if (routerInfo.size()==0)
|
||||
continue;
|
||||
filename = root.string();
|
||||
#ifndef _WIN32
|
||||
filename += "/netDb/r";
|
||||
#else
|
||||
filename += "\\netDb\\r";
|
||||
#endif
|
||||
filename += name.at(11); // first char in id
|
||||
#ifndef _WIN32
|
||||
filename.append("/");
|
||||
#else
|
||||
filename.append("\\");
|
||||
#endif
|
||||
filename.append(name.c_str());
|
||||
std::ofstream outfile (filename, std::ios::binary);
|
||||
outfile << routerInfo;
|
||||
outfile.close();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
//TODO: error reporting
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
23
Reseed.h
Normal file
23
Reseed.h
Normal file
@ -0,0 +1,23 @@
|
||||
#ifndef RESEED_H
|
||||
#define RESEED_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace data
|
||||
{
|
||||
|
||||
class Reseeder
|
||||
{
|
||||
public:
|
||||
Reseeder();
|
||||
~Reseeder();
|
||||
bool reseedNow();
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -3,6 +3,7 @@
|
||||
#include <cryptopp/dsa.h>
|
||||
#include "CryptoConst.h"
|
||||
#include "RouterContext.h"
|
||||
#include "util.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
|
@ -13,19 +13,18 @@
|
||||
#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;
|
||||
@ -48,6 +47,11 @@ namespace data
|
||||
{
|
||||
s.seekg (0,std::ios::end);
|
||||
m_BufferLen = s.tellg ();
|
||||
if (m_BufferLen < 40)
|
||||
{
|
||||
LogPrint("File", filename, " is malformed");
|
||||
return;
|
||||
}
|
||||
s.seekg(0, std::ios::beg);
|
||||
s.read(m_Buffer,m_BufferLen);
|
||||
ReadFromBuffer ();
|
||||
@ -112,9 +116,19 @@ namespace data
|
||||
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);
|
||||
}
|
||||
@ -275,22 +289,27 @@ namespace data
|
||||
|
||||
bool RouterInfo::IsNTCP (bool v4only) const
|
||||
{
|
||||
for (auto& address : m_Addresses)
|
||||
{
|
||||
if (address.transportStyle == eTransportNTCP)
|
||||
{
|
||||
if (!v4only || address.host.is_v4 ())
|
||||
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::GetSSUAddress (bool v4only)
|
||||
{
|
||||
return GetAddress (eTransportSSU, v4only);
|
||||
}
|
||||
|
||||
RouterInfo::Address * RouterInfo::GetAddress (TransportStyle s, bool v4only)
|
||||
{
|
||||
for (auto& address : m_Addresses)
|
||||
{
|
||||
if (address.transportStyle == eTransportNTCP)
|
||||
if (address.transportStyle == s)
|
||||
{
|
||||
if (!v4only || address.host.is_v4 ())
|
||||
return &address;
|
||||
|
14
RouterInfo.h
14
RouterInfo.h
@ -17,6 +17,14 @@ namespace data
|
||||
{
|
||||
public:
|
||||
|
||||
enum SupportedTranports
|
||||
{
|
||||
eNTCPV4 = 0x01,
|
||||
eNTCPV6 = 0x20,
|
||||
eSSUV4 = 0x40,
|
||||
eSSUV6 = 0x80
|
||||
};
|
||||
|
||||
enum TransportStyle
|
||||
{
|
||||
eTransportUnknown = 0,
|
||||
@ -31,6 +39,7 @@ namespace data
|
||||
int port;
|
||||
uint64_t date;
|
||||
uint8_t cost;
|
||||
uint8_t key[32]; // into key for SSU
|
||||
};
|
||||
|
||||
RouterInfo (const char * filename);
|
||||
@ -46,6 +55,7 @@ namespace data
|
||||
uint64_t GetTimestamp () const { return m_Timestamp; };
|
||||
const std::vector<Address>& GetAddresses () const { return m_Addresses; };
|
||||
Address * GetNTCPAddress (bool v4only = true);
|
||||
Address * GetSSUAddress (bool v4only = true);
|
||||
const RoutingKey& GetRoutingKey () const { return m_RoutingKey; };
|
||||
|
||||
void AddNTCPAddress (const char * host, int port);
|
||||
@ -53,6 +63,8 @@ namespace data
|
||||
const char * GetProperty (const char * key) const;
|
||||
bool IsFloodfill () 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;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
279
SSU.cpp
279
SSU.cpp
@ -1,5 +1,11 @@
|
||||
#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"
|
||||
|
||||
@ -8,16 +14,244 @@ namespace i2p
|
||||
namespace ssu
|
||||
{
|
||||
|
||||
SSUSession::SSUSession (): m_State (eSessionStateUnknown)
|
||||
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::ProcessNextMessage (uint8_t * buf, std::size_t len)
|
||||
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");
|
||||
// use our intro key
|
||||
if (ProcessIntroKeyEncryptedMessage (PAYLOAD_TYPE_SESSION_REQUEST,
|
||||
i2p::context.GetRouterInfo (), buf, len))
|
||||
{
|
||||
m_State = eSessionStateRequestReceived;
|
||||
LogPrint ("Session request received");
|
||||
m_RemoteEndpoint = senderEndpoint;
|
||||
SendSessionCreated (buf + sizeof (SSUHeader));
|
||||
}
|
||||
}
|
||||
|
||||
void SSUSession::ProcessSessionCreated (uint8_t * buf, size_t len)
|
||||
{
|
||||
LogPrint ("Process session created");
|
||||
if (!m_RemoteRouter)
|
||||
{
|
||||
LogPrint ("Unsolicited session created message");
|
||||
return;
|
||||
}
|
||||
|
||||
// use remote intro key
|
||||
if (ProcessIntroKeyEncryptedMessage (PAYLOAD_TYPE_SESSION_CREATED, *m_RemoteRouter, buf, len))
|
||||
{
|
||||
m_State = eSessionStateCreatedReceived;
|
||||
LogPrint ("Session created received");
|
||||
boost::asio::ip::address_v4 ourAddress (be32toh (*(uint32_t* )(buf + sizeof (SSUHeader) + 257)));
|
||||
uint16_t ourPort = be16toh (*(uint16_t *)(buf + sizeof (SSUHeader) + 261));
|
||||
LogPrint ("Our external address is ", ourAddress.to_string (), ":", ourPort);
|
||||
}
|
||||
}
|
||||
|
||||
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) = htobe32 (address->host.to_v4 ().to_ulong ());
|
||||
|
||||
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 uint8_t * x)
|
||||
{
|
||||
auto address = m_RemoteRouter ? m_RemoteRouter->GetSSUAddress () : nullptr;
|
||||
if (!address)
|
||||
{
|
||||
LogPrint ("Missing remote SSU address");
|
||||
return;
|
||||
}
|
||||
uint8_t signedData[532]; // x,y, remote IP, remote port, our IP, our port, relayTag, signed on time
|
||||
memcpy (signedData, x, 256); // x
|
||||
|
||||
uint8_t buf[368 + 18];
|
||||
uint8_t * payload = buf + sizeof (SSUHeader);
|
||||
memcpy (payload, i2p::context.GetRouterIdentity ().publicKey, 256);
|
||||
memcpy (signedData + 256, payload, 256); // y
|
||||
payload += 256;
|
||||
*payload = 4; // we assume ipv4
|
||||
payload++;
|
||||
*(uint32_t *)(payload) = htobe32 (m_RemoteEndpoint.address ().to_v4 ().to_ulong ());
|
||||
payload += 4;
|
||||
*(uint16_t *)(payload) = htobe16 (m_RemoteEndpoint.port ());
|
||||
payload += 2;
|
||||
memcpy (signedData + 512, payload - 6, 6); // remote endpoint IP and port
|
||||
*(uint32_t *)(signedData + 518) = m_Server->GetEndpoint ().address ().to_v4 ().to_ulong (); // our IP
|
||||
*(uint16_t *)(signedData + 522) = htobe16 (m_Server->GetEndpoint ().port ()); // our port
|
||||
*(uint32_t *)(payload) = 0; // relay tag, always 0 for now
|
||||
payload += 4;
|
||||
*(uint32_t *)(payload) = htobe32 (i2p::util::GetSecondsSinceEpoch ()); // signed on time
|
||||
payload += 4;
|
||||
memcpy (signedData + 524, payload - 8, 8); // relayTag and signed on time
|
||||
i2p::context.Sign (signedData, 532, payload); // DSA signature
|
||||
// TODO: fill padding with random data
|
||||
|
||||
uint8_t iv[16];
|
||||
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
|
||||
rnd.GenerateBlock (iv, 16); // random iv
|
||||
// encrypt signature and 8 bytes padding with newly created session key
|
||||
m_Encryption.SetKeyWithIV (m_SessionKey, 32, iv);
|
||||
m_Encryption.ProcessData (payload, payload, 48);
|
||||
|
||||
// encrypt message with intro key
|
||||
FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_CREATED, buf, 368, address->key, iv, address->key);
|
||||
m_State = eSessionStateRequestSent;
|
||||
m_Server->Send (buf, 368, m_RemoteEndpoint);
|
||||
}
|
||||
|
||||
bool SSUSession::ProcessIntroKeyEncryptedMessage (uint8_t expectedPayloadType, i2p::data::RouterInfo& r, uint8_t * buf, size_t len)
|
||||
{
|
||||
auto address = r.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 by ", r.GetIdentHashAbbreviation ());
|
||||
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);
|
||||
encryptedLen = (encryptedLen/16)*16; // make sure 16 bytes boundary
|
||||
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::v4 (), port)
|
||||
m_Endpoint (boost::asio::ip::udp::v4 (), port), m_Socket (service, m_Endpoint)
|
||||
{
|
||||
}
|
||||
|
||||
@ -37,6 +271,11 @@ namespace ssu
|
||||
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,
|
||||
@ -52,18 +291,46 @@ namespace ssu
|
||||
auto it = m_Sessions.find (m_SenderEndpoint);
|
||||
if (it != m_Sessions.end ())
|
||||
session = it->second;
|
||||
if (session)
|
||||
if (!session)
|
||||
{
|
||||
session = new SSUSession ();
|
||||
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);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
52
SSU.h
52
SSU.h
@ -4,17 +4,33 @@
|
||||
#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 (4 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_SESSION_DESTROY = 8;
|
||||
const uint8_t PAYLOAD_TYPE_RELAY_REQUEST = 3;
|
||||
const uint8_t PAYLOAD_TYPE_RELAY_RESPONSE = 4;
|
||||
const uint8_t PAYLOAD_TYPE_RELAY_INTRO = 5;
|
||||
@ -33,16 +49,41 @@ namespace ssu
|
||||
eSessionStateEstablised
|
||||
};
|
||||
|
||||
class SSUServer;
|
||||
class SSUSession
|
||||
{
|
||||
public:
|
||||
|
||||
SSUSession ();
|
||||
void ProcessNextMessage (uint8_t * buf, std::size_t len);
|
||||
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 uint8_t * x);
|
||||
|
||||
bool ProcessIntroKeyEncryptedMessage (uint8_t expectedPayloadType, i2p::data::RouterInfo& r, 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
|
||||
@ -53,6 +94,10 @@ namespace ssu
|
||||
~SSUServer ();
|
||||
void Start ();
|
||||
void Stop ();
|
||||
SSUSession * GetSession (i2p::data::RouterInfo * router);
|
||||
|
||||
const boost::asio::ip::udp::endpoint& GetEndpoint () const { return m_Endpoint; };
|
||||
void Send (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& to);
|
||||
|
||||
private:
|
||||
|
||||
@ -61,9 +106,10 @@ namespace ssu
|
||||
|
||||
private:
|
||||
|
||||
boost::asio::ip::udp::endpoint m_Endpoint;
|
||||
boost::asio::ip::udp::socket m_Socket;
|
||||
boost::asio::ip::udp::endpoint m_SenderEndpoint;
|
||||
uint8_t m_ReceiveBuffer[SSU_MTU];
|
||||
uint8_t m_ReceiveBuffer[2*SSU_MTU];
|
||||
std::map<boost::asio::ip::udp::endpoint, SSUSession *> m_Sessions;
|
||||
};
|
||||
}
|
||||
|
111
Streaming.cpp
111
Streaming.cpp
@ -1,4 +1,3 @@
|
||||
#include "I2PEndian.h"
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <cryptopp/gzip.h>
|
||||
@ -26,60 +25,35 @@ 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
|
||||
if (flags & PACKET_FLAG_SYNCHRONIZE)
|
||||
{
|
||||
LogPrint ("Synchronize");
|
||||
}
|
||||
|
||||
if (flags & PACKET_FLAG_SIGNATURE_INCLUDED)
|
||||
{
|
||||
LogPrint ("Signature");
|
||||
optionalData += 40;
|
||||
}
|
||||
|
||||
if (flags & PACKET_FLAG_FROM_INCLUDED)
|
||||
{
|
||||
LogPrint ("From identity");
|
||||
optionalData += sizeof (i2p::data::Identity);
|
||||
}
|
||||
|
||||
// we have reached payload section
|
||||
LogPrint ("seqn=", receivedSeqn, ", flags=", flags);
|
||||
uint32_t receivedSeqn = packet->GetSeqn ();
|
||||
LogPrint ("Received seqn=", receivedSeqn);
|
||||
if (!receivedSeqn || receivedSeqn == m_LastReceivedSequenceNumber + 1)
|
||||
{
|
||||
// we have received next message
|
||||
packet->offset = buf - packet->buf;
|
||||
if (packet->GetLength () > 0)
|
||||
m_ReceiveQueue.Put (packet);
|
||||
else
|
||||
delete packet;
|
||||
// we have received next in sequence message
|
||||
ProcessPacket (packet);
|
||||
|
||||
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 * savedPacket = *it;
|
||||
m_SavedPackets.erase (it++);
|
||||
|
||||
ProcessPacket (savedPacket);
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -90,14 +64,55 @@ 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
|
||||
}
|
||||
}
|
||||
|
||||
void Stream::SavePacket (Packet * packet)
|
||||
{
|
||||
m_SavedPackets.insert (packet);
|
||||
}
|
||||
|
||||
void Stream::ProcessPacket (Packet * packet)
|
||||
{
|
||||
// process flags
|
||||
uint32_t receivedSeqn = packet->GetSeqn ();
|
||||
uint16_t flags = packet->GetFlags ();
|
||||
LogPrint ("Process seqn=", receivedSeqn, ", flags=", flags);
|
||||
|
||||
const uint8_t * optionData = packet->GetOptionData ();
|
||||
if (flags & PACKET_FLAG_SYNCHRONIZE)
|
||||
{
|
||||
LogPrint ("Synchronize");
|
||||
}
|
||||
|
||||
if (flags & PACKET_FLAG_SIGNATURE_INCLUDED)
|
||||
{
|
||||
LogPrint ("Signature");
|
||||
optionData += 40;
|
||||
}
|
||||
|
||||
if (flags & PACKET_FLAG_FROM_INCLUDED)
|
||||
{
|
||||
LogPrint ("From identity");
|
||||
optionData += sizeof (i2p::data::Identity);
|
||||
}
|
||||
|
||||
packet->offset = packet->GetPayload () - packet->buf;
|
||||
if (packet->GetLength () > 0)
|
||||
m_ReceiveQueue.Put (packet);
|
||||
else
|
||||
delete packet;
|
||||
|
||||
m_LastReceivedSequenceNumber = receivedSeqn;
|
||||
SendQuickAck ();
|
||||
|
||||
if (flags & PACKET_FLAG_CLOSE)
|
||||
{
|
||||
|
25
Streaming.h
25
Streaming.h
@ -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;
|
||||
@ -62,6 +83,9 @@ namespace stream
|
||||
void ConnectAndSend (uint8_t * buf, size_t len);
|
||||
void SendQuickAck ();
|
||||
|
||||
void SavePacket (Packet * packet);
|
||||
void ProcessPacket (Packet * packet);
|
||||
|
||||
private:
|
||||
|
||||
uint32_t m_SendStreamID, m_RecvStreamID, m_SequenceNumber, m_LastReceivedSequenceNumber;
|
||||
@ -69,6 +93,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;
|
||||
};
|
||||
|
||||
|
@ -397,11 +397,12 @@ namespace tunnel
|
||||
{
|
||||
|
||||
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 (), // first hop must be NTCP
|
||||
i2p::data::netdb.GetRandomRouter ()
|
||||
firstHop,
|
||||
i2p::data::netdb.GetRandomRouter (firstHop)
|
||||
},
|
||||
inboundTunnel->GetTunnelConfig ()));
|
||||
}
|
||||
@ -449,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);
|
||||
}
|
||||
|
3
Tunnel.h
3
Tunnel.h
@ -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
|
||||
|
@ -27,6 +27,11 @@ namespace data
|
||||
'4', '5', '6', '7', '8', '9', '-', '~'
|
||||
};
|
||||
|
||||
const char * GetBase64SubstitutionTable ()
|
||||
{
|
||||
return T64;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reverse Substitution Table (built in run time)
|
||||
*/
|
||||
|
2
base64.h
2
base64.h
@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
8
hmac.h
8
hmac.h
@ -18,8 +18,7 @@ namespace crypto
|
||||
// digest is 16 bytes
|
||||
// block size is 64 bytes
|
||||
{
|
||||
size_t totalLen = len + 64 + 32;
|
||||
uint8_t * buf = new uint8_t[totalLen]; // TODO: reuse buffers
|
||||
uint8_t buf[2048];
|
||||
// ikeypad
|
||||
((uint64_t *)buf)[0] = ((uint64_t *)key)[0] ^ IPAD;
|
||||
((uint64_t *)buf)[1] = ((uint64_t *)key)[1] ^ IPAD;
|
||||
@ -47,11 +46,10 @@ namespace crypto
|
||||
// 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);
|
||||
memset (buf + 80, 0, 16);
|
||||
|
||||
// calculate digest
|
||||
CryptoPP::Weak1::MD5().CalculateDigest (digest, buf, totalLen);
|
||||
delete[] buf;
|
||||
CryptoPP::Weak1::MD5().CalculateDigest (digest, buf, 96);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
18
i2p.cpp
18
i2p.cpp
@ -1,6 +1,7 @@
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <cryptopp/integer.h>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include "Log.h"
|
||||
#include "base64.h"
|
||||
#include "Transports.h"
|
||||
@ -10,10 +11,11 @@
|
||||
#include "Tunnel.h"
|
||||
#include "NetDb.h"
|
||||
#include "HTTPServer.h"
|
||||
#include "util.h"
|
||||
|
||||
int main( int, char** )
|
||||
int main( int argc, char* argv[] )
|
||||
{
|
||||
|
||||
i2p::util::config::OptionParser(argc,argv);
|
||||
#ifdef _WIN32
|
||||
setlocale(LC_CTYPE, "");
|
||||
SetConsoleCP(1251);
|
||||
@ -21,7 +23,17 @@ int main( int, char** )
|
||||
setlocale(LC_ALL, "Russian");
|
||||
#endif
|
||||
|
||||
i2p::util::HTTPServer httpServer (7070);
|
||||
LogPrint("\n\n\n\ni2pd starting\n");
|
||||
LogPrint("data directory: ", i2p::util::filesystem::GetDataDir().string());
|
||||
i2p::util::filesystem::ReadConfigFile(i2p::util::config::mapArgs, i2p::util::config::mapMultiArgs);
|
||||
|
||||
//TODO: This is an ugly workaround. fix it.
|
||||
//TODO: Autodetect public IP.
|
||||
i2p::context.OverrideNTCPAddress(i2p::util::config::GetCharArg("-host", "127.0.0.1"),
|
||||
i2p::util::config::GetArg("-port", 17070));
|
||||
int httpport = i2p::util::config::GetArg("-httpport", 7070);
|
||||
|
||||
i2p::util::HTTPServer httpServer (httpport);
|
||||
|
||||
httpServer.Start ();
|
||||
i2p::data::netdb.Start ();
|
||||
|
261
util.cpp
Normal file
261
util.cpp
Normal file
@ -0,0 +1,261 @@
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <functional>
|
||||
#include <fstream>
|
||||
#include <set>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/program_options/detail/config_file.hpp>
|
||||
#include <boost/program_options/parsers.hpp>
|
||||
#include "util.h"
|
||||
#include "Log.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
|
||||
namespace config
|
||||
{
|
||||
std::map<std::string, std::string> mapArgs;
|
||||
std::map<std::string, std::vector<std::string> > mapMultiArgs;
|
||||
|
||||
void OptionParser(int argc, const char* const argv[])
|
||||
{
|
||||
mapArgs.clear();
|
||||
mapMultiArgs.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);
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
boost::to_lower(strKey);
|
||||
if (boost::algorithm::starts_with(strKey, "/"))
|
||||
strKey = "-" + strKey.substr(1);
|
||||
#endif
|
||||
if (strKey[0] != '-')
|
||||
break;
|
||||
|
||||
mapArgs[strKey] = strValue;
|
||||
mapMultiArgs[strKey].push_back(strValue);
|
||||
}
|
||||
|
||||
BOOST_FOREACH(PAIRTYPE(const std::string,std::string)& entry, mapArgs)
|
||||
{
|
||||
std::string name = entry.first;
|
||||
|
||||
// interpret --foo as -foo (as long as both are not set)
|
||||
if (name.find("--") == 0)
|
||||
{
|
||||
std::string singleDash(name.begin()+1, name.end());
|
||||
if (mapArgs.count(singleDash) == 0)
|
||||
mapArgs[singleDash] = entry.second;
|
||||
name = singleDash;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char* GetCharArg(const std::string& strArg, const std::string& nDefault)
|
||||
{
|
||||
if (mapArgs.count(strArg))
|
||||
return mapArgs[strArg].c_str();
|
||||
return nDefault.c_str();
|
||||
}
|
||||
|
||||
std::string GetArg(const std::string& strArg, const std::string& strDefault)
|
||||
{
|
||||
if (mapArgs.count(strArg))
|
||||
return mapArgs[strArg];
|
||||
return strDefault;
|
||||
}
|
||||
|
||||
int GetArg(const std::string& strArg, int nDefault)
|
||||
{
|
||||
if (mapArgs.count(strArg))
|
||||
return atoi(mapArgs[strArg].c_str());
|
||||
return nDefault;
|
||||
}
|
||||
}
|
||||
|
||||
namespace filesystem
|
||||
{
|
||||
const boost::filesystem::path &GetDataDir()
|
||||
{
|
||||
static boost::filesystem::path path;
|
||||
|
||||
if (i2p::util::config::mapArgs.count("-datadir")) {
|
||||
path = boost::filesystem::system_complete(i2p::util::config::mapArgs["-datadir"]);
|
||||
} else {
|
||||
path = GetDefaultDataDir();
|
||||
}
|
||||
|
||||
if (!boost::filesystem::exists( path ))
|
||||
{
|
||||
// Create data directory
|
||||
if (!boost::filesystem::create_directory( path ))
|
||||
{
|
||||
LogPrint("Failed to create data directory!");
|
||||
path = "";
|
||||
return path;
|
||||
}
|
||||
}
|
||||
if (!boost::filesystem::is_directory(path)) {
|
||||
path = GetDefaultDataDir();
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
boost::filesystem::path GetConfigFile()
|
||||
{
|
||||
boost::filesystem::path pathConfigFile(i2p::util::config::GetArg("-conf", "i2p.conf"));
|
||||
if (!pathConfigFile.is_complete()) pathConfigFile = GetDataDir() / pathConfigFile;
|
||||
return pathConfigFile;
|
||||
}
|
||||
|
||||
void ReadConfigFile(std::map<std::string, std::string>& mapSettingsRet,
|
||||
std::map<std::string, std::vector<std::string> >& mapMultiSettingsRet)
|
||||
{
|
||||
boost::filesystem::ifstream streamConfig(GetConfigFile());
|
||||
if (!streamConfig.good())
|
||||
return; // No i2pd.conf file is OK
|
||||
|
||||
std::set<std::string> setOptions;
|
||||
setOptions.insert("*");
|
||||
|
||||
for (boost::program_options::detail::config_file_iterator it(streamConfig, setOptions), end; it != end; ++it)
|
||||
{
|
||||
// Don't overwrite existing settings so command line settings override i2pd.conf
|
||||
std::string strKey = std::string("-") + it->string_key;
|
||||
if (mapSettingsRet.count(strKey) == 0)
|
||||
{
|
||||
mapSettingsRet[strKey] = it->value[0];
|
||||
}
|
||||
mapMultiSettingsRet[strKey].push_back(it->value[0]);
|
||||
}
|
||||
}
|
||||
|
||||
boost::filesystem::path GetDefaultDataDir()
|
||||
{
|
||||
// Windows < Vista: C:\Documents and Settings\Username\Application Data\i2pd
|
||||
// Windows >= Vista: C:\Users\Username\AppData\Roaming\i2pd
|
||||
// Mac: ~/Library/Application Support/i2pd
|
||||
// Unix: ~/.i2pd
|
||||
#ifdef WIN32
|
||||
// Windows
|
||||
return GetSpecialFolderPath(CSIDL_APPDATA) / "i2pd";
|
||||
#else
|
||||
boost::filesystem::path pathRet;
|
||||
char* pszHome = getenv("HOME");
|
||||
if (pszHome == NULL || strlen(pszHome) == 0)
|
||||
pathRet = boost::filesystem::path("/");
|
||||
else
|
||||
pathRet = boost::filesystem::path(pszHome);
|
||||
#ifdef MAC_OSX
|
||||
// Mac
|
||||
pathRet /= "Library/Application Support";
|
||||
boost::filesystem::create_directory(pathRet);
|
||||
return pathRet / "i2pd";
|
||||
#else
|
||||
// Unix
|
||||
return pathRet / ".i2pd";
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
namespace http
|
||||
{
|
||||
std::string httpRequest(const std::string& address)
|
||||
{
|
||||
try
|
||||
{
|
||||
i2p::util::http::url u(address);
|
||||
boost::asio::ip::tcp::iostream site;
|
||||
// please don't uncomment following line because it's not compatible with boost 1.46
|
||||
// 1.46 is default boost for Ubuntu 12.04 LTS
|
||||
//site.expires_from_now (boost::posix_time::seconds(30));
|
||||
site.connect(u.host_, "http");
|
||||
if (site)
|
||||
{
|
||||
// User-Agent is needed to get the server list routerInfo files.
|
||||
site << "GET " << u.path_ << " HTTP/1.0\r\nHost: " << u.host_
|
||||
<< "\r\nAccept: */*\r\n" << "User-Agent: Wget/1.11.4\r\n" << "Connection: close\r\n\r\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 (std::getline(site, header) && header != "\r"){}
|
||||
std::stringstream ss;
|
||||
ss << site.rdbuf();
|
||||
return ss.str();
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint ("HTTP response ", status);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint ("Can't connect to ", address);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint ("Failed to download ", address, " : ", ex.what ());
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
url::url(const std::string& url_s)
|
||||
{
|
||||
parse(url_s);
|
||||
}
|
||||
|
||||
void url::parse(const std::string& url_s)
|
||||
{
|
||||
const std::string prot_end("://");
|
||||
std::string::const_iterator prot_i = search(url_s.begin(), url_s.end(),
|
||||
prot_end.begin(), prot_end.end());
|
||||
protocol_.reserve(distance(url_s.begin(), prot_i));
|
||||
transform(url_s.begin(), prot_i,
|
||||
back_inserter(protocol_),
|
||||
std::ptr_fun<int,int>(tolower)); // protocol is icase
|
||||
if( prot_i == url_s.end() )
|
||||
return;
|
||||
advance(prot_i, prot_end.length());
|
||||
std::string::const_iterator path_i = find(prot_i, url_s.end(), '/');
|
||||
host_.reserve(distance(prot_i, path_i));
|
||||
transform(prot_i, path_i,
|
||||
back_inserter(host_),
|
||||
std::ptr_fun<int,int>(tolower)); // host is icase
|
||||
std::string::const_iterator query_i = find(path_i, url_s.end(), '?');
|
||||
path_.assign(path_i, query_i);
|
||||
if( query_i != url_s.end() )
|
||||
++query_i;
|
||||
query_.assign(query_i, url_s.end());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
} // Namespace end
|
||||
}
|
49
util.h
Normal file
49
util.h
Normal file
@ -0,0 +1,49 @@
|
||||
#ifndef UTIL_H
|
||||
#define UTIL_H
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
|
||||
#define PAIRTYPE(t1, t2) std::pair<t1, t2>
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
namespace config
|
||||
{
|
||||
extern std::map<std::string, std::string> mapArgs;
|
||||
extern std::map<std::string, std::vector<std::string> > mapMultiArgs;
|
||||
void OptionParser(int argc, const char* const argv[]);
|
||||
int GetArg(const std::string& strArg, int nDefault);
|
||||
std::string GetArg(const std::string& strArg, const std::string& strDefault);
|
||||
const char* GetCharArg(const std::string& strArg, const std::string& nDefault);
|
||||
}
|
||||
|
||||
namespace filesystem
|
||||
{
|
||||
const boost::filesystem::path &GetDataDir();
|
||||
boost::filesystem::path GetDefaultDataDir();
|
||||
boost::filesystem::path GetConfigFile();
|
||||
void ReadConfigFile(std::map<std::string, std::string>& mapSettingsRet,
|
||||
std::map<std::string, std::vector<std::string> >& mapMultiSettingsRet);
|
||||
}
|
||||
|
||||
namespace http
|
||||
{
|
||||
std::string httpRequest(const std::string& address);
|
||||
struct url {
|
||||
url(const std::string& url_s); // omitted copy, ==, accessors, ...
|
||||
private:
|
||||
void parse(const std::string& url_s);
|
||||
public:
|
||||
std::string protocol_, host_, path_, query_;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user