Merge pull request #7 from orignal/master

Merge pull request from orignal/master
This commit is contained in:
chertov 2014-02-03 01:07:10 -08:00
commit 5337f4c9a4
24 changed files with 1167 additions and 221 deletions

7
.gitignore vendored
View File

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

View File

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

View File

@ -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

View File

@ -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
View File

@ -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
View File

@ -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;

View File

@ -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
View 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
View 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

View File

@ -3,6 +3,7 @@
#include <cryptopp/dsa.h>
#include "CryptoConst.h"
#include "RouterContext.h"
#include "util.h"
namespace i2p
{

View File

@ -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;

View File

@ -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
View File

@ -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
View File

@ -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;
};
}

View File

@ -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)
{

View File

@ -3,7 +3,9 @@
#include <inttypes.h>
#include <map>
#include <set>
#include <cryptopp/dsa.h>
#include "I2PEndian.h"
#include "Queue.h"
#include "Identity.h"
#include "LeaseSet.h"
@ -37,6 +39,25 @@ namespace stream
Packet (): len (0), offset (0) {};
uint8_t * GetBuffer () { return buf + offset; };
size_t GetLength () const { return len - offset; };
uint32_t GetSendStreamID () const { return be32toh (*(uint32_t *)buf); };
uint32_t GetReceiveStreamID () const { return be32toh (*(uint32_t *)(buf + 4)); };
uint32_t GetSeqn () const { return be32toh (*(uint32_t *)(buf + 8)); };
uint32_t GetAckThrough () const { return be32toh (*(uint32_t *)(buf + 12)); };
uint8_t GetNACKCount () const { return buf[16]; };
const uint8_t * GetOption () const { return buf + 17 + GetNACKCount ()*4 + 3; }; // 3 = resendDelay + flags
uint16_t GetFlags () const { return be16toh (*(uint16_t *)(GetOption () - 2)); };
uint16_t GetOptionSize () const { return be16toh (*(uint16_t *)GetOption ()); };
const uint8_t * GetOptionData () const { return GetOption () + 2; };
const uint8_t * GetPayload () const { return GetOptionData () + GetOptionSize (); };
};
struct PacketCmp
{
bool operator() (const Packet * p1, const Packet * p2) const
{
return p1->GetSeqn () < p2->GetSeqn ();
};
};
class StreamingDestination;
@ -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;
};

View File

@ -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);
}

View File

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

View File

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

View File

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

8
hmac.h
View File

@ -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
View File

@ -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
View 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
View 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