Merge pull request #6 from orignal/master

update
This commit is contained in:
mikhail4021 2014-02-09 04:21:42 -08:00
commit 0438fc77bd
32 changed files with 1276 additions and 416 deletions

2
.gitignore vendored
View file

@ -1,5 +1,5 @@
# i2pd # i2pd
*.o obj/*.o
router.info router.info
router.keys router.keys
i2p i2p

View file

@ -4,6 +4,7 @@
#include <inttypes.h> #include <inttypes.h>
#include <set> #include <set>
#include <string.h> #include <string.h>
#include "I2PEndian.h"
#include "RouterInfo.h" #include "RouterInfo.h"
namespace i2p namespace i2p
@ -19,6 +20,12 @@ namespace i2p
uint8_t chks; uint8_t chks;
}; };
struct I2NPHeaderShort
{
uint8_t typeID;
uint32_t shortExpiration;
};
struct I2NPDatabaseStoreMsg struct I2NPDatabaseStoreMsg
{ {
uint8_t key[32]; uint8_t key[32];
@ -101,6 +108,19 @@ namespace i2p
len = offset + other.GetLength (); len = offset + other.GetLength ();
return *this; return *this;
} }
// for SSU only
uint8_t * GetSSUHeader () { return buf + offset + sizeof(I2NPHeader) - sizeof(I2NPHeaderShort); };
void FromSSU (uint32_t msgID) // we have received SSU message and convert it to regular
{
I2NPHeaderShort ssu = *(I2NPHeaderShort *)GetSSUHeader ();
I2NPHeader * header = GetHeader ();
header->typeID = ssu.typeID;
header->msgID = htobe32 (msgID);
header->expiration = htobe64 (be32toh (ssu.shortExpiration)*1000LL);
header->size = htobe16 (len - offset - sizeof (I2NPHeader));
header->chks = 0;
}
}; };
I2NPMessage * NewI2NPMessage (); I2NPMessage * NewI2NPMessage ();
void DeleteI2NPMessage (I2NPMessage * msg); void DeleteI2NPMessage (I2NPMessage * msg);

View file

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

View file

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

View file

@ -57,13 +57,13 @@ namespace data
LogPrint ("LeaseSet verification failed"); LogPrint ("LeaseSet verification failed");
} }
std::vector<Lease> LeaseSet::GetNonExpiredLeases () const std::set<Lease> LeaseSet::GetNonExpiredLeases () const
{ {
auto ts = i2p::util::GetMillisecondsSinceEpoch (); auto ts = i2p::util::GetMillisecondsSinceEpoch ();
std::vector<Lease> leases; std::set<Lease> leases;
for (auto& it: m_Leases) for (auto& it: m_Leases)
if (ts < it.endDate) if (ts < it.endDate)
leases.push_back (it); leases.insert (it);
return leases; return leases;
} }

View file

@ -4,6 +4,7 @@
#include <inttypes.h> #include <inttypes.h>
#include <string.h> #include <string.h>
#include <vector> #include <vector>
#include <set>
#include "Identity.h" #include "Identity.h"
namespace i2p namespace i2p
@ -18,6 +19,14 @@ namespace data
uint8_t tunnelGateway[32]; uint8_t tunnelGateway[32];
uint32_t tunnelID; uint32_t tunnelID;
uint64_t endDate; uint64_t endDate;
bool operator< (const Lease& other) const
{
if (endDate != other.endDate)
return endDate > other.endDate;
else
return tunnelID < other.tunnelID;
}
}; };
#pragma pack() #pragma pack()
@ -34,7 +43,7 @@ namespace data
const Identity& GetIdentity () const { return m_Identity; }; const Identity& GetIdentity () const { return m_Identity; };
const IdentHash& GetIdentHash () const { return m_IdentHash; }; const IdentHash& GetIdentHash () const { return m_IdentHash; };
const std::vector<Lease>& GetLeases () const { return m_Leases; }; const std::vector<Lease>& GetLeases () const { return m_Leases; };
std::vector<Lease> GetNonExpiredLeases () const; std::set<Lease> GetNonExpiredLeases () const;
bool HasExpiredLeases () const; bool HasExpiredLeases () const;
bool HasNonExpiredLeases () const; bool HasNonExpiredLeases () const;
const uint8_t * GetEncryptionPublicKey () const { return m_EncryptionKey; }; const uint8_t * GetEncryptionPublicKey () const { return m_EncryptionKey; };

View file

@ -59,12 +59,12 @@ struct LittleEndian
return t; return t;
} }
const T operator = (const T t) const T operator = (const T t)
{ {
for (unsigned i = 0; i < sizeof(T); i++) for (unsigned i = 0; i < sizeof(T); i++)
bytes[i] = t >> (i << 3); bytes[sizeof(T)-1 - i] = static_cast<unsigned char>(t >> (i << 3));
return t; return t;
} }
// operators // operators

View file

@ -1,26 +1,31 @@
CC = g++ CC = g++
CFLAGS = -g -Wall -std=c++0x CFLAGS = -g -Wall -std=c++0x
OBJECTS = i2p.o base64.o NTCPSession.o RouterInfo.o Transports.o RouterContext.o \ OBJECTS = obj/i2p.o obj/base64.o obj/NTCPSession.o obj/RouterInfo.o obj/Transports.o \
NetDb.o LeaseSet.o Tunnel.o TunnelEndpoint.o TunnelGateway.o TransitTunnel.o \ obj/RouterContext.o obj/NetDb.o obj/LeaseSet.o obj/Tunnel.o obj/TunnelEndpoint.o \
I2NPProtocol.o Log.o Garlic.o HTTPServer.o Streaming.o Identity.o SSU.o util.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 = INCFLAGS =
LDFLAGS = -Wl,-rpath,/usr/local/lib -lcryptopp -lboost_system -lboost_filesystem -lpthread LDFLAGS = -Wl,-rpath,/usr/local/lib -lcryptopp -lboost_system -lboost_filesystem -lboost_regex -lboost_program_options -lpthread
LIBS = LIBS =
all: i2p all: obj i2p
i2p: $(OBJECTS) i2p: $(OBJECTS:obj/%=obj/%)
$(CC) -o i2p $(OBJECTS) $(LDFLAGS) $(LIBS) $(CC) -o $@ $^ $(LDFLAGS) $(LIBS)
.SUFFIXES: .SUFFIXES:
.SUFFIXES: .c .cc .C .cpp .o .SUFFIXES: .c .cc .C .cpp .o
.cpp.o : obj/%.o : %.cpp
$(CC) -o $@ -c $(CFLAGS) $< $(INCFLAGS) $(CC) -o $@ $< -c $(CFLAGS) $(INCFLAGS)
obj:
mkdir -p obj
clean: clean:
rm -f *.o rm -fr obj i2p
.PHONY: all .PHONY: all
.PHONY: clean .PHONY: clean

View file

@ -474,7 +474,9 @@ namespace ntcp
if (ecode) if (ecode)
{ {
LogPrint ("Couldn't send msg: ", ecode.message ()); LogPrint ("Couldn't send msg: ", ecode.message ());
Terminate (); // we shouldn't call Terminate () here, because HandleReceive takes care
// TODO: 'delete this' statement in Terminate () must be eliminated later
// Terminate ();
} }
else else
{ {

107
NetDb.cpp
View file

@ -2,7 +2,6 @@
#include <fstream> #include <fstream>
#include <vector> #include <vector>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include <boost/filesystem.hpp>
#include <cryptopp/gzip.h> #include <cryptopp/gzip.h>
#include "base64.h" #include "base64.h"
#include "Log.h" #include "Log.h"
@ -13,12 +12,13 @@
#include "RouterContext.h" #include "RouterContext.h"
#include "Garlic.h" #include "Garlic.h"
#include "NetDb.h" #include "NetDb.h"
#include "Reseed.h"
#include "util.h"
namespace i2p namespace i2p
{ {
namespace data namespace data
{ {
I2NPMessage * RequestedDestination::CreateRequestMessage (const RouterInfo * router, I2NPMessage * RequestedDestination::CreateRequestMessage (const RouterInfo * router,
const i2p::tunnel::InboundTunnel * replyTunnel) const i2p::tunnel::InboundTunnel * replyTunnel)
{ {
@ -41,10 +41,15 @@ namespace data
m_LastReplyTunnel = nullptr; m_LastReplyTunnel = nullptr;
return msg; return msg;
} }
#ifndef _WIN32
const char NetDb::m_NetDbPath[] = "/netDb";
#else
const char NetDb::m_NetDbPath[] = "\\netDb";
#endif
NetDb netdb; NetDb netdb;
NetDb::NetDb (): m_IsRunning (false), m_Thread (0) NetDb::NetDb (): m_IsRunning (false), m_ReseedRetries (0), m_Thread (0)
{ {
} }
@ -60,8 +65,15 @@ namespace data
} }
void NetDb::Start () 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)); m_Thread = new std::thread (std::bind (&NetDb::Run, this));
} }
@ -111,7 +123,7 @@ namespace data
if (ts - lastTs >= 60) // save routers every minute if (ts - lastTs >= 60) // save routers every minute
{ {
if (lastTs) if (lastTs)
SaveUpdated ("netDb"); SaveUpdated (m_NetDbPath);
lastTs = ts; lastTs = ts;
} }
} }
@ -180,13 +192,12 @@ namespace data
} }
// TODO: Move to reseed and/or scheduled tasks. (In java version, scheduler fix this as well as sort RIs.) // TODO: Move to reseed and/or scheduled tasks. (In java version, scheduler fix this as well as sort RIs.)
bool NetDb::CreateNetDb(const char * directory) bool NetDb::CreateNetDb(boost::filesystem::path directory)
{ {
boost::filesystem::path p (directory); LogPrint (directory.string(), " doesn't exist, trying to create it.");
LogPrint (directory, " doesn't exist, trying to create it."); if (!boost::filesystem::create_directory (directory))
if (!boost::filesystem::create_directory (p))
{ {
LogPrint("Failed to create directory ", directory); LogPrint("Failed to create directory ", directory.string());
return false; return false;
} }
@ -200,19 +211,26 @@ namespace data
#else #else
suffix = std::string ("\\r") + chars[i]; suffix = std::string ("\\r") + chars[i];
#endif #endif
if (!boost::filesystem::create_directory( boost::filesystem::path (p / suffix) )) return false; if (!boost::filesystem::create_directory( boost::filesystem::path (directory / suffix) )) return false;
} }
return true; return true;
} }
void NetDb::Load (const char * directory) void NetDb::Load (const char * directory)
{ {
boost::filesystem::path p (directory); boost::filesystem::path p (i2p::util::filesystem::GetDataDir());
p /= (directory);
if (!boost::filesystem::exists (p)) if (!boost::filesystem::exists (p))
{ {
if (!CreateNetDb(directory)) return; // seems netDb doesn't exist yet
if (!CreateNetDb(p)) return;
} }
// TODO: Reseed if needed. // 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; int numRouters = 0;
boost::filesystem::directory_iterator end; boost::filesystem::directory_iterator end;
for (boost::filesystem::directory_iterator it (p); it != end; ++it) for (boost::filesystem::directory_iterator it (p); it != end; ++it)
@ -247,7 +265,14 @@ namespace data
#endif #endif
routerInfo->GetIdentHashBase64 () + ".dat"; 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; int count = 0, deletedCount = 0;
auto total = m_RouterInfos.size (); auto total = m_RouterInfos.size ();
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch (); uint64_t ts = i2p::util::GetMillisecondsSinceEpoch ();
@ -255,7 +280,7 @@ namespace data
{ {
if (it.second->IsUpdated ()) 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 ()); r.write ((char *)it.second->GetBuffer (), it.second->GetBufferLen ());
it.second->SetUpdated (false); it.second->SetUpdated (false);
count++; count++;
@ -271,9 +296,9 @@ namespace data
if (it.second->IsUnreachable ()) 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++; deletedCount++;
} }
} }
@ -601,45 +626,5 @@ namespace data
return r; return r;
} }
//TODO: Move to reseed.
//TODO: Implement v1 & v2 reseeding. Lightweight zip library is needed for v2.
// orignal: zip is part of crypto++, see implementation of DatabaseStoreMsg
//TODO: Implement SU3, utils.
void NetDb::DownloadRouterInfo (const std::string& address, const std::string& filename)
{
try
{
boost::asio::ip::tcp::iostream site(address, "http");
if (!site)
{
//site.expires_from_now (boost::posix_time::seconds (10)); // wait for 10 seconds
site << "GET " << filename << "HTTP/1.0\nHost: " << address << "\nAccept: */*\nConnection: close\n\n";
// read response
std::string version, statusMessage;
site >> version; // HTTP version
int status;
site >> status; // status
std::getline (site, statusMessage);
if (status == 200) // OK
{
std::string header;
while (header != "\n")
std::getline (site, header);
// read content
std::stringstream ss;
ss << site.rdbuf();
AddRouterInfo ((uint8_t *)ss.str ().c_str (), ss.str ().size ());
}
else
LogPrint ("HTTP response ", status);
}
else
LogPrint ("Can't connect to ", address);
}
catch (std::exception& ex)
{
LogPrint ("Failed to download ", filename, " : ", ex.what ());
}
}
} }
} }

View file

@ -6,6 +6,7 @@
#include <map> #include <map>
#include <string> #include <string>
#include <thread> #include <thread>
#include <boost/filesystem.hpp>
#include "Queue.h" #include "Queue.h"
#include "I2NPProtocol.h" #include "I2NPProtocol.h"
#include "RouterInfo.h" #include "RouterInfo.h"
@ -76,10 +77,9 @@ namespace data
private: private:
bool CreateNetDb(const char * directory); bool CreateNetDb(boost::filesystem::path directory);
void Load (const char * directory); void Load (const char * directory);
void SaveUpdated (const char * directory); void SaveUpdated (const char * directory);
void DownloadRouterInfo (const std::string& address, const std::string& filename); // for reseed
void Run (); // exploratory thread void Run (); // exploratory thread
void Explore (); void Explore ();
const RouterInfo * GetClosestFloodfill (const IdentHash& destination, const std::set<IdentHash>& excluded) const; const RouterInfo * GetClosestFloodfill (const IdentHash& destination, const std::set<IdentHash>& excluded) const;
@ -95,8 +95,11 @@ namespace data
std::map<IdentHash, RequestedDestination *> m_RequestedDestinations; std::map<IdentHash, RequestedDestination *> m_RequestedDestinations;
bool m_IsRunning; bool m_IsRunning;
int m_ReseedRetries;
std::thread * m_Thread; std::thread * m_Thread;
i2p::util::Queue<I2NPMessage> m_Queue; // of I2NPDatabaseStoreMsg i2p::util::Queue<I2NPMessage> m_Queue; // of I2NPDatabaseStoreMsg
static const char m_NetDbPath[];
}; };
extern NetDb netdb; extern NetDb netdb;

10
Queue.h
View file

@ -98,13 +98,18 @@ namespace util
{ {
public: public:
MsgQueue (): m_Thread (std::bind (&MsgQueue<Msg>::Run, this)) {}; MsgQueue (): m_Thread (std::bind (&MsgQueue<Msg>::Run, this)) , running(1) {};
void Stop()
{
running = 0;
m_Thread.join();
}
private: private:
void Run () void Run ()
{ {
Msg * msg = nullptr; Msg * msg = nullptr;
while ((msg = Queue<Msg>::GetNext ()) != nullptr) while ((msg = Queue<Msg>::GetNext ()) != nullptr && running)
{ {
msg->Process (); msg->Process ();
delete msg; delete msg;
@ -113,6 +118,7 @@ namespace util
private: private:
std::thread m_Thread; std::thread m_Thread;
int running;
}; };
} }
} }

View file

@ -8,3 +8,39 @@ Requires gcc 4.6 and higher, boost 1.46 and higher, crypto++
on Windows on Windows
Requires msvs2013, boost 1.46 and higher, crypto++ 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.
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
Options
-------
* --host= - The external IP
* --port= - The port to listen on
* --httpport= - The http port to listen on
* --log= - Enable or disable logging to file. 1 for yes, 0 for no.
* --daemon= - Eanble or disable daemon mode. 1 for yes, 0 for no.

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

@ -52,6 +52,13 @@ namespace i2p
m_RouterInfo.CreateBuffer (); m_RouterInfo.CreateBuffer ();
} }
void RouterContext::UpdateAddress (const char * host)
{
for (auto& address : m_RouterInfo.GetAddresses ())
address.host = boost::asio::ip::address::from_string (host);
m_RouterInfo.CreateBuffer ();
}
void RouterContext::Sign (uint8_t * buf, int len, uint8_t * signature) void RouterContext::Sign (uint8_t * buf, int len, uint8_t * signature)
{ {

View file

@ -28,6 +28,7 @@ namespace i2p
void Sign (uint8_t * buf, int len, uint8_t * signature); void Sign (uint8_t * buf, int len, uint8_t * signature);
void OverrideNTCPAddress (const char * host, int port); // temporary void OverrideNTCPAddress (const char * host, int port); // temporary
void UpdateAddress (const char * host); // called from SSU
private: private:

View file

@ -46,7 +46,12 @@ namespace data
if (s.is_open ()) if (s.is_open ())
{ {
s.seekg (0,std::ios::end); s.seekg (0,std::ios::end);
m_BufferLen = s.tellg (); m_BufferLen = s.tellg ();
if (m_BufferLen < 40)
{
LogPrint("File", filename, " is malformed");
return;
}
s.seekg(0, std::ios::beg); s.seekg(0, std::ios::beg);
s.read(m_Buffer,m_BufferLen); s.read(m_Buffer,m_BufferLen);
ReadFromBuffer (); ReadFromBuffer ();
@ -289,6 +294,14 @@ namespace data
else else
return m_SupportedTransports & (eNTCPV4 | eNTCPV6); return m_SupportedTransports & (eNTCPV4 | eNTCPV6);
} }
bool RouterInfo::IsSSU (bool v4only) const
{
if (v4only)
return m_SupportedTransports & eSSUV4;
else
return m_SupportedTransports & (eSSUV4 | eSSUV6);
}
RouterInfo::Address * RouterInfo::GetNTCPAddress (bool v4only) RouterInfo::Address * RouterInfo::GetNTCPAddress (bool v4only)
{ {

View file

@ -53,7 +53,7 @@ namespace data
const char * GetIdentHashBase64 () const { return m_IdentHashBase64; }; const char * GetIdentHashBase64 () const { return m_IdentHashBase64; };
const char * GetIdentHashAbbreviation () const { return m_IdentHashAbbreviation; }; const char * GetIdentHashAbbreviation () const { return m_IdentHashAbbreviation; };
uint64_t GetTimestamp () const { return m_Timestamp; }; uint64_t GetTimestamp () const { return m_Timestamp; };
const std::vector<Address>& GetAddresses () const { return m_Addresses; }; std::vector<Address>& GetAddresses () { return m_Addresses; };
Address * GetNTCPAddress (bool v4only = true); Address * GetNTCPAddress (bool v4only = true);
Address * GetSSUAddress (bool v4only = true); Address * GetSSUAddress (bool v4only = true);
const RoutingKey& GetRoutingKey () const { return m_RoutingKey; }; const RoutingKey& GetRoutingKey () const { return m_RoutingKey; };
@ -63,6 +63,7 @@ namespace data
const char * GetProperty (const char * key) const; const char * GetProperty (const char * key) const;
bool IsFloodfill () const; bool IsFloodfill () const;
bool IsNTCP (bool v4only = true) const; bool IsNTCP (bool v4only = true) const;
bool IsSSU (bool v4only = true) const;
bool IsCompatible (const RouterInfo& other) const { return m_SupportedTransports & other.m_SupportedTransports; }; bool IsCompatible (const RouterInfo& other) const { return m_SupportedTransports & other.m_SupportedTransports; };
void SetUnreachable (bool unreachable) { m_IsUnreachable = unreachable; }; void SetUnreachable (bool unreachable) { m_IsUnreachable = unreachable; };

331
SSU.cpp
View file

@ -20,7 +20,7 @@ namespace ssu
{ {
} }
void SSUSession::CreateAESKey (uint8_t * pubKey, uint8_t * aesKey) // TODO: move it to base class for NTCP and SSU void SSUSession::CreateAESandMacKey (uint8_t * pubKey, uint8_t * aesKey, uint8_t * macKey)
{ {
CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg); CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg);
CryptoPP::SecByteBlock secretKey(dh.AgreedValueLength()); CryptoPP::SecByteBlock secretKey(dh.AgreedValueLength());
@ -34,15 +34,24 @@ namespace ssu
{ {
aesKey[0] = 0; aesKey[0] = 0;
memcpy (aesKey + 1, secretKey, 31); memcpy (aesKey + 1, secretKey, 31);
memcpy (macKey, secretKey + 31, 32);
} }
else else
{
memcpy (aesKey, secretKey, 32); memcpy (aesKey, secretKey, 32);
memcpy (macKey, secretKey + 32, 32);
}
} }
void SSUSession::ProcessNextMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint) void SSUSession::ProcessNextMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint)
{ {
switch (m_State) switch (m_State)
{ {
case eSessionStateEstablished:
// most common case
ProcessMessage (buf, len);
break;
// establishing
case eSessionStateUnknown: case eSessionStateUnknown:
// session request // session request
ProcessSessionRequest (buf, len, senderEndpoint); ProcessSessionRequest (buf, len, senderEndpoint);
@ -51,33 +60,103 @@ namespace ssu
// session created // session created
ProcessSessionCreated (buf, len); ProcessSessionCreated (buf, len);
break; break;
case eSessionStateCreatedSent:
// session confirmed
ProcessSessionConfirmed (buf, len);
break;
default: default:
LogPrint ("SSU state not implemented yet"); LogPrint ("SSU state not implemented yet");
} }
} }
void SSUSession::ProcessMessage (uint8_t * buf, size_t len)
{
if (Validate (buf, len, m_MacKey))
{
Decrypt (buf, len, m_SessionKey);
SSUHeader * header = (SSUHeader *)buf;
uint8_t payloadType = header->flag >> 4;
switch (payloadType)
{
case PAYLOAD_TYPE_DATA:
LogPrint ("SSU data received");
ProcessData (buf + sizeof (SSUHeader), len - sizeof (SSUHeader));
break;
case PAYLOAD_TYPE_TEST:
LogPrint ("SSU test received");
break;
case PAYLOAD_TYPE_SESSION_DESTROYED:
LogPrint ("SSU session destroy received");
break;
default:
LogPrint ("Unexpected SSU payload type ", (int)payloadType);
}
}
// TODO: try intro key as well
else
LogPrint ("MAC verifcation failed");
}
void SSUSession::ProcessSessionRequest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint) void SSUSession::ProcessSessionRequest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint)
{ {
LogPrint ("Process session request"); LogPrint ("Process session request");
if (ProcessIntroKeyEncryptedMessage (PAYLOAD_TYPE_SESSION_REQUEST, buf, len)) // use our intro key
if (ProcessIntroKeyEncryptedMessage (PAYLOAD_TYPE_SESSION_REQUEST,
i2p::context.GetRouterInfo (), buf, len))
{ {
m_State = eSessionStateRequestReceived; m_State = eSessionStateRequestReceived;
LogPrint ("Session request received"); LogPrint ("Session request received");
SendSessionCreated (senderEndpoint); m_RemoteEndpoint = senderEndpoint;
SendSessionCreated (buf + sizeof (SSUHeader));
} }
} }
void SSUSession::ProcessSessionCreated (uint8_t * buf, size_t len) void SSUSession::ProcessSessionCreated (uint8_t * buf, size_t len)
{ {
LogPrint ("Process session created"); LogPrint ("Process session created");
if (ProcessIntroKeyEncryptedMessage (PAYLOAD_TYPE_SESSION_CREATED, buf, len)) 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; m_State = eSessionStateCreatedReceived;
LogPrint ("Session request received"); LogPrint ("Session created received");
// TODO: uint8_t * ourAddress = buf + sizeof (SSUHeader) + 257;
boost::asio::ip::address_v4 ourIP (be32toh (*(uint32_t* )(ourAddress)));
uint16_t ourPort = be16toh (*(uint16_t *)(ourAddress + 4));
LogPrint ("Our external address is ", ourIP.to_string (), ":", ourPort);
i2p::context.UpdateAddress (ourIP.to_string ().c_str ());
uint32_t relayTag = be32toh (*(uint32_t *)(buf + sizeof (SSUHeader) + 263));
SendSessionConfirmed (buf + sizeof (SSUHeader), ourAddress, relayTag);
m_State = eSessionStateEstablished;
} }
} }
void SSUSession::ProcessSessionConfirmed (uint8_t * buf, size_t len)
{
LogPrint ("Process session confirmed");
if (Validate (buf, len, m_MacKey))
{
Decrypt (buf, len, m_SessionKey);
SSUHeader * header = (SSUHeader *)buf;
if ((header->flag >> 4) == PAYLOAD_TYPE_SESSION_CONFIRMED)
{
m_State = eSessionStateConfirmedReceived;
LogPrint ("Session confirmed received");
// TODO:
m_State = eSessionStateEstablished;
}
else
LogPrint ("Unexpected payload type ", (int)(header->flag >> 4));
}
else
LogPrint ("MAC verifcation failed");
}
void SSUSession::SendSessionRequest () void SSUSession::SendSessionRequest ()
{ {
auto address = m_RemoteRouter ? m_RemoteRouter->GetSSUAddress () : nullptr; auto address = m_RemoteRouter ? m_RemoteRouter->GetSSUAddress () : nullptr;
@ -91,7 +170,7 @@ namespace ssu
uint8_t * payload = buf + sizeof (SSUHeader); uint8_t * payload = buf + sizeof (SSUHeader);
memcpy (payload, i2p::context.GetRouterIdentity ().publicKey, 256); memcpy (payload, i2p::context.GetRouterIdentity ().publicKey, 256);
payload[256] = 4; // we assume ipv4 payload[256] = 4; // we assume ipv4
*(uint32_t *)(payload + 257) = address->host.to_v4 ().to_ulong (); // network bytes order already *(uint32_t *)(payload + 257) = htobe32 (m_RemoteEndpoint.address ().to_v4 ().to_ulong ());
uint8_t iv[16]; uint8_t iv[16];
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
@ -102,7 +181,53 @@ namespace ssu
m_Server->Send (buf, 304, m_RemoteEndpoint); m_Server->Send (buf, 304, m_RemoteEndpoint);
} }
void SSUSession::SendSessionCreated (const boost::asio::ip::udp::endpoint& senderEndpoint) 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) = htobe32 (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);
}
void SSUSession::SendSessionConfirmed (const uint8_t * y, const uint8_t * ourAddress, uint32_t relayTag)
{ {
auto address = m_RemoteRouter ? m_RemoteRouter->GetSSUAddress () : nullptr; auto address = m_RemoteRouter ? m_RemoteRouter->GetSSUAddress () : nullptr;
if (!address) if (!address)
@ -111,17 +236,46 @@ namespace ssu
return; return;
} }
uint8_t buf[368 + 18]; uint8_t buf[480 + 18];
uint8_t * payload = buf + sizeof (SSUHeader); uint8_t * payload = buf + sizeof (SSUHeader);
memcpy (payload, i2p::context.GetRouterIdentity ().publicKey, 256); *payload = 1; // 1 fragment
payload++; // info
size_t identLen = sizeof (i2p::context.GetRouterIdentity ()); // 387 bytes
*(uint16_t *)(payload) = htobe16 (identLen);
payload += 2; // cursize
memcpy (payload, (uint8_t *)&i2p::context.GetRouterIdentity (), identLen);
payload += identLen;
uint32_t signedOnTime = i2p::util::GetSecondsSinceEpoch ();
*(uint32_t *)(payload) = htobe32 (signedOnTime); // signed on time
payload += 4;
size_t paddingSize = ((payload - buf) + 40)%16;
if (paddingSize > 0) paddingSize = 16 - paddingSize;
// TODO: fill padding
payload += paddingSize; // padding size
m_State = eSessionStateRequestSent; // signature
m_Server->Send (buf, 368, m_RemoteEndpoint); uint8_t signedData[532]; // x,y, our IP, our port, remote IP, remote port, relayTag, our signed on time
memcpy (signedData, i2p::context.GetRouterIdentity ().publicKey, 256); // x
memcpy (signedData + 256, y, 256); // y
memcpy (signedData + 512, ourAddress, 6); // our address/port as seem by party
*(uint32_t *)(signedData + 518) = htobe32 (m_RemoteEndpoint.address ().to_v4 ().to_ulong ()); // remote IP
*(uint16_t *)(signedData + 522) = htobe16 (m_RemoteEndpoint.port ()); // remote port
*(uint32_t *)(signedData + 524) = htobe32 (relayTag); // relay tag
*(uint32_t *)(signedData + 528) = htobe32 (signedOnTime); // signed on time
i2p::context.Sign (signedData, 532, payload); // DSA signature
uint8_t iv[16];
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
rnd.GenerateBlock (iv, 16); // random iv
// encrypt message with session key
FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_CONFIRMED, buf, 480, m_SessionKey, iv, m_MacKey);
m_State = eSessionStateConfirmedSent;
m_Server->Send (buf, 480, m_RemoteEndpoint);
} }
bool SSUSession::ProcessIntroKeyEncryptedMessage (uint8_t expectedPayloadType, uint8_t * buf, size_t len) bool SSUSession::ProcessIntroKeyEncryptedMessage (uint8_t expectedPayloadType, i2p::data::RouterInfo& r, uint8_t * buf, size_t len)
{ {
auto address = i2p::context.GetRouterInfo ().GetSSUAddress (); auto address = r.GetSSUAddress ();
if (address) if (address)
{ {
// use intro key for verification and decryption // use intro key for verification and decryption
@ -131,7 +285,7 @@ namespace ssu
SSUHeader * header = (SSUHeader *)buf; SSUHeader * header = (SSUHeader *)buf;
if ((header->flag >> 4) == expectedPayloadType) if ((header->flag >> 4) == expectedPayloadType)
{ {
CreateAESKey (buf + sizeof (SSUHeader), m_SessionKey); CreateAESandMacKey (buf + sizeof (SSUHeader), m_SessionKey, m_MacKey);
return true; return true;
} }
else else
@ -141,7 +295,7 @@ namespace ssu
LogPrint ("MAC verifcation failed"); LogPrint ("MAC verifcation failed");
} }
else else
LogPrint ("SSU is not supported"); LogPrint ("SSU is not supported by ", r.GetIdentHashAbbreviation ());
return false; return false;
} }
@ -177,6 +331,7 @@ namespace ssu
uint8_t * encrypted = &header->flag; uint8_t * encrypted = &header->flag;
uint16_t encryptedLen = len - (encrypted - buf); uint16_t encryptedLen = len - (encrypted - buf);
m_Decryption.SetKeyWithIV (aesKey, 32, header->iv); m_Decryption.SetKeyWithIV (aesKey, 32, header->iv);
encryptedLen = (encryptedLen/16)*16; // make sure 16 bytes boundary
m_Decryption.ProcessData (encrypted, encrypted, encryptedLen); m_Decryption.ProcessData (encrypted, encrypted, encryptedLen);
} }
@ -203,14 +358,132 @@ namespace ssu
SendSessionRequest (); SendSessionRequest ();
} }
void SSUSession::Close ()
{
SendSesionDestroyed ();
}
void SSUSession::SendI2NPMessage (I2NPMessage * msg) void SSUSession::SendI2NPMessage (I2NPMessage * msg)
{ {
// TODO: // TODO:
} }
SSUServer::SSUServer (boost::asio::io_service& service, int port): void SSUSession::ProcessData (uint8_t * buf, size_t len)
m_Socket (service, boost::asio::ip::udp::endpoint (boost::asio::ip::udp::v4 (), port))
{ {
//uint8_t * start = buf;
uint8_t flag = *buf;
buf++;
LogPrint ("Process SSU data flags=", (int)flag);
if (flag & DATA_FLAG_EXPLICIT_ACKS_INCLUDED)
{
// explicit ACKs
uint8_t numAcks =*buf;
buf++;
// TODO: process ACKs
buf += numAcks*4;
}
if (flag & DATA_FLAG_ACK_BITFIELDS_INCLUDED)
{
// explicit ACK bitfields
uint8_t numBitfields =*buf;
buf++;
for (int i = 0; i < numBitfields; i++)
{
buf += 4; // msgID
// TODO: process ACH bitfields
while (*buf & 0x80) // not last
buf++;
buf++; // last byte
}
}
uint8_t numFragments = *buf; // number of fragments
buf++;
for (int i = 0; i < numFragments; i++)
{
uint32_t msgID = be32toh (*(uint32_t *)buf); // message ID
buf += 4;
uint8_t frag[4];
frag[0] = 0;
memcpy (frag + 1, buf, 3);
buf += 3;
uint32_t fragmentInfo = be32toh (*(uint32_t *)frag); // fragment info
uint16_t fragmentSize = fragmentInfo & 0x1FFF; // bits 0 - 13
bool isLast = fragmentInfo & 0x010000; // bit 16
uint8_t fragmentNum = fragmentInfo >> 17; // bits 23 - 17
LogPrint ("SSU data fragment ", (int)fragmentNum, " of message ", msgID, " size=", (int)fragmentSize, isLast ? " last" : " non-last");
I2NPMessage * msg = nullptr;
if (fragmentNum > 0) // follow-up fragment
{
auto it = m_IncomleteMessages.find (msgID);
if (it != m_IncomleteMessages.end ())
{
msg = it->second;
memcpy (msg->buf + msg->len, buf, fragmentSize);
msg->len += fragmentSize;
}
else
// TODO:
LogPrint ("Unexpected follow-on fragment ", fragmentNum, " of message ", msgID);
}
else // first fragment
{
msg = NewI2NPMessage ();
memcpy (msg->GetSSUHeader (), buf, fragmentSize);
msg->len += fragmentSize - sizeof (I2NPHeaderShort);
}
if (msg)
{
if (!fragmentNum && !isLast)
m_IncomleteMessages[msgID] = msg;
if (isLast)
{
if (fragmentNum > 0)
m_IncomleteMessages.erase (msgID);
msg->FromSSU (msgID);
i2p::HandleI2NPMessage (msg, false);
SendMsgAck (msgID);
}
}
buf += fragmentSize;
}
}
void SSUSession::SendMsgAck (uint32_t msgID)
{
uint8_t buf[48 + 18]; // actual length is 44 = 37 + 7 but pad it to multiple of 16
uint8_t iv[16];
uint8_t * payload = buf + sizeof (SSUHeader);
*payload = DATA_FLAG_EXPLICIT_ACKS_INCLUDED; // flag
payload++;
*payload = 1; // number of ACKs
payload++;
*(uint32_t *)(payload) = htobe32 (msgID); // msgID
payload += 4;
*payload = 0; // number of fragments
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
rnd.GenerateBlock (iv, 16); // random iv
// encrypt message with session key
FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, 48, m_SessionKey, iv, m_MacKey);
m_Server->Send (buf, 48, m_RemoteEndpoint);
}
void SSUSession::SendSesionDestroyed ()
{
uint8_t buf[48 + 18], iv[16];
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
rnd.GenerateBlock (iv, 16); // random iv
// encrypt message with session key
FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_DESTROYED, buf, 48, m_SessionKey, iv, m_MacKey);
m_Server->Send (buf, 48, m_RemoteEndpoint);
}
SSUServer::SSUServer (boost::asio::io_service& service, int port):
m_Endpoint (boost::asio::ip::udp::v4 (), port), m_Socket (service, m_Endpoint)
{
m_Socket.set_option (boost::asio::socket_base::receive_buffer_size (65535));
m_Socket.set_option (boost::asio::socket_base::send_buffer_size (65535));
} }
SSUServer::~SSUServer () SSUServer::~SSUServer ()
@ -289,6 +562,26 @@ namespace ssu
} }
return session; return session;
} }
void SSUServer::DeleteSession (SSUSession * session)
{
if (session)
{
session->Close ();
m_Sessions.erase (session->GetRemoteEndpoint ());
delete session;
}
}
void SSUServer::DeleteAllSessions ()
{
for (auto it: m_Sessions)
{
it.second->Close ();
delete it.second;
}
m_Sessions.clear ();
}
} }
} }

36
SSU.h
View file

@ -26,7 +26,7 @@ namespace ssu
const int SSU_MTU = 1484; const int SSU_MTU = 1484;
// payload types (3 bits) // payload types (4 bits)
const uint8_t PAYLOAD_TYPE_SESSION_REQUEST = 0; const uint8_t PAYLOAD_TYPE_SESSION_REQUEST = 0;
const uint8_t PAYLOAD_TYPE_SESSION_CREATED = 1; const uint8_t PAYLOAD_TYPE_SESSION_CREATED = 1;
const uint8_t PAYLOAD_TYPE_SESSION_CONFIRMED = 2; const uint8_t PAYLOAD_TYPE_SESSION_CONFIRMED = 2;
@ -35,6 +35,15 @@ namespace ssu
const uint8_t PAYLOAD_TYPE_RELAY_INTRO = 5; const uint8_t PAYLOAD_TYPE_RELAY_INTRO = 5;
const uint8_t PAYLOAD_TYPE_DATA = 6; const uint8_t PAYLOAD_TYPE_DATA = 6;
const uint8_t PAYLOAD_TYPE_TEST = 7; const uint8_t PAYLOAD_TYPE_TEST = 7;
const uint8_t PAYLOAD_TYPE_SESSION_DESTROYED = 8;
// data flags
const uint8_t DATA_FLAG_EXTENDED_DATA_INCLUDED = 0x02;
const uint8_t DATA_FLAG_WANT_REPLY = 0x04;
const uint8_t DATA_FLAG_REQUEST_PREVIOUS_ACKS = 0x08;
const uint8_t DATA_FLAG_EXPLICIT_CONGESTION_NOTIFICATION = 0x10;
const uint8_t DATA_FLAG_ACK_BITFIELDS_INCLUDED = 0x40;
const uint8_t DATA_FLAG_EXPLICIT_ACKS_INCLUDED = 0x80;
enum SessionState enum SessionState
{ {
@ -45,7 +54,7 @@ namespace ssu
eSessionStateCreatedReceived, eSessionStateCreatedReceived,
eSessionStateConfirmedSent, eSessionStateConfirmedSent,
eSessionStateConfirmedReceived, eSessionStateConfirmedReceived,
eSessionStateEstablised eSessionStateEstablished
}; };
class SSUServer; class SSUServer;
@ -58,18 +67,26 @@ namespace ssu
void ProcessNextMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); void ProcessNextMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint);
void Connect (); void Connect ();
void Close ();
boost::asio::ip::udp::endpoint& GetRemoteEndpoint () { return m_RemoteEndpoint; };
void SendI2NPMessage (I2NPMessage * msg); void SendI2NPMessage (I2NPMessage * msg);
private: private:
void CreateAESKey (uint8_t * pubKey, uint8_t * aesKey); // TODO: shouldn't be here void CreateAESandMacKey (uint8_t * pubKey, uint8_t * aesKey, uint8_t * macKey);
void ProcessMessage (uint8_t * buf, size_t len); // call for established session
void ProcessSessionRequest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); void ProcessSessionRequest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint);
void SendSessionRequest (); void SendSessionRequest ();
void ProcessSessionCreated (uint8_t * buf, size_t len); void ProcessSessionCreated (uint8_t * buf, size_t len);
void SendSessionCreated (const boost::asio::ip::udp::endpoint& senderEndpoint); void SendSessionCreated (const uint8_t * x);
void ProcessSessionConfirmed (uint8_t * buf, size_t len);
bool ProcessIntroKeyEncryptedMessage (uint8_t expectedPayloadType, uint8_t * buf, size_t len); void SendSessionConfirmed (const uint8_t * y, const uint8_t * ourAddress, uint32_t relayTag);
void ProcessData (uint8_t * buf, size_t len);
void SendMsgAck (uint32_t msgID);
void SendSesionDestroyed ();
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 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); void Decrypt (uint8_t * buf, size_t len, uint8_t * aesKey);
bool Validate (uint8_t * buf, size_t len, uint8_t * macKey); bool Validate (uint8_t * buf, size_t len, uint8_t * macKey);
@ -82,7 +99,8 @@ namespace ssu
SessionState m_State; SessionState m_State;
CryptoPP::CBC_Mode<CryptoPP::AES>::Encryption m_Encryption; CryptoPP::CBC_Mode<CryptoPP::AES>::Encryption m_Encryption;
CryptoPP::CBC_Mode<CryptoPP::AES>::Decryption m_Decryption; CryptoPP::CBC_Mode<CryptoPP::AES>::Decryption m_Decryption;
uint8_t m_SessionKey[32]; uint8_t m_SessionKey[32], m_MacKey[32];
std::map<uint32_t, I2NPMessage *> m_IncomleteMessages;
}; };
class SSUServer class SSUServer
@ -94,7 +112,10 @@ namespace ssu
void Start (); void Start ();
void Stop (); void Stop ();
SSUSession * GetSession (i2p::data::RouterInfo * router); SSUSession * GetSession (i2p::data::RouterInfo * router);
void DeleteSession (SSUSession * session);
void DeleteAllSessions ();
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); void Send (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& to);
private: private:
@ -104,6 +125,7 @@ namespace ssu
private: private:
boost::asio::ip::udp::endpoint m_Endpoint;
boost::asio::ip::udp::socket m_Socket; boost::asio::ip::udp::socket m_Socket;
boost::asio::ip::udp::endpoint m_SenderEndpoint; boost::asio::ip::udp::endpoint m_SenderEndpoint;
uint8_t m_ReceiveBuffer[2*SSU_MTU]; uint8_t m_ReceiveBuffer[2*SSU_MTU];

View file

@ -34,55 +34,22 @@ namespace stream
if (!m_SendStreamID) if (!m_SendStreamID)
m_SendStreamID = packet->GetReceiveStreamID (); m_SendStreamID = packet->GetReceiveStreamID ();
// process flags
uint16_t flags = packet->GetFlags ();
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);
}
uint32_t receivedSeqn = packet->GetSeqn (); uint32_t receivedSeqn = packet->GetSeqn ();
LogPrint ("seqn=", receivedSeqn, ", flags=", flags); LogPrint ("Received seqn=", receivedSeqn);
if (!receivedSeqn || receivedSeqn == m_LastReceivedSequenceNumber + 1) if (!receivedSeqn || receivedSeqn == m_LastReceivedSequenceNumber + 1)
{ {
// we have received next message // we have received next in sequence message
packet->offset = packet->GetPayload () - packet->buf; ProcessPacket (packet);
if (packet->GetLength () > 0)
m_ReceiveQueue.Put (packet);
else
delete packet;
m_LastReceivedSequenceNumber = receivedSeqn;
SendQuickAck ();
// we should also try stored messages if any // we should also try stored messages if any
for (auto it = m_SavedPackets.begin (); it != m_SavedPackets.end ();) for (auto it = m_SavedPackets.begin (); it != m_SavedPackets.end ();)
{ {
if ((*it)->GetSeqn () == m_LastReceivedSequenceNumber + 1) if ((*it)->GetSeqn () == m_LastReceivedSequenceNumber + 1)
{ {
Packet * packet = *it; Packet * savedPacket = *it;
m_SavedPackets.erase (it++); m_SavedPackets.erase (it++);
LogPrint ("Process saved packet seqn=", packet->GetSeqn ()); ProcessPacket (savedPacket);
if (packet->GetLength () > 0)
m_ReceiveQueue.Put (packet);
else
delete packet;
m_LastReceivedSequenceNumber++;
SendQuickAck ();
} }
else else
break; break;
@ -106,7 +73,47 @@ namespace stream
SavePacket (packet); SavePacket (packet);
} }
} }
}
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) if (flags & PACKET_FLAG_CLOSE)
{ {
LogPrint ("Closed"); LogPrint ("Closed");
@ -114,11 +121,6 @@ namespace stream
m_ReceiveQueue.WakeUp (); m_ReceiveQueue.WakeUp ();
} }
} }
void Stream::SavePacket (Packet * packet)
{
m_SavedPackets.insert (packet);
}
size_t Stream::Send (uint8_t * buf, size_t len, int timeout) size_t Stream::Send (uint8_t * buf, size_t len, int timeout)
{ {
@ -170,9 +172,10 @@ namespace stream
if (!m_OutboundTunnel) if (!m_OutboundTunnel)
m_OutboundTunnel = i2p::tunnel::tunnels.GetNextOutboundTunnel (); m_OutboundTunnel = i2p::tunnel::tunnels.GetNextOutboundTunnel ();
if (m_OutboundTunnel) auto leases = m_RemoteLeaseSet->GetNonExpiredLeases ();
if (m_OutboundTunnel && !leases.empty ())
{ {
auto& lease = m_RemoteLeaseSet->GetLeases ()[0]; // TODO: auto& lease = *leases.begin (); // TODO:
m_OutboundTunnel->SendTunnelDataMsg (lease.tunnelGateway, lease.tunnelID, msg); m_OutboundTunnel->SendTunnelDataMsg (lease.tunnelGateway, lease.tunnelID, msg);
} }
else else
@ -206,7 +209,7 @@ namespace stream
auto leases = m_RemoteLeaseSet->GetNonExpiredLeases (); auto leases = m_RemoteLeaseSet->GetNonExpiredLeases ();
if (!leases.empty ()) if (!leases.empty ())
{ {
auto& lease = leases[0]; // TODO: auto& lease = *leases.begin (); // TODO:
m_OutboundTunnel->SendTunnelDataMsg (lease.tunnelGateway, lease.tunnelID, msg); m_OutboundTunnel->SendTunnelDataMsg (lease.tunnelGateway, lease.tunnelID, msg);
LogPrint ("Quick Ack sent"); LogPrint ("Quick Ack sent");
} }
@ -249,11 +252,12 @@ namespace stream
I2NPMessage * msg = i2p::garlic::routing.WrapSingleMessage (m_RemoteLeaseSet, I2NPMessage * msg = i2p::garlic::routing.WrapSingleMessage (m_RemoteLeaseSet,
CreateDataMessage (this, packet, size)); CreateDataMessage (this, packet, size));
if (m_OutboundTunnel) auto leases = m_RemoteLeaseSet->GetNonExpiredLeases ();
if (m_OutboundTunnel && !leases.empty ())
{ {
auto& lease = m_RemoteLeaseSet->GetLeases ()[0]; // TODO: auto& lease = *leases.begin (); // TODO:
m_OutboundTunnel->SendTunnelDataMsg (lease.tunnelGateway, lease.tunnelID, msg); m_OutboundTunnel->SendTunnelDataMsg (lease.tunnelGateway, lease.tunnelID, msg);
LogPrint ("FIN sent"); LogPrint ("FIN sent");
} }
else else
DeleteI2NPMessage (msg); DeleteI2NPMessage (msg);

View file

@ -84,6 +84,7 @@ namespace stream
void SendQuickAck (); void SendQuickAck ();
void SavePacket (Packet * packet); void SavePacket (Packet * packet);
void ProcessPacket (Packet * packet);
private: private:

View file

@ -25,6 +25,7 @@ namespace i2p
{ {
m_IsRunning = true; m_IsRunning = true;
m_Thread = new std::thread (std::bind (&Transports::Run, this)); m_Thread = new std::thread (std::bind (&Transports::Run, this));
m_Timer = new boost::asio::deadline_timer (m_Service);
// create acceptors // create acceptors
auto addresses = context.GetRouterInfo ().GetAddresses (); auto addresses = context.GetRouterInfo ().GetAddresses ();
for (auto& address : addresses) for (auto& address : addresses)
@ -38,9 +39,11 @@ namespace i2p
auto conn = new i2p::ntcp::NTCPServerConnection (m_Service); auto conn = new i2p::ntcp::NTCPServerConnection (m_Service);
m_NTCPAcceptor->async_accept(conn->GetSocket (), boost::bind (&Transports::HandleAccept, this, m_NTCPAcceptor->async_accept(conn->GetSocket (), boost::bind (&Transports::HandleAccept, this,
conn, boost::asio::placeholders::error)); conn, boost::asio::placeholders::error));
} // temporary always run SSU server
// TODO: uncomment following lines later
/*}
else if (address.transportStyle == RouterInfo::eTransportSSU) else if (address.transportStyle == RouterInfo::eTransportSSU)
{ {*/
if (!m_SSUServer) if (!m_SSUServer)
{ {
m_SSUServer = new i2p::ssu::SSUServer (m_Service, address.port); m_SSUServer = new i2p::ssu::SSUServer (m_Service, address.port);
@ -51,6 +54,9 @@ namespace i2p
LogPrint ("SSU server already exists"); LogPrint ("SSU server already exists");
} }
} }
// TODO: do it for SSU only
DetectExternalIP ();
} }
void Transports::Stop () void Transports::Stop ()
@ -59,7 +65,10 @@ namespace i2p
delete session.second; delete session.second;
m_NTCPSessions.clear (); m_NTCPSessions.clear ();
delete m_NTCPAcceptor; delete m_NTCPAcceptor;
m_Timer->cancel ();
delete m_Timer;
if (m_SSUServer) if (m_SSUServer)
{ {
m_SSUServer->Stop (); m_SSUServer->Stop ();
@ -173,4 +182,30 @@ namespace i2p
else else
LogPrint ("Session not found"); LogPrint ("Session not found");
} }
void Transports::DetectExternalIP ()
{
for (int i = 0; i < 5; i ++)
{
auto router = i2p::data::netdb.GetRandomRouter ();
if (router && router->IsSSU () && m_SSUServer)
m_SSUServer->GetSession (const_cast<i2p::data::RouterInfo *>(router)); //TODO
}
if (m_Timer)
{
m_Timer->expires_from_now (boost::posix_time::seconds(5)); // 5 seconds
m_Timer->async_wait (boost::bind (&Transports::HandleTimer, this, boost::asio::placeholders::error));
}
}
void Transports::HandleTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
// end of external IP detection
if (m_SSUServer)
m_SSUServer->DeleteAllSessions ();
}
}
} }

View file

@ -38,6 +38,9 @@ namespace i2p
void Run (); void Run ();
void HandleAccept (i2p::ntcp::NTCPServerConnection * conn, const boost::system::error_code& error); void HandleAccept (i2p::ntcp::NTCPServerConnection * conn, const boost::system::error_code& error);
void PostMessage (const i2p::data::IdentHash& ident, i2p::I2NPMessage * msg); void PostMessage (const i2p::data::IdentHash& ident, i2p::I2NPMessage * msg);
void DetectExternalIP ();
void HandleTimer (const boost::system::error_code& ecode);
private: private:
@ -49,6 +52,7 @@ namespace i2p
std::map<i2p::data::IdentHash, i2p::ntcp::NTCPSession *> m_NTCPSessions; std::map<i2p::data::IdentHash, i2p::ntcp::NTCPSession *> m_NTCPSessions;
i2p::ssu::SSUServer * m_SSUServer; i2p::ssu::SSUServer * m_SSUServer;
boost::asio::deadline_timer * m_Timer;
public: public:

View file

@ -22,6 +22,7 @@
<ClCompile Include="..\Log.cpp" /> <ClCompile Include="..\Log.cpp" />
<ClCompile Include="..\NetDb.cpp" /> <ClCompile Include="..\NetDb.cpp" />
<ClCompile Include="..\NTCPSession.cpp" /> <ClCompile Include="..\NTCPSession.cpp" />
<ClCompile Include="..\Reseed.cpp" />
<ClCompile Include="..\RouterContext.cpp" /> <ClCompile Include="..\RouterContext.cpp" />
<ClCompile Include="..\RouterInfo.cpp" /> <ClCompile Include="..\RouterInfo.cpp" />
<ClCompile Include="..\SSU.cpp" /> <ClCompile Include="..\SSU.cpp" />
@ -31,6 +32,7 @@
<ClCompile Include="..\Tunnel.cpp" /> <ClCompile Include="..\Tunnel.cpp" />
<ClCompile Include="..\TunnelEndpoint.cpp" /> <ClCompile Include="..\TunnelEndpoint.cpp" />
<ClCompile Include="..\TunnelGateway.cpp" /> <ClCompile Include="..\TunnelGateway.cpp" />
<ClCompile Include="..\util.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\base64.h" /> <ClInclude Include="..\base64.h" />
@ -47,6 +49,7 @@
<ClInclude Include="..\NetDb.h" /> <ClInclude Include="..\NetDb.h" />
<ClInclude Include="..\NTCPSession.h" /> <ClInclude Include="..\NTCPSession.h" />
<ClInclude Include="..\Queue.h" /> <ClInclude Include="..\Queue.h" />
<ClInclude Include="..\Reseed.h" />
<ClInclude Include="..\RouterContext.h" /> <ClInclude Include="..\RouterContext.h" />
<ClInclude Include="..\RouterInfo.h" /> <ClInclude Include="..\RouterInfo.h" />
<ClInclude Include="..\SSU.h" /> <ClInclude Include="..\SSU.h" />
@ -59,6 +62,7 @@
<ClInclude Include="..\TunnelConfig.h" /> <ClInclude Include="..\TunnelConfig.h" />
<ClInclude Include="..\TunnelEndpoint.h" /> <ClInclude Include="..\TunnelEndpoint.h" />
<ClInclude Include="..\TunnelGateway.h" /> <ClInclude Include="..\TunnelGateway.h" />
<ClInclude Include="..\util.h" />
</ItemGroup> </ItemGroup>
<PropertyGroup Label="Globals"> <PropertyGroup Label="Globals">
<ProjectGuid>{930568EC-31C9-406A-AD1C-9636DF5D8FAA}</ProjectGuid> <ProjectGuid>{930568EC-31C9-406A-AD1C-9636DF5D8FAA}</ProjectGuid>

View file

@ -75,6 +75,12 @@
<ClCompile Include="..\SSU.cpp"> <ClCompile Include="..\SSU.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\util.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\Reseed.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\Identity.h"> <ClInclude Include="..\Identity.h">
@ -155,5 +161,11 @@
<ClInclude Include="..\SSU.h"> <ClInclude Include="..\SSU.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\util.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\Reseed.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
</Project> </Project>

5
hmac.h
View file

@ -18,7 +18,6 @@ namespace crypto
// digest is 16 bytes // digest is 16 bytes
// block size is 64 bytes // block size is 64 bytes
{ {
size_t totalLen = len + 64 + 32;
uint8_t buf[2048]; uint8_t buf[2048];
// ikeypad // ikeypad
((uint64_t *)buf)[0] = ((uint64_t *)key)[0] ^ IPAD; ((uint64_t *)buf)[0] = ((uint64_t *)key)[0] ^ IPAD;
@ -47,10 +46,10 @@ namespace crypto
// copy first hash after okeypad // copy first hash after okeypad
memcpy (buf + 64, hash, 16); memcpy (buf + 64, hash, 16);
// fill next 16 bytes with zeros (first hash size assumed 32 bytes in I2P) // 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 // calculate digest
CryptoPP::Weak1::MD5().CalculateDigest (digest, buf, totalLen); CryptoPP::Weak1::MD5().CalculateDigest (digest, buf, 96);
} }
} }
} }

161
i2p.cpp
View file

@ -1,6 +1,18 @@
#ifdef _WIN32
#define _CRT_SECURE_NO_WARNINGS // to use freopen
#endif
#include <iostream> #include <iostream>
#include <thread> #include <thread>
#include <cryptopp/integer.h> #include <cryptopp/integer.h>
#include <boost/filesystem.hpp>
#ifndef _WIN32
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#endif
#include "Log.h" #include "Log.h"
#include "base64.h" #include "base64.h"
#include "Transports.h" #include "Transports.h"
@ -12,9 +24,43 @@
#include "HTTPServer.h" #include "HTTPServer.h"
#include "util.h" #include "util.h"
// Global
int running = 1;
int isDaemon;
#ifndef _WIN32
void handle_signal(int sig)
{
switch (sig)
{
case SIGHUP:
if (i2p::util::config::GetArg("daemon", 0) == 1)
{
static bool first=true;
if (first)
{
first=false;
return;
}
}
LogPrint("Reloading config.");
i2p::util::filesystem::ReadConfigFile(i2p::util::config::mapArgs, i2p::util::config::mapMultiArgs);
break;
case SIGABRT:
case SIGTERM:
case SIGINT:
running = 0; // Exit loop
break;
}
}
#endif
int main( int argc, char* argv[] ) int main( int argc, char* argv[] )
{ {
i2p::util::ParseArguments(argc,argv); i2p::util::config::OptionParser(argc,argv);
isDaemon = i2p::util::config::GetArg("-daemon", 0);
#ifdef _WIN32 #ifdef _WIN32
setlocale(LC_CTYPE, ""); setlocale(LC_CTYPE, "");
SetConsoleCP(1251); SetConsoleCP(1251);
@ -22,19 +68,110 @@ int main( int argc, char* argv[] )
setlocale(LC_ALL, "Russian"); setlocale(LC_ALL, "Russian");
#endif #endif
int httpport = i2p::util::GetIntArg("--httpport", 7070);
i2p::util::HTTPServer httpServer (httpport); 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);
httpServer.Start (); int isLogging = i2p::util::config::GetArg("-log", 0);
if (isLogging == 1)
{
std::string logfile = i2p::util::filesystem::GetDataDir().string();
#ifndef _WIN32
logfile.append("/debug.log");
#else
logfile.append("\\debug.log");
#endif
freopen(logfile.c_str(),"a",stdout);
LogPrint("Logging to file enabled.");
}
#ifndef _WIN32
if (isDaemon == 1)
{
pid_t pid;
pid = fork();
if (pid > 0)
{
g_Log.Stop();
return 0;
}
if (pid < 0)
{
return -1;
}
umask(0);
int sid = setsid();
if (sid < 0)
{
LogPrint("Error, could not create process group.");
return -1;
}
chdir(i2p::util::filesystem::GetDataDir().string().c_str());
}
// Pidfile
std::string pidfile = i2p::util::filesystem::GetDataDir().string();
pidfile.append("/i2pd.pid");
int pidFilehandle = open(pidfile.c_str(), O_RDWR|O_CREAT, 0600);
if (pidFilehandle == -1 )
{
LogPrint("Error, could not create pid file (", pidfile, ")\nIs an instance already running?");
return -1;
}
if (lockf(pidFilehandle,F_TLOCK,0) == -1)
{
LogPrint("Error, could not lock pid file (", pidfile, ")\nIs an instance already running?");
return -1;
}
char pid[10];
sprintf(pid,"%d\n",getpid());
write(pidFilehandle, pid, strlen(pid));
// Signal handler
struct sigaction sa;
sa.sa_handler = handle_signal;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sigaction(SIGHUP,&sa,0);
sigaction(SIGABRT,&sa,0);
sigaction(SIGTERM,&sa,0);
sigaction(SIGINT,&sa,0);
#endif
//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));
i2p::util::HTTPServer httpServer (i2p::util::config::GetArg("-httpport", 7070));
httpServer.Start ();
i2p::data::netdb.Start (); i2p::data::netdb.Start ();
i2p::transports.Start (); i2p::transports.Start ();
i2p::tunnel::tunnels.Start (); i2p::tunnel::tunnels.Start ();
std::this_thread::sleep_for (std::chrono::seconds(10000)); while (running)
i2p::tunnel::tunnels.Stop (); {
i2p::transports.Stop (); //TODO Meeh: Find something better to do here.
i2p::data::netdb.Stop (); std::this_thread::sleep_for (std::chrono::seconds(1));
httpServer.Stop (); }
LogPrint("Shutdown started.");
i2p::tunnel::tunnels.Stop ();
i2p::transports.Stop ();
i2p::data::netdb.Stop ();
httpServer.Stop ();
if (isLogging == 1)
{
fclose (stdout);
}
#ifndef _WIN32
close(pidFilehandle);
unlink(pidfile.c_str());
#endif
return 0; return 0;
} }

View file

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

274
util.cpp
View file

@ -1,45 +1,269 @@
#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 <boost/algorithm/string.hpp>
#include "util.h" #include "util.h"
#include "Log.h"
#ifdef WIN32
#include <Windows.h>
#include <shlobj.h>
#endif
namespace i2p namespace i2p
{ {
namespace util namespace util
{ {
std::map<std::string, std::string> mapArgs;
void ParseArguments(int argc, const char* const argv[]) namespace config
{ {
mapArgs.clear(); std::map<std::string, std::string> mapArgs;
for (int i = 1; i < argc; i++) std::map<std::string, std::vector<std::string> > mapMultiArgs;
{
std::string strKey (argv[i]);
std::string strValue;
size_t has_data = strKey.find('=');
if (has_data != std::string::npos)
{
strValue = strKey.substr(has_data+1);
strKey = strKey.substr(0, has_data);
}
if (strKey[0] != '-')
break;
mapArgs[strKey] = strValue; 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;
}
} }
int GetIntArg(const std::string& strArg, int nDefault) namespace filesystem
{ {
if (mapArgs.count(strArg)) const boost::filesystem::path &GetDataDir()
return atoi(mapArgs[strArg].c_str()); {
return nDefault; 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
char localAppData[MAX_PATH];
SHGetFolderPath(NULL, CSIDL_APPDATA, 0, NULL, localAppData);
return boost::filesystem::path(std::string(localAppData) + "\\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
}
} }
std::string GetStringArg(const std::string& strArg, std::string nDefault) namespace http
{ {
if (mapArgs.count(strArg)) std::string httpRequest(const std::string& address)
return mapArgs[strArg]; {
return nDefault; 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 } // Namespace end
} }

36
util.h
View file

@ -3,15 +3,45 @@
#include <map> #include <map>
#include <string> #include <string>
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
#define PAIRTYPE(t1, t2) std::pair<t1, t2>
namespace i2p namespace i2p
{ {
namespace util namespace util
{ {
extern std::map<std::string, std::string> mapArgs; namespace config
void ParseArguments(int argc, const char* const argv[]); {
int GetIntArg(const std::string& strArg, int nDefault); 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_;
};
}
} }
} }