Merge pull request #6 from PrivacySolutions/master

Merge pull request from orignal/master
This commit is contained in:
chertov 2014-08-07 12:55:36 +04:00
commit 2d78ad0940
78 changed files with 2888 additions and 961 deletions

View File

@ -1,28 +0,0 @@
language: cpp
compiler: gcc
cache: apt
branches:
only:
- master
before_install:
- sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test # GCC 4.7
- sudo add-apt-repository -y "deb http://archive.ubuntu.com/ubuntu/ quantal main universe" # Boost 1.50
- sudo apt-get update -qq
- sudo apt-get install -qq libboost1.50-all-dev libcrypto++9 libcrypto++-dev
script:
- make
notifications:
email:
recipients:
- meeh@sigterm.no
on_success: change
on_failure: always
irc:
channels:
- "irc.freenode.net#i2p-dev"
template:
- "%{repository}/%{branch} (%{commit} - %{author}): %{message}"
on_failure: always
on_success: change

View File

@ -15,6 +15,7 @@
#include "Streaming.h"
#include "HTTPServer.h"
#include "HTTPProxy.h"
#include "SOCKS.h"
namespace i2p
{
@ -23,14 +24,16 @@ namespace i2p
class Daemon_Singleton::Daemon_Singleton_Private
{
public:
Daemon_Singleton_Private() : httpServer(nullptr), httpProxy(nullptr) { };
Daemon_Singleton_Private() : httpServer(nullptr), httpProxy(nullptr), socksProxy(nullptr) { };
~Daemon_Singleton_Private() {
delete httpServer;
delete httpProxy;
delete socksProxy;
};
i2p::util::HTTPServer *httpServer;
i2p::proxy::HTTPProxy *httpProxy;
i2p::proxy::SOCKSProxy *socksProxy;
};
Daemon_Singleton::Daemon_Singleton() : running(1), d(*new Daemon_Singleton_Private()) {};
@ -55,7 +58,19 @@ namespace i2p
i2p::context.OverrideNTCPAddress(i2p::util::config::GetCharArg("-host", "127.0.0.1"),
i2p::util::config::GetArg("-port", 17007));
if (isLogging == 1)
LogPrint("CMD parameters:");
for (int i = 0; i < argc; ++i)
LogPrint(i, " ", argv[i]);
return true;
}
bool Daemon_Singleton::start()
{
// initialize log
if (isLogging)
{
if (isDaemon)
{
std::string logfile_path = i2p::util::filesystem::GetDataDir().string();
#ifndef _WIN32
@ -63,18 +78,12 @@ namespace i2p
#else
logfile_path.append("\\debug.log");
#endif
g_Log.SetLogFile (logfile_path);
LogPrint("CMD parameters:");
for (int i = 0; i < argc; ++i)
LogPrint(i, " ", argv[i]);
StartLog (logfile_path);
}
return true;
else
StartLog (""); // write to stdout
}
bool Daemon_Singleton::start()
{
d.httpServer = new i2p::util::HTTPServer(i2p::util::config::GetArg("-httpport", 7070));
d.httpServer->Start();
LogPrint("HTTP Server started");
@ -92,8 +101,10 @@ namespace i2p
d.httpProxy = new i2p::proxy::HTTPProxy(i2p::util::config::GetArg("-httpproxyport", 4446));
d.httpProxy->Start();
LogPrint("Proxy started");
LogPrint("HTTP Proxy started");
d.socksProxy = new i2p::proxy::SOCKSProxy(i2p::util::config::GetArg("-socksproxyport", 4447));
d.socksProxy->Start();
LogPrint("SOCKS Proxy Started");
return true;
}
@ -103,6 +114,8 @@ namespace i2p
d.httpProxy->Stop();
LogPrint("HTTP Proxy stoped");
d.socksProxy->Stop();
LogPrint("SOCKS Proxy stoped");
i2p::stream::StopStreaming();
LogPrint("Streaming stoped");
i2p::garlic::routing.Stop();
@ -115,15 +128,11 @@ namespace i2p
LogPrint("NetDB stoped");
d.httpServer->Stop();
LogPrint("HTTP Server stoped");
StopLog ();
delete d.socksProxy; d.socksProxy = nullptr;
delete d.httpProxy; d.httpProxy = nullptr;
delete d.httpServer; d.httpServer = nullptr;
if (isLogging == 1)
{
fclose(stdout);
}
return true;
}
}

View File

@ -48,24 +48,29 @@ namespace i2p
{
pid_t pid;
pid = fork();
if (pid > 0)
{
g_Log.Stop();
return 0;
}
if (pid < 0)
{
return -1;
}
if (pid > 0) // parent
::exit (EXIT_SUCCESS);
if (pid < 0) // error
return false;
// child
umask(0);
int sid = setsid();
if (sid < 0)
{
LogPrint("Error, could not create process group.");
return -1;
return false;
}
chdir(i2p::util::filesystem::GetDataDir().string().c_str());
// close stdin/stdout/stderr descriptors
::close (0);
::open ("/dev/null", O_RDWR);
::close (1);
::open ("/dev/null", O_RDWR);
::close (2);
::open ("/dev/null", O_RDWR);
}
// Pidfile
@ -75,12 +80,12 @@ namespace i2p
if (pidFilehandle == -1)
{
LogPrint("Error, could not create pid file (", pidfile, ")\nIs an instance already running?");
return -1;
return false;
}
if (lockf(pidFilehandle, F_TLOCK, 0) == -1)
{
LogPrint("Error, could not lock pid file (", pidfile, ")\nIs an instance already running?");
return -1;
return false;
}
char pid[10];
sprintf(pid, "%d\n", getpid());
@ -101,12 +106,10 @@ namespace i2p
bool DaemonLinux::stop()
{
Daemon_Singleton::stop();
close(pidFilehandle);
unlink(pidfile.c_str());
return true;
return Daemon_Singleton::stop();
}
}

View File

@ -14,7 +14,7 @@ namespace i2p
{
namespace garlic
{
GarlicRoutingSession::GarlicRoutingSession (const i2p::data::RoutingDestination& destination, int numTags):
GarlicRoutingSession::GarlicRoutingSession (const i2p::data::RoutingDestination * destination, int numTags):
m_Destination (destination), m_FirstMsgID (0), m_IsAcknowledged (false),
m_NumTags (numTags), m_NextTag (-1), m_SessionTags (0), m_TagsCreationTime (0)
{
@ -23,13 +23,23 @@ namespace garlic
m_Encryption.SetKey (m_SessionKey);
if (m_NumTags > 0)
{
m_SessionTags = new uint8_t[m_NumTags*32];
m_SessionTags = new SessionTag[m_NumTags];
GenerateSessionTags ();
}
else
m_SessionTags = nullptr;
}
GarlicRoutingSession::GarlicRoutingSession (const uint8_t * sessionKey, const SessionTag& sessionTag):
m_Destination (nullptr), m_FirstMsgID (0), m_IsAcknowledged (true), m_NumTags (1), m_NextTag (0)
{
memcpy (m_SessionKey, sessionKey, 32);
m_Encryption.SetKey (m_SessionKey);
m_SessionTags = new SessionTag[1]; // 1 tag
m_SessionTags[0] = sessionTag;
m_TagsCreationTime = i2p::util::GetSecondsSinceEpoch ();
}
GarlicRoutingSession::~GarlicRoutingSession ()
{
delete[] m_SessionTags;
@ -40,13 +50,13 @@ namespace garlic
if (m_SessionTags)
{
for (int i = 0; i < m_NumTags; i++)
m_Rnd.GenerateBlock (m_SessionTags + i*32, 32);
m_Rnd.GenerateBlock (m_SessionTags[i], 32);
m_TagsCreationTime = i2p::util::GetSecondsSinceEpoch ();
SetAcknowledged (false);
}
}
I2NPMessage * GarlicRoutingSession::WrapSingleMessage (I2NPMessage * msg, const I2NPMessage * leaseSet)
I2NPMessage * GarlicRoutingSession::WrapSingleMessage (I2NPMessage * msg, I2NPMessage * leaseSet)
{
I2NPMessage * m = NewI2NPMessage ();
size_t len = 0;
@ -71,13 +81,18 @@ namespace garlic
// create message
if (m_NextTag < 0 || !m_NumTags) // new session
{
if (!m_Destination)
{
LogPrint ("Can't use ElGamal for unknown destination");
return nullptr;
}
// create ElGamal block
ElGamalBlock elGamal;
memcpy (elGamal.sessionKey, m_SessionKey, 32);
m_Rnd.GenerateBlock (elGamal.preIV, 32); // Pre-IV
uint8_t iv[32]; // IV is first 16 bytes
CryptoPP::SHA256().CalculateDigest(iv, elGamal.preIV, 32);
m_Destination.GetElGamalEncryption ()->Encrypt ((uint8_t *)&elGamal, sizeof(elGamal), buf, true);
m_Destination->GetElGamalEncryption ()->Encrypt ((uint8_t *)&elGamal, sizeof(elGamal), buf, true);
m_Encryption.SetIV (iv);
buf += 514;
len += 514;
@ -85,9 +100,9 @@ namespace garlic
else // existing session
{
// session tag
memcpy (buf, m_SessionTags + m_NextTag*32, 32);
memcpy (buf, m_SessionTags[m_NextTag], 32);
uint8_t iv[32]; // IV is first 16 bytes
CryptoPP::SHA256().CalculateDigest(iv, m_SessionTags + m_NextTag*32, 32);
CryptoPP::SHA256().CalculateDigest(iv, m_SessionTags[m_NextTag], 32);
m_Encryption.SetIV (iv);
buf += 32;
len += 32;
@ -107,6 +122,8 @@ namespace garlic
FillI2NPMessageHeader (m, eI2NPGarlic);
if (msg)
DeleteI2NPMessage (msg);
if (leaseSet)
DeleteI2NPMessage (leaseSet);
return m;
}
@ -117,8 +134,11 @@ namespace garlic
blockSize += 2;
if (m_NextTag < 0) // session tags recreated
{
memcpy (buf + blockSize, m_SessionTags, m_NumTags*32); // tags
blockSize += m_NumTags*32;
for (int i = 0; i < m_NumTags; i++)
{
memcpy (buf + blockSize, m_SessionTags[i], 32); // tags
blockSize += 32;
}
}
uint32_t * payloadSize = (uint32_t *)(buf + blockSize);
blockSize += 4;
@ -161,7 +181,7 @@ namespace garlic
}
if (msg) // clove message ifself if presented
{
size += CreateGarlicClove (payload + size, msg, m_Destination.IsDestination ());
size += CreateGarlicClove (payload + size, msg, m_Destination ? m_Destination->IsDestination () : false);
(*numCloves)++;
}
@ -178,11 +198,11 @@ namespace garlic
{
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 5000; // 5 sec
size_t size = 0;
if (isDestination)
if (isDestination && m_Destination)
{
buf[size] = eGarlicDeliveryTypeDestination << 5;// delivery instructions flag destination
size++;
memcpy (buf + size, m_Destination.GetIdentHash (), 32);
memcpy (buf + size, m_Destination->GetIdentHash (), 32);
size += 32;
}
else
@ -249,9 +269,16 @@ namespace garlic
for (auto it: m_Sessions)
delete it.second;
m_Sessions.clear ();
for (auto it: m_SessionDecryptions)
delete it;
m_SessionDecryptions.clear ();
// TODO: delete remaining session decryptions
m_SessionTags.clear ();
}
void GarlicRouting::AddSessionKey (const uint8_t * key, const uint8_t * tag)
{
SessionDecryption * decryption = new SessionDecryption;
decryption->SetKey (key);
decryption->SetTagCount (1);
m_SessionTags[SessionTag(tag)] = decryption;
}
I2NPMessage * GarlicRouting::WrapSingleMessage (const i2p::data::RoutingDestination& destination, I2NPMessage * msg)
@ -262,14 +289,14 @@ namespace garlic
delete it->second;
m_Sessions.erase (it);
}
GarlicRoutingSession * session = new GarlicRoutingSession (destination, 0); // not follow-on messages expected
GarlicRoutingSession * session = new GarlicRoutingSession (&destination, 0); // not follow-on messages expected
m_Sessions[destination.GetIdentHash ()] = session;
return session->WrapSingleMessage (msg, nullptr);
}
I2NPMessage * GarlicRouting::WrapMessage (const i2p::data::RoutingDestination& destination,
I2NPMessage * msg, const I2NPMessage * leaseSet)
I2NPMessage * msg, I2NPMessage * leaseSet)
{
auto it = m_Sessions.find (destination.GetIdentHash ());
GarlicRoutingSession * session = nullptr;
@ -277,7 +304,7 @@ namespace garlic
session = it->second;
if (!session)
{
session = new GarlicRoutingSession (destination, 32);
session = new GarlicRoutingSession (&destination, 32);
m_Sessions[destination.GetIdentHash ()] = session;
}
@ -297,8 +324,7 @@ namespace garlic
uint8_t * buf = msg->GetPayload ();
uint32_t length = be32toh (*(uint32_t *)buf);
buf += 4;
std::string sessionTag((const char *)buf, 32);
auto it = m_SessionTags.find (sessionTag);
auto it = m_SessionTags.find (SessionTag(buf));
if (it != m_SessionTags.end ())
{
// existing session
@ -306,7 +332,9 @@ namespace garlic
CryptoPP::SHA256().CalculateDigest(iv, buf, 32);
it->second->SetIV (iv);
it->second->Decrypt (buf + 32, length - 32, buf + 32);
it->second->UseTag ();
HandleAESBlock (buf + 32, length - 32, it->second);
if (!it->second->GetTagCount ()) delete it->second; // all tags were used
m_SessionTags.erase (it); // tag might be used only once
}
else
@ -320,8 +348,7 @@ namespace garlic
pool ? pool->GetEncryptionPrivateKey () : i2p::context.GetPrivateKey (),
buf, (uint8_t *)&elGamal, true))
{
i2p::crypto::CBCDecryption * decryption = new i2p::crypto::CBCDecryption;
m_SessionDecryptions.push_back (decryption);
SessionDecryption * decryption = new SessionDecryption;
decryption->SetKey (elGamal.sessionKey);
uint8_t iv[32]; // IV is first 16 bytes
CryptoPP::SHA256().CalculateDigest(iv, elGamal.preIV, 32);
@ -335,12 +362,16 @@ namespace garlic
DeleteI2NPMessage (msg);
}
void GarlicRouting::HandleAESBlock (uint8_t * buf, size_t len, i2p::crypto::CBCDecryption * decryption)
void GarlicRouting::HandleAESBlock (uint8_t * buf, size_t len, SessionDecryption * decryption)
{
uint16_t tagCount = be16toh (*(uint16_t *)buf);
buf += 2;
if (tagCount > 0)
{
decryption->AddTagCount (tagCount);
for (int i = 0; i < tagCount; i++)
m_SessionTags[std::string ((const char *)(buf + i*32), 32)] = decryption;
m_SessionTags[SessionTag(buf + i*32)] = decryption;
}
buf += tagCount*32;
uint32_t payloadSize = be32toh (*(uint32_t *)buf);
if (payloadSize > len)
@ -387,7 +418,7 @@ namespace garlic
{
case eGarlicDeliveryTypeLocal:
LogPrint ("Garlic type local");
i2p::HandleI2NPMessage (buf, len);
i2p::HandleI2NPMessage (CreateI2NPMessage (buf, len));
break;
case eGarlicDeliveryTypeDestination:
{

View File

@ -36,13 +36,16 @@ namespace garlic
#pragma pack()
const int TAGS_EXPIRATION_TIMEOUT = 900; // 15 minutes
typedef i2p::data::Tag<32> SessionTag;
class GarlicRoutingSession
{
public:
GarlicRoutingSession (const i2p::data::RoutingDestination& destination, int numTags);
GarlicRoutingSession (const i2p::data::RoutingDestination * destination, int numTags);
GarlicRoutingSession (const uint8_t * sessionKey, const SessionTag& sessionTag); // one time encryption
~GarlicRoutingSession ();
I2NPMessage * WrapSingleMessage (I2NPMessage * msg, const I2NPMessage * leaseSet);
I2NPMessage * WrapSingleMessage (I2NPMessage * msg, I2NPMessage * leaseSet);
int GetNextTag () const { return m_NextTag; };
uint32_t GetFirstMsgID () const { return m_FirstMsgID; };
@ -60,12 +63,12 @@ namespace garlic
private:
const i2p::data::RoutingDestination& m_Destination;
const i2p::data::RoutingDestination * m_Destination;
uint8_t m_SessionKey[32];
uint32_t m_FirstMsgID; // first message ID
bool m_IsAcknowledged;
int m_NumTags, m_NextTag;
uint8_t * m_SessionTags; // m_NumTags*32 bytes
SessionTag * m_SessionTags; // m_NumTags*32 bytes
uint32_t m_TagsCreationTime; // seconds since epoch
i2p::crypto::CBCEncryption m_Encryption;
@ -74,6 +77,21 @@ namespace garlic
class GarlicRouting
{
class SessionDecryption: public i2p::crypto::CBCDecryption
{
public:
SessionDecryption (): m_TagCount (0) {};
void SetTagCount (int tagCount) { m_TagCount = tagCount; };
void AddTagCount (int tagCount) { m_TagCount += tagCount; };
int GetTagCount () const { return m_TagCount; };
bool UseTag () { m_TagCount--; return m_TagCount > 0; };
private:
int m_TagCount;
};
public:
GarlicRouting ();
@ -81,19 +99,20 @@ namespace garlic
void Start ();
void Stop ();
void AddSessionKey (const uint8_t * key, const uint8_t * tag); // one tag
void HandleGarlicMessage (I2NPMessage * msg);
void HandleDeliveryStatusMessage (uint8_t * buf, size_t len);
I2NPMessage * WrapSingleMessage (const i2p::data::RoutingDestination& destination, I2NPMessage * msg);
I2NPMessage * WrapMessage (const i2p::data::RoutingDestination& destination,
I2NPMessage * msg, const I2NPMessage * leaseSet = nullptr);
I2NPMessage * msg, I2NPMessage * leaseSet = nullptr);
private:
void Run ();
void ProcessGarlicMessage (I2NPMessage * msg);
void HandleAESBlock (uint8_t * buf, size_t len, i2p::crypto::CBCDecryption * decryption);
void HandleAESBlock (uint8_t * buf, size_t len, SessionDecryption * decryption);
void HandleGarlicPayload (uint8_t * buf, size_t len);
private:
@ -105,8 +124,8 @@ namespace garlic
std::map<i2p::data::IdentHash, GarlicRoutingSession *> m_Sessions;
std::map<uint32_t, GarlicRoutingSession *> m_CreatedSessions; // msgID -> session
// incoming session
std::list<i2p::crypto::CBCDecryption *> m_SessionDecryptions; // multiple tags refer to one decyption
std::map<std::string, i2p::crypto::CBCDecryption *> m_SessionTags; // tag -> decryption
// multiple tags refer to one decyption
std::map<SessionTag, SessionDecryption *> m_SessionTags; // tag -> decryption
};
extern GarlicRouting routing;

View File

@ -303,6 +303,7 @@ namespace util
s << it.second->GetRemoteRouterInfo ().GetIdentHashAbbreviation () << ": "
<< it.second->GetSocket ().remote_endpoint().address ().to_string ();
if (!outgoing) s << "-->";
s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
s << "<BR>";
}
}
@ -318,14 +319,20 @@ namespace util
if (outgoing) s << "-->";
s << endpoint.address ().to_string () << ":" << endpoint.port ();
if (!outgoing) s << "-->";
s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
s << "<BR>";
}
}
s << "<p><a href=\"zmw2cyw2vj7f6obx3msmdvdepdhnw2ctc4okza2zjxlukkdfckhq\">Flibusta</a></p>";
}
void HTTPConnection::HandleDestinationRequest (const std::string& address, const std::string& uri)
{
HandleDestinationRequest(address, "GET", "", uri);
}
void HTTPConnection::HandleDestinationRequest (const std::string& address, const std::string& method, const std::string& data, const std::string& uri)
{
const i2p::data::LeaseSet * leaseSet = nullptr;
i2p::data::IdentHash destination;
std::string fullAddress;
if (address.find(".b32.i2p") != std::string::npos)
@ -353,6 +360,16 @@ namespace util
fullAddress = address;
}
else
{
if (address == "local")
{
// TODO: remove later
fullAddress = "local.i2p";
auto destination = i2p::stream::GetSharedLocalDestination ();
leaseSet = destination->GetLeaseSet ();
EepAccept (destination);
}
else
{
if (i2p::data::Base32ToByteStream(address.c_str(), address.length(), (uint8_t *)destination, 32) != 32)
{
@ -363,8 +380,10 @@ namespace util
fullAddress = address + ".b32.i2p";
}
}
}
auto leaseSet = i2p::data::netdb.FindLeaseSet (destination);
if (!leaseSet)
leaseSet = i2p::data::netdb.FindLeaseSet (destination);
if (!leaseSet || !leaseSet->HasNonExpiredLeases ())
{
i2p::data::netdb.Subscribe(destination);
@ -380,7 +399,13 @@ namespace util
m_Stream = i2p::stream::CreateStream (*leaseSet);
if (m_Stream)
{
std::string request = "GET " + uri + " HTTP/1.1\n Host:" + fullAddress + "\n";
std::string request = method+" " + uri + " HTTP/1.1\n Host:" + fullAddress + "\r\n";
if (!strcmp(method.c_str(), "GET"))
{
// POST/PUT, apply body
request += "\r\n"+ data;
}
LogPrint("HTTP Client Request: ", request);
m_Stream->Send ((uint8_t *)request.c_str (), request.length (), 10);
AsyncStreamReceive ();
}
@ -404,7 +429,7 @@ namespace util
}
else
{
if (m_Stream && m_Stream->IsOpen ())
if (ecode == boost::asio::error::timed_out)
SendReply ("<html>" + itoopieImage + "<br>Not responding</html>", 504);
else
Terminate ();
@ -483,6 +508,48 @@ namespace util
new HTTPConnection (m_NewSocket);
}
// eepSite. TODO: move away
void HTTPConnection::EepAccept (i2p::stream::StreamingDestination * destination)
{
if (destination)
destination->SetAcceptor (std::bind (&HTTPConnection::HandleEepAccept, this, std::placeholders::_1));
}
void HTTPConnection::HandleEepAccept (i2p::stream::Stream * stream)
{
if (stream)
{
auto conn = new EepSiteDummyConnection (stream);
conn->AsyncStreamReceive ();
}
}
void EepSiteDummyConnection::AsyncStreamReceive ()
{
if (m_Stream)
m_Stream->AsyncReceive (boost::asio::buffer (m_StreamBuffer, 8192),
boost::bind (&EepSiteDummyConnection::HandleStreamReceive, this,
boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred),
60); // 60 seconds timeout
}
void EepSiteDummyConnection::HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
if (ecode)
{
LogPrint ("eepSite error: ", ecode.message ());
DeleteStream (m_Stream);
}
else
{
std::string response ("HTTP/1.0 503 Not Implemented\r\n");
m_Stream->Send ((uint8_t *)response.c_str (), response.length (), 30);
m_Stream->Close ();
}
delete this;
}
}
}

View File

@ -59,6 +59,10 @@ namespace util
void FillContent (std::stringstream& s);
std::string ExtractAddress ();
// for eepsite
void EepAccept (i2p::stream::StreamingDestination * destination);
void HandleEepAccept (i2p::stream::Stream * stream);
protected:
boost::asio::ip::tcp::socket * m_Socket;
@ -69,7 +73,9 @@ namespace util
protected:
virtual void HandleDestinationRequest(const std::string& address, const std::string& uri);
virtual void HandleDestinationRequest(const std::string& address, const std::string& method, const std::string& data, const std::string& uri);
virtual void RunRequest ();
private:
@ -104,6 +110,24 @@ namespace util
protected:
virtual void CreateConnection(boost::asio::ip::tcp::socket * m_NewSocket);
};
// TODO: move away
class EepSiteDummyConnection
{
public:
EepSiteDummyConnection (i2p::stream::Stream * stream): m_Stream (stream) {};
void AsyncStreamReceive ();
private:
void HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred);
private:
i2p::stream::Stream * m_Stream;
char m_StreamBuffer[8192];
};
}
}

View File

@ -1,4 +1,5 @@
#include <string.h>
#include <atomic>
#include "I2PEndian.h"
#include <cryptopp/sha.h>
#include <cryptopp/gzip.h>
@ -17,11 +18,17 @@ namespace i2p
I2NPMessage * NewI2NPMessage ()
{
I2NPMessage * msg = new I2NPMessage;
msg->offset = 2; // reserve 2 bytes for NTCP header, should reserve more for SSU in future
msg->len = sizeof (I2NPHeader) + 2;
msg->from = nullptr;
return msg;
return new I2NPMessageBuffer<I2NP_MAX_MESSAGE_SIZE>();
}
I2NPMessage * NewI2NPShortMessage ()
{
return new I2NPMessageBuffer<I2NP_MAX_SHORT_MESSAGE_SIZE>();
}
I2NPMessage * NewI2NPMessage (size_t len)
{
return (len < I2NP_MAX_SHORT_MESSAGE_SIZE/2) ? NewI2NPShortMessage () : NewI2NPMessage ();
}
void DeleteI2NPMessage (I2NPMessage * msg)
@ -29,7 +36,7 @@ namespace i2p
delete msg;
}
static uint32_t I2NPmsgID = 0; // TODO: create class
static std::atomic<uint32_t> I2NPmsgID(0); // TODO: create class
void FillI2NPMessageHeader (I2NPMessage * msg, I2NPMessageType msgType, uint32_t replyMsgID)
{
I2NPHeader * header = msg->GetHeader ();
@ -62,7 +69,7 @@ namespace i2p
I2NPMessage * CreateI2NPMessage (I2NPMessageType msgType, const uint8_t * buf, int len, uint32_t replyMsgID)
{
I2NPMessage * msg = NewI2NPMessage ();
I2NPMessage * msg = NewI2NPMessage (len);
memcpy (msg->GetPayload (), buf, len);
msg->len += len;
FillI2NPMessageHeader (msg, msgType, replyMsgID);
@ -94,7 +101,8 @@ namespace i2p
}
I2NPMessage * CreateDatabaseLookupMsg (const uint8_t * key, const uint8_t * from,
uint32_t replyTunnelID, bool exploratory, std::set<i2p::data::IdentHash> * excludedPeers)
uint32_t replyTunnelID, bool exploratory, std::set<i2p::data::IdentHash> * excludedPeers,
bool encryption)
{
I2NPMessage * m = NewI2NPMessage ();
uint8_t * buf = m->GetPayload ();
@ -104,12 +112,13 @@ namespace i2p
buf += 32;
if (replyTunnelID)
{
*buf = 0x01; // set delivery flag
*buf = encryption ? 0x03: 0x01; // set delivery flag
*(uint32_t *)(buf+1) = htobe32 (replyTunnelID);
buf += 5;
}
else
{
encryption = false; // encryption can we set for tunnels only
*buf = 0; // flag
buf++;
}
@ -142,58 +151,63 @@ namespace i2p
buf += 2;
}
}
if (encryption)
{
// session key and tag for reply
auto& rnd = i2p::context.GetRandomNumberGenerator ();
rnd.GenerateBlock (buf, 32); // key
buf[32] = 1; // 1 tag
rnd.GenerateBlock (buf + 33, 32); // tag
i2p::garlic::routing.AddSessionKey (buf, buf + 33); // introduce new key-tag to garlic engine
buf += 65;
}
m->len += (buf - m->GetPayload ());
FillI2NPMessageHeader (m, eI2NPDatabaseLookup);
return m;
}
void HandleDatabaseLookupMsg (uint8_t * buf, size_t len)
I2NPMessage * CreateDatabaseSearchReply (const i2p::data::IdentHash& ident,
const i2p::data::RouterInfo * floodfill)
{
char key[48];
int l = i2p::data::ByteStreamToBase64 (buf, 32, key, 48);
key[l] = 0;
LogPrint ("DatabaseLookup for ", key, " recieved");
uint8_t flag = buf[64];
uint32_t replyTunnelID = 0;
if (flag & 0x01) //reply to yunnel
replyTunnelID = be32toh (*(uint32_t *)(buf + 64));
// TODO: implement search. We send non-found for now
I2NPMessage * replyMsg = CreateDatabaseSearchReply (buf);
if (replyTunnelID)
i2p::tunnel::tunnels.GetNextOutboundTunnel ()->SendTunnelDataMsg (buf+32, replyTunnelID, replyMsg);
else
i2p::transports.SendMessage (buf, replyMsg);
}
I2NPMessage * CreateDatabaseSearchReply (const i2p::data::IdentHash& ident)
{
I2NPMessage * m = NewI2NPMessage ();
I2NPMessage * m = NewI2NPShortMessage ();
uint8_t * buf = m->GetPayload ();
size_t len = 0;
memcpy (buf, ident, 32);
buf[32] = 0; // TODO:
memcpy (buf + 33, i2p::context.GetRouterInfo ().GetIdentHash (), 32);
m->len += 65;
len += 32;
buf[len] = floodfill ? 1 : 0; // 1 router for now
len++;
if (floodfill)
{
memcpy (buf + len, floodfill->GetIdentHash (), 32);
len += 32;
}
memcpy (buf + len, i2p::context.GetRouterInfo ().GetIdentHash (), 32);
len += 32;
m->len += len;
FillI2NPMessageHeader (m, eI2NPDatabaseSearchReply);
return m;
}
I2NPMessage * CreateDatabaseStoreMsg ()
I2NPMessage * CreateDatabaseStoreMsg (const i2p::data::RouterInfo * router)
{
I2NPMessage * m = NewI2NPMessage ();
if (!router) // we send own RouterInfo
router = &context.GetRouterInfo ();
I2NPMessage * m = NewI2NPShortMessage ();
I2NPDatabaseStoreMsg * msg = (I2NPDatabaseStoreMsg *)m->GetPayload ();
memcpy (msg->key, context.GetRouterInfo ().GetIdentHash (), 32);
memcpy (msg->key, router->GetIdentHash (), 32);
msg->type = 0;
msg->replyToken = 0;
CryptoPP::Gzip compressor;
compressor.Put ((uint8_t *)context.GetRouterInfo ().GetBuffer (), context.GetRouterInfo ().GetBufferLen ());
compressor.Put (router->GetBuffer (), router->GetBufferLen ());
compressor.MessageEnd();
// WARNING!!! MaxRetrievable() return uint64_t. Åñòü ïîäîçðåíèå, ÷òî ÷òî-òî íå òàê
int size = compressor.MaxRetrievable ();
auto size = compressor.MaxRetrievable ();
uint8_t * buf = m->GetPayload () + sizeof (I2NPDatabaseStoreMsg);
*(uint16_t *)buf = htobe16 (size); // size
buf += 2;
// TODO: check if size doesn't exceed buffer
compressor.Get (buf, size);
m->len += sizeof (I2NPDatabaseStoreMsg) + 2 + size; // payload size
FillI2NPMessageHeader (m, eI2NPDatabaseStore);
@ -201,6 +215,19 @@ namespace i2p
return m;
}
I2NPMessage * CreateDatabaseStoreMsg (const i2p::data::LeaseSet * leaseSet)
{
if (!leaseSet) return nullptr;
I2NPMessage * m = NewI2NPShortMessage ();
I2NPDatabaseStoreMsg * msg = (I2NPDatabaseStoreMsg *)m->GetPayload ();
memcpy (msg->key, leaseSet->GetIdentHash (), 32);
msg->type = 1; // LeaseSet
msg->replyToken = 0;
memcpy (m->GetPayload () + sizeof (I2NPDatabaseStoreMsg), leaseSet->GetBuffer (), leaseSet->GetBufferLen ());
m->len += leaseSet->GetBufferLen () + sizeof (I2NPDatabaseStoreMsg);
FillI2NPMessageHeader (m, eI2NPDatabaseStore);
return m;
}
I2NPBuildRequestRecordClearText CreateBuildRequestRecord (
const uint8_t * ourIdent, uint32_t receiveTunnelID,
@ -346,6 +373,7 @@ namespace i2p
else
{
LogPrint ("Outbound tunnel ", tunnel->GetTunnelID (), " has been declined");
i2p::transports.CloseSession (tunnel->GetTunnelConfig ()->GetFirstHop ()->router);
delete tunnel;
}
}
@ -375,7 +403,7 @@ namespace i2p
I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, const uint8_t * buf, size_t len)
{
I2NPMessage * msg = NewI2NPMessage ();
I2NPMessage * msg = NewI2NPMessage (len);
TunnelGatewayHeader * header = (TunnelGatewayHeader *)msg->GetPayload ();
header->tunnelID = htobe32 (tunnelID);
header->length = htobe16 (len);
@ -410,7 +438,7 @@ namespace i2p
I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessageType msgType,
const uint8_t * buf, size_t len, uint32_t replyMsgID)
{
I2NPMessage * msg = NewI2NPMessage ();
I2NPMessage * msg = NewI2NPMessage (len);
size_t gatewayMsgOffset = sizeof (I2NPHeader) + sizeof (TunnelGatewayHeader);
msg->offset += gatewayMsgOffset;
msg->len += gatewayMsgOffset;
@ -435,6 +463,13 @@ namespace i2p
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);
if (msg->GetHeader()->typeID == eI2NPDatabaseStore)
{
// transit DatabaseStore my contain new/updated RI
auto ds = NewI2NPMessage ();
*ds = *msg;
i2p::data::netdb.PostI2NPMsg (ds);
}
i2p::tunnel::TransitTunnel * tunnel = i2p::tunnel::tunnels.GetTransitTunnel (tunnelID);
if (tunnel)
tunnel->SendTunnelDataMsg (msg);
@ -477,10 +512,6 @@ namespace i2p
LogPrint ("TunnelBuildReply");
// TODO:
break;
case eI2NPDatabaseLookup:
LogPrint ("DatabaseLookup");
HandleDatabaseLookupMsg (buf, size);
break;
default:
LogPrint ("Unexpected message ", (int)header->typeID);
}
@ -505,11 +536,9 @@ namespace i2p
i2p::garlic::routing.HandleGarlicMessage (msg);
break;
case eI2NPDatabaseStore:
LogPrint ("DatabaseStore");
i2p::data::netdb.PostI2NPMsg (msg);
break;
case eI2NPDatabaseSearchReply:
LogPrint ("DatabaseSearchReply");
case eI2NPDatabaseLookup:
// forward to netDb
i2p::data::netdb.PostI2NPMsg (msg);
break;
case eI2NPDeliveryStatus:

View File

@ -6,6 +6,7 @@
#include <string.h>
#include "I2PEndian.h"
#include "RouterInfo.h"
#include "LeaseSet.h"
namespace i2p
{
@ -100,13 +101,17 @@ namespace tunnel
class InboundTunnel;
}
const int NTCP_MAX_MESSAGE_SIZE = 16384;
const size_t I2NP_MAX_MESSAGE_SIZE = 32768;
const size_t I2NP_MAX_SHORT_MESSAGE_SIZE = 2400;
struct I2NPMessage
{
uint8_t buf[NTCP_MAX_MESSAGE_SIZE];
size_t len, offset;
uint8_t * buf;
size_t len, offset, maxLen;
i2p::tunnel::InboundTunnel * from;
I2NPMessage (): buf (nullptr),len (sizeof (I2NPHeader) + 2),
offset(2), maxLen (0), from (nullptr) {};
// reserve 2 bytes for NTCP header
I2NPHeader * GetHeader () { return (I2NPHeader *)GetBuffer (); };
uint8_t * GetPayload () { return GetBuffer () + sizeof(I2NPHeader); };
uint8_t * GetBuffer () { return buf + offset; };
@ -143,7 +148,17 @@ namespace tunnel
return be32toh (header.msgID);
}
};
template<int sz>
struct I2NPMessageBuffer: public I2NPMessage
{
I2NPMessageBuffer () { buf = m_Buffer; maxLen = sz; };
uint8_t m_Buffer[sz];
};
I2NPMessage * NewI2NPMessage ();
I2NPMessage * NewI2NPShortMessage ();
I2NPMessage * NewI2NPMessage (size_t len);
void DeleteI2NPMessage (I2NPMessage * msg);
void FillI2NPMessageHeader (I2NPMessage * msg, I2NPMessageType msgType, uint32_t replyMsgID = 0);
void RenewI2NPMessageHeader (I2NPMessage * msg);
@ -153,11 +168,11 @@ namespace tunnel
I2NPMessage * CreateDeliveryStatusMsg (uint32_t msgID);
I2NPMessage * CreateDatabaseLookupMsg (const uint8_t * key, const uint8_t * from,
uint32_t replyTunnelID, bool exploratory = false,
std::set<i2p::data::IdentHash> * excludedPeers = nullptr);
void HandleDatabaseLookupMsg (uint8_t * buf, size_t len);
I2NPMessage * CreateDatabaseSearchReply (const i2p::data::IdentHash& ident);
std::set<i2p::data::IdentHash> * excludedPeers = nullptr, bool encryption = false);
I2NPMessage * CreateDatabaseSearchReply (const i2p::data::IdentHash& ident, const i2p::data::RouterInfo * floodfill);
I2NPMessage * CreateDatabaseStoreMsg ();
I2NPMessage * CreateDatabaseStoreMsg (const i2p::data::RouterInfo * router = nullptr);
I2NPMessage * CreateDatabaseStoreMsg (const i2p::data::LeaseSet * leaseSet);
I2NPBuildRequestRecordClearText CreateBuildRequestRecord (
const uint8_t * ourIdent, uint32_t receiveTunnelID,

View File

@ -16,20 +16,27 @@ namespace data
{
// copy public and signing keys together
memcpy (publicKey, keys.publicKey, sizeof (publicKey) + sizeof (signingKey));
memset (certificate, 0, sizeof (certificate));
memset (&certificate, 0, sizeof (certificate));
return *this;
}
bool Identity::FromBase64 (const std::string& s)
{
size_t count = Base64ToByteStream (s.c_str(), s.length(), publicKey, sizeof (Identity));
return count == sizeof(Identity);
size_t count = Base64ToByteStream (s.c_str(), s.length(), publicKey, DEFAULT_IDENTITY_SIZE);
return count == DEFAULT_IDENTITY_SIZE;
}
size_t Identity::FromBuffer (const uint8_t * buf, size_t len)
{
memcpy (publicKey, buf, DEFAULT_IDENTITY_SIZE);
// TODO: process certificate
return DEFAULT_IDENTITY_SIZE;
}
IdentHash Identity::Hash() const
{
IdentHash hash;
CryptoPP::SHA256().CalculateDigest(hash, publicKey, sizeof (Identity));
CryptoPP::SHA256().CalculateDigest(hash, publicKey, DEFAULT_IDENTITY_SIZE);
return hash;
}
@ -40,11 +47,6 @@ namespace data
return *this;
}
bool IdentHash::FromBase32(const std::string& s)
{
size_t count = Base32ToByteStream(s.c_str(), s.length(), m_Hash, sizeof(m_Hash));
return count == sizeof(m_Hash);
}
Keys CreateRandomKeys ()
{

View File

@ -3,13 +3,57 @@
#include <inttypes.h>
#include <string.h>
#include <string>
#include "base64.h"
#include "ElGamal.h"
namespace i2p
{
namespace data
{
class IdentHash;
template<int sz>
class Tag
{
public:
Tag (const uint8_t * buf) { memcpy (m_Buf, buf, sz); };
Tag (const Tag<sz>& ) = default;
#ifndef _WIN32 // FIXME!!! msvs 2013 can't compile it
Tag (Tag<sz>&& ) = default;
#endif
Tag () = default;
Tag<sz>& operator= (const Tag<sz>& ) = default;
#ifndef _WIN32
Tag<sz>& operator= (Tag<sz>&& ) = default;
#endif
uint8_t * operator()() { return m_Buf; };
const uint8_t * operator()() const { return m_Buf; };
operator uint8_t * () { return m_Buf; };
operator const uint8_t * () const { return m_Buf; };
bool operator== (const Tag<sz>& other) const { return !memcmp (m_Buf, other.m_Buf, sz); };
bool operator< (const Tag<sz>& other) const { return memcmp (m_Buf, other.m_Buf, sz) < 0; };
std::string ToBase64 () const
{
char str[sz*2];
int l = i2p::data::ByteStreamToBase64 (m_Buf, sz, str, sz*2);
str[l] = 0;
return std::string (str);
}
private:
union // 8 bytes alignment
{
uint8_t m_Buf[sz];
uint64_t ll[sz/8];
};
};
typedef Tag<32> IdentHash;
#pragma pack(1)
@ -27,14 +71,28 @@ namespace data
uint8_t signingKey[128];
};
const uint8_t CERTIFICATE_TYPE_NULL = 0;
const uint8_t CERTIFICATE_TYPE_HASHCASH = 1;
const uint8_t CERTIFICATE_TYPE_HIDDEN = 2;
const uint8_t CERTIFICATE_TYPE_SIGNED = 3;
const uint8_t CERTIFICATE_TYPE_MULTIPLE = 4;
const uint8_t CERTIFICATE_TYPE_KEY = 5;
const size_t DEFAULT_IDENTITY_SIZE = 387;
struct Identity
{
uint8_t publicKey[256];
uint8_t signingKey[128];
uint8_t certificate[3];
struct
{
uint8_t type;
uint16_t length;
} certificate;
Identity& operator=(const Keys& keys);
bool FromBase64(const std::string& );
size_t FromBuffer (const uint8_t * buf, size_t len);
IdentHash Hash() const;
};
@ -53,38 +111,6 @@ namespace data
#pragma pack()
class IdentHash
{
public:
IdentHash (const uint8_t * hash) { memcpy (m_Hash, hash, 32); };
IdentHash (const IdentHash& ) = default;
#ifndef _WIN32 // FIXME!!! msvs 2013 can't compile it
IdentHash (IdentHash&& ) = default;
#endif
IdentHash () = default;
IdentHash& operator= (const IdentHash& ) = default;
#ifndef _WIN32
IdentHash& operator= (IdentHash&& ) = default;
#endif
uint8_t * operator()() { return m_Hash; };
const uint8_t * operator()() const { return m_Hash; };
operator uint8_t * () { return m_Hash; };
operator const uint8_t * () const { return m_Hash; };
bool operator== (const IdentHash& other) const { return !memcmp (m_Hash, other.m_Hash, 32); };
bool operator< (const IdentHash& other) const { return memcmp (m_Hash, other.m_Hash, 32) < 0; };
bool FromBase32(const std::string&);
private:
uint8_t m_Hash[32];
};
Keys CreateRandomKeys ();
void CreateRandomDHKeysPair (DHKeysPair * keys); // for transport sessions
@ -141,9 +167,10 @@ namespace data
virtual ~LocalDestination() {};
virtual const IdentHash& GetIdentHash () const = 0;
virtual const Identity& GetIdentity () const = 0;
virtual const uint8_t * GetEncryptionPrivateKey () const = 0;
virtual const uint8_t * GetEncryptionPublicKey () const = 0;
virtual void UpdateLeaseSet () = 0; // LeaseSet must be updated
virtual void Sign (const uint8_t * buf, int len, uint8_t * signature) const = 0;
};
}
}

View File

@ -4,6 +4,7 @@
#include "Log.h"
#include "Timestamp.h"
#include "NetDb.h"
#include "TunnelPool.h"
#include "LeaseSet.h"
namespace i2p
@ -11,26 +12,64 @@ namespace i2p
namespace data
{
LeaseSet::LeaseSet (const uint8_t * buf, int len)
LeaseSet::LeaseSet (const uint8_t * buf, int len, bool unsolicited):
m_IsUnsolicited (unsolicited)
{
#pragma pack(1)
struct H
{
Identity destination;
uint8_t encryptionKey[256];
uint8_t signingKey[128];
uint8_t num;
};
#pragma pack ()
memcpy (m_Buffer, buf, len);
m_BufferLen = len;
ReadFromBuffer ();
}
const H * header = (const H *)buf;
LeaseSet::LeaseSet (const i2p::tunnel::TunnelPool& pool):
m_IsUnsolicited (false)
{
m_BufferLen = 0;
// header
const i2p::data::LocalDestination& localDestination = pool.GetLocalDestination ();
LeaseSetHeader * header = (LeaseSetHeader *)m_Buffer;
header->destination = localDestination.GetIdentity ();
memcpy (header->encryptionKey, localDestination.GetEncryptionPublicKey (), 256);
memset (header->signingKey, 0, 128);
auto tunnels = pool.GetInboundTunnels (5); // 5 tunnels maximum
header->num = tunnels.size (); // num leases
m_BufferLen += sizeof (LeaseSetHeader);
// leases
for (auto it: tunnels)
{
Lease * lease = (Lease *)(m_Buffer + m_BufferLen);
memcpy (lease->tunnelGateway, it->GetNextIdentHash (), 32);
lease->tunnelID = htobe32 (it->GetNextTunnelID ());
uint64_t ts = it->GetCreationTime () + i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT - 60; // 1 minute before expiration
ts *= 1000; // in milliseconds
lease->endDate = htobe64 (ts);
m_BufferLen += sizeof (Lease);
}
// signature
localDestination.Sign (m_Buffer, m_BufferLen, m_Buffer + m_BufferLen);
m_BufferLen += 40;
LogPrint ("Local LeaseSet of ", tunnels.size (), " leases created");
ReadFromBuffer ();
}
void LeaseSet::Update (const uint8_t * buf, int len)
{
m_Leases.clear ();
memcpy (m_Buffer, buf, len);
m_BufferLen = len;
ReadFromBuffer ();
}
void LeaseSet::ReadFromBuffer ()
{
const LeaseSetHeader * header = (const LeaseSetHeader *)m_Buffer;
m_Identity = header->destination;
m_IdentHash = m_Identity.Hash();
memcpy (m_EncryptionKey, header->encryptionKey, 256);
LogPrint ("LeaseSet num=", (int)header->num);
// process leases
const uint8_t * leases = buf + sizeof (H);
const uint8_t * leases = m_Buffer + sizeof (LeaseSetHeader);
for (int i = 0; i < header->num; i++)
{
Lease lease = *(Lease *)leases;
@ -53,7 +92,7 @@ namespace data
pubKey.Initialize (i2p::crypto::dsap, i2p::crypto::dsaq, i2p::crypto::dsag,
CryptoPP::Integer (m_Identity.signingKey, 128));
CryptoPP::DSA::Verifier verifier (pubKey);
if (!verifier.VerifyMessage (buf, leases - buf, leases, 40))
if (!verifier.VerifyMessage (m_Buffer, leases - m_Buffer, leases, 40))
LogPrint ("LeaseSet verification failed");
}

View File

@ -8,6 +8,12 @@
namespace i2p
{
namespace tunnel
{
class TunnelPool;
}
namespace data
{
@ -28,15 +34,32 @@ namespace data
}
};
struct LeaseSetHeader
{
Identity destination;
uint8_t encryptionKey[256];
uint8_t signingKey[128];
uint8_t num;
};
#pragma pack()
const int MAX_LS_BUFFER_SIZE = 2048;
class LeaseSet: public RoutingDestination
{
public:
LeaseSet (const uint8_t * buf, int len);
LeaseSet (const uint8_t * buf, int len, bool unsolicited = false);
LeaseSet (const LeaseSet& ) = default;
LeaseSet (const i2p::tunnel::TunnelPool& pool);
LeaseSet& operator=(const LeaseSet& ) = default;
void Update (const uint8_t * buf, int len);
const uint8_t * GetBuffer () const { return m_Buffer; };
size_t GetBufferLen () const { return m_BufferLen; };
bool IsUnsolicited () const { return m_IsUnsolicited; };
void SetUnsolicited (bool unsolicited) { m_IsUnsolicited = unsolicited; };
// implements RoutingDestination
const Identity& GetIdentity () const { return m_Identity; };
@ -48,12 +71,19 @@ namespace data
const uint8_t * GetEncryptionPublicKey () const { return m_EncryptionKey; };
bool IsDestination () const { return true; };
private:
void ReadFromBuffer ();
private:
std::vector<Lease> m_Leases;
Identity m_Identity;
IdentHash m_IdentHash;
uint8_t m_EncryptionKey[256];
uint8_t m_Buffer[MAX_LS_BUFFER_SIZE];
size_t m_BufferLen;
bool m_IsUnsolicited;
};
}
}

View File

@ -1,12 +1,10 @@
#include "Log.h"
Log g_Log;
Log * g_Log = nullptr;
void LogMsg::Process()
{
output << s.str();
std::cout << s.str (); // TODO: delete later
}
void Log::Flush ()

31
Log.h
View File

@ -37,7 +37,26 @@ class Log: public i2p::util::MsgQueue<LogMsg>
std::ofstream * m_LogFile;
};
extern Log g_Log;
extern Log * g_Log;
inline void StartLog (const std::string& fullFilePath)
{
if (!g_Log)
{
g_Log = new Log ();
if (fullFilePath.length () > 0)
g_Log->SetLogFile (fullFilePath);
}
}
inline void StopLog ()
{
if (g_Log)
{
delete g_Log;
g_Log = nullptr;
}
}
template<typename TValue>
void LogPrint (std::stringstream& s, TValue arg)
@ -55,10 +74,16 @@ void LogPrint (std::stringstream& s, TValue arg, TArgs... args)
template<typename... TArgs>
void LogPrint (TArgs... args)
{
LogMsg * msg = g_Log.GetLogFile () ? new LogMsg (*g_Log.GetLogFile ()) : new LogMsg ();
LogMsg * msg = (g_Log && g_Log->GetLogFile ()) ? new LogMsg (*g_Log->GetLogFile ()) : new LogMsg ();
LogPrint (msg->s, args...);
msg->s << std::endl;
g_Log.Put (msg);
if (g_Log)
g_Log->Put (msg);
else
{
msg->Process ();
delete msg;
}
}
#endif

View File

@ -1,38 +1,9 @@
CC = g++
CFLAGS = -g -Wall -std=c++0x
OBJECTS = obj/CryptoConst.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 \
obj/UPnP.o obj/TunnelPool.o obj/HTTPProxy.o obj/AddressBook.o obj/Daemon.o \
obj/DaemonLinux.o obj/SSUData.o obj/i2p.o obj/aes.o
INCFLAGS =
LDFLAGS = -Wl,-rpath,/usr/local/lib -lcryptopp -lboost_system -lboost_filesystem -lboost_regex -lboost_program_options -lpthread
LIBS =
UNAME := $(shell uname -s)
#check if AES-NI is supported by CPU
ifneq ($(shell grep -c aes /proc/cpuinfo),0)
CPU_FLAGS = -DAESNI
ifeq ($(UNAME),Darwin)
include Makefile.osx
else
include Makefile.linux
endif
all: obj i2p
i2p: $(OBJECTS:obj/%=obj/%)
$(CC) -o $@ $^ $(LDFLAGS) $(LIBS)
.SUFFIXES:
.SUFFIXES: .c .cc .C .cpp .o
obj/%.o : %.cpp
$(CC) -o $@ $< -c $(CFLAGS) $(INCFLAGS) $(CPU_FLAGS)
obj:
mkdir -p obj
clean:
rm -fr obj i2p
.PHONY: all
.PHONY: clean

33
Makefile.linux Normal file
View File

@ -0,0 +1,33 @@
CC = g++
CFLAGS = -g -Wall -std=c++0x
include filelist.mk
INCFLAGS =
LDFLAGS = -Wl,-rpath,/usr/local/lib -lcryptopp -lboost_system -lboost_filesystem -lboost_regex -lboost_program_options -lpthread
LIBS =
#check if AES-NI is supported by CPU
ifneq ($(shell grep -c aes /proc/cpuinfo),0)
CPU_FLAGS = -DAESNI
endif
all: obj i2p
i2p: $(OBJECTS:obj/%=obj/%)
$(CC) -o $@ $^ $(LDFLAGS) $(LIBS)
.SUFFIXES:
.SUFFIXES: .c .cc .C .cpp .o
obj/%.o : %.cpp
$(CC) -o $@ $< -c $(CFLAGS) $(INCFLAGS) $(CPU_FLAGS)
obj:
mkdir -p obj
clean:
rm -fr obj i2p
.PHONY: all
.PHONY: clean

View File

@ -1,20 +1,15 @@
#CC = clang++
CC = g++
CFLAGS = -g -Wall -std=c++11 -lstdc++
OBJECTS = obj/CryptoConst.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 \
obj/UPnP.o obj/TunnelPool.o obj/HTTPProxy.o obj/AddressBook.o obj/Daemon.o \
obj/DaemonLinux.o obj/SSUData.o obj/i2p.o obj/aes.o
CC = clang++
CFLAGS = -g -Wall -std=c++11 -lstdc++ -I/usr/local/include
include filelist.mk
INCFLAGS = -DCRYPTOPP_DISABLE_ASM
LDFLAGS = -Wl,-rpath,/usr/local/lib -lcryptopp -lboost_system -lboost_filesystem -lboost_regex -lboost_program_options -lpthread
LDFLAGS = -Wl,-rpath,/usr/local/lib -L/usr/local/lib -lcryptopp -lboost_system -lboost_filesystem -lboost_regex -lboost_program_options -lpthread
LIBS =
#check if AES-NI is supported by CPU
ifneq ($(shell grep -c aes /proc/cpuinfo),0)
# OSX Notes
# http://www.hutsby.net/2011/08/macs-with-aes-ni.html
# Seems like all recent Mac's have AES-NI, after firmware upgrade 2.2
# Found no good way to detect it from command line. TODO: Might be some osx sysinfo magic
CPU_FLAGS = -DAESNI
endif
# Apple Mac OSX
UNAME_S := $(shell uname -s)

View File

@ -21,7 +21,8 @@ namespace ntcp
{
NTCPSession::NTCPSession (boost::asio::io_service& service, i2p::data::RouterInfo& in_RemoteRouterInfo):
m_Socket (service), m_TerminationTimer (service), m_IsEstablished (false),
m_RemoteRouterInfo (in_RemoteRouterInfo), m_ReceiveBufferOffset (0), m_NextMessage (nullptr)
m_RemoteRouterInfo (in_RemoteRouterInfo), m_ReceiveBufferOffset (0), m_NextMessage (nullptr),
m_NumSentBytes (0), m_NumReceivedBytes (0)
{
m_DHKeysPair = i2p::transports.GetNextDHKeysPair ();
}
@ -402,7 +403,7 @@ namespace ntcp
}
else
{
LogPrint ("Received: ", bytes_transferred);
m_NumReceivedBytes += bytes_transferred;
m_ReceiveBufferOffset += bytes_transferred;
if (m_ReceiveBufferOffset >= 16)
@ -513,7 +514,7 @@ namespace ntcp
}
else
{
LogPrint ("Msg sent: ", bytes_transferred);
m_NumSentBytes += bytes_transferred;
ScheduleTermination (); // reset termination timer
}
}

View File

@ -62,6 +62,7 @@ namespace ntcp
#pragma pack()
const size_t NTCP_MAX_MESSAGE_SIZE = 16384;
const int NTCP_TERMINATION_TIMEOUT = 120; // 2 minutes
class NTCPSession
{
@ -78,6 +79,9 @@ namespace ntcp
void ServerLogin ();
void SendI2NPMessage (I2NPMessage * msg);
size_t GetNumSentBytes () const { return m_NumSentBytes; };
size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; };
protected:
void Terminate ();
@ -135,12 +139,14 @@ namespace ntcp
NTCPPhase3 m_Phase3;
NTCPPhase4 m_Phase4;
uint8_t m_ReceiveBuffer[i2p::NTCP_MAX_MESSAGE_SIZE*2], m_TimeSyncBuffer[16];
uint8_t m_ReceiveBuffer[NTCP_MAX_MESSAGE_SIZE*2], m_TimeSyncBuffer[16];
int m_ReceiveBufferOffset;
i2p::I2NPMessage * m_NextMessage;
std::list<i2p::I2NPMessage *> m_DelayedMessages;
size_t m_NextMessageOffset;
size_t m_NumSentBytes, m_NumReceivedBytes;
};
class NTCPClient: public NTCPSession

246
NetDb.cpp
View File

@ -23,12 +23,12 @@ namespace data
const i2p::tunnel::InboundTunnel * replyTunnel)
{
I2NPMessage * msg = i2p::CreateDatabaseLookupMsg (m_Destination,
replyTunnel->GetNextIdentHash (), replyTunnel->GetNextTunnelID (), m_IsExploratory, &m_ExcludedPeers);
replyTunnel->GetNextIdentHash (), replyTunnel->GetNextTunnelID (), m_IsExploratory, &m_ExcludedPeers, m_IsLeaseSet);
if (m_IsLeaseSet) // wrap lookup message into garlic
msg = i2p::garlic::routing.WrapSingleMessage (*router, msg);
m_ExcludedPeers.insert (router->GetIdentHash ());
m_LastRouter = router;
m_LastReplyTunnel = replyTunnel;
m_CreationTime = i2p::util::GetSecondsSinceEpoch ();
return msg;
}
@ -38,7 +38,7 @@ namespace data
i2p::context.GetRouterInfo ().GetIdentHash () , 0, false, &m_ExcludedPeers);
m_ExcludedPeers.insert (floodfill);
m_LastRouter = nullptr;
m_LastReplyTunnel = nullptr;
m_CreationTime = i2p::util::GetSecondsSinceEpoch ();
return msg;
}
@ -95,7 +95,7 @@ namespace data
void NetDb::Run ()
{
uint32_t lastSave = 0, lastPublish = 0;
uint32_t lastSave = 0, lastPublish = 0, lastKeyspaceRotation = 0;
m_IsRunning = true;
while (m_IsRunning)
{
@ -106,15 +106,22 @@ namespace data
{
while (msg)
{
if (msg->GetHeader ()->typeID == eI2NPDatabaseStore)
switch (msg->GetHeader ()->typeID)
{
case eI2NPDatabaseStore:
LogPrint ("DatabaseStore");
HandleDatabaseStoreMsg (msg->GetPayload (), msg->GetLength ()); // TODO
i2p::DeleteI2NPMessage (msg);
}
else if (msg->GetHeader ()->typeID == eI2NPDatabaseSearchReply)
break;
case eI2NPDatabaseSearchReply:
LogPrint ("DatabaseSearchReply");
HandleDatabaseSearchReplyMsg (msg);
else // WTF?
{
break;
case eI2NPDatabaseLookup:
LogPrint ("DatabaseLookup");
HandleDatabaseLookupMsg (msg);
break;
default: // WTF?
LogPrint ("NetDb: unexpected message type ", msg->GetHeader ()->typeID);
i2p::HandleI2NPMessage (msg);
}
@ -128,11 +135,12 @@ namespace data
}
uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
if (ts - lastSave >= 60) // save routers and validate subscriptions every minute
if (ts - lastSave >= 60) // save routers, manage leasesets and validate subscriptions every minute
{
if (lastSave)
{
SaveUpdated (m_NetDbPath);
ManageLeaseSets ();
ValidateSubscriptions ();
}
lastSave = ts;
@ -142,6 +150,11 @@ namespace data
Publish ();
lastPublish = ts;
}
if (ts % 86400 < 60 && ts - lastKeyspaceRotation >= 60) // wihhin 1 minutes since midnight (86400 = 24*3600)
{
KeyspaceRotation ();
lastKeyspaceRotation = ts;
}
}
catch (std::exception& ex)
{
@ -150,44 +163,40 @@ namespace data
}
}
void NetDb::AddRouterInfo (uint8_t * buf, int len)
void NetDb::AddRouterInfo (const IdentHash& ident, uint8_t * buf, int len)
{
RouterInfo * r = new RouterInfo (buf, len);
DeleteRequestedDestination (r->GetIdentHash ());
auto it = m_RouterInfos.find(r->GetIdentHash ());
DeleteRequestedDestination (ident);
auto it = m_RouterInfos.find(ident);
if (it != m_RouterInfos.end ())
{
if (r->GetTimestamp () > it->second->GetTimestamp ())
{
auto ts = it->second->GetTimestamp ();
it->second->Update (buf, len);
if (it->second->GetTimestamp () > ts)
LogPrint ("RouterInfo updated");
*(it->second) = *r; // we can't replace pointer because it's used by tunnels
}
delete r;
}
else
{
LogPrint ("New RouterInfo added");
RouterInfo * r = new RouterInfo (buf, len);
m_RouterInfos[r->GetIdentHash ()] = r;
if (r->IsFloodfill ())
m_Floodfills.push_back (r);
}
}
void NetDb::AddLeaseSet (uint8_t * buf, int len)
void NetDb::AddLeaseSet (const IdentHash& ident, uint8_t * buf, int len)
{
LeaseSet * l = new LeaseSet (buf, len);
DeleteRequestedDestination (l->GetIdentHash ());
auto it = m_LeaseSets.find(l->GetIdentHash ());
bool unsolicited = !DeleteRequestedDestination (ident);
auto it = m_LeaseSets.find(ident);
if (it != m_LeaseSets.end ())
{
it->second->Update (buf, len);
LogPrint ("LeaseSet updated");
*(it->second) = *l; // we can't replace pointer because it's used by streams
delete l;
}
else
{
LogPrint ("New LeaseSet added");
m_LeaseSets[l->GetIdentHash ()] = l;
m_LeaseSets[ident] = new LeaseSet (buf, len, unsolicited);
}
}
@ -259,15 +268,26 @@ namespace data
for (boost::filesystem::directory_iterator it1 (it->path ()); it1 != end; ++it1)
{
#if BOOST_VERSION > 10500
RouterInfo * r = new RouterInfo (it1->path().string().c_str ());
const std::string& fullPath = it1->path().string();
#else
RouterInfo * r = new RouterInfo(it1->path().c_str());
const std::string& fullPath = it1->path();
#endif
RouterInfo * r = new RouterInfo(fullPath);
if (!r->IsUnreachable ())
{
r->DeleteBuffer ();
m_RouterInfos[r->GetIdentHash ()] = r;
if (r->IsFloodfill ())
m_Floodfills.push_back (r);
numRouters++;
}
else
{
if (boost::filesystem::exists (fullPath))
boost::filesystem::remove (fullPath);
delete r;
}
}
}
}
LogPrint (numRouters, " routers loaded");
@ -302,9 +322,9 @@ namespace data
{
if (it.second->IsUpdated ())
{
std::ofstream r (GetFilePath(fullDirectory, it.second), std::ofstream::binary);
r.write ((char *)it.second->GetBuffer (), it.second->GetBufferLen ());
it.second->SaveToFile (GetFilePath(fullDirectory, it.second));
it.second->SetUpdated (false);
it.second->DeleteBuffer ();
count++;
}
else
@ -362,7 +382,6 @@ namespace data
if (msgs.size () > 0)
{
dest->ClearExcludedPeers ();
dest->SetLastOutboundTunnel (outbound);
outbound->SendTunnelDataMsg (msgs);
}
else
@ -379,12 +398,9 @@ namespace data
RequestedDestination * dest = CreateRequestedDestination (destination, false);
auto floodfill = GetClosestFloodfill (destination, dest->GetExcludedPeers ());
if (floodfill)
{
dest->SetLastOutboundTunnel (nullptr);
i2p::transports.SendMessage (floodfill->GetIdentHash (), dest->CreateRequestMessage (floodfill->GetIdentHash ()));
}
}
}
void NetDb::HandleDatabaseStoreMsg (uint8_t * buf, size_t len)
{
@ -395,7 +411,7 @@ namespace data
if (msg->type)
{
LogPrint ("LeaseSet");
AddLeaseSet (buf + offset, len - offset);
AddLeaseSet (msg->key, buf + offset, len - offset);
}
else
{
@ -413,7 +429,7 @@ namespace data
uint8_t uncompressed[2048];
size_t uncomressedSize = decompressor.MaxRetrievable ();
decompressor.Get (uncompressed, uncomressedSize);
AddRouterInfo (uncompressed, uncomressedSize);
AddRouterInfo (msg->key, uncompressed, uncomressedSize);
}
}
@ -429,10 +445,12 @@ namespace data
if (it != m_RequestedDestinations.end ())
{
RequestedDestination * dest = it->second;
bool deleteDest = true;
if (num > 0)
{
i2p::tunnel::OutboundTunnel * outbound = dest->GetLastOutboundTunnel ();
const i2p::tunnel::InboundTunnel * inbound = dest->GetLastReplyTunnel ();
auto exploratoryPool = i2p::tunnel::tunnels.GetExploratoryPool ();
auto outbound = exploratoryPool ? exploratoryPool->GetNextOutboundTunnel () : nullptr;
auto inbound = exploratoryPool ? exploratoryPool->GetNextInboundTunnel () : nullptr;
std::vector<i2p::tunnel::TunnelMessageBlock> msgs;
for (int i = 0; i < num; i++)
@ -450,11 +468,10 @@ namespace data
{
// router with ident not found or too old (1 hour)
LogPrint ("Found new/outdated router. Requesting RouterInfo ...");
if (outbound && inbound)
if (outbound && inbound && dest->GetLastRouter ())
{
RequestedDestination * d1 = CreateRequestedDestination (router, false, false);
d1->SetLastOutboundTunnel (outbound);
auto msg = d1->CreateRequestMessage (dest->GetLastRouter (), dest->GetLastReplyTunnel ());
auto msg = d1->CreateRequestMessage (dest->GetLastRouter (), inbound);
msgs.push_back (i2p::tunnel::TunnelMessageBlock
{
i2p::tunnel::eDeliveryTypeRouter,
@ -468,7 +485,7 @@ namespace data
else
{
// reply to our destination. Try other floodfills
if (outbound && inbound)
if (outbound && inbound && dest->GetLastRouter ())
{
auto r = FindRouter (router);
// do we have that floodfill router in our database?
@ -477,6 +494,7 @@ namespace data
// we do
if (!dest->IsExcluded (r->GetIdentHash ()) && dest->GetNumExcludedPeers () < 30) // TODO: fix TunnelGateway first
{
LogPrint ("Try ", key, " at floodfill ", peerHash);
// tell floodfill about us
msgs.push_back (i2p::tunnel::TunnelMessageBlock
{
@ -485,12 +503,13 @@ namespace data
CreateDatabaseStoreMsg ()
});
// request destination
auto msg = dest->CreateRequestMessage (r, dest->GetLastReplyTunnel ());
auto msg = dest->CreateRequestMessage (r, inbound);
msgs.push_back (i2p::tunnel::TunnelMessageBlock
{
i2p::tunnel::eDeliveryTypeRouter,
r->GetIdentHash (), 0, msg
});
deleteDest = false;
}
}
else
@ -498,7 +517,6 @@ namespace data
// request router
LogPrint ("Found new floodfill. Request it");
RequestedDestination * d2 = CreateRequestedDestination (router, false, false);
d2->SetLastOutboundTunnel (outbound);
I2NPMessage * msg = d2->CreateRequestMessage (dest->GetLastRouter (), inbound);
msgs.push_back (i2p::tunnel::TunnelMessageBlock
{
@ -512,7 +530,11 @@ namespace data
if (!dest->IsLeaseSet ()) // if not LeaseSet
{
if (!dest->IsExcluded (router) && dest->GetNumExcludedPeers () < 30)
{
LogPrint ("Try ", key, " at floodfill ", peerHash, " directly");
i2p::transports.SendMessage (router, dest->CreateRequestMessage (router));
deleteDest = false;
}
}
else
LogPrint ("Can't request LeaseSet");
@ -522,6 +544,12 @@ namespace data
if (outbound && msgs.size () > 0)
outbound->SendTunnelDataMsg (msgs);
if (deleteDest)
{
// no more requests for tha destinationation. delete it
delete it->second;
m_RequestedDestinations.erase (it);
}
}
else
{
@ -535,8 +563,104 @@ namespace data
i2p::DeleteI2NPMessage (msg);
}
void NetDb::HandleDatabaseLookupMsg (I2NPMessage * msg)
{
uint8_t * buf = msg->GetPayload ();
char key[48];
int l = i2p::data::ByteStreamToBase64 (buf, 32, key, 48);
key[l] = 0;
LogPrint ("DatabaseLookup for ", key, " recieved");
uint8_t flag = buf[64];
uint8_t * excluded = buf + 65;
uint32_t replyTunnelID = 0;
if (flag & 0x01) //reply to tunnel
{
replyTunnelID = be32toh (*(uint32_t *)(buf + 64));
excluded += 4;
}
uint16_t numExcluded = be16toh (*(uint16_t *)excluded);
excluded += 2;
if (numExcluded > 512)
{
LogPrint ("Number of excluded peers", numExcluded, " exceeds 512");
numExcluded = 0; // TODO:
}
I2NPMessage * replyMsg = nullptr;
{
auto router = FindRouter (buf);
if (router)
{
LogPrint ("Requested RouterInfo ", key, " found");
router->LoadBuffer ();
if (router->GetBuffer ())
replyMsg = CreateDatabaseStoreMsg (router);
}
}
if (!replyMsg)
{
auto leaseSet = FindLeaseSet (buf);
if (leaseSet && leaseSet->IsUnsolicited ()) // we don't send back our LeaseSets
{
LogPrint ("Requested LeaseSet ", key, " found");
replyMsg = CreateDatabaseStoreMsg (leaseSet);
}
}
if (!replyMsg)
{
LogPrint ("Requested ", key, " not found. ", numExcluded, " excluded");
std::set<IdentHash> excludedRouters;
for (int i = 0; i < numExcluded; i++)
{
// TODO: check for all zeroes (exploratory)
excludedRouters.insert (excluded);
excluded += 32;
}
replyMsg = CreateDatabaseSearchReply (buf, GetClosestFloodfill (buf, excludedRouters));
}
else
excluded += numExcluded*32; // we don't care about exluded
if (replyMsg)
{
if (replyTunnelID)
{
// encryption might be used though tunnel only
if (flag & 0x02) // encrypted reply requested
{
uint8_t * sessionKey = excluded;
uint8_t numTags = sessionKey[32];
if (numTags > 0)
{
uint8_t * sessionTag = sessionKey + 33; // take first tag
i2p::garlic::GarlicRoutingSession garlic (sessionKey, sessionTag);
replyMsg = garlic.WrapSingleMessage (replyMsg, nullptr);
}
}
i2p::tunnel::tunnels.GetNextOutboundTunnel ()->SendTunnelDataMsg (buf+32, replyTunnelID, replyMsg);
}
else
i2p::transports.SendMessage (buf, replyMsg);
}
i2p::DeleteI2NPMessage (msg);
}
void NetDb::Explore (int numDestinations)
{
// clean up previous exploratories
uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
for (auto it = m_RequestedDestinations.begin (); it != m_RequestedDestinations.end ();)
{
if (it->second->IsExploratory () || ts > it->second->GetCreationTime () + 60) // no response for 1 minute
{
delete it->second;
it = m_RequestedDestinations.erase (it);
}
else
it++;
}
// new requests
auto exploratoryPool = i2p::tunnel::tunnels.GetExploratoryPool ();
auto outbound = exploratoryPool ? exploratoryPool->GetNextOutboundTunnel () : nullptr;
auto inbound = exploratoryPool ? exploratoryPool->GetNextInboundTunnel () : nullptr;
@ -557,7 +681,6 @@ namespace data
floodfills.insert (floodfill);
if (throughTunnels)
{
dest->SetLastOutboundTunnel (outbound);
msgs.push_back (i2p::tunnel::TunnelMessageBlock
{
i2p::tunnel::eDeliveryTypeRouter,
@ -572,12 +695,8 @@ namespace data
});
}
else
{
dest->SetLastOutboundTunnel (nullptr);
dest->SetLastReplyTunnel (nullptr);
i2p::transports.SendMessage (floodfill->GetIdentHash (), dest->CreateRequestMessage (floodfill->GetIdentHash ()));
}
}
else
DeleteRequestedDestination (dest);
}
@ -614,14 +733,16 @@ namespace data
return it->second;
}
void NetDb::DeleteRequestedDestination (const IdentHash& dest)
bool NetDb::DeleteRequestedDestination (const IdentHash& dest)
{
auto it = m_RequestedDestinations.find (dest);
if (it != m_RequestedDestinations.end ())
{
delete it->second;
m_RequestedDestinations.erase (it);
return true;
}
return false;
}
void NetDb::DeleteRequestedDestination (RequestedDestination * dest)
@ -692,6 +813,8 @@ namespace data
LogPrint ("LeaseSet requested");
RequestDestination (ident, true);
}
else
leaseSet->SetUnsolicited (false);
m_Subscriptions.insert (ident);
}
@ -712,5 +835,28 @@ namespace data
}
}
}
void NetDb::KeyspaceRotation ()
{
for (auto it: m_RouterInfos)
it.second->UpdateRoutingKey ();
LogPrint ("Keyspace rotation complete");
Publish ();
}
void NetDb::ManageLeaseSets ()
{
for (auto it = m_LeaseSets.begin (); it != m_LeaseSets.end ();)
{
if (it->second->IsUnsolicited () && !it->second->HasNonExpiredLeases ()) // all leases expired
{
LogPrint ("LeaseSet ", it->second->GetIdentHash ().ToBase64 (), " expired");
delete it->second;
it = m_LeaseSets.erase (it);
}
else
it++;
}
}
}
}

20
NetDb.h
View File

@ -25,32 +25,27 @@ namespace data
RequestedDestination (const IdentHash& destination, bool isLeaseSet, bool isExploratory = false):
m_Destination (destination), m_IsLeaseSet (isLeaseSet), m_IsExploratory (isExploratory),
m_LastRouter (nullptr), m_LastReplyTunnel (nullptr), m_LastOutboundTunnel (nullptr) {};
m_LastRouter (nullptr), m_CreationTime (0) {};
const IdentHash& GetDestination () const { return m_Destination; };
int GetNumExcludedPeers () const { return m_ExcludedPeers.size (); };
const std::set<IdentHash>& GetExcludedPeers () { return m_ExcludedPeers; };
void ClearExcludedPeers ();
const RouterInfo * GetLastRouter () const { return m_LastRouter; };
const i2p::tunnel::InboundTunnel * GetLastReplyTunnel () const { return m_LastReplyTunnel; };
void SetLastReplyTunnel (i2p::tunnel::InboundTunnel * tunnel) { m_LastReplyTunnel = tunnel; };
bool IsExploratory () const { return m_IsExploratory; };
bool IsLeaseSet () const { return m_IsLeaseSet; };
bool IsExcluded (const IdentHash& ident) const { return m_ExcludedPeers.count (ident); };
uint64_t GetCreationTime () const { return m_CreationTime; };
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; };
private:
IdentHash m_Destination;
bool m_IsLeaseSet, m_IsExploratory;
std::set<IdentHash> m_ExcludedPeers;
const RouterInfo * m_LastRouter;
const i2p::tunnel::InboundTunnel * m_LastReplyTunnel;
i2p::tunnel::OutboundTunnel * m_LastOutboundTunnel;
uint64_t m_CreationTime;
};
class NetDb
@ -63,8 +58,8 @@ namespace data
void Start ();
void Stop ();
void AddRouterInfo (uint8_t * buf, int len);
void AddLeaseSet (uint8_t * buf, int len);
void AddRouterInfo (const IdentHash& ident, uint8_t * buf, int len);
void AddLeaseSet (const IdentHash& ident, uint8_t * buf, int len);
RouterInfo * FindRouter (const IdentHash& ident) const;
LeaseSet * FindLeaseSet (const IdentHash& destination) const;
const IdentHash * FindAddress (const std::string& address) { return m_AddressBook.FindAddress (address); }; // TODO: move AddressBook away from NetDb
@ -75,6 +70,7 @@ namespace data
void HandleDatabaseStoreMsg (uint8_t * buf, size_t len);
void HandleDatabaseSearchReplyMsg (I2NPMessage * msg);
void HandleDatabaseLookupMsg (I2NPMessage * msg);
const RouterInfo * GetRandomRouter (const RouterInfo * compatibleWith = nullptr) const;
@ -95,10 +91,12 @@ namespace data
void Publish ();
void ValidateSubscriptions ();
const RouterInfo * GetClosestFloodfill (const IdentHash& destination, const std::set<IdentHash>& excluded) const;
void KeyspaceRotation ();
void ManageLeaseSets ();
RequestedDestination * CreateRequestedDestination (const IdentHash& dest,
bool isLeaseSet, bool isExploratory = false);
void DeleteRequestedDestination (const IdentHash& dest);
bool DeleteRequestedDestination (const IdentHash& dest); // returns true if found
void DeleteRequestedDestination (RequestedDestination * dest);
private:

View File

@ -65,7 +65,7 @@ namespace util
return m_Queue.empty ();
}
void WakeUp () { m_NonEmpty.notify_one (); };
void WakeUp () { m_NonEmpty.notify_all (); };
Element * Get ()
{
@ -108,12 +108,16 @@ namespace util
typedef std::function<void()> OnEmpty;
MsgQueue (): m_IsRunning (true), m_Thread (std::bind (&MsgQueue<Msg>::Run, this)) {};
~MsgQueue () { Stop (); };
void Stop()
{
if (m_IsRunning)
{
m_IsRunning = false;
Queue<Msg>::WakeUp ();
m_Thread.join();
}
}
void SetOnEmpty (OnEmpty const & e) { m_OnEmpty = e; };

View File

@ -9,7 +9,13 @@ on Windows
Requires msvs2013, boost 1.46 and higher, crypto++
[![Build Status](https://travis-ci.org/orignal/i2pd.svg?branch=master)](https://travis-ci.org/orignal/i2pd)
Build Statuses
---------------
- Linux x64 - [![Build Status](https://jenkins.nordcloud.no/buildStatus/icon?job=i2pd-linux)](https://jenkins.nordcloud.no/job/i2pd-linux/)
- Linux ARM - Too be added
- Mac OS X - Too be added
- Microsoft VC13 - Too be added
Testing

View File

@ -19,12 +19,26 @@ namespace data
"http://cowpuncher.drollette.com/netdb/",
"http://i2p.mooo.com/netDb/",
"http://reseed.info/",
"http://reseed.pkol.de/",
"http://uk.reseed.i2p2.no/",
"http://us.reseed.i2p2.no/",
"http://jp.reseed.i2p2.no/",
"http://i2p-netdb.innovatio.no/",
"http://ieb9oopo.mooo.com"
};
//TODO: Remember to add custom port support. Not all serves on 443
static std::vector<std::string> httpsReseedHostList = {
"https://193.150.121.66/netDb/",
"https://netdb.i2p2.no/",
"https://reseed.i2p-projekt.de/",
"https://cowpuncher.drollette.com/netdb/",
"https://i2p.mooo.com/netDb/",
"https://reseed.info/",
"https://i2p-netdb.innovatio.no/",
"https://ieb9oopo.mooo.com/",
"https://ssl.webpack.de/ivae2he9.sg4.e-plaza.de/" // Only HTTPS and SU3 (v2) support
};
//TODO: Implement v2 reseeding. Lightweight zip library is needed.
//TODO: Implement SU3, utils.
Reseeder::Reseeder()
@ -39,6 +53,15 @@ namespace data
{
try
{
// Seems like the best place to try to intercept with SSL
/*ssl_server = true;
try {
// SSL
}
catch (std::exception& e)
{
LogPrint("Exception in SSL: ", e.what());
}*/
std::string reseedHost = httpReseedHostList[(rand() % httpReseedHostList.size())];
LogPrint("Reseeding from ", reseedHost);
std::string content = i2p::util::http::httpRequest(reseedHost);
@ -56,7 +79,7 @@ namespace data
std::string routerInfo;
std::string tmpUrl;
std::string filename;
std::string ignoreFileSuffix = ".zip";
std::string ignoreFileSuffix = ".su3";
boost::filesystem::path root = i2p::util::filesystem::GetDataDir();
while (i != j)
{

View File

@ -34,13 +34,13 @@ namespace i2p
routerInfo.AddSSUAddress ("127.0.0.1", 17007, routerInfo.GetIdentHash ());
routerInfo.AddNTCPAddress ("127.0.0.1", 17007); // TODO:
routerInfo.SetProperty ("caps", "LR");
routerInfo.SetProperty ("coreVersion", "0.9.8.1");
routerInfo.SetProperty ("coreVersion", "0.9.11");
routerInfo.SetProperty ("netId", "2");
routerInfo.SetProperty ("router.version", "0.9.8.1");
routerInfo.SetProperty ("router.version", "0.9.11");
routerInfo.SetProperty ("start_uptime", "90m");
routerInfo.CreateBuffer ();
m_RouterInfo = routerInfo;
m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ());
}
void RouterContext::OverrideNTCPAddress (const char * host, int port)
@ -64,10 +64,10 @@ namespace i2p
m_RouterInfo.CreateBuffer ();
}
void RouterContext::Sign (uint8_t * buf, int len, uint8_t * signature)
void RouterContext::Sign (const uint8_t * buf, int len, uint8_t * signature) const
{
CryptoPP::DSA::Signer signer (m_SigningPrivateKey);
signer.SignMessage (m_Rnd, buf, len, signature);
signer.SignMessage (i2p::context.GetRandomNumberGenerator (), buf, len, signature);
}
bool RouterContext::Load ()
@ -79,7 +79,8 @@ namespace i2p
m_SigningPrivateKey.Initialize (i2p::crypto::dsap, i2p::crypto::dsaq, i2p::crypto::dsag,
CryptoPP::Integer (m_Keys.signingPrivateKey, 20));
m_RouterInfo = i2p::data::RouterInfo (i2p::util::filesystem::GetFullPath (ROUTER_INFO).c_str ()); // TODO
i2p::data::RouterInfo routerInfo(i2p::util::filesystem::GetFullPath (ROUTER_INFO)); // TODO
m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ());
return true;
}
@ -92,7 +93,6 @@ namespace i2p
fk.write ((char *)&m_Keys, sizeof (m_Keys));
}
std::ofstream fi (i2p::util::filesystem::GetFullPath (ROUTER_INFO).c_str (), std::ofstream::binary | std::ofstream::out);
fi.write ((char *)m_RouterInfo.GetBuffer (), m_RouterInfo.GetBufferLen ());
m_RouterInfo.SaveToFile (i2p::util::filesystem::GetFullPath (ROUTER_INFO));
}
}

View File

@ -24,16 +24,15 @@ namespace i2p
const i2p::data::Identity& GetRouterIdentity () const { return m_RouterInfo.GetRouterIdentity (); };
CryptoPP::RandomNumberGenerator& GetRandomNumberGenerator () { return m_Rnd; };
void Sign (uint8_t * buf, int len, uint8_t * signature);
void OverrideNTCPAddress (const char * host, int port); // temporary
void UpdateAddress (const char * host); // called from SSU
// implements LocalDestination
void UpdateLeaseSet () {};
const i2p::data::IdentHash& GetIdentHash () const { return m_RouterInfo.GetIdentHash (); };
const i2p::data::Identity& GetIdentity () const { return GetRouterIdentity (); };
const uint8_t * GetEncryptionPrivateKey () const { return GetPrivateKey (); };
const uint8_t * GetEncryptionPublicKey () const { return m_Keys.publicKey; };
void Sign (const uint8_t * buf, int len, uint8_t * signature) const;
private:

View File

@ -17,20 +17,44 @@ namespace i2p
{
namespace data
{
RouterInfo::RouterInfo (const char * filename):
m_IsUpdated (false), m_IsUnreachable (false), m_SupportedTransports (0), m_Caps (0)
RouterInfo::RouterInfo (const std::string& fullPath):
m_FullPath (fullPath), m_IsUpdated (false), m_IsUnreachable (false),
m_SupportedTransports (0), m_Caps (0)
{
ReadFromFile (filename);
m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE];
ReadFromFile ();
}
RouterInfo::RouterInfo (const uint8_t * buf, int len):
m_IsUpdated (true), m_IsUnreachable (false), m_SupportedTransports (0), m_Caps (0)
{
m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE];
memcpy (m_Buffer, buf, len);
m_BufferLen = len;
ReadFromBuffer ();
}
RouterInfo::~RouterInfo ()
{
delete m_Buffer;
}
void RouterInfo::Update (const uint8_t * buf, int len)
{
if (!m_Buffer)
m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE];
m_IsUpdated = true;
m_IsUnreachable = false;
m_SupportedTransports = 0;
m_Caps = 0;
m_Addresses.clear ();
m_Properties.clear ();
memcpy (m_Buffer, buf, len);
m_BufferLen = len;
ReadFromBuffer ();
// don't delete buffer until save to file
}
void RouterInfo::SetRouterIdentity (const Identity& identity)
{
m_RouterIdentity = identity;
@ -40,29 +64,40 @@ namespace data
m_Timestamp = i2p::util::GetMillisecondsSinceEpoch ();
}
void RouterInfo::ReadFromFile (const char * filename)
bool RouterInfo::LoadFile ()
{
std::ifstream s(filename, std::ifstream::binary);
std::ifstream s(m_FullPath.c_str (), std::ifstream::binary);
if (s.is_open ())
{
s.seekg (0,std::ios::end);
m_BufferLen = s.tellg ();
if (m_BufferLen < 40)
{
LogPrint("File", filename, " is malformed");
return;
LogPrint("File", m_FullPath, " is malformed");
return false;
}
s.seekg(0, std::ios::beg);
s.read(m_Buffer,m_BufferLen);
ReadFromBuffer ();
if (!m_Buffer)
m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE];
s.read((char *)m_Buffer, m_BufferLen);
}
else
LogPrint ("Can't open file ", filename);
{
LogPrint ("Can't open file ", m_FullPath);
return false;
}
return true;
}
void RouterInfo::ReadFromFile ()
{
if (LoadFile ())
ReadFromBuffer ();
}
void RouterInfo::ReadFromBuffer ()
{
std::stringstream str (std::string (m_Buffer, m_BufferLen));
std::stringstream str (std::string ((char *)m_Buffer, m_BufferLen));
ReadFromStream (str);
// verify signature
CryptoPP::DSA::PublicKey pubKey;
@ -83,8 +118,10 @@ namespace data
// read addresses
uint8_t numAddresses;
s.read ((char *)&numAddresses, sizeof (numAddresses));
bool introducers = false;
for (int i = 0; i < numAddresses; i++)
{
bool isValidAddress = true;
Address address;
s.read ((char *)&address.cost, sizeof (address.cost));
s.read ((char *)&address.date, sizeof (address.date));
@ -114,7 +151,7 @@ namespace data
{
// TODO: we should try to resolve address here
LogPrint ("Unexpected address ", value);
SetUnreachable (true);
isValidAddress = false;
}
else
{
@ -134,6 +171,7 @@ namespace data
else if (key[0] == 'i')
{
// introducers
introducers = true;
size_t l = strlen(key);
unsigned char index = key[l-1] - '0'; // TODO:
key[l-1] = 0;
@ -153,6 +191,7 @@ namespace data
Base64ToByteStream (value, strlen (value), introducer.iKey, 32);
}
}
if (isValidAddress)
m_Addresses.push_back(address);
}
// read peers
@ -187,7 +226,7 @@ namespace data
UpdateIdentHashBase64 ();
UpdateRoutingKey ();
if (!m_SupportedTransports)
if (!m_SupportedTransports || !m_Addresses.size() || (UsesIntroducer () && !introducers))
SetUnreachable (true);
}
@ -311,18 +350,42 @@ namespace data
s.write (properties.str ().c_str (), properties.str ().size ());
}
const uint8_t * RouterInfo::LoadBuffer ()
{
if (!m_Buffer)
{
if (LoadFile ())
LogPrint ("Buffer for ", m_IdentHashAbbreviation, " loaded from file");
}
return m_Buffer;
}
void RouterInfo::CreateBuffer ()
{
m_Timestamp = i2p::util::GetMillisecondsSinceEpoch (); // refresh timstamp
std::stringstream s;
WriteToStream (s);
m_BufferLen = s.str ().size ();
if (!m_Buffer)
m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE];
memcpy (m_Buffer, s.str ().c_str (), m_BufferLen);
// signature
i2p::context.Sign ((uint8_t *)m_Buffer, m_BufferLen, (uint8_t *)m_Buffer + m_BufferLen);
m_BufferLen += 40;
}
void RouterInfo::SaveToFile (const std::string& fullPath)
{
m_FullPath = fullPath;
if (m_Buffer)
{
std::ofstream f (fullPath, std::ofstream::binary | std::ofstream::out);
f.write ((char *)m_Buffer, m_BufferLen);
}
else
LogPrint ("Can't save to file");
}
size_t RouterInfo::ReadString (char * str, std::istream& s)
{
uint8_t len;

View File

@ -13,6 +13,7 @@ namespace i2p
{
namespace data
{
const int MAX_RI_BUFFER_SIZE = 2048;
class RouterInfo: public RoutingDestination
{
public:
@ -62,11 +63,12 @@ namespace data
std::vector<Introducer> introducers;
};
RouterInfo (const char * filename);
RouterInfo () = default;
RouterInfo (const std::string& fullPath);
RouterInfo (): m_Buffer (nullptr) { m_IdentHashBase64[0] = 0; m_IdentHashAbbreviation[0] = 0; };
RouterInfo (const RouterInfo& ) = default;
RouterInfo& operator=(const RouterInfo& ) = default;
RouterInfo (const uint8_t * buf, int len);
~RouterInfo ();
const Identity& GetRouterIdentity () const { return m_RouterIdentity; };
void SetRouterIdentity (const Identity& identity);
@ -96,22 +98,30 @@ namespace data
void SetUnreachable (bool unreachable) { m_IsUnreachable = unreachable; };
bool IsUnreachable () const { return m_IsUnreachable; };
const uint8_t * GetBuffer () const { return m_Buffer; };
const uint8_t * LoadBuffer (); // load if necessary
int GetBufferLen () const { return m_BufferLen; };
void CreateBuffer ();
void UpdateRoutingKey ();
const char * GetBuffer () const { return m_Buffer; };
int GetBufferLen () const { return m_BufferLen; };
bool IsUpdated () const { return m_IsUpdated; };
void SetUpdated (bool updated) { m_IsUpdated = updated; };
void SaveToFile (const std::string& fullPath);
void Update (const uint8_t * buf, int len);
void DeleteBuffer () { delete m_Buffer; m_Buffer = nullptr; };
// implements RoutingDestination
const IdentHash& GetIdentHash () const { return m_IdentHash; };
const uint8_t * GetEncryptionPublicKey () const { return m_RouterIdentity.publicKey; };
bool IsDestination () const { return false; };
private:
void ReadFromFile (const char * filename);
bool LoadFile ();
void ReadFromFile ();
void ReadFromStream (std::istream& s);
void ReadFromBuffer ();
void WriteToStream (std::ostream& s);
@ -123,11 +133,12 @@ namespace data
private:
std::string m_FullPath;
Identity m_RouterIdentity;
IdentHash m_IdentHash;
RoutingKey m_RoutingKey;
char m_IdentHashBase64[48], m_IdentHashAbbreviation[5];
char m_Buffer[2048];
uint8_t * m_Buffer;
int m_BufferLen;
uint64_t m_Timestamp;
std::vector<Address> m_Addresses;

282
SOCKS.cpp Normal file
View File

@ -0,0 +1,282 @@
#include "SOCKS.h"
#include "Identity.h"
#include "NetDb.h"
#include <cstring>
#include <stdexcept>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/bind.hpp>
namespace i2p
{
namespace proxy
{
constexpr uint8_t socks_leaseset_timeout = 10;
constexpr uint8_t socks_timeout = 60;
void SOCKS4AHandler::AsyncSockRead()
{
LogPrint("--- socks4a async sock read");
if(m_sock) {
if (m_state == INITIAL) {
m_sock->async_receive(boost::asio::buffer(m_sock_buff, socks_buffer_size),
boost::bind(&SOCKS4AHandler::HandleSockRecv, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
} else {
m_sock->async_receive(boost::asio::buffer(m_sock_buff, socks_buffer_size),
boost::bind(&SOCKS4AHandler::HandleSockForward, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
} else {
LogPrint("--- socks4a no socket for read");
}
}
void SOCKS4AHandler::AsyncStreamRead()
{
LogPrint("--- socks4a async stream read");
if (m_stream) {
m_stream->AsyncReceive(
boost::asio::buffer(m_stream_buff, socks_buffer_size),
boost::bind(&SOCKS4AHandler::HandleStreamRecv, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred), socks_timeout);
} else {
LogPrint("--- socks4a no stream for read");
}
}
void SOCKS4AHandler::Terminate() {
CloseStream();
CloseSock();
delete this; // ew
}
void SOCKS4AHandler::SocksFailed()
{
LogPrint("--- socks4a failed");
m_sock->send(boost::asio::buffer("\x00\x5b 12345"));
Terminate();
}
void SOCKS4AHandler::CloseSock()
{
if (m_sock) {
LogPrint("--- socks4a close sock");
m_sock->close();
delete m_sock;
m_sock = nullptr;
}
}
void SOCKS4AHandler::CloseStream()
{
if (m_stream) {
LogPrint("--- socks4a close stream");
delete m_stream;
m_stream = nullptr;
}
}
constexpr size_t socks_hostname_size = 1024;
constexpr size_t socks_ident_size = 1024;
constexpr size_t destb32_len = 52;
void SOCKS4AHandler::HandleSockForward(const boost::system::error_code & ecode, std::size_t len)
{
if(ecode) {
LogPrint("--- socks4a forward got error: ", ecode);
Terminate();
return;
}
LogPrint("--- socks4a sock forward: ", len);
m_stream->Send(m_sock_buff, len, 1);
}
void SOCKS4AHandler::HandleSockRecv(const boost::system::error_code & ecode, std::size_t len)
{
LogPrint("--- socks4a sock recv: ", len);
if(ecode) {
LogPrint(" --- sock recv got error: ", ecode);
Terminate();
return;
}
if (m_state == INITIAL) {
char hostbuff[socks_hostname_size];
char identbuff[socks_ident_size];
std::memset(hostbuff, 0, sizeof(hostbuff));
std::memset(identbuff, 0, sizeof(hostbuff));
std::string dest;
// get port
uint16_t port = 0;
uint16_t idx1 = 0;
uint16_t idx2 = 0;
LogPrint("--- socks4a state initial ", len);
// check valid request
if( m_sock_buff[0] != 4 || m_sock_buff[1] != 1 || m_sock_buff[len-1] ) {
LogPrint("--- socks4a rejected invalid");
SocksFailed();
return;
}
// get port
port = m_sock_buff[3] | m_sock_buff[2] << 8;
// read ident
do {
LogPrint("--- socks4a ", (int) m_sock_buff[9+idx1]);
identbuff[idx1] = m_sock_buff[8+idx1];
} while( identbuff[idx1++] && idx1 < socks_ident_size );
LogPrint("--- socks4a ident ", identbuff);
// read hostname
do {
hostbuff[idx2] = m_sock_buff[8+idx1+idx2];
} while( hostbuff[idx2++] && idx2 < socks_hostname_size );
LogPrint("--- socks4a requested ", hostbuff, ":" , port);
dest = std::string(hostbuff);
if(dest.find(".b32.i2p") == std::string::npos) {
LogPrint("--- socks4a invalid hostname: ", dest);
SocksFailed();
return;
}
if ( i2p::data::Base32ToByteStream(hostbuff, destb32_len, (uint8_t *) m_dest, 32) != 32 ) {
LogPrint("--- sock4a invalid b32: ", dest);
}
LogPrint("--- sock4a find lease set");
m_ls = i2p::data::netdb.FindLeaseSet(m_dest);
if (!m_ls || m_ls->HasNonExpiredLeases()) {
i2p::data::netdb.Subscribe(m_dest);
m_ls_timer.expires_from_now(boost::posix_time::seconds(socks_leaseset_timeout));
m_ls_timer.async_wait(boost::bind(&SOCKS4AHandler::LeaseSetTimeout, this, boost::asio::placeholders::error));
} else {
ConnectionSuccess();
}
} else {
LogPrint("--- socks4a state?? ", m_state);
}
}
void SOCKS4AHandler::HandleStreamRecv(const boost::system::error_code & ecode, std::size_t len)
{
if(ecode) { LogPrint("--- socks4a stream recv error: ", ecode); m_state = END; }
switch(m_state) {
case INITIAL:
case END:
Terminate();
return;
case OKAY:
LogPrint("--- socks4a stream recv ", len);
boost::asio::async_write(*m_sock, boost::asio::buffer(m_stream_buff, len),
boost::bind(&SOCKS4AHandler::StreamWrote, this,
boost::asio::placeholders::error));
}
}
void SOCKS4AHandler::SockWrote(const boost::system::error_code & ecode)
{
LogPrint("--- socks4a sock wrote");
if(ecode) { LogPrint("--- socks4a SockWrote error: ",ecode); }
else { AsyncSockRead(); }
}
void SOCKS4AHandler::StreamWrote(const boost::system::error_code & ecode)
{
LogPrint("--- socks4a stream wrote");
if(ecode) { LogPrint("--- socks4a StreamWrote error: ",ecode); }
else { AsyncStreamRead(); }
}
void SOCKS4AHandler::LeaseSetTimeout(const boost::system::error_code & ecode)
{
m_ls = i2p::data::netdb.FindLeaseSet(m_dest);
if(m_ls) {
ConnectionSuccess();
} else {
LogPrint("--- socks4a ls timeout");
SocksFailed();
}
}
void SOCKS4AHandler::ConnectionSuccess()
{
LogPrint("--- socks4a connection success");
boost::asio::async_write(*m_sock, boost::asio::buffer("\x00\x5a 12345"),
boost::bind(&SOCKS4AHandler::SentConnectionSuccess, this,
boost::asio::placeholders::error));
}
void SOCKS4AHandler::SentConnectionSuccess(const boost::system::error_code & ecode)
{
LogPrint("--- socks4a making connection");
m_stream = i2p::stream::CreateStream(*m_ls);
m_state = OKAY;
LogPrint("--- socks4a state is ", m_state);
AsyncSockRead();
AsyncStreamRead();
}
void SOCKS4AServer::Run()
{
LogPrint("--- socks4a run");
m_run = true;
while(m_run) {
try {
m_ios.run();
} catch (std::runtime_error & exc) {
LogPrint("--- socks4a exception: ", exc.what());
}
}
}
void SOCKS4AServer::Accept()
{
m_new_sock = new boost::asio::ip::tcp::socket(m_ios);
m_acceptor.async_accept(*m_new_sock,
boost::bind(
&SOCKS4AServer::HandleAccept, this, boost::asio::placeholders::error));
}
void SOCKS4AServer::Start()
{
m_run = true;
m_thread = new std::thread(std::bind(&SOCKS4AServer::Run, this));
m_acceptor.listen();
Accept();
}
void SOCKS4AServer::Stop()
{
m_acceptor.close();
m_run = false;
m_ios.stop();
if (m_thread) {
m_thread->join();
delete m_thread;
m_thread = nullptr;
}
}
void SOCKS4AServer::HandleAccept(const boost::system::error_code & ecode)
{
if (!ecode) {
LogPrint("--- socks4a accepted");
new SOCKS4AHandler(&m_ios, m_new_sock);
Accept();
}
}
}
}

98
SOCKS.h Normal file
View File

@ -0,0 +1,98 @@
#ifndef SOCKS4A_H__
#define SOCKS4A_H__
#include <thread>
#include <boost/asio.hpp>
#include <vector>
#include <mutex>
#include "Identity.h"
#include "Streaming.h"
namespace i2p
{
namespace proxy
{
constexpr size_t socks_buffer_size = 8192;
class SOCKS4AHandler {
private:
enum state {
INITIAL,
OKAY,
END
};
void GotClientRequest(boost::system::error_code & ecode, std::string & host, uint16_t port);
void HandleSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered);
void HandleSockForward(const boost::system::error_code & ecode, std::size_t bytes_transfered);
void HandleStreamRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered);
void Terminate();
void CloseSock();
void CloseStream();
void AsyncSockRead();
void AsyncStreamRead();
void SocksFailed();
void LeaseSetTimeout(const boost::system::error_code & ecode);
void StreamWrote(const boost::system::error_code & ecode);
void SockWrote(const boost::system::error_code & ecode);
void SentConnectionSuccess(const boost::system::error_code & ecode);
void ConnectionSuccess();
uint8_t m_sock_buff[socks_buffer_size];
uint8_t m_stream_buff[socks_buffer_size];
boost::asio::io_service * m_ios;
boost::asio::ip::tcp::socket * m_sock;
boost::asio::deadline_timer m_ls_timer;
i2p::stream::Stream * m_stream;
i2p::data::LeaseSet * m_ls;
i2p::data::IdentHash m_dest;
state m_state;
public:
SOCKS4AHandler(boost::asio::io_service * ios, boost::asio::ip::tcp::socket * sock) :
m_ios(ios), m_sock(sock), m_ls_timer(*ios),
m_stream(nullptr), m_ls(nullptr), m_state(INITIAL) { AsyncSockRead(); }
~SOCKS4AHandler() { CloseSock(); CloseStream(); }
bool isComplete() { return m_state == END; }
};
class SOCKS4AServer {
public:
SOCKS4AServer(int port) : m_run(false),
m_thread(nullptr),
m_work(m_ios),
m_acceptor(m_ios, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)),
m_new_sock(nullptr) { }
~SOCKS4AServer() { Stop(); }
void Start();
void Stop();
private:
void Run();
void Accept();
void HandleAccept(const boost::system::error_code& ecode);
bool m_run;
std::thread * m_thread;
boost::asio::io_service m_ios;
boost::asio::io_service::work m_work;
boost::asio::ip::tcp::acceptor m_acceptor;
boost::asio::ip::tcp::socket * m_new_sock;
};
typedef SOCKS4AServer SOCKSProxy;
}
}
#endif

59
SSU.cpp
View File

@ -19,7 +19,8 @@ namespace ssu
const i2p::data::RouterInfo * router, bool peerTest ):
m_Server (server), m_RemoteEndpoint (remoteEndpoint), m_RemoteRouter (router),
m_Timer (m_Server.GetService ()), m_PeerTest (peerTest), m_State (eSessionStateUnknown),
m_IsSessionKey (false), m_RelayTag (0), m_Data (*this)
m_IsSessionKey (false), m_RelayTag (0), m_Data (*this),
m_NumSentBytes (0), m_NumReceivedBytes (0)
{
m_DHKeysPair = i2p::transports.GetNextDHKeysPair ();
}
@ -74,20 +75,18 @@ namespace ssu
void SSUSession::ProcessNextMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint)
{
m_NumReceivedBytes += len;
if (m_State == eSessionStateIntroduced)
{
// HolePunch received
LogPrint ("SSU HolePuch of ", len, " bytes received");
LogPrint ("SSU HolePunch of ", len, " bytes received");
m_State = eSessionStateUnknown;
Connect ();
}
else
{
if (m_State == eSessionStateEstablished)
ScheduleTermination ();
// check for duplicate
const uint8_t * iv = ((SSUHeader *)buf)->iv;
if (m_ReceivedIVs.count (iv)) return; // duplicate detected
m_ReceivedIVs.insert (iv);
if (m_IsSessionKey && Validate (buf, len, m_MacKey)) // try session key first
DecryptSessionKey (buf, len);
@ -151,6 +150,7 @@ namespace ssu
}
case PAYLOAD_TYPE_RELAY_RESPONSE:
ProcessRelayResponse (buf, len);
if (m_State != eSessionStateEstablished)
m_Server.DeleteSession (this);
break;
case PAYLOAD_TYPE_RELAY_REQUEST:
@ -626,7 +626,7 @@ namespace ssu
if (!m_DelayedMessages.empty ())
{
for (auto it :m_DelayedMessages)
delete it;
DeleteI2NPMessage (it);
m_DelayedMessages.clear ();
}
}
@ -651,7 +651,6 @@ namespace ssu
if (m_State != eSessionStateFailed)
{
m_State = eSessionStateFailed;
Close ();
m_Server.DeleteSession (this); // delete this
}
}
@ -821,6 +820,7 @@ namespace ssu
// encrypt message with session key
FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_DESTROYED, buf, 48);
Send (buf, 48);
LogPrint ("SSU session destoryed sent");
}
}
@ -841,6 +841,7 @@ namespace ssu
void SSUSession::Send (const uint8_t * buf, size_t size)
{
m_NumSentBytes += size;
m_Server.Send (buf, size, m_RemoteEndpoint);
}
@ -909,7 +910,6 @@ namespace ssu
void SSUServer::Send (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& to)
{
m_Socket.send_to (boost::asio::buffer (buf, len), to);
LogPrint ("SSU sent ", len, " bytes");
}
void SSUServer::Receive ()
@ -922,7 +922,6 @@ namespace ssu
{
if (!ecode)
{
LogPrint ("SSU received ", bytes_transferred, " bytes");
SSUSession * session = nullptr;
auto it = m_Sessions.find (m_SenderEndpoint);
if (it != m_Sessions.end ())
@ -985,30 +984,46 @@ namespace ssu
else
{
// connect through introducer
session->WaitForIntroduction ();
if (address->introducers.size () > 0)
int numIntroducers = address->introducers.size ();
if (numIntroducers > 0)
{
auto& introducer = address->introducers[0]; // TODO:
boost::asio::ip::udp::endpoint introducerEndpoint (introducer.iHost, introducer.iPort);
LogPrint ("Creating new SSU session to [", router->GetIdentHashAbbreviation (),
"] through introducer ", introducerEndpoint.address ().to_string (), ":", introducerEndpoint.port ());
it = m_Sessions.find (introducerEndpoint);
SSUSession * introducerSession = nullptr;
const i2p::data::RouterInfo::Introducer * introducer = nullptr;
// we might have a session to introducer already
for (int i = 0; i < numIntroducers; i++)
{
introducer = &(address->introducers[i]);
it = m_Sessions.find (boost::asio::ip::udp::endpoint (introducer->iHost, introducer->iPort));
if (it != m_Sessions.end ())
{
LogPrint ("Session to introducer already exists");
introducerSession = it->second;
break;
}
else
}
if (introducerSession) // session found
LogPrint ("Session to introducer already exists");
else // create new
{
LogPrint ("New session to introducer created");
LogPrint ("Creating new session to introducer");
introducer = &(address->introducers[0]); // TODO:
boost::asio::ip::udp::endpoint introducerEndpoint (introducer->iHost, introducer->iPort);
introducerSession = new SSUSession (*this, introducerEndpoint, router);
m_Sessions[introducerEndpoint] = introducerSession;
}
introducerSession->Introduce (introducer.iTag, introducer.iKey);
// introduce
LogPrint ("Introduce new SSU session to [", router->GetIdentHashAbbreviation (),
"] through introducer ", introducer->iHost, ":", introducer->iPort);
session->WaitForIntroduction ();
introducerSession->Introduce (introducer->iTag, introducer->iKey);
}
else
LogPrint ("Router is unreachable, but not introducers presentd. Ignored");
{
LogPrint ("Router is unreachable, but no introducers presented. Ignored");
m_Sessions.erase (remoteEndpoint);
delete session;
session = nullptr;
}
}
}
}

20
SSU.h
View File

@ -31,7 +31,6 @@ namespace ssu
};
#pragma pack()
const size_t SSU_MTU = 1484;
const int SSU_CONNECT_TIMEOUT = 5; // 5 seconds
const int SSU_TERMINATION_TIMEOUT = 330; // 5.5 minutes
@ -74,6 +73,9 @@ namespace ssu
void SendPeerTest (); // Alice
SessionState GetState () const { return m_State; };
size_t GetNumSentBytes () const { return m_NumSentBytes; };
size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; };
private:
@ -114,20 +116,6 @@ namespace ssu
private:
union IV
{
uint8_t buf[16];
uint64_t ll[2];
IV (const IV&) = default;
IV (const uint8_t * iv) { memcpy (buf, iv, 16); };
bool operator< (const IV& other) const
{
if (ll[0] != other.ll[0]) return ll[0] < other.ll[0];
return ll[1] < other.ll[1];
};
};
friend class SSUData; // TODO: change in later
SSUServer& m_Server;
boost::asio::ip::udp::endpoint m_RemoteEndpoint;
@ -143,8 +131,8 @@ namespace ssu
i2p::crypto::CBCDecryption m_SessionKeyDecryption;
uint8_t m_SessionKey[32], m_MacKey[32];
std::list<i2p::I2NPMessage *> m_DelayedMessages;
std::set<IV> m_ReceivedIVs;
SSUData m_Data;
size_t m_NumSentBytes, m_NumReceivedBytes;
};
class SSUServer

View File

@ -1,4 +1,7 @@
#include <stdlib.h>
#include <boost/bind.hpp>
#include "Log.h"
#include "Timestamp.h"
#include "SSU.h"
#include "SSUData.h"
@ -7,7 +10,7 @@ namespace i2p
namespace ssu
{
SSUData::SSUData (SSUSession& session):
m_Session (session)
m_Session (session), m_ResendTimer (session.m_Server.GetService ())
{
}
@ -20,10 +23,7 @@ namespace ssu
delete it.second;
}
for (auto it: m_SentMessages)
{
for (auto f: it.second)
delete[] f;
}
delete it.second;
}
void SSUData::ProcessSentMessageAck (uint32_t msgID)
@ -31,19 +31,15 @@ namespace ssu
auto it = m_SentMessages.find (msgID);
if (it != m_SentMessages.end ())
{
// delete all ack-ed message's fragments
for (auto f: it->second)
delete[] f;
delete it->second;
m_SentMessages.erase (it);
if (m_SentMessages.empty ())
m_ResendTimer.cancel ();
}
}
void SSUData::ProcessMessage (uint8_t * buf, size_t len)
void SSUData::ProcessAcks (uint8_t *& buf, uint8_t flag)
{
//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
@ -60,13 +56,45 @@ namespace ssu
buf++;
for (int i = 0; i < numBitfields; i++)
{
uint32_t msgID = be32toh (*(uint32_t *)buf);
buf += 4; // msgID
// TODO: process individual Ack bitfields
while (*buf & 0x80) // not last
auto it = m_SentMessages.find (msgID);
// process individual Ack bitfields
bool isNonLast = false;
int fragment = 0;
do
{
uint8_t bitfield = *buf;
isNonLast = bitfield & 0x80;
bitfield &= 0x7F; // clear MSB
if (bitfield && it != m_SentMessages.end ())
{
int numSentFragments = it->second->fragments.size ();
// process bits
uint8_t mask = 0x40;
for (int j = 0; j < 7; j++)
{
if (bitfield & mask)
{
if (fragment < numSentFragments)
{
delete it->second->fragments[fragment];
it->second->fragments[fragment] = nullptr;
}
}
fragment++;
mask >>= 1;
}
}
buf++;
buf++; // last byte
}
while (isNonLast);
}
}
}
void SSUData::ProcessFragments (uint8_t * buf)
{
uint8_t numFragments = *buf; // number of fragments
buf++;
for (int i = 0; i < numFragments; i++)
@ -82,55 +110,80 @@ namespace ssu
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");
// find message with msgID
I2NPMessage * msg = nullptr;
if (fragmentNum > 0) // follow-up fragment
{
IncompleteMessage * incompleteMessage = nullptr;
auto it = m_IncomleteMessages.find (msgID);
if (it != m_IncomleteMessages.end ())
{
if (fragmentNum == it->second->nextFragmentNum)
// message exists
incompleteMessage = it->second;
msg = incompleteMessage->msg;
}
else
{
// create new message
msg = NewI2NPMessage ();
msg->len -= sizeof (I2NPHeaderShort);
incompleteMessage = new IncompleteMessage (msg);
m_IncomleteMessages[msgID] = incompleteMessage;
}
// handle current fragment
if (fragmentNum == incompleteMessage->nextFragmentNum)
{
// expected fragment
msg = it->second->msg;
memcpy (msg->buf + msg->len, buf, fragmentSize);
msg->len += fragmentSize;
it->second->nextFragmentNum++;
incompleteMessage->nextFragmentNum++;
if (!isLast && !incompleteMessage->savedFragments.empty ())
{
// try saved fragments
for (auto it1 = incompleteMessage->savedFragments.begin (); it1 != incompleteMessage->savedFragments.end ();)
{
auto savedFragment = *it1;
if (savedFragment->fragmentNum == incompleteMessage->nextFragmentNum)
{
memcpy (msg->buf + msg->len, savedFragment->buf, savedFragment->len);
msg->len += savedFragment->len;
isLast = savedFragment->isLast;
incompleteMessage->nextFragmentNum++;
incompleteMessage->savedFragments.erase (it1++);
delete savedFragment;
}
else if (fragmentNum < it->second->nextFragmentNum)
else
break;
}
if (isLast)
LogPrint ("Message ", msgID, " complete");
}
}
else
{
if (fragmentNum < incompleteMessage->nextFragmentNum)
// duplicate fragment
LogPrint ("Duplicate fragment ", fragmentNum, " of message ", msgID, ". Ignored");
LogPrint ("Duplicate fragment ", (int)fragmentNum, " of message ", msgID, ". Ignored");
else
{
// missing fragment
LogPrint ("Missing fragments from ", it->second->nextFragmentNum, " to ", fragmentNum - 1, " of message ", msgID);
//TODO
LogPrint ("Missing fragments from ", (int)incompleteMessage->nextFragmentNum, " to ", fragmentNum - 1, " of message ", msgID);
auto savedFragment = new Fragment (fragmentNum, buf, fragmentSize, isLast);
if (!incompleteMessage->savedFragments.insert (savedFragment).second)
{
LogPrint ("Fragment ", (int)fragmentNum, " of message ", msgID, " already saved");
delete savedFragment;
}
}
isLast = false;
}
if (isLast)
{
if (!msg)
DeleteI2NPMessage (it->second->msg);
delete it->second;
m_IncomleteMessages.erase (it);
}
}
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] = new IncompleteMessage (msg);
if (isLast)
{
// delete incomplete message
delete incompleteMessage;
m_IncomleteMessages.erase (msgID);
// process message
SendMsgAck (msgID);
msg->FromSSU (msgID);
if (m_Session.GetState () == eSessionStateEstablished)
@ -148,15 +201,49 @@ namespace ssu
DeleteI2NPMessage (msg);
}
}
}
else
SendFragmentAck (msgID, fragmentNum);
buf += fragmentSize;
}
}
void SSUData::ProcessMessage (uint8_t * buf, size_t len)
{
//uint8_t * start = buf;
uint8_t flag = *buf;
buf++;
LogPrint ("Process SSU data flags=", (int)flag);
// process acks if presented
if (flag & (DATA_FLAG_ACK_BITFIELDS_INCLUDED | DATA_FLAG_EXPLICIT_ACKS_INCLUDED))
ProcessAcks (buf, flag);
// extended data if presented
if (flag & DATA_FLAG_EXTENDED_DATA_INCLUDED)
{
uint8_t extendedDataSize = *buf;
buf++; // size
LogPrint ("SSU extended data of ", extendedDataSize, " bytes presented");
buf += extendedDataSize;
}
// process data
ProcessFragments (buf);
}
void SSUData::Send (i2p::I2NPMessage * msg)
{
uint32_t msgID = msg->ToSSU ();
auto fragments = m_SentMessages[msgID];
if (m_SentMessages.count (msgID) > 0)
{
LogPrint ("SSU message ", msgID, " already sent");
DeleteI2NPMessage (msg);
return;
}
if (m_SentMessages.empty ()) // schedule resend at first message only
ScheduleResend ();
SentMessage * sentMessage = new SentMessage;
m_SentMessages[msgID] = sentMessage;
sentMessage->nextResendTime = i2p::util::GetSecondsSinceEpoch () + RESEND_INTERVAL;
sentMessage->numResends = 0;
auto& fragments = sentMessage->fragments;
msgID = htobe32 (msgID);
size_t payloadSize = SSU_MTU - sizeof (SSUHeader) - 9; // 9 = flag + #frg(1) + messageID(4) + frag info (3)
size_t len = msg->GetLength ();
@ -165,8 +252,9 @@ namespace ssu
uint32_t fragmentNum = 0;
while (len > 0)
{
uint8_t * buf = new uint8_t[SSU_MTU + 18];
fragments.push_back (buf);
Fragment * fragment = new Fragment;
uint8_t * buf = fragment->buf;
fragments.push_back (fragment);
uint8_t * payload = buf + sizeof (SSUHeader);
*payload = DATA_FLAG_WANT_REPLY; // for compatibility
payload++;
@ -189,6 +277,7 @@ namespace ssu
size += payload - buf;
if (size & 0x0F) // make sure 16 bytes boundary
size = ((size >> 4) + 1) << 4; // (/16 + 1)*16
fragment->len = size;
// encrypt message with session key
m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, size);
@ -222,6 +311,63 @@ namespace ssu
m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, 48);
m_Session.Send (buf, 48);
}
void SSUData::SendFragmentAck (uint32_t msgID, int fragmentNum)
{
if (fragmentNum > 64)
{
LogPrint ("Fragment number ", fragmentNum, " exceeds 64");
return;
}
uint8_t buf[64 + 18];
uint8_t * payload = buf + sizeof (SSUHeader);
*payload = DATA_FLAG_ACK_BITFIELDS_INCLUDED; // flag
payload++;
*payload = 1; // number of ACK bitfields
payload++;
// one ack
*(uint32_t *)(payload) = htobe32 (msgID); // msgID
payload += 4;
div_t d = div (fragmentNum, 7);
memset (payload, 0x80, d.quot); // 0x80 means non-last
payload += d.quot;
*payload = 0x40 >> d.rem; // set corresponding bit
payload++;
*payload = 0; // number of fragments
size_t len = d.quot < 4 ? 48 : 64; // 48 = 37 + 7 + 4 (3+1)
// encrypt message with session key
m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, len);
m_Session.Send (buf, len);
}
void SSUData::ScheduleResend()
{
m_ResendTimer.cancel ();
m_ResendTimer.expires_from_now (boost::posix_time::seconds(RESEND_INTERVAL));
m_ResendTimer.async_wait (boost::bind (&SSUData::HandleResendTimer,
this, boost::asio::placeholders::error));
}
void SSUData::HandleResendTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
for (auto it : m_SentMessages)
{
if (ts >= it.second->nextResendTime && it.second->numResends < MAX_NUM_RESENDS)
{
for (auto f: it.second->fragments)
if (f) m_Session.Send (f->buf, f->len); // resend
it.second->numResends++;
it.second->nextResendTime += it.second->numResends*RESEND_INTERVAL;
}
}
ScheduleResend ();
}
}
}
}

View File

@ -2,8 +2,11 @@
#define SSU_DATA_H__
#include <inttypes.h>
#include <string.h>
#include <map>
#include <vector>
#include <set>
#include <boost/asio.hpp>
#include "I2NPProtocol.h"
namespace i2p
@ -11,6 +14,9 @@ namespace i2p
namespace ssu
{
const size_t SSU_MTU = 1484;
const int RESEND_INTERVAL = 3; // in seconds
const int MAX_NUM_RESENDS = 5;
// data flags
const uint8_t DATA_FLAG_EXTENDED_DATA_INCLUDED = 0x02;
const uint8_t DATA_FLAG_WANT_REPLY = 0x04;
@ -19,6 +25,45 @@ namespace ssu
const uint8_t DATA_FLAG_ACK_BITFIELDS_INCLUDED = 0x40;
const uint8_t DATA_FLAG_EXPLICIT_ACKS_INCLUDED = 0x80;
struct Fragment
{
int fragmentNum;
size_t len;
bool isLast;
uint8_t buf[SSU_MTU + 18];
Fragment () = default;
Fragment (int n, const uint8_t * b, int l, bool last):
fragmentNum (n), len (l), isLast (last) { memcpy (buf, b, len); };
};
struct FragmentCmp
{
bool operator() (const Fragment * f1, const Fragment * f2) const
{
return f1->fragmentNum < f2->fragmentNum;
};
};
struct IncompleteMessage
{
I2NPMessage * msg;
int nextFragmentNum;
std::set<Fragment *, FragmentCmp> savedFragments;
IncompleteMessage (I2NPMessage * m): msg (m), nextFragmentNum (0) {};
~IncompleteMessage () { for (auto it: savedFragments) { delete it; }; };
};
struct SentMessage
{
std::vector<Fragment *> fragments;
uint32_t nextResendTime; // in seconds
int numResends;
~SentMessage () { for (auto it: fragments) { delete it; }; };
};
class SSUSession;
class SSUData
{
@ -33,21 +78,20 @@ namespace ssu
private:
void SendMsgAck (uint32_t msgID);
void SendFragmentAck (uint32_t msgID, int fragmentNum);
void ProcessAcks (uint8_t *& buf, uint8_t flag);
void ProcessFragments (uint8_t * buf);
void ProcessSentMessageAck (uint32_t msgID);
void ScheduleResend ();
void HandleResendTimer (const boost::system::error_code& ecode);
private:
struct IncompleteMessage
{
I2NPMessage * msg;
uint8_t nextFragmentNum;
IncompleteMessage (I2NPMessage * m): msg (m), nextFragmentNum (1) {};
};
SSUSession& m_Session;
std::map<uint32_t, IncompleteMessage *> m_IncomleteMessages;
std::map<uint32_t, std::vector<uint8_t *> > m_SentMessages; // msgID -> fragments
std::map<uint32_t, SentMessage *> m_SentMessages;
boost::asio::deadline_timer m_ResendTimer;
};
}
}

View File

@ -2,6 +2,7 @@
#include <algorithm>
#include <cryptopp/dh.h>
#include <cryptopp/gzip.h>
#include "util.h"
#include "Log.h"
#include "RouterInfo.h"
#include "RouterContext.h"
@ -9,6 +10,7 @@
#include "Timestamp.h"
#include "CryptoConst.h"
#include "Garlic.h"
#include "NetDb.h"
#include "Streaming.h"
namespace i2p
@ -17,14 +19,22 @@ namespace stream
{
Stream::Stream (boost::asio::io_service& service, StreamingDestination * local,
const i2p::data::LeaseSet& remote): m_Service (service), m_SendStreamID (0),
m_SequenceNumber (0), m_LastReceivedSequenceNumber (0), m_IsOpen (false),
m_LeaseSetUpdated (true), m_LocalDestination (local), m_RemoteLeaseSet (remote),
m_OutboundTunnel (nullptr), m_ReceiveTimer (m_Service)
m_SequenceNumber (0), m_LastReceivedSequenceNumber (-1), m_IsOpen (false),
m_IsOutgoing(true), m_LeaseSetUpdated (true), m_LocalDestination (local),
m_RemoteLeaseSet (&remote), m_ReceiveTimer (m_Service)
{
m_RecvStreamID = i2p::context.GetRandomNumberGenerator ().GenerateWord32 ();
UpdateCurrentRemoteLease ();
}
Stream::Stream (boost::asio::io_service& service, StreamingDestination * local):
m_Service (service), m_SendStreamID (0), m_SequenceNumber (0), m_LastReceivedSequenceNumber (-1),
m_IsOpen (false), m_IsOutgoing(true), m_LeaseSetUpdated (true), m_LocalDestination (local),
m_RemoteLeaseSet (nullptr), m_ReceiveTimer (m_Service)
{
m_RecvStreamID = i2p::context.GetRandomNumberGenerator ().GenerateWord32 ();
}
Stream::~Stream ()
{
m_ReceiveTimer.cancel ();
@ -43,9 +53,18 @@ namespace stream
if (!m_SendStreamID)
m_SendStreamID = packet->GetReceiveStreamID ();
uint32_t receivedSeqn = packet->GetSeqn ();
int32_t receivedSeqn = packet->GetSeqn ();
bool isSyn = packet->IsSYN ();
if (!receivedSeqn && !isSyn)
{
// plain ack
LogPrint ("Plain ACK received");
delete packet;
return;
}
LogPrint ("Received seqn=", receivedSeqn);
if (!receivedSeqn || receivedSeqn == m_LastReceivedSequenceNumber + 1)
if (isSyn || receivedSeqn == m_LastReceivedSequenceNumber + 1)
{
// we have received next in sequence message
ProcessPacket (packet);
@ -53,7 +72,7 @@ namespace stream
// we should also try stored messages if any
for (auto it = m_SavedPackets.begin (); it != m_SavedPackets.end ();)
{
if ((*it)->GetSeqn () == m_LastReceivedSequenceNumber + 1)
if ((*it)->GetSeqn () == (uint32_t)(m_LastReceivedSequenceNumber + 1))
{
Packet * savedPacket = *it;
m_SavedPackets.erase (it++);
@ -67,6 +86,13 @@ namespace stream
// send ack for last message
if (m_IsOpen)
SendQuickAck ();
else if (isSyn)
{
// we have to send SYN back to incoming connection
m_IsOpen = true;
SendQuickAck (true);
}
}
else
{
@ -75,8 +101,6 @@ namespace stream
// we have received duplicate. Most likely our outbound tunnel is dead
LogPrint ("Duplicate message ", receivedSeqn, " received");
UpdateCurrentRemoteLease (); // pick another lease
m_OutboundTunnel = i2p::tunnel::tunnels.GetNextOutboundTunnel (); // pick another tunnel
if (m_OutboundTunnel)
SendQuickAck (); // resend ack for previous message again
delete packet; // packet dropped
}
@ -107,16 +131,30 @@ namespace stream
LogPrint ("Synchronize");
}
if (flags & PACKET_FLAG_SIGNATURE_INCLUDED)
if (flags & PACKET_FLAG_DELAY_REQUESTED)
{
LogPrint ("Signature");
optionData += 40;
optionData += 2;
}
if (flags & PACKET_FLAG_FROM_INCLUDED)
{
LogPrint ("From identity");
optionData += sizeof (i2p::data::Identity);
optionData += m_RemoteIdentity.FromBuffer (optionData, i2p::data::DEFAULT_IDENTITY_SIZE);
LogPrint ("From identity ", m_RemoteIdentity.Hash ().ToBase64 ());
if (!m_RemoteLeaseSet)
LogPrint ("Incoming stream from ", m_RemoteIdentity.Hash ().ToBase64 ());
}
if (flags & PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED)
{
uint16_t maxPacketSize = be16toh (*(uint16_t *)optionData);
LogPrint ("Max packet size ", maxPacketSize);
optionData += 2;
}
if (flags & PACKET_FLAG_SIGNATURE_INCLUDED)
{
LogPrint ("Signature");
optionData += 40;
}
packet->offset = packet->GetPayload () - packet->buf;
@ -135,6 +173,7 @@ namespace stream
LogPrint ("Closed");
SendQuickAck (); // send ack for close explicitly?
m_IsOpen = false;
m_ReceiveTimer.cancel ();
}
}
@ -163,10 +202,10 @@ namespace stream
PACKET_FLAG_FROM_INCLUDED | PACKET_FLAG_SIGNATURE_INCLUDED |
PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED | PACKET_FLAG_NO_ACK);
size += 2; // flags
*(uint16_t *)(packet + size) = htobe16 (sizeof (i2p::data::Identity) + 40 + 2); // identity + signature + packet size
*(uint16_t *)(packet + size) = htobe16 (i2p::data::DEFAULT_IDENTITY_SIZE + 40 + 2); // identity + signature + packet size
size += 2; // options size
memcpy (packet + size, &m_LocalDestination->GetIdentity (), sizeof (i2p::data::Identity));
size += sizeof (i2p::data::Identity); // from
memcpy (packet + size, &m_LocalDestination->GetIdentity (), i2p::data::DEFAULT_IDENTITY_SIZE);
size += i2p::data::DEFAULT_IDENTITY_SIZE; // from
*(uint16_t *)(packet + size) = htobe16 (STREAMING_MTU);
size += 2; // max packet size
uint8_t * signature = packet + size; // set it later
@ -193,7 +232,7 @@ namespace stream
}
void Stream::SendQuickAck ()
void Stream::SendQuickAck (bool syn)
{
uint8_t packet[MAX_PACKET_SIZE];
size_t size = 0;
@ -208,7 +247,7 @@ namespace stream
packet[size] = 0;
size++; // NACK count
size++; // resend delay
*(uint16_t *)(packet + size) = 0; // nof flags set
*(uint16_t *)(packet + size) = syn ? htobe16 (PACKET_FLAG_SYNCHRONIZE) : 0; // nof flags set
size += 2; // flags
*(uint16_t *)(packet + size) = 0; // no options
size += 2; // options size
@ -222,7 +261,8 @@ namespace stream
if (m_IsOpen)
{
m_IsOpen = false;
uint8_t packet[MAX_PACKET_SIZE];
Packet * p = new Packet ();
uint8_t * packet = p->GetBuffer ();
size_t size = 0;
*(uint32_t *)(packet + size) = htobe32 (m_SendStreamID);
size += 4; // sendStreamID
@ -235,7 +275,7 @@ namespace stream
packet[size] = 0;
size++; // NACK count
size++; // resend delay
*(uint16_t *)(packet + size) = PACKET_FLAG_CLOSE | PACKET_FLAG_SIGNATURE_INCLUDED;
*(uint16_t *)(packet + size) = htobe16 (PACKET_FLAG_CLOSE | PACKET_FLAG_SIGNATURE_INCLUDED);
size += 2; // flags
*(uint16_t *)(packet + size) = htobe16 (40); // 40 bytes signature
size += 2; // options size
@ -244,7 +284,8 @@ namespace stream
size += 40; // signature
m_LocalDestination->Sign (packet, size, signature);
if (SendPacket (packet, size))
p->len = size;
m_Service.post (boost::bind (&Stream::SendPacket, this, p));
LogPrint ("FIN sent");
}
}
@ -282,26 +323,35 @@ namespace stream
bool Stream::SendPacket (const uint8_t * buf, size_t len)
{
const I2NPMessage * leaseSet = nullptr;
if (!m_RemoteLeaseSet)
{
UpdateCurrentRemoteLease ();
if (!m_RemoteLeaseSet)
{
LogPrint ("Can't send packet. Missing remote LeaseSet");
return false;
}
}
I2NPMessage * leaseSet = nullptr;
if (m_LeaseSetUpdated)
{
leaseSet = m_LocalDestination->GetLeaseSet ();
leaseSet = m_LocalDestination->GetLeaseSetMsg ();
m_LeaseSetUpdated = false;
}
I2NPMessage * msg = i2p::garlic::routing.WrapMessage (m_RemoteLeaseSet,
I2NPMessage * msg = i2p::garlic::routing.WrapMessage (*m_RemoteLeaseSet,
CreateDataMessage (this, buf, len), leaseSet);
if (!m_OutboundTunnel || m_OutboundTunnel->IsFailed ())
m_OutboundTunnel = m_LocalDestination->GetTunnelPool ()->GetNextOutboundTunnel ();
if (m_OutboundTunnel)
auto outboundTunnel = m_LocalDestination->GetTunnelPool ()->GetNextOutboundTunnel ();
if (outboundTunnel)
{
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
if (ts >= m_CurrentRemoteLease.endDate)
UpdateCurrentRemoteLease ();
if (ts < m_CurrentRemoteLease.endDate)
{
m_OutboundTunnel->SendTunnelDataMsg (m_CurrentRemoteLease.tunnelGateway, m_CurrentRemoteLease.tunnelID, msg);
outboundTunnel->SendTunnelDataMsg (m_CurrentRemoteLease.tunnelGateway, m_CurrentRemoteLease.tunnelID, msg);
return true;
}
else
@ -320,7 +370,15 @@ namespace stream
void Stream::UpdateCurrentRemoteLease ()
{
auto leases = m_RemoteLeaseSet.GetNonExpiredLeases ();
if (!m_RemoteLeaseSet)
{
m_RemoteLeaseSet = i2p::data::netdb.FindLeaseSet (m_RemoteIdentity.Hash ());
if (!m_RemoteLeaseSet)
LogPrint ("LeaseSet ", m_RemoteIdentity.Hash ().ToBase64 (), " not found");
}
if (m_RemoteLeaseSet)
{
auto leases = m_RemoteLeaseSet->GetNonExpiredLeases ();
if (!leases.empty ())
{
uint32_t i = i2p::context.GetRandomNumberGenerator ().GenerateWord32 (0, leases.size () - 1);
@ -329,9 +387,13 @@ namespace stream
else
m_CurrentRemoteLease.endDate = 0;
}
else
m_CurrentRemoteLease.endDate = 0;
}
StreamingDestination::StreamingDestination (): m_LeaseSet (nullptr)
StreamingDestination::StreamingDestination (boost::asio::io_service& service):
m_Service (service), m_LeaseSet (nullptr)
{
m_Keys = i2p::data::CreateRandomKeys ();
@ -343,7 +405,8 @@ namespace stream
m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (*this, 3); // 3-hops tunnel
}
StreamingDestination::StreamingDestination (const std::string& fullPath): m_LeaseSet (nullptr)
StreamingDestination::StreamingDestination (boost::asio::io_service& service, const std::string& fullPath):
m_Service (service), m_LeaseSet (nullptr)
{
std::ifstream s(fullPath.c_str (), std::ifstream::binary);
if (s.is_open ())
@ -361,15 +424,16 @@ namespace stream
StreamingDestination::~StreamingDestination ()
{
if (m_LeaseSet)
DeleteI2NPMessage (m_LeaseSet);
if (m_Pool)
i2p::tunnel::tunnels.DeleteTunnelPool (m_Pool);
delete m_LeaseSet;
}
void StreamingDestination::HandleNextPacket (Packet * packet)
{
uint32_t sendStreamID = packet->GetSendStreamID ();
if (sendStreamID)
{
auto it = m_Streams.find (sendStreamID);
if (it != m_Streams.end ())
it->second->HandleNextPacket (packet);
@ -379,11 +443,25 @@ namespace stream
delete packet;
}
}
Stream * StreamingDestination::CreateNewStream (boost::asio::io_service& service,
const i2p::data::LeaseSet& remote)
else // new incoming stream
{
Stream * s = new Stream (service, this, remote);
auto incomingStream = CreateNewIncomingStream ();
incomingStream->HandleNextPacket (packet);
if (m_Acceptor != nullptr)
m_Acceptor (incomingStream);
}
}
Stream * StreamingDestination::CreateNewOutgoingStream (const i2p::data::LeaseSet& remote)
{
Stream * s = new Stream (m_Service, this, remote);
m_Streams[s->GetRecvStreamID ()] = s;
return s;
}
Stream * StreamingDestination::CreateNewIncomingStream ()
{
Stream * s = new Stream (m_Service, this);
m_Streams[s->GetRecvStreamID ()] = s;
return s;
}
@ -397,67 +475,28 @@ namespace stream
}
}
void StreamingDestination::UpdateLeaseSet ()
I2NPMessage * StreamingDestination::GetLeaseSetMsg ()
{
auto newLeaseSet = CreateLeaseSet ();
return CreateDatabaseStoreMsg (GetLeaseSet ());
}
const i2p::data::LeaseSet * StreamingDestination::GetLeaseSet ()
{
if (!m_Pool) return nullptr;
if (!m_LeaseSet || m_LeaseSet->HasExpiredLeases ())
{
auto newLeaseSet = new i2p::data::LeaseSet (*m_Pool);
// TODO: make it atomic
auto oldLeaseSet = m_LeaseSet;
m_LeaseSet = newLeaseSet;
if (oldLeaseSet)
DeleteI2NPMessage (oldLeaseSet);
delete oldLeaseSet;
for (auto it: m_Streams)
it.second->SetLeaseSetUpdated ();
}
const I2NPMessage * StreamingDestination::GetLeaseSet ()
{
if (!m_LeaseSet)
m_LeaseSet = CreateLeaseSet ();
else
RenewI2NPMessageHeader (m_LeaseSet);
return m_LeaseSet;
}
I2NPMessage * StreamingDestination::CreateLeaseSet () const
{
I2NPMessage * m = NewI2NPMessage ();
I2NPDatabaseStoreMsg * msg = (I2NPDatabaseStoreMsg *)m->GetPayload ();
memcpy (msg->key, (const uint8_t *)m_IdentHash, 32);
msg->type = 1; // LeaseSet
msg->replyToken = 0;
uint8_t * buf = m->GetPayload () + sizeof (I2NPDatabaseStoreMsg);
size_t size = 0;
memcpy (buf + size, &m_Keys.pub, sizeof (m_Keys.pub));
size += sizeof (m_Keys.pub); // destination
memcpy (buf + size, m_Pool->GetEncryptionPublicKey (), 256);
size += 256; // encryption key
memset (buf + size, 0, 128);
size += 128; // signing key
auto tunnels = m_Pool->GetInboundTunnels (5); // 5 tunnels maximum
buf[size] = tunnels.size (); // num leases
size++; // num
for (auto it: tunnels)
{
auto tunnel = it;
memcpy (buf + size, (const uint8_t *)tunnel->GetNextIdentHash (), 32);
size += 32; // tunnel_gw
*(uint32_t *)(buf + size) = htobe32 (tunnel->GetNextTunnelID ());
size += 4; // tunnel_id
uint64_t ts = tunnel->GetCreationTime () + i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT - 60; // 1 minute before expiration
ts *= 1000; // in milliseconds
*(uint64_t *)(buf + size) = htobe64 (ts);
size += 8; // end_date
}
Sign (buf, size, buf+ size);
size += 40; // signature
LogPrint ("Local LeaseSet of ", tunnels.size (), " leases created");
m->len += size + sizeof (I2NPDatabaseStoreMsg);
FillI2NPMessageHeader (m, eI2NPDatabaseStore);
return m;
}
void StreamingDestination::Sign (uint8_t * buf, int len, uint8_t * signature) const
void StreamingDestination::Sign (const uint8_t * buf, int len, uint8_t * signature) const
{
CryptoPP::DSA::Signer signer (m_SigningPrivateKey);
signer.SignMessage (i2p::context.GetRandomNumberGenerator (), buf, len, signature);
@ -467,7 +506,11 @@ namespace stream
void StreamingDestinations::Start ()
{
if (!m_SharedLocalDestination)
m_SharedLocalDestination = new StreamingDestination ();
{
m_SharedLocalDestination = new StreamingDestination (m_Service);
m_Destinations[m_SharedLocalDestination->GetIdentHash ()] = m_SharedLocalDestination;
}
LoadLocalDestinations ();
m_IsRunning = true;
m_Thread = new std::thread (std::bind (&StreamingDestinations::Run, this));
@ -475,7 +518,10 @@ namespace stream
void StreamingDestinations::Stop ()
{
delete m_SharedLocalDestination;
for (auto it: m_Destinations)
delete it.second;
m_Destinations.clear ();
m_SharedLocalDestination = 0; // deleted through m_Destination
m_IsRunning = false;
m_Service.stop ();
@ -492,18 +538,47 @@ namespace stream
m_Service.run ();
}
void StreamingDestinations::LoadLocalDestinations ()
{
int numDestinations = 0;
boost::filesystem::path p (i2p::util::filesystem::GetDataDir());
boost::filesystem::directory_iterator end;
for (boost::filesystem::directory_iterator it (p); it != end; ++it)
{
if (boost::filesystem::is_regular_file (*it) && it->path ().extension () == ".dat")
{
auto fullPath =
#if BOOST_VERSION > 10500
it->path().string();
#else
it->path();
#endif
auto localDestination = new StreamingDestination (m_Service, fullPath);
m_Destinations[localDestination->GetIdentHash ()] = localDestination;
numDestinations++;
}
}
if (numDestinations > 0)
LogPrint (numDestinations, " local destinations loaded");
}
Stream * StreamingDestinations::CreateClientStream (const i2p::data::LeaseSet& remote)
{
if (!m_SharedLocalDestination) return nullptr;
return m_SharedLocalDestination->CreateNewStream (m_Service, remote);
return m_SharedLocalDestination->CreateNewOutgoingStream (remote);
}
void StreamingDestinations::DeleteClientStream (Stream * stream)
void StreamingDestinations::DeleteStream (Stream * stream)
{
if (m_SharedLocalDestination)
m_SharedLocalDestination->DeleteStream (stream);
else
delete stream;
if (stream)
{
m_Service.post (
[=](void)
{
stream->GetLocalDestination ()->DeleteStream (stream);
}
);
}
}
void StreamingDestinations::HandleNextPacket (i2p::data::IdentHash destination, Packet * packet)
@ -513,9 +588,14 @@ namespace stream
void StreamingDestinations::PostNextPacket (i2p::data::IdentHash destination, Packet * packet)
{
// TODO: we have onle one destination, might be more
if (m_SharedLocalDestination)
m_SharedLocalDestination->HandleNextPacket (packet);
auto it = m_Destinations.find (destination);
if (it != m_Destinations.end ())
it->second->HandleNextPacket (packet);
else
{
LogPrint ("Local destination ", destination.ToBase64 (), " not found");
delete packet;
}
}
Stream * CreateStream (const i2p::data::LeaseSet& remote)
@ -525,7 +605,7 @@ namespace stream
void DeleteStream (Stream * stream)
{
destinations.DeleteClientStream (stream);
destinations.DeleteStream (stream);
}
void StartStreaming ()
@ -538,6 +618,11 @@ namespace stream
destinations.Stop ();
}
StreamingDestination * GetSharedLocalDestination ()
{
return destinations.GetSharedLocalDestination ();
}
void HandleDataMessage (i2p::data::IdentHash destination, const uint8_t * buf, size_t len)
{
uint32_t length = be32toh (*(uint32_t *)buf);
@ -554,7 +639,7 @@ namespace stream
uncompressed->len = decompressor.MaxRetrievable ();
if (uncompressed->len > MAX_PACKET_SIZE)
{
LogPrint ("Recieved packet size exceeds mac packet size");
LogPrint ("Received packet size ", uncompressed->len, " exceeds max packet size");
uncompressed->len = MAX_PACKET_SIZE;
}
decompressor.Get (uncompressed->buf, uncompressed->len);

View File

@ -7,6 +7,7 @@
#include <set>
#include <queue>
#include <thread>
#include <functional>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <cryptopp/dsa.h>
@ -14,7 +15,6 @@
#include "Identity.h"
#include "LeaseSet.h"
#include "I2NPProtocol.h"
#include "Tunnel.h"
#include "TunnelPool.h"
namespace i2p
@ -34,7 +34,7 @@ namespace stream
const uint16_t PACKET_FLAG_NO_ACK = 0x0400;
const size_t STREAMING_MTU = 1730;
const size_t MAX_PACKET_SIZE = 1754;
const size_t MAX_PACKET_SIZE = 4096;
struct Packet
{
@ -55,6 +55,8 @@ namespace stream
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 (); };
bool IsSYN () const { return GetFlags () & PACKET_FLAG_SYNCHRONIZE; };
};
struct PacketCmp
@ -70,13 +72,16 @@ namespace stream
{
public:
Stream (boost::asio::io_service& service, StreamingDestination * local, const i2p::data::LeaseSet& remote);
Stream (boost::asio::io_service& service, StreamingDestination * local, const i2p::data::LeaseSet& remote); // outgoing
Stream (boost::asio::io_service& service, StreamingDestination * local); // incoming
~Stream ();
uint32_t GetSendStreamID () const { return m_SendStreamID; };
uint32_t GetRecvStreamID () const { return m_RecvStreamID; };
const i2p::data::LeaseSet& GetRemoteLeaseSet () const { return m_RemoteLeaseSet; };
const i2p::data::LeaseSet * GetRemoteLeaseSet () const { return m_RemoteLeaseSet; };
bool IsOpen () const { return m_IsOpen; };
bool IsEstablished () const { return m_SendStreamID; };
StreamingDestination * GetLocalDestination () { return m_LocalDestination; };
void HandleNextPacket (Packet * packet);
size_t Send (const uint8_t * buf, size_t len, int timeout); // timeout in seconds
@ -90,7 +95,7 @@ namespace stream
private:
void SendQuickAck ();
void SendQuickAck (bool syn = false);
bool SendPacket (Packet * packet);
bool SendPacket (const uint8_t * buf, size_t len);
@ -106,14 +111,15 @@ namespace stream
private:
boost::asio::io_service& m_Service;
uint32_t m_SendStreamID, m_RecvStreamID, m_SequenceNumber, m_LastReceivedSequenceNumber;
bool m_IsOpen, m_LeaseSetUpdated;
uint32_t m_SendStreamID, m_RecvStreamID, m_SequenceNumber;
int32_t m_LastReceivedSequenceNumber;
bool m_IsOpen, m_IsOutgoing, m_LeaseSetUpdated;
StreamingDestination * m_LocalDestination;
const i2p::data::LeaseSet& m_RemoteLeaseSet;
i2p::data::Identity m_RemoteIdentity;
const i2p::data::LeaseSet * m_RemoteLeaseSet;
i2p::data::Lease m_CurrentRemoteLease;
std::queue<Packet *> m_ReceiveQueue;
std::set<Packet *, PacketCmp> m_SavedPackets;
i2p::tunnel::OutboundTunnel * m_OutboundTunnel;
boost::asio::deadline_timer m_ReceiveTimer;
};
@ -121,41 +127,44 @@ namespace stream
{
public:
StreamingDestination ();
StreamingDestination (const std::string& fullPath);
StreamingDestination (boost::asio::io_service& service);
StreamingDestination (boost::asio::io_service& service, const std::string& fullPath);
~StreamingDestination ();
const i2p::data::PrivateKeys& GetKeys () const { return m_Keys; };
const i2p::data::Identity& GetIdentity () const { return m_Keys.pub; };
const I2NPMessage * GetLeaseSet ();
I2NPMessage * GetLeaseSetMsg ();
const i2p::data::LeaseSet * GetLeaseSet ();
i2p::tunnel::TunnelPool * GetTunnelPool () const { return m_Pool; };
void Sign (uint8_t * buf, int len, uint8_t * signature) const;
Stream * CreateNewStream (boost::asio::io_service& service, const i2p::data::LeaseSet& remote);
Stream * CreateNewOutgoingStream (const i2p::data::LeaseSet& remote);
void DeleteStream (Stream * stream);
void SetAcceptor (const std::function<void (Stream *)>& acceptor) { m_Acceptor = acceptor; };
void HandleNextPacket (Packet * packet);
// implements LocalDestination
void UpdateLeaseSet ();
const i2p::data::IdentHash& GetIdentHash () const { return m_IdentHash; };
const i2p::data::Identity& GetIdentity () const { return m_Keys.pub; };
const uint8_t * GetEncryptionPrivateKey () const { return m_EncryptionPrivateKey; };
const uint8_t * GetEncryptionPublicKey () const { return m_EncryptionPublicKey; };
void Sign (const uint8_t * buf, int len, uint8_t * signature) const;
private:
I2NPMessage * CreateLeaseSet () const;
Stream * CreateNewIncomingStream ();
private:
boost::asio::io_service& m_Service;
std::map<uint32_t, Stream *> m_Streams;
i2p::data::PrivateKeys m_Keys;
i2p::data::IdentHash m_IdentHash;
uint8_t m_EncryptionPublicKey[256], m_EncryptionPrivateKey[256];
i2p::tunnel::TunnelPool * m_Pool;
I2NPMessage * m_LeaseSet;
i2p::data::LeaseSet * m_LeaseSet;
CryptoPP::DSA::PrivateKey m_SigningPrivateKey;
std::function<void (Stream *)> m_Acceptor;
};
class StreamingDestinations
@ -172,11 +181,13 @@ namespace stream
void HandleNextPacket (i2p::data::IdentHash destination, Packet * packet);
Stream * CreateClientStream (const i2p::data::LeaseSet& remote);
void DeleteClientStream (Stream * stream);
void DeleteStream (Stream * stream);
StreamingDestination * GetSharedLocalDestination () const { return m_SharedLocalDestination; };
private:
void Run ();
void LoadLocalDestinations ();
void PostNextPacket (i2p::data::IdentHash destination, Packet * packet);
private:
@ -186,6 +197,7 @@ namespace stream
boost::asio::io_service m_Service;
boost::asio::io_service::work m_Work;
std::map<i2p::data::IdentHash, StreamingDestination *> m_Destinations;
StreamingDestination * m_SharedLocalDestination;
};
@ -193,6 +205,7 @@ namespace stream
void DeleteStream (Stream * stream);
void StartStreaming ();
void StopStreaming ();
StreamingDestination * GetSharedLocalDestination ();
// assuming data is I2CP message
void HandleDataMessage (i2p::data::IdentHash destination, const uint8_t * buf, size_t len);
@ -213,6 +226,11 @@ namespace stream
return;
}
}
if (!m_IsOpen)
{
handler (boost::asio::error::make_error_code (boost::asio::error::connection_reset), 0);
return;
}
m_ReceiveTimer.expires_from_now (boost::posix_time::seconds(timeout));
m_ReceiveTimer.async_wait ([=](const boost::system::error_code& ecode)
{ this->HandleReceiveTimer (ecode, buffer, handler); });

View File

@ -30,11 +30,11 @@ namespace tunnel
EncryptTunnelMsg (tunnelMsg);
LogPrint ("TransitTunnel: ",m_TunnelID,"->", m_NextTunnelID);
m_NumTransmittedBytes += tunnelMsg->GetLength ();
*(uint32_t *)(tunnelMsg->GetPayload ()) = htobe32 (m_NextTunnelID);
FillI2NPMessageHeader (tunnelMsg, eI2NPTunnelData);
i2p::transports.SendMessage (m_NextIdent, tunnelMsg);
m_NumTransmittedBytes += tunnelMsg->GetLength ();
}
void TransitTunnel::SendTunnelDataMsg (i2p::I2NPMessage * msg)
@ -48,6 +48,7 @@ namespace tunnel
TunnelMessageBlock block;
block.deliveryType = eDeliveryTypeLocal;
block.data = msg;
std::unique_lock<std::mutex> l(m_SendMutex);
m_Gateway.SendTunnelDataMsg (block);
}

View File

@ -2,6 +2,7 @@
#define TRANSIT_TUNNEL_H__
#include <inttypes.h>
#include <mutex>
#include "aes.h"
#include "I2NPProtocol.h"
#include "TunnelEndpoint.h"
@ -55,6 +56,7 @@ namespace tunnel
private:
std::mutex m_SendMutex;
TunnelGateway m_Gateway;
};
@ -65,7 +67,8 @@ namespace tunnel
TransitTunnelEndpoint (uint32_t receiveTunnelID,
const uint8_t * nextIdent, uint32_t nextTunnelID,
const uint8_t * layerKey,const uint8_t * ivKey):
TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey) {};
TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey),
m_Endpoint (false) {}; // transit endpoint is always outbound
void HandleTunnelDataMsg (i2p::I2NPMessage * tunnelMsg);
size_t GetNumTransmittedBytes () const { return m_Endpoint.GetNumReceivedBytes (); }

View File

@ -52,6 +52,7 @@ namespace i2p
{
i2p::data::DHKeysPair * pair = new i2p::data::DHKeysPair ();
i2p::data::CreateRandomDHKeysPair (pair);
std::unique_lock<std::mutex> l(m_AcquiredMutex);
m_Queue.push (pair);
}
}
@ -61,6 +62,7 @@ namespace i2p
{
if (!m_Queue.empty ())
{
std::unique_lock<std::mutex> l(m_AcquiredMutex);
auto pair = m_Queue.front ();
m_Queue.pop ();
m_Acquired.notify_one ();
@ -230,9 +232,9 @@ namespace i2p
else
{
// existing session not found. create new
// try NTCP first
// try NTCP first if message size < 16K
auto address = r->GetNTCPAddress ();
if (address && !r->UsesIntroducer () && !r->IsUnreachable ())
if (address && !r->UsesIntroducer () && !r->IsUnreachable () && msg->GetLength () < i2p::ntcp::NTCP_MAX_MESSAGE_SIZE)
{
auto s = new i2p::ntcp::NTCPClient (m_Service, address->host, address->port, *r);
AddNTCPSession (s);
@ -245,7 +247,10 @@ namespace i2p
if (s)
s->SendI2NPMessage (msg);
else
{
LogPrint ("No NTCP and SSU addresses available");
DeleteI2NPMessage (msg);
}
}
}
}
@ -253,10 +258,28 @@ namespace i2p
{
LogPrint ("Router not found. Requested");
i2p::data::netdb.RequestDestination (ident);
DeleteI2NPMessage (msg); // TODO: implement a placeholder for router and send once it's available
}
}
}
void Transports::CloseSession (const i2p::data::RouterInfo * router)
{
if (!router) return;
m_Service.post (boost::bind (&Transports::PostCloseSession, this, router));
}
void Transports::PostCloseSession (const i2p::data::RouterInfo * router)
{
auto ssuSession = m_SSUServer ? m_SSUServer->FindSession (router) : nullptr;
if (ssuSession) // try SSU first
{
m_SSUServer->DeleteSession (ssuSession);
LogPrint ("SSU session closed");
}
// TODO: delete NTCP
}
void Transports::DetectExternalIP ()
{
for (int i = 0; i < 5; i ++)

View File

@ -63,12 +63,14 @@ namespace i2p
i2p::ntcp::NTCPSession * FindNTCPSession (const i2p::data::IdentHash& ident);
void SendMessage (const i2p::data::IdentHash& ident, i2p::I2NPMessage * msg);
void CloseSession (const i2p::data::RouterInfo * router);
private:
void Run ();
void HandleAccept (i2p::ntcp::NTCPServerConnection * conn, const boost::system::error_code& error);
void PostMessage (const i2p::data::IdentHash& ident, i2p::I2NPMessage * msg);
void PostCloseSession (const i2p::data::RouterInfo * router);
void DetectExternalIP ();

View File

@ -14,8 +14,8 @@ namespace i2p
namespace tunnel
{
Tunnel::Tunnel (TunnelConfig * config): m_Config (config), m_Pool (nullptr),
m_IsEstablished (false), m_IsFailed (false)
Tunnel::Tunnel (TunnelConfig * config):
m_Config (config), m_Pool (nullptr), m_State (eTunnelStatePending)
{
}
@ -111,7 +111,7 @@ namespace tunnel
hop = hop->prev;
}
m_IsEstablished = true;
bool established = true;
hop = m_Config->GetFirstHop ();
while (hop)
{
@ -119,10 +119,10 @@ namespace tunnel
LogPrint ("Ret code=", (int)record->ret);
if (record->ret)
// if any of participants declined the tunnel is not established
m_IsEstablished = false;
established = false;
hop = hop->next;
}
if (m_IsEstablished)
if (established)
{
// change reply keys to layer keys
hop = m_Config->GetFirstHop ();
@ -132,7 +132,8 @@ namespace tunnel
hop = hop->next;
}
}
return m_IsEstablished;
if (established) m_State = eTunnelStateEstablished;
return established;
}
void Tunnel::EncryptTunnelMsg (I2NPMessage * tunnelMsg)
@ -148,7 +149,7 @@ namespace tunnel
void InboundTunnel::HandleTunnelDataMsg (I2NPMessage * msg)
{
if (IsFailed ()) SetFailed (false); // incoming messages means a tunnel is alive
if (IsFailed ()) SetState (eTunnelStateEstablished); // incoming messages means a tunnel is alive
msg->from = this;
EncryptTunnelMsg (msg);
m_Endpoint.HandleDecryptedTunnelDataMsg (msg);
@ -361,10 +362,12 @@ namespace tunnel
void Tunnels::ManageTunnels ()
{
// check pending tunnel. if something is still there, wipe it out
// because it wouldn't be reponded anyway
// because it wouldn't be responded anyway
for (auto& it : m_PendingTunnels)
{
LogPrint ("Pending tunnel build request ", it.first, " has not been responded. Deleted");
if (it.second->GetTunnelConfig ()->GetFirstHop ()->isGateway) // outbound
i2p::transports.CloseSession (it.second->GetTunnelConfig ()->GetFirstHop ()->router);
delete it.second;
}
m_PendingTunnels.clear ();
@ -398,6 +401,7 @@ namespace tunnel
auto pool = (*it)->GetTunnelPool ();
if (pool)
pool->TunnelExpired (*it);
delete *it;
it = m_OutboundTunnels.erase (it);
}
else
@ -430,6 +434,7 @@ namespace tunnel
auto pool = it->second->GetTunnelPool ();
if (pool)
pool->TunnelExpired (it->second);
delete it->second;
it = m_InboundTunnels.erase (it);
}
else
@ -465,7 +470,9 @@ namespace tunnel
if (ts > it->second->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT)
{
LogPrint ("Transit tunnel ", it->second->GetTunnelID (), " expired");
auto tmp = it->second;
it = m_TransitTunnels.erase (it);
delete tmp;
}
else
it++;

View File

@ -23,6 +23,14 @@ namespace tunnel
{
const int TUNNEL_EXPIRATION_TIMEOUT = 660; // 11 minutes
enum TunnelState
{
eTunnelStatePending,
eTunnelStateEstablished,
eTunnelStateTestFailed,
eTunnelStateFailed
};
class OutboundTunnel;
class InboundTunnel;
class Tunnel: public TunnelBase
@ -35,9 +43,10 @@ namespace tunnel
void Build (uint32_t replyMsgID, OutboundTunnel * outboundTunnel = 0);
TunnelConfig * GetTunnelConfig () const { return m_Config; }
bool IsEstablished () const { return m_IsEstablished; };
bool IsFailed () const { return m_IsFailed; };
void SetFailed (bool failed) { m_IsFailed = failed; }
TunnelState GetState () const { return m_State; };
void SetState (TunnelState state) { m_State = state; };
bool IsEstablished () const { return m_State == eTunnelStateEstablished; };
bool IsFailed () const { return m_State == eTunnelStateFailed; };
TunnelPool * GetTunnelPool () const { return m_Pool; };
void SetTunnelPool (TunnelPool * pool) { m_Pool = pool; };
@ -53,7 +62,7 @@ namespace tunnel
TunnelConfig * m_Config;
TunnelPool * m_Pool; // pool, tunnel belongs to, or null
bool m_IsEstablished, m_IsFailed;
TunnelState m_State;
};
class OutboundTunnel: public Tunnel
@ -81,7 +90,7 @@ namespace tunnel
{
public:
InboundTunnel (TunnelConfig * config): Tunnel (config) {};
InboundTunnel (TunnelConfig * config): Tunnel (config), m_Endpoint (true) {};
void HandleTunnelDataMsg (I2NPMessage * msg);
size_t GetNumReceivedBytes () const { return m_Endpoint.GetNumReceivedBytes (); };

View File

@ -1,14 +1,22 @@
#include "I2PEndian.h"
#include <string.h>
#include "Log.h"
#include "NetDb.h"
#include "I2NPProtocol.h"
#include "Transports.h"
#include "RouterContext.h"
#include "TunnelEndpoint.h"
namespace i2p
{
namespace tunnel
{
TunnelEndpoint::~TunnelEndpoint ()
{
for (auto it: m_IncompleteMessages)
i2p::DeleteI2NPMessage (it.second.data);
}
void TunnelEndpoint::HandleDecryptedTunnelDataMsg (I2NPMessage * msg)
{
m_NumReceivedBytes += TUNNEL_DATA_MSG_SIZE;
@ -19,6 +27,17 @@ namespace tunnel
{
LogPrint ("TunnelMessage: zero found at ", (int)(zero-decrypted));
uint8_t * fragment = zero + 1;
// verify checksum
memcpy (msg->GetPayload () + TUNNEL_DATA_MSG_SIZE, msg->GetPayload () + 4, 16); // copy iv to the end
uint8_t hash[32];
CryptoPP::SHA256().CalculateDigest (hash, fragment, TUNNEL_DATA_MSG_SIZE -(fragment - msg->GetPayload ()) + 16); // payload + iv
if (memcmp (hash, decrypted, 4))
{
LogPrint ("TunnelMessage: checksum verification failed");
i2p::DeleteI2NPMessage (msg);
return;
}
// process fragments
while (fragment < decrypted + TUNNEL_DATA_ENCRYPTED_SIZE)
{
uint8_t flag = fragment[0];
@ -80,7 +99,6 @@ namespace tunnel
msg->offset = fragment - msg->buf;
msg->len = msg->offset + size;
bool isLastMessage = false;
if (fragment + size < decrypted + TUNNEL_DATA_ENCRYPTED_SIZE)
{
// this is not last message. we have to copy it
@ -90,10 +108,7 @@ namespace tunnel
*(m.data) = *msg;
}
else
{
m.data = msg;
isLastMessage = true;
}
if (!isFollowOnFragment && isLastFragment)
HandleNextMessage (m);
@ -108,36 +123,8 @@ namespace tunnel
}
else
{
auto it = m_IncompleteMessages.find (msgID);
if (it != m_IncompleteMessages.end())
{
if (fragmentNum == it->second.nextFragmentNum)
{
I2NPMessage * incompleteMessage = it->second.data;
memcpy (incompleteMessage->buf + incompleteMessage->len, fragment, size); // concatenate fragment
incompleteMessage->len += size;
if (isLastFragment)
{
// message complete
HandleNextMessage (it->second);
m_IncompleteMessages.erase (it);
}
else
it->second.nextFragmentNum++;
}
else
{
LogPrint ("Unexpected fragment ", fragmentNum, " instead ", it->second.nextFragmentNum, " of message ", msgID, ". Discarded");
m_IncompleteMessages.erase (it); // TODO: store unexpect fragment for a while
}
}
else
LogPrint ("First fragment of message ", msgID, " not found. Discarded");
if (isLastMessage)
// last message is follow-on fragment
// not passed to anywhere because first fragment
i2p::DeleteI2NPMessage (msg);
m.nextFragmentNum = fragmentNum;
HandleFollowOnFragment (msgID, isLastFragment, m);
}
}
else
@ -154,6 +141,49 @@ namespace tunnel
}
}
void TunnelEndpoint::HandleFollowOnFragment (uint32_t msgID, bool isLastFragment, const TunnelMessageBlockEx& m)
{
auto fragment = m.data->GetBuffer ();
auto size = m.data->GetLength ();
auto it = m_IncompleteMessages.find (msgID);
if (it != m_IncompleteMessages.end())
{
if (m.nextFragmentNum == it->second.nextFragmentNum)
{
I2NPMessage * incompleteMessage = it->second.data;
if (incompleteMessage->len + size < I2NP_MAX_MESSAGE_SIZE) // check if messega is not too long
{
memcpy (incompleteMessage->buf + incompleteMessage->len, fragment, size); // concatenate fragment
incompleteMessage->len += size;
if (isLastFragment)
{
// message complete
HandleNextMessage (it->second);
m_IncompleteMessages.erase (it);
}
else
it->second.nextFragmentNum++;
}
else
{
LogPrint ("Fragment ", m.nextFragmentNum, " of message ", msgID, "exceeds max I2NP message size. Message dropped");
i2p::DeleteI2NPMessage (it->second.data);
m_IncompleteMessages.erase (it);
}
}
else
{
LogPrint ("Unexpected fragment ", m.nextFragmentNum, " instead ", it->second.nextFragmentNum, " of message ", msgID, ". Discarded");
i2p::DeleteI2NPMessage (it->second.data);
m_IncompleteMessages.erase (it); // TODO: store unexpected fragment for a while
}
}
else
LogPrint ("First fragment of message ", msgID, " not found. Discarded");
i2p::DeleteI2NPMessage (m.data);
}
void TunnelEndpoint::HandleNextMessage (const TunnelMessageBlock& msg)
{
LogPrint ("TunnelMessage: handle fragment of ", msg.data->GetLength ()," bytes. Msg type ", (int)msg.data->GetHeader()->typeID);
@ -166,7 +196,28 @@ namespace tunnel
i2p::transports.SendMessage (msg.hash, i2p::CreateTunnelGatewayMsg (msg.tunnelID, msg.data));
break;
case eDeliveryTypeRouter:
if (msg.hash == i2p::context.GetRouterInfo ().GetIdentHash ()) // check if message is sent to us
i2p::HandleI2NPMessage (msg.data);
else
{
// to somebody else
if (!m_IsInbound) // outbound transit tunnel
{
if (msg.data->GetHeader()->typeID == eI2NPDatabaseStore)
{
// catch RI
auto ds = NewI2NPMessage ();
*ds = *(msg.data);
i2p::data::netdb.PostI2NPMsg (ds);
}
i2p::transports.SendMessage (msg.hash, msg.data);
}
else // we shouldn't send this message. possible leakage
{
LogPrint ("Message to another router arrived from an inbound tunnel. Dropped");
i2p::DeleteI2NPMessage (msg.data);
}
}
break;
default:
LogPrint ("TunnelMessage: Unknown delivery type ", (int)msg.deliveryType);

View File

@ -13,25 +13,28 @@ namespace tunnel
{
class TunnelEndpoint
{
struct TunnelMessageBlockEx: public TunnelMessageBlock
{
uint8_t nextFragmentNum;
};
public:
TunnelEndpoint (): m_NumReceivedBytes (0) {};
TunnelEndpoint (bool isInbound): m_IsInbound (isInbound), m_NumReceivedBytes (0) {};
~TunnelEndpoint ();
size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; };
void HandleDecryptedTunnelDataMsg (I2NPMessage * msg);
private:
void HandleFollowOnFragment (uint32_t msgID, bool isLastFragment, const TunnelMessageBlockEx& m);
void HandleNextMessage (const TunnelMessageBlock& msg);
private:
struct TunnelMessageBlockEx: public TunnelMessageBlock
{
uint8_t nextFragmentNum;
};
std::map<uint32_t, TunnelMessageBlockEx> m_IncompleteMessages;
bool m_IsInbound;
size_t m_NumReceivedBytes;
};
}

View File

@ -107,12 +107,9 @@ namespace tunnel
}
}
const std::vector<I2NPMessage *> TunnelGatewayBuffer::GetTunnelDataMsgs ()
void TunnelGatewayBuffer::ClearTunnelDataMsgs ()
{
CompleteCurrentTunnelDataMessage ();
std::vector<I2NPMessage *> ret = m_TunnelDataMsgs; // TODO: implement it better
m_TunnelDataMsgs.clear ();
return ret;
}
void TunnelGatewayBuffer::CreateCurrentTunnelDataMessage ()
@ -162,6 +159,7 @@ namespace tunnel
void TunnelGateway::SendBuffer ()
{
m_Buffer.CompleteCurrentTunnelDataMessage ();
auto tunnelMsgs = m_Buffer.GetTunnelDataMsgs ();
for (auto tunnelMsg : tunnelMsgs)
{
@ -170,6 +168,7 @@ namespace tunnel
i2p::transports.SendMessage (m_Tunnel->GetNextIdentHash (), tunnelMsg);
m_NumSentBytes += TUNNEL_DATA_MSG_SIZE;
}
m_Buffer.ClearTunnelDataMsgs ();
}
}
}

View File

@ -16,12 +16,13 @@ namespace tunnel
TunnelGatewayBuffer (uint32_t tunnelID): m_TunnelID (tunnelID),
m_CurrentTunnelDataMsg (nullptr), m_RemainingSize (0) {};
void PutI2NPMsg (const TunnelMessageBlock& block);
const std::vector<I2NPMessage *> GetTunnelDataMsgs ();
const std::vector<I2NPMessage *>& GetTunnelDataMsgs () const { return m_TunnelDataMsgs; };
void ClearTunnelDataMsgs ();
void CompleteCurrentTunnelDataMessage ();
private:
void CreateCurrentTunnelDataMessage ();
void CompleteCurrentTunnelDataMessage ();
private:

View File

@ -11,7 +11,7 @@ namespace i2p
namespace tunnel
{
TunnelPool::TunnelPool (i2p::data::LocalDestination& localDestination, int numHops, int numTunnels):
m_LocalDestination (localDestination), m_NumHops (numHops), m_NumTunnels (numTunnels), m_LastOutboundTunnel (nullptr)
m_LocalDestination (localDestination), m_NumHops (numHops), m_NumTunnels (numTunnels)
{
}
@ -34,8 +34,10 @@ namespace tunnel
{
expiredTunnel->SetTunnelPool (nullptr);
m_InboundTunnels.erase (expiredTunnel);
for (auto it: m_Tests)
if (it.second.second == expiredTunnel) it.second.second = nullptr;
}
m_LocalDestination.UpdateLeaseSet ();
}
void TunnelPool::TunnelCreated (OutboundTunnel * createdTunnel)
@ -49,9 +51,9 @@ namespace tunnel
{
expiredTunnel->SetTunnelPool (nullptr);
m_OutboundTunnels.erase (expiredTunnel);
for (auto it: m_Tests)
if (it.second.first == expiredTunnel) it.second.first = nullptr;
}
if (expiredTunnel == m_LastOutboundTunnel)
m_LastOutboundTunnel = nullptr;
}
std::vector<InboundTunnel *> TunnelPool::GetInboundTunnels (int num) const
@ -72,19 +74,7 @@ namespace tunnel
OutboundTunnel * TunnelPool::GetNextOutboundTunnel ()
{
if (m_OutboundTunnels.empty ()) return nullptr;
auto tunnel = *m_OutboundTunnels.begin ();
if (m_LastOutboundTunnel && tunnel == m_LastOutboundTunnel)
{
for (auto it: m_OutboundTunnels)
if (it != m_LastOutboundTunnel && !it->IsFailed ())
{
tunnel = it;
break;
}
}
m_LastOutboundTunnel = tunnel;
return tunnel;
return GetNextTunnel (m_OutboundTunnels);
}
InboundTunnel * TunnelPool::GetNextInboundTunnel ()
@ -118,9 +108,27 @@ namespace tunnel
for (auto it: m_Tests)
{
LogPrint ("Tunnel test ", (int)it.first, " failed");
// both outbound and inbound tunnels considered as invalid
it.second.first->SetFailed (true);
it.second.second->SetFailed (true);
// if test failed again with another tunnel we consider it failed
if (it.second.first)
{
if (it.second.first->GetState () == eTunnelStateTestFailed)
{
it.second.first->SetState (eTunnelStateFailed);
m_OutboundTunnels.erase (it.second.first);
}
else
it.second.first->SetState (eTunnelStateTestFailed);
}
if (it.second.second)
{
if (it.second.second->GetState () == eTunnelStateTestFailed)
{
it.second.second->SetState (eTunnelStateFailed);
m_InboundTunnels.erase (it.second.second);
}
else
it.second.second->SetState (eTunnelStateTestFailed);
}
}
m_Tests.clear ();
auto it1 = m_OutboundTunnels.begin ();
@ -155,6 +163,9 @@ namespace tunnel
auto it = m_Tests.find (be32toh (deliveryStatus->msgID));
if (it != m_Tests.end ())
{
// restore from test failed state if any
it->second.first->SetState (eTunnelStateEstablished);
it->second.second->SetState (eTunnelStateEstablished);
LogPrint ("Tunnel test ", it->first, " successive. ", i2p::util::GetMillisecondsSinceEpoch () - be64toh (deliveryStatus->timestamp), " milliseconds");
m_Tests.erase (it);
}
@ -168,17 +179,28 @@ namespace tunnel
OutboundTunnel * outboundTunnel = m_OutboundTunnels.size () > 0 ?
*m_OutboundTunnels.begin () : tunnels.GetNextOutboundTunnel ();
LogPrint ("Creating destination inbound tunnel...");
auto firstHop = i2p::data::netdb.GetRandomRouter (outboundTunnel ? outboundTunnel->GetEndpointRouter () : nullptr);
auto secondHop = outboundTunnel ? outboundTunnel->GetTunnelConfig ()->GetFirstHop ()->router : nullptr;
if (!secondHop || secondHop->GetIdentHash () == i2p::context.GetIdentHash ())
secondHop = i2p::data::netdb.GetRandomRouter (firstHop);
auto * tunnel = tunnels.CreateTunnel<InboundTunnel> (
new TunnelConfig (std::vector<const i2p::data::RouterInfo *>
const i2p::data::RouterInfo * prevHop = &i2p::context.GetRouterInfo ();
std::vector<const i2p::data::RouterInfo *> hops;
int numHops = m_NumHops;
if (outboundTunnel)
{
firstHop,
secondHop
}),
outboundTunnel);
// last hop
auto hop = outboundTunnel->GetTunnelConfig ()->GetFirstHop ()->router;
if (hop->GetIdentHash () != i2p::context.GetIdentHash ()) // outbound shouldn't be zero-hop tunnel
{
prevHop = hop;
hops.push_back (prevHop);
numHops--;
}
}
for (int i = 0; i < numHops; i++)
{
auto hop = i2p::data::netdb.GetRandomRouter (prevHop);
prevHop = hop;
hops.push_back (hop);
}
std::reverse (hops.begin (), hops.end ());
auto * tunnel = tunnels.CreateTunnel<InboundTunnel> (new TunnelConfig (hops), outboundTunnel);
tunnel->SetTunnelPool (this);
}

View File

@ -28,6 +28,7 @@ namespace tunnel
const uint8_t * GetEncryptionPrivateKey () const { return m_LocalDestination.GetEncryptionPrivateKey (); };
const uint8_t * GetEncryptionPublicKey () const { return m_LocalDestination.GetEncryptionPublicKey (); };
const i2p::data::LocalDestination& GetLocalDestination () const { return m_LocalDestination; };
bool IsExploratory () const { return m_LocalDestination.GetIdentHash () == i2p::context.GetIdentHash (); };
void CreateTunnels ();
@ -57,7 +58,6 @@ namespace tunnel
std::set<InboundTunnel *, TunnelCreationTimeCmp> m_InboundTunnels; // recent tunnel appears first
std::set<OutboundTunnel *, TunnelCreationTimeCmp> m_OutboundTunnels;
std::map<uint32_t, std::pair<OutboundTunnel *, InboundTunnel *> > m_Tests;
OutboundTunnel * m_LastOutboundTunnel;
};
}
}

View File

@ -43,6 +43,7 @@
<ClCompile Include="..\TunnelPool.cpp" />
<ClCompile Include="..\UPnP.cpp" />
<ClCompile Include="..\util.cpp" />
<ClCompile Include="..\SOCKS.cpp" />
<ClCompile Include="Win32Service.cpp" />
</ItemGroup>
<ItemGroup>
@ -80,6 +81,7 @@
<ClInclude Include="..\TunnelPool.h" />
<ClInclude Include="..\UPnP.h" />
<ClInclude Include="..\util.h" />
<ClInclude Include="..\SOCKS.h" />
<ClInclude Include="Win32Service.h" />
</ItemGroup>
<PropertyGroup Label="Globals">

View File

@ -75,6 +75,9 @@
<ClCompile Include="..\I2PEndian.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\SOCKS.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\SSU.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@ -194,6 +197,9 @@
<ClInclude Include="..\I2PEndian.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\SOCKS.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\SSU.h">
<Filter>Header Files</Filter>
</ClInclude>

View File

@ -313,6 +313,7 @@ namespace crypto
);
#else
m_IVEncryption.Encrypt ((ChipherBlock *)payload, (ChipherBlock *)payload); // iv
m_LayerEncryption.SetIV (payload);
m_LayerEncryption.Encrypt (payload + 16, i2p::tunnel::TUNNEL_DATA_ENCRYPTED_SIZE, payload + 16); // data
m_IVEncryption.Encrypt ((ChipherBlock *)payload, (ChipherBlock *)payload); // double iv
#endif
@ -348,6 +349,7 @@ namespace crypto
);
#else
m_IVDecryption.Decrypt ((ChipherBlock *)payload, (ChipherBlock *)payload); // iv
m_LayerDecryption.SetIV (payload);
m_LayerDecryption.Decrypt (payload + 16, i2p::tunnel::TUNNEL_DATA_ENCRYPTED_SIZE, payload + 16); // data
m_IVDecryption.Decrypt ((ChipherBlock *)payload, (ChipherBlock *)payload); // double iv
#endif

View File

@ -38,7 +38,9 @@ set ( SOURCES
TunnelEndpoint.cpp
TunnelPool.cpp
util.cpp
aes.cpp
Daemon.cpp
SOCKS.cpp
)
set ( HEADERS
@ -67,7 +69,9 @@ set ( HEADERS
TunnelEndpoint.h
TunnelPool.h
util.h
aes.h
Daemon.h
SOCKS.h
)
if (WIN32)

View File

@ -1,83 +0,0 @@
TEMPLATE = app
CONFIG += console
CONFIG -= app_bundle
CONFIG -= qt
TARGET = ./../../i2pd_qt
QMAKE_CXXFLAGS += -std=c++0x
LIBS += -lcrypto++
LIBS += \
-lboost_system\
-lboost_filesystem\
-lboost_regex\
-lboost_program_options\
-lpthread
SOURCES += \
../LeaseSet.cpp \
../i2p.cpp \
../HTTPServer.cpp \
../HTTPProxy.cpp \
../Garlic.cpp \
../base64.cpp \
../AddressBook.cpp \
../util.cpp \
../UPnP.cpp \
../TunnelPool.cpp \
../TunnelGateway.cpp \
../TunnelEndpoint.cpp \
../Tunnel.cpp \
../Transports.cpp \
../TransitTunnel.cpp \
../Streaming.cpp \
../SSU.cpp \
../RouterInfo.cpp \
../RouterContext.cpp \
../Reseed.cpp \
../NTCPSession.cpp \
../NetDb.cpp \
../Log.cpp \
../Identity.cpp \
../I2NPProtocol.cpp
HEADERS += \
../LeaseSet.h \
../Identity.h \
../HTTPServer.h \
../HTTPProxy.h \
../hmac.h \
../Garlic.h \
../ElGamal.h \
../CryptoConst.h \
../base64.h \
../AddressBook.h \
../util.h \
../UPnP.h \
../TunnelPool.h \
../TunnelGateway.h \
../TunnelEndpoint.h \
../TunnelConfig.h \
../TunnelBase.h \
../Tunnel.h \
../Transports.h \
../TransitTunnel.h \
../Timestamp.h \
../Streaming.h \
../SSU.h \
../RouterInfo.h \
../RouterContext.h \
../Reseed.h \
../Queue.h \
../NTCPSession.h \
../NetDb.h \
../Log.h \
../LittleBigEndian.h \
../I2PEndian.h \
../I2NPProtocol.h
OTHER_FILES += \
../README.md \
../Makefile \
../LICENSE

View File

@ -0,0 +1,32 @@
-----BEGIN CERTIFICATE-----
MIIFfTCCA2WgAwIBAgIEOprmhjANBgkqhkiG9w0BAQ0FADBvMQswCQYDVQQGEwJY
WDELMAkGA1UECBMCWFgxCzAJBgNVBAcTAlhYMR4wHAYDVQQKExVJMlAgQW5vbnlt
b3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEYMBYGA1UEAwwPYmFja3VwQG1haWwu
aTJwMB4XDTEzMTAxMzEzNDQ1NVoXDTIzMTAxMzEzNDQ1NVowbzELMAkGA1UEBhMC
WFgxCzAJBgNVBAgTAlhYMQswCQYDVQQHEwJYWDEeMBwGA1UEChMVSTJQIEFub255
bW91cyBOZXR3b3JrMQwwCgYDVQQLEwNJMlAxGDAWBgNVBAMMD2JhY2t1cEBtYWls
LmkycDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAIoAkobXwk/Enf1d
roHyqCyvcJfZJVTwb/LgYWAvCBMCr+RGqlSgtk3g69Y3I0xU08fD2kGt3r5Pwsbr
omXIbJAcccyLqmQ5QX6QgL+X9VpMDp9C4h2RogCrqLBAWw4cuZ4RS9VCpP1Yis7H
uejYqENP86p7BsRnuW/4cYnfunAdMpss4LpRGQXt1nTX+kfgCYgnKFbFqwAHt7yV
Ds+Pe6FuBHPlp+sc1amKRcUnSvhXLsv43VicnT7xYL/kUsN83wrtHA3B4aGDx3aA
3/EzuRmIXQB0BlTZILMEyYwG/nc4OsW82QYrvEZ9BIg9A4lF/wS/KZCICPxLF2zo
dGjnmlgkiA4s8eO+va/ElHyELjckVXqmG1eXHhSkEsDvOQJy01IUuwLinvq7cUbJ
HfJBZJllEg+sLDCv3FkEqN+XjBNFfQN4oNew4w6IPY6YH1INVB9LL0Cmdu4DudLv
TY8OcI8eSfez3hmm+pYQ23PJRYYnvRDnRECyIWBegkckWRh8U/WvZUYUvETK6EDl
/0KpTtfzX6MqHA5D6bTAB8Y3ijGMLrZ/B5vj5yCoZbLiGme9X2moR2k1LEhdhtzV
exsqezCpg6dn48FTX7mHjvR5/r4kz2jqBGmdPUWIIxnjFUzDUK3llVQiHihleHpe
jL4LqnhBGKWFRTaVwaIkBG4zAfIzAgMBAAGjITAfMB0GA1UdDgQWBBQNkfW7bSMl
1/4KDbgwrkf9x1Zu/TANBgkqhkiG9w0BAQ0FAAOCAgEAGg3a3rTf0EznQocmio0T
5gCoL0n8h6yKW/PyPAIELrd9wiYjhJFcWvMTcJJJnVqmAL5vpvhaAFVtAfx70MGa
0DZ7FvytK5hEfF4IqOFDyEEVGJR5rIpVK4MeI1nmwEsxdbW+FhODjtRzgYO8XBME
Xj4aY1FWg9vxc3reUj6PSFsZtsB0aLiRgL9JDovJIiRw0Uqr1v2wXBte5yVCxDge
vTREZtpK4cKetoOa68pwSXI32JwKE18j6bfdKVBCcYQKlKP/3gHGduaDrQv3w32S
DRym5s6MREeTUOtAw4wq46KpdOX8yyAqJPrCfMwS6ORd3t+egqOw0PUnsqb97w4O
lUtrRYvb2cOj60SmRx4vJvItyuHbKqIK7o2e1RcUZPXYoAVx2ww4XB2Wk4D7LSAs
cS7nLj8yAqzJ2qqtBzxu+zILJtkVa12dKF0xmS0BxBp4sCYiBtmAVE8AWQqEuSHA
FrMWqoXcjcfdvvyX487FFWWUE7ZBIn0hee2sK9J9+SPtqczJaN7TF3K3nzo65WJG
1epltmq2Ugjb67Gz7v4y7H23DJ/qhm8yLtCHTj69HTta5I08j6Kut924WLZaiMO/
4YoEL5AE63X0sxYibKFQiq7FW5nUJA280GRlY3xSMFzlB2ggazrUV3YAWVDhfdnI
flpzWXkFM2D36OUaubfe9YY=
-----END CERTIFICATE-----

View File

@ -0,0 +1,32 @@
-----BEGIN CERTIFICATE-----
MIIFfzCCA2egAwIBAgIEcpgq/jANBgkqhkiG9w0BAQ0FADBwMQswCQYDVQQGEwJY
WDELMAkGA1UECBMCWFgxCzAJBgNVBAcTAlhYMR4wHAYDVQQKExVJMlAgQW5vbnlt
b3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEZMBcGA1UEAwwQZWNoZWxvbkBtYWls
LmkycDAeFw0xNDA2MjcxNTQwMTJaFw0yNDA2MjYxNTQwMTJaMHAxCzAJBgNVBAYT
AlhYMQswCQYDVQQIEwJYWDELMAkGA1UEBxMCWFgxHjAcBgNVBAoTFUkyUCBBbm9u
eW1vdXMgTmV0d29yazEMMAoGA1UECxMDSTJQMRkwFwYDVQQDDBBlY2hlbG9uQG1h
aWwuaTJwMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAgcD+2Ma/q1zo
Ae9Iurlxj6YwyUTtd6P1ctAOvw9bpgSLW0wMRnRhaCj3d0BWUOY5/42KDvX2JXiO
kz/VuYy29bMHK1pdJZuY1FGFrKudFYJ6qOr+xkiEk5YT8D/RLxWQ1oibFL18nJwd
pSlOT6XhP0uZAdrZy/CaRmIKKZwAcsdUXt+hNVVoyCJHM2x4d5eqahEgjI/39IY/
5a3kBZDiZI55jDJKsUr4jyq392W6TY03mTeacJzIyMIW6/ut0JkphrZaRY5dtB3i
1RMUWhjuCyg+zHrETSlwIgGhuzxVJLXlSpmCk3UiCsu79lUulZ2ReuifdACJ/e3A
oet2fyjOp+siPaGzdTJ4MjAoMl0uzd1+D4tiq8ajneoEPYxhaeYjeRwPEUcihHjP
cgOKZiV/zUQPoYD3YQ4rwM07sfKLoHO+jxN21t3qXFHce6NwMuiid6mGzl5gVHvq
mt8nI3gJCIfbhUPWyUvdMtMbSHvWnKSwtLthOGLuXrwYMiV/6x5l68+y8waagjkH
fI1H/tKmUT7wcHCldxsQaXbmoEGbboEsjpcm+7+wbk5sTxkqrGxWFrMtlvnBH0fj
dQX/UeG1IPpDi2QqWmPdrIOdKng1a+CMMyDJZX0LcEBmVZzNhRKvZmqAe1vhf+LF
s8adUCnpPm1p3quLrIvoKb2YS4VIl50CAwEAAaMhMB8wHQYDVR0OBBYEFFeW8HLy
KaFZ71umI2XnnddzJO8rMA0GCSqGSIb3DQEBDQUAA4ICAQAKXytX51clQl1jYwGE
in+nUo/XdTIKXlT322SHS36Keg2SlFTnlREIFkuI5J1TM+lILxwgCm+JrN/lknd4
ZMzJ4RGOMqBhqQG1x/Dgde52pamQqFMeLkh3GiwWYcHfekUt66PqxtQizuG4UNFc
Le98hpLNQoF2p/zW/5f4CtSs/HsO1lGlO9LB0zQtjbegmIZnwj4WOL+qpRbsDeZi
jf1gW0qyLeiQNyXW/e+sujNYe4PIioGombeKrzPoABKD0x2gsI5SVr7e8X3HgtkK
kDUXsQqevRFrifHvF1TjVC1J3YqnuTwYKXqA2d72icLp1U2OY2RFYgJPLZgbj2MK
32EZ1YW4rEf5gfPosN5NBIIsmsxfkji8vC7pe8UeJzxTjM/EuVTTiGjPZbpz/PHY
YliGKKFOe1tStq2zo7FxGRAlH9OGuKtBSw/WyN+NyTTsndOO6RXz4jIf0PBvtyjS
FoyV3W6x7R5WjjlyQOVLEgAfJkEjoACuI6rz48/FKjnyxSRlRo7+9/LfgwiU8ye3
IuEmA0W2lSSY8lSeJA7p9utKT6zuQwCkgL69u0QSK4GD0G6dteVTlpd99c5xwgVu
cEGSP30XpQMrSknKwv2KHv7nBqRJSPO7JYMl4TjbKr8BM1q5AK8pfYPFgMjFTCwT
/hCfobYS9FLpJCFkIMcnYrhM8g==
-----END CERTIFICATE-----

View File

@ -0,0 +1,32 @@
-----BEGIN CERTIFICATE-----
MIIFgzCCA2ugAwIBAgIEB52rdjANBgkqhkiG9w0BAQ0FADByMQswCQYDVQQGEwJY
WDELMAkGA1UECBMCWFgxCzAJBgNVBAcTAlhYMR4wHAYDVQQKExVJMlAgQW5vbnlt
b3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEbMBkGA1UEAwwSbWF0dEBkcm9sbGV0
dGUuY29tMB4XDTE0MDcyMTEzMjYxM1oXDTI0MDcyMDEzMjYxM1owcjELMAkGA1UE
BhMCWFgxCzAJBgNVBAgTAlhYMQswCQYDVQQHEwJYWDEeMBwGA1UEChMVSTJQIEFu
b255bW91cyBOZXR3b3JrMQwwCgYDVQQLEwNJMlAxGzAZBgNVBAMMEm1hdHRAZHJv
bGxldHRlLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL5M9wKT
csNLg4EA3fW7LleTQdrm3stPnoUvFmsNZHGgsKt1Nc1qCNis3kr2QEY+4Z398U7r
7xGEQFa7D/9SPHf6n1uVXc9DIcmwBtEB0FPB1XPFp2h00ZXIv24yiLN3GQT1woAM
yEbBWsUgn8K/iMBeA5dU2vPwAbGO/0ibD62frgGdYqU2EeiJ/U6vBmKxvC+q2noL
gnyfQJEJANXgf+Cw/gBaS6yn5ZsYcenLNenID2TQKQ6Q/NxYrDYRdWdId29iwldt
dmNSmASv8C7g9d/isZkpmtYNkE4J4m0W9wKziOoyvLSMo8ec67QmCKaPaYKTHTjx
aUuja02+mnlV4DSdZo6nPkSdokRY0+5e6q7+dIPefu8ealGEAE5oedEfl5iM5Fnz
phTR+ePodBK3sB+bMi1NMppbWugpFpdqs1hg2KNKSSG8C4/eTqf2nnlDiVvvFANc
imt6tk0pZcKqveRiDSgI8mTzTcrNgVClsCLoInY5Vab7onZjY9bGijPQ2i1P6+qu
5G6LiLFW7xFq2BcX1DnTztcJ8Yu9NYHhR21J6u7Dr8YHntes3mnth1F0BX3FVA1s
9SaE9/pNhdqap9owpEhNoE1Ke3LorVLL8jyQsqgRHx8VdhWdi9Ao0mzzeI9HYX0j
nZ7uXK5DqGG74K6eWoS9jZSDJLj3IBkIr3B/AgMBAAGjITAfMB0GA1UdDgQWBBTK
YjH+9Jv82Zqi86r95/1sXUCOnDANBgkqhkiG9w0BAQ0FAAOCAgEAsDyl3dS/5pR1
iDN0zE70HN1Sjv55c5um6N39rgz8JSObbAMihhpjRXPR6yl0PdfVcswdCuEaaykp
ppPNY5ObqZIdqI92XOaOhSA3AkZwZffbwaoXFYiawq1aQG1HP7oxXzWwbnbPOxgz
6ThNP5DJan53Mk8TAhxoJkEJxVlMwIiC+QEgqDNYrP8oNOR2J1EXgzsHheEKObyP
xTwRYFqZU/7BQlFeB0LG1LIy9zXAHlb/XIor10w6ChPDW7DiDwGq3zDJw1d8eiUn
RoPRmFjTqn+3rGaEkk+vUFHoWo7cLCEIC3+P9wlY4Kel+ldXMmuJ+BZ1glFXeO3L
VO85n7iVIyBbwo7RLtNaBvrRQIEG3ld5UOKklLlWwhrX/FXksEhdFvmuF9sbiYNr
cg81sbwZlX7Gi7VicXkykFFXwRRr3UblDtfeevouxk4nMVzcDsmzGeAZKQBvcxHa
Pzc70YwnVRqTc87c0bEwPoxK1Vb26+DILyDjKb/AkTw/rwj6vcJZP2ad+hpiz5Ka
nlbY2cI3JJb0TQiDiOIk+xFqC5oHUTSEmfqA6sA5o/RqdwDpkfpgI5mCwhYzDSLD
jfS+263ylhanl7oz0sM+GtH63owVbYJAFT2EozT9siTIErvJESL4Z80yUQG63d/7
fss8T6gOo19esb/KEMZGZE4pAApakWM=
-----END CERTIFICATE-----

View File

@ -0,0 +1,32 @@
-----BEGIN CERTIFICATE-----
MIIFeTCCA2GgAwIBAgIEZZozujANBgkqhkiG9w0BAQ0FADBtMQswCQYDVQQGEwJY
WDELMAkGA1UECBMCWFgxCzAJBgNVBAcTAlhYMR4wHAYDVQQKExVJMlAgQW5vbnlt
b3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEWMBQGA1UEAwwNbWVlaEBtYWlsLmky
cDAeFw0xNDA2MjgyMjQ5MDlaFw0yNDA2MjcyMjQ5MDlaMG0xCzAJBgNVBAYTAlhY
MQswCQYDVQQIEwJYWDELMAkGA1UEBxMCWFgxHjAcBgNVBAoTFUkyUCBBbm9ueW1v
dXMgTmV0d29yazEMMAoGA1UECxMDSTJQMRYwFAYDVQQDDA1tZWVoQG1haWwuaTJw
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAnVnmPE4uUvCky0yCnnVH
cJEDqzwDPupx0zr0YDlhZk5VOPPecx5haayJ/V6nXPc1aVVWn+CHfedcF2aBgN4K
5aBueS/l6l5WHcv02DofAqlTmyAws3oQeR1qoTuW24cKRtLR7h5bxv63f6bgp6e+
RihFNez6UxErnRPuJOJEO2Im6EgVp6fz7tQ7R35zxAUeES2YILPySvzy2vYm/EEG
jXX7Ap2A5svVo90xCMOeUZ/55vLsjyIshN+tV87U4xwvAkUmwsmWVHm3BQpHkI6z
zMJie6epB8Bqm0GYm0EcElJH4OCxGTvDLoghpswbuUO7iy3JSfoL7ZCnoiQdK9K4
yVVChj8lG+r7KaTowK96iZep+sZefjOt5VFGuW2Fi/WBv3ldiLlJAo/ZfrUM4+vG
fyNBXbl6bX87uTCGOT1p3dazo+zJMsAZ+Y93DlM/mDEWFa1kKNrs74syzaWEqF4L
KQE6VoYn80OOzafSigTVQgSwUtQtB0XGhMzJhyxU2XHWe1LFIy7Pta0B+lDiZj7c
I8nXxYjsDfEu/Elj/Ra9N6bH0awmgB5JDa+Tbir+oEM5SyDfpSaCGuatdGxjweGI
kVmFU0SqCZV/8TXbIu6MUVzTZMZVT94edifFSRad4fqw7eZbSXlPu++3d1/btn6h
ibM04nkv0mm+FxCKB/wdAkECAwEAAaMhMB8wHQYDVR0OBBYEFO7jIkSRkoXyJcho
9/Q0gDOINa5EMA0GCSqGSIb3DQEBDQUAA4ICAQBzfWO7+8HWOKLaYWToJ6XZbpNF
3wXv1yC4W/HRR80m4JSsq9r0d7838Nvd7vLVP6MY6MaVb/JnV76FdQ5WQ6ticD0Y
o3zmpqqbKVSspN0lrkig4surT88AjfVQz/vEIzKNQEbpzc3hC2LCiE2u+cK/ix4j
b9RohnaPvwLnew5RNQRpcmk+XejaNITISr2yQIwXL7TEYy8HdGCfzFSSFhKe9vkb
GsWS5ASrUzRoprswmlgRe8gEHI+d51Z7mWgna0/5mBz9bH/3QXtpxlLWm3bVV+kt
pZjQDTHE0GqG2YsD1Gmp4LU/JFhCojMTtiPCXmr9KFtpiVlx06DuKm5PC8Ak+5w+
m/DQYYfv9z+AA5Y430bjnzwg67bhqVyyek4wcDQinFswv3h4bIB7CJujDcEqXXza
lhG1ufPPCUTMrVjh7AShohZraqlSlyQPY9vEppLwD4W1d+MqDHM7ljOH7gQYaUPi
wE30AdXEOxLZcT3aRKxkKf2esNofSuUC/+NXQvPjpuI4UJKO3eegi+M9dbnKoNWs
MPPLPpycecWPheFYM5K6Ao63cjlUY2wYwCfDTFgjA5q8i/Rp7i6Z6fLE3YWJ4VdR
WOFB7hlluQ//jMW6M1qz6IYXmlUjcXl81VEvlOH/QBNrPvX3I3SYXYgVRnVGUudB
o3eNsanvTU+TIFBh2Q==
-----END CERTIFICATE-----

View File

@ -0,0 +1,32 @@
-----BEGIN CERTIFICATE-----
MIIFezCCA2OgAwIBAgIEPSs1SjANBgkqhkiG9w0BAQ0FADBuMQswCQYDVQQGEwJY
WDELMAkGA1UECBMCWFgxCzAJBgNVBAcTAlhYMR4wHAYDVQQKExVJMlAgQW5vbnlt
b3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEXMBUGA1UEAwwOc2luZHVAbWFpbC5p
MnAwHhcNMTQwNzIxMDAwNjUwWhcNMjQwNzIwMDAwNjUwWjBuMQswCQYDVQQGEwJY
WDELMAkGA1UECBMCWFgxCzAJBgNVBAcTAlhYMR4wHAYDVQQKExVJMlAgQW5vbnlt
b3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEXMBUGA1UEAwwOc2luZHVAbWFpbC5p
MnAwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCnYMNclgj1gIJfot6f
65jbcXpcqf9hhL/uYiU4+uxVevWyJvNHpMzhPWNN4l3HihJJsINsPs/MsZ83Guva
GW5S/93I617kyjs/ZVpEtfABGewho0m9VCBV2/N1mJpHvvR+9OR+YVuWlTB/8sTG
2smdRj/dkKvewN5PSTQH350yT18NR/DmZUU1Iwa7vrNw8ol3rP3qx9UGpFN3JE7V
Q9cA1nktMiFUm76eOPOoln04WDqW2rvArXzvhSApvt0JsLBrZDzM3cx2Rc2UdjIC
h+Ha+G4CLjszfZfQAFJYPred38Gg6wuXiza/wCBSPiB92i94hIQF/OSeukaMiqwG
dRAcBT84/U9bddqHlIICw14PkNHOGUyJGjGKWQl/2bLX43ghWkUJmsTXS3iVcOTc
gb/7MoCRBdL0q2GyEJXuAoKXD9VqD3g+EdcBTQxS9lhZ0iTR7423pg6FP43VMEUC
HUi/BOX1tCY6iRzD1Su6ISIx7klH/sAWWa+SybLFXWtZJxHXXJICiBHJWRbWgtlu
5V+at66yg/LNpyfW3Am08gDV0kiWUBN2Ct4TX9PAQmNDisNgi2AzdZHIfX6tRpU8
UnNcnZGOh4+HXQwJtI0y83C8TsXJUFYfGFWqXN69sMEmgtX8/w+YUqjtb2GcX1HN
6z9u9lH40JCFHTA/clPqOSQ+MQIDAQABoyEwHzAdBgNVHQ4EFgQU4R6x7ArVpSVs
b8VTBXmodXzyraEwDQYJKoZIhvcNAQENBQADggIBAJEHLSDBRU2x6EalmN2W952p
MEO5lGD+ZfUVK0c44t1O53naffwZx9QmDmrC4TjeQrLOpAXLQ8GJHAGeZVmYRgkf
OioKde5uuqVcxqNxArO8VyYGwsuNVPCaBV+SyIO+EmWogidSIrOP2WsRRS2NBhLV
2dp3TvMeod9bPwRl00guvv9iqL0UVSpQSlfGkAQTVpyADOaQHOzeoCpmtPOfB6OK
syB/Z/6HElKoUbvhynaASLgmo3wM93PVJQ2Ho294bQHtDl2qcOksJQvWfCgi7Zrt
KuHaM/a2kItzI6JmyNFXgsKQSDJ4UvoppppgD7K48zOtSipGuZAADC5w5HdVvIGJ
1Czva8kTcmC6AMc+4tACGqYZEAEokkeXn+pIIqKVj2eQukT/0dLGGHbKmxp3Z0f2
pIH2Draq8JPdacr9P/xqEWUuViaOuC5OBjY8Fg3fmVCpwefIuk+DBhbJjEugB0Cu
brJpqNznoYahkbyAXIA8T+QJYMhoGWmaIcaPWK6K3nArvaxzwJbb9Egyivhyp9Rr
r2QMEZ+cPO8p1mEhKpL/wGqAzYyla8SJ06PzLc1lQeGiClu1nbZj5AgkZ1DLa8SD
iO7+e6rS0q1bzc7smE5JzZRiOVqKij/ReKa2uebLLI4wgAhz5ymaD1HfZY+3dV9T
WX89Xn2UyQf5kHifiDKL
-----END CERTIFICATE-----

View File

@ -0,0 +1,31 @@
-----BEGIN CERTIFICATE-----
MIIFVjCCAz6gAwIBAgIEU71jgDANBgkqhkiG9w0BAQ0FADBtMQswCQYDVQQGEwJY
WDELMAkGA1UECBMCWFgxCzAJBgNVBAcTAlhYMR4wHAYDVQQKExVJMlAgQW5vbnlt
b3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEWMBQGA1UEAwwNc3dhdEBtYWlsLmky
cDAeFw0xNDA3MDkxNTQ1MDRaFw0yNDA3MDgxNTQ1MDRaMG0xCzAJBgNVBAYTAlhY
MQswCQYDVQQIEwJYWDELMAkGA1UEBxMCWFgxHjAcBgNVBAoTFUkyUCBBbm9ueW1v
dXMgTmV0d29yazEMMAoGA1UECxMDSTJQMRYwFAYDVQQDDA1zd2F0QG1haWwuaTJw
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAjSj53hsbTqtzbnlf5LbR
HmfdC2br9QZaB9e5IQKprlTptdzqTrt2LRS6ZaJ06BKJgX3AfLflvyeUDUPoyg63
I9a1kb1AsrcxvMkHXTUwPaO09caO5/CiQ3zx/iuTl4e0MmGe3cz7jsvZNdOVH0ba
B691GB2UBq2QjobGx01qjWtACCmoQIcEur2ns/l+VzAextBL70dSPN6EJAonQnWc
JvHf1vhXp5DWasaIovm8haNo48QpCo7NllsAjiONQM9rrJITvzFG9hX9cv/B1Kr4
LCebTXv58ViXXFsnxYhktAFwP33fn1eCLraJ/BpaDR4+s3ovMC/7S+g5//+sQTd1
pR/kXx4BmZWZdzs083Z/2skQON75j/qnMhUQqpFCqUImm4lOhlIGbzFJ4GQM6VCR
V4BbvC3XuDc6vlivLzWpUEU7Kc6YnfGgi2G//ZCj2CAoR7qZs/n9997C8oAvGY5z
XGVC/GqIFHuFvnDfPDvxGovYjLJ0KrNtAmp2Rb5812glnVdPwbRYRUBg3ICbNey3
rmGPURDq0aHMTzX4gtM+/hCYYVnkzNMQvYuw9EZLZrK/XdM1a0U4kajZSKKJsTmW
uQwXSUVjTKQh//yL4zPoELucFk5r7apLePEm6aCeWuY2wVkR8KEFgNanwDckWQAm
Lk9r2t+Y/l2jS2NqBWFyr+cCAwEAATANBgkqhkiG9w0BAQ0FAAOCAgEAgFSquj/0
iZYpFI1XarSIVpGMo0WLAmb9GZCn5yoXSeE6eypI/hHhXA4Bjdk2Ae33pXPNcCV8
oT/gHj8943Wx7CTxty2zHzIsd92/HG2EG6U/HPp5l7yIJQaWoe/9tjQoaBhipZOK
+MiytkoBWkyXFqXnKQPExiadWB8axHtt66vrikOcSx6Ur3u5DPKybvY4fsuvo4+I
cLLgoueFm6I1WhmkVmjtm4k2yZ/Z3NEYjg52rv8NuYhRwK2JrQeRzMZv/zt5KhOt
05woHrzymjfFBu0M8uxX7EGZBIsc8zcEY7JL/NSMArw/QCgLU5bQF6+CsyxWUkt1
obMRXU1oS9GjC/1F0kw52NOz2qzBn9tZBc1zs8+GLpYBUf9KiUMFOfJpkr706VqC
orgxRYwncicq+de2PlesxJb3DNPFuAzUNzAqxcVYDoFPAiL1zCEl0nhBrbN+x93X
ojTfV3UlbMjMkQKveYJxsi5/+jO1dHIkXpzK4bwFwHmJ2RCa6PualWhuXldX6mR+
APoY6xeoPRlyKk+POrSwU+hywUudyPuFyzDMo8n1w4CyqL+/ky3YsLfGBM1phbb2
GEnZ0J1HW34Pnie1rzaCak+3RfaZsImCwh1xXl/H7Ka9bLeUIfOuipSSroctdaiG
84wIiEjxgjW2ldM37gTX2XtE/blB1YPIZ5U=
-----END CERTIFICATE-----

View File

@ -0,0 +1,32 @@
-----BEGIN CERTIFICATE-----
MIIFhTCCA22gAwIBAgIELuRWgDANBgkqhkiG9w0BAQ0FADBzMQswCQYDVQQGEwJY
WDELMAkGA1UECBMCWFgxCzAJBgNVBAcTAlhYMR4wHAYDVQQKExVJMlAgQW5vbnlt
b3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEcMBoGA1UEAwwTa2lsbHlvdXJ0dkBt
YWlsLmkycDAeFw0xMzEwMDYyMTM5MzFaFw0yMzEwMDYyMTM5MzFaMHMxCzAJBgNV
BAYTAlhYMQswCQYDVQQIEwJYWDELMAkGA1UEBxMCWFgxHjAcBgNVBAoTFUkyUCBB
bm9ueW1vdXMgTmV0d29yazEMMAoGA1UECxMDSTJQMRwwGgYDVQQDDBNraWxseW91
cnR2QG1haWwuaTJwMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAig3u
niLWm0y/TFJtciHgmWUt20FOdQrxkiSZ87G8xjuGfq7TbGIiVDn7pQZcHidpq+Dk
47sm+Swqhb4psSijj0AXUEVKlV39jF5IZE+VUgmEtMqQbnBkWudaTJPWcEe9T/Kd
8Oz2jgsnrD/EGVTMKBBjt/gk8VqTWvpCdCF1GhqcCeUTFHzjhN9jtoRCaJ2DClpO
Px+86+d3s9PqUFo8gcD/dbbyJCMqUCMBLtIy/Ooouxb9cfWtXfyOlphU+enmdvuA
0BDewb9pOJg2/kVd9/9moDWcBGChLOlfSlxpDwyUtcclcpvwnG7c6o4or6gqLeOf
AbCpse623utV7fWlFWG7M4AQ/2emhhe4YoMJQnflydzV8bPRJxRTeW1j/9UfpvLT
nO5LHp0oBXE0GqAPjxuAr+r5IDXFbkKYNjK5oWQB/Ul3LkexulYdCzHWbGd1Ja5b
sbiOy6t/hH6G8DD75HYb+PQZaNZWBv90EyOq1JDSUPw6nxVbhiBldi3ipc8/1X51
FbzBqJ+QO1XKrKqxWxBKoTekuy38KRzsmkSCpY+WJ9f0gLOKtxzVO2HNNqqVFGQf
RGIbrNA0JSRQ1fgelccfrcRIXIZ3B8Tk/wxCIzCY6Yvg2jezz2xJkVdqOUsznS2v
+xJe67PYIAeMVtcfO4kmuCvyIYhsUEpob2n/5lkCAwEAAaMhMB8wHQYDVR0OBBYE
FCLneov6QMtvra5FSoSLhdymi++rMA0GCSqGSIb3DQEBDQUAA4ICAQAIcqbiwjdQ
M9VlGBiHe5eVsL6OM9zfRqR1wnRg4Q6ce65XDfEOYleBWaaNJA4BdykcA4fkUN1h
M2D9FDQScsyPTOuzJ6o75TYh0JOtF51yCi9iuemcosxAwsm90ZXGuMDfDYeyND5c
PAkWfyCP+jwLYbNo/hkNqyv+XWHXPQmT2adRnPXINVUQuBxVPC//C9wv2uDYWhgS
f8M425VPp4/R/uks9mlzTx08DwacvouD0YOC+HZE4sWq+2smgeBInMiyr/THYzl+
baMtYgVs8IKUD2gtjfXZoaQNg3eq5SedSf/5F0S/LCdu9/ccQ8CzSEoVTiQFtO78
SaU37xai8+QTSVpPuINigxCoXmkubBd+voEmWRcBd/XB5L+u+MFU/jXyyBj2BXVj
6agqVzY53KVYt23/63QliAUWyxT+ns9gRxVN1jrMhHdiDwsdT4NbzHxg1Su4eiHv
C/wjD3Dga0BRTEGylpHZGzb1U1rZRHM3ho3f1QkmRPPLcBUMTyUTxJm+GEeuhPvp
+TBf3Kg/YkdpnEMlagqcyHuIrf3m8Z/pTmpOIbekJWbbA7tluvWbMWw2ARB7dUOE
fHYVISh0DTw2oVXxM82/q8XXHnhEXv2nW3K40x1VabxUN+sF4M/7YA8nJqwsPJei
749STYJRfZXdIe69M9zpM5unxENAsiPJgQ==
-----END CERTIFICATE-----

View File

@ -0,0 +1,32 @@
-----BEGIN CERTIFICATE-----
MIIFezCCA2OgAwIBAgIEHLJfZzANBgkqhkiG9w0BAQ0FADBuMQswCQYDVQQGEwJY
WDELMAkGA1UECBMCWFgxCzAJBgNVBAcTAlhYMR4wHAYDVQQKExVJMlAgQW5vbnlt
b3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEXMBUGA1UEAwwOc3RyNGRAbWFpbC5p
MnAwHhcNMTMxMDI2MTExODQxWhcNMjMxMDI2MTExODQxWjBuMQswCQYDVQQGEwJY
WDELMAkGA1UECBMCWFgxCzAJBgNVBAcTAlhYMR4wHAYDVQQKExVJMlAgQW5vbnlt
b3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEXMBUGA1UEAwwOc3RyNGRAbWFpbC5p
MnAwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCvw0vTay1IPOgxvwe8
yt5jGakha20kw9qDb6zbEL87EWEkeOzdu7iUC69lkxVP9Ws8EbLtkeMf/CXg6CC1
e+w8WpOHj5prOsiOlrIO+2I1tKMaMUuJDX2wK4I5ZSw/Kieimh9xqOBZknDmtwjw
2HPW8rpxMqrScaGAP6sQD8Gh4XKKkLogfxYPzF8NnC6O8vBkFKVU2WSVZ0jPAQfv
6luPdA+5lES+5UPWr9Yhv/CX4siGKUTxchqJRf2VU4o5BzzXae4asVA/NY7lKgEw
eDDufbm0mRFWP4mbmXRlODuJ8GMnJbMQkNcAvZUnUcvpSTnGnIvxyxtXP5P6ic8V
3b9HV2eIsbfO1xrgyr6/9qgGpXcdDJejhvNg6fZgQeO40bOGQYwV8bNvsNQHqnZl
KsVhsMQkOubMxcHTBadcifi8PmdeJ5hxyyqJmyrwkmg2ijnN521M6YkoBzl+8VAi
zLmqKZfvN5t+pb9PZ3U3jHfkeIEwDRYRAOsvVqch5+ZfSv8x/Te6o15zDKPJQtWK
ty42GV1vERw30oSZQdrRRy/+4+HSRs3/Zb368OdAbcr+f/xPvwceYGWPeNNIoZ/x
xkIQE3xgEK+eJyPM9McjlCAezZZclT7fWfiEYNJAiS3fGALi+a+cGYWWULxCXpz+
y397OHhZBhnh7D9K8aPePB8tCwIDAQABoyEwHzAdBgNVHQ4EFgQUezvGHq3h1gbC
Hs2LLVoll5fIUWMwDQYJKoZIhvcNAQENBQADggIBAF7SG1WBcE1r5eyTp/BLFZfG
iPtvqu+B1L2HutPum/Xf8A5fxR4kcKAKpVdu6vnDzCRAsAC9YvyETgAzI2nfVgLk
l9YZ31tSi6qxnMsQsV5o9lt/q2Rvsf2Zi/Ir8AlWtvnP8YG0Aj/8AG8MyhMLaIdj
M2FuakPs8RqEjoJL9dTOC9VTQpNTwBH9guP9UalWYwlkaXDzMoyO4nswT/GpCpg8
4m4RO6grzdsEIamD/PCBM5f/vq+y08GaqfXpX9+8CbaX3tdzd3x48wPphmdpkptk
aRELIpLJZiK+Mos7W+0ZS8SHxGDIosjqVsgbZPmk12+VBcVgLOr8W1D7osS4OY59
2GMUVV/GhoDh8wR/Td5wpZlcPE0NWmljjVg9+1E8ePAyMZy+U1KCiMlRVdRy518O
dOzzUUQGqGQHosRrH0ypS3MGbMLmbuWFRiz7q/3mUmW2xikH9I1t/6ZMNUvh+IWL
kGAaEf2JIv/D8+QsC0Un1W09DgvYz7qmKSeHhBixlLe68vgXtz/Fa+rRMsmPrueo
4wk/u/VyILo0BJP860APJMZbm+DPfGhV9DF9L5Gx9+d/BlduBVGHc+AQSWbU70dS
eH4/rgUYRikWlgwUxjY8/QQTlfx5xl28tG0xdO9libN22z7UwTGfm48BQIdrTyER
hqQ7usTy3oaWD85MbJ0q
-----END CERTIFICATE-----

View File

@ -0,0 +1,32 @@
-----BEGIN CERTIFICATE-----
MIIFdzCCA1+gAwIBAgIEcwrwsjANBgkqhkiG9w0BAQ0FADBsMQswCQYDVQQGEwJY
WDELMAkGA1UECBMCWFgxCzAJBgNVBAcTAlhYMR4wHAYDVQQKExVJMlAgQW5vbnlt
b3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEVMBMGA1UEAwwMenp6QG1haWwuaTJw
MB4XDTEzMDkzMDE3NDEyNVoXDTIzMDkzMDE3NDEyNVowbDELMAkGA1UEBhMCWFgx
CzAJBgNVBAgTAlhYMQswCQYDVQQHEwJYWDEeMBwGA1UEChMVSTJQIEFub255bW91
cyBOZXR3b3JrMQwwCgYDVQQLEwNJMlAxFTATBgNVBAMMDHp6ekBtYWlsLmkycDCC
AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAJV4ptvhlfrcize9/ARz4lUy
iLtLMSvSST1BdZjLJTwus05EUs0oiqnv9qXYIWGRB97aKlAmqSxsn4ZgBttCgmev
IkuiZ8kbdqI5YaT98yKW5P2Prt9p9cPbnz5/qjwZ5L9W+k/Itx7bv2pkNEP0NLYo
NrgHHTb1hsyRxc0lfPYk2BwsIi8hIWBHNrRpR41EWFXfqPcdsxS8cQhxVj4zLG/R
aMm4H8T+V1R1Khl4R4qqRgXBP305xqqRoawHmZ/S9/RkF0Ji6IYwBq9iWthWol6W
sMDn1xhZk9765fk+ohAC2XWuGSFCr02JOILRV3x/8OUxT1GYgYjc7FfyWIekg/pZ
yotlhL2I3SMWOH3PdG58iDY121hq/LsSKM9aP20rwtvssnw+8Aex01YDkI3bM6yO
HNi+tRojaJcJciBWv6cuiFKvQdxj/mOhOr0u0lHLlJ4jqES8uvVJkS7X/C4BB7ra
bJYQgumZMYvVQJFIjo8vZxMXue53o65FRidvAUT29ay54UTiL7jRV9w1wHnzLapU
xT1v7kWpWJcZ1zzC8coJjW+6ijkk38cVLb80u1Q4kEbmP2rDxw6jRvmqg6DcCKjK
oqDt+XQ6P5grxAxLT+VMfB404WHHwNs6BB841//4ZnXvy3msMONY/5y0fsblURgh
IS2UG1TAjR+x7+XikGx9AgMBAAGjITAfMB0GA1UdDgQWBBSvx/fCCP8UeHwjN65p
EoHjgRfiIzANBgkqhkiG9w0BAQ0FAAOCAgEAYgVE1Aa/Ok5k+Jvujbx72bktRWXo
Y4UfbWH/426VdgqXt3n9XtJUNM2oI4ODwITM4O15SyXQTLJhnvJz5ELcJV8nqviZ
RjK2HNX1BW7IEta3tacCvVnjzZ265kCT59uW+qmd+5PiaAYI5lYUn8P6pe+6neSa
HW6ecXCrdxJetSYfUUuKeV6YHpdzfjtZClLmwl91sJUBKcjK+Q9G/cE6HnwcDH1s
uXr7SgkBt/qc/OlNuu4fnTqUA58TAumdq9cD+eLBilDFrux1HsUZMuBUp64x5oPi
gme+3VewsczfFEtrxaG6+l6UA40Lerdx9XECZcDCcFsK6MS1uQ2HYjsyZcWnNT3l
6eDNUbjrllwxDdRAk0cbWiMuc21CFq/1v2QMXk88EiBjEajqzyXUPmKzwFhit6pr
5kfjfXNq+pxQSCoaqjpzVKjb3CqMhSlC8cLgrPw6HEgGnjCy4cTLFHlVmD64M778
tj6rE7CntcmUi8GKmZKyaMyUo3QQUcrjO5IQ4+3iGUgMkZuujyjrZiOJbvircPmK
4IQEXzJ/G00upqtqKstRybaWSbJ/k6iuturtA2n8MJiCBjhLy8dtTgDbFaDaNF7F
NHeqQjIJDLhYDy6mi4gya3A0ort777Inl/rWYLo067pYM+EWDw66GdpbEIB0Bp71
pwvcQcjIzbUzEK0=
-----END CERTIFICATE-----

View File

@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDgDCCAmgCCQCAKEkFUJcEezANBgkqhkiG9w0BAQUFADCBgTELMAkGA1UEBhMC
Tk8xDTALBgNVBAgMBE9zbG8xDTALBgNVBAcMBE9zbG8xDDAKBgNVBAoMA0kyUDEM
MAoGA1UECwwDSTJQMRcwFQYDVQQDDA4xOTMuMTUwLjEyMS42NjEfMB0GCSqGSIb3
DQEJARYQbWVlaEBpMnBtYWlsLm9yZzAeFw0xMzA2MjcxODM2MjhaFw0yMDA2MjUx
ODM2MjhaMIGBMQswCQYDVQQGEwJOTzENMAsGA1UECAwET3NsbzENMAsGA1UEBwwE
T3NsbzEMMAoGA1UECgwDSTJQMQwwCgYDVQQLDANJMlAxFzAVBgNVBAMMDjE5My4x
NTAuMTIxLjY2MR8wHQYJKoZIhvcNAQkBFhBtZWVoQGkycG1haWwub3JnMIIBIjAN
BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuBuFY4ZFvsbr5l1/s/GeUBLIWQLB
nqrRkonrwCyxgjSnnG1uz/Z5nf6QDUjiVnFKMXenLaDn4KCmEi4LjWQllhK9r6pj
BRkR7C0DTHq7WqfyvWnGSZZsOJDiH2vLlivV8N9oGdjxvv0N9No3AJcsmLYrxSLi
6/JF8xZ2HGuT/oWW6aWvpIOKpIqti865BJw5P5KgYAS24J8vHRFM3FA4dfLNTBA2
IGqPqYLQA+2zfOC4z01aArmcYnT1iJLT7krgKnr/BXdJfGQ2GjxkRSt8IwB6WmXA
byz6QdNYM/0eubi102/zpD/DrySTU2kc8xKjknGUqBJvVdsL+iLK98uJrQIDAQAB
MA0GCSqGSIb3DQEBBQUAA4IBAQCTimMu3X7+ztXxlIFhwGh42GfMjeBYT0NHOLAy
ZtQNRqhNvkl3jZ4ERPLxP99+bcAfCX0wgVpgD32OWEZopwveRyMImP8HfFr4NnZ+
edbM37fRYiVJv57kbi6O0rhEC7J5JF+fnCaZVLCuvYIrIXTdxTjvxuLhyan6Ej7V
7iGDJ8t16tpLVJgcXfRg+dvAa6aDOK6x3w78j0bvh6rhvpOd9sW/Nk3LBKP4Xgkx
PHkqm3hNfDIu8Hubeav9SA1kLVMS/uce52VyYMEDauObfC65ds0GRmCtYhZqMvj+
FFCbssLraVJE9Hi/ZKGu33jNngDCG+wG+nmleksMYE1yTSRt
-----END CERTIFICATE-----

View File

@ -0,0 +1,44 @@
-----BEGIN CERTIFICATE-----
MIIHyTCCBbGgAwIBAgIBATANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEW
MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg
Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh
dGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0NjM2WhcNMzYwOTE3MTk0NjM2WjB9
MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMi
U2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3Rh
cnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUA
A4ICDwAwggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZk
pMyONvg45iPwbm2xPN1yo4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rf
OQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/C
Ji/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/deMotHweXMAEtcnn6RtYT
Kqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt2PZE4XNi
HzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMM
Av+Z6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w
+2OqqGwaVLRcJXrJosmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+
Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3
Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVcUjyJthkqcwEKDwOzEmDyei+B
26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT37uMdBNSSwID
AQABo4ICUjCCAk4wDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAa4wHQYDVR0OBBYE
FE4L7xqkQFulF2mHMMo0aEPQQa7yMGQGA1UdHwRdMFswLKAqoCiGJmh0dHA6Ly9j
ZXJ0LnN0YXJ0Y29tLm9yZy9zZnNjYS1jcmwuY3JsMCugKaAnhiVodHRwOi8vY3Js
LnN0YXJ0Y29tLm9yZy9zZnNjYS1jcmwuY3JsMIIBXQYDVR0gBIIBVDCCAVAwggFM
BgsrBgEEAYG1NwEBATCCATswLwYIKwYBBQUHAgEWI2h0dHA6Ly9jZXJ0LnN0YXJ0
Y29tLm9yZy9wb2xpY3kucGRmMDUGCCsGAQUFBwIBFilodHRwOi8vY2VydC5zdGFy
dGNvbS5vcmcvaW50ZXJtZWRpYXRlLnBkZjCB0AYIKwYBBQUHAgIwgcMwJxYgU3Rh
cnQgQ29tbWVyY2lhbCAoU3RhcnRDb20pIEx0ZC4wAwIBARqBl0xpbWl0ZWQgTGlh
YmlsaXR5LCByZWFkIHRoZSBzZWN0aW9uICpMZWdhbCBMaW1pdGF0aW9ucyogb2Yg
dGhlIFN0YXJ0Q29tIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFBvbGljeSBhdmFp
bGFibGUgYXQgaHR0cDovL2NlcnQuc3RhcnRjb20ub3JnL3BvbGljeS5wZGYwEQYJ
YIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilTdGFydENvbSBGcmVlIFNT
TCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQUFAAOCAgEAFmyZ
9GYMNPXQhV59CuzaEE44HF7fpiUFS5Eyweg78T3dRAlbB0mKKctmArexmvclmAk8
jhvh3TaHK0u7aNM5Zj2gJsfyOZEdUauCe37Vzlrk4gNXcGmXCPleWKYK34wGmkUW
FjgKXlf2Ysd6AgXmvB618p70qSmD+LIU424oh0TDkBreOKk8rENNZEXO3SipXPJz
ewT4F+irsfMuXGRuczE6Eri8sxHkfY+BUZo7jYn0TZNmezwD7dOaHZrzZVD1oNB1
ny+v8OqCQ5j4aZyJecRDjkZy42Q2Eq/3JR44iZB3fsNrarnDy0RLrHiQi+fHLB5L
EUTINFInzQpdn4XBidUaePKVEFMy3YCEZnXZtWgo+2EuvoSoOMCZEoalHmdkrQYu
L6lwhceWD3yJZfWOQ1QOq92lgDmUYMA0yZZwLKMS9R9Ie70cfmu3nZD0Ijuu+Pwq
yvqCUqDvr0tVk+vBtfAii6w0TiYiBKGHLHVKt+V9E9e4DGTANtLJL4YSjCMJwRuC
O3NJo2pXh5Tl1njFmUNj403gdy3hZZlyaQQaRwnmDwFWJPsfvw55qVguucQJAX6V
um0ABj6y6koQOdjQK/W/7HW/lwLFCRsI3FU34oH7N4RDYiDK51ZLZer+bMEkkySh
NOsF/5oirpt9P/FlUQqmMGqz9IgcgA38corog14=
-----END CERTIFICATE-----

View File

@ -0,0 +1,23 @@
-----BEGIN CERTIFICATE-----
MIID2zCCApOgAwIBAgIBBzANBgkqhkiG9w0BAQsFADBCMR8wHQYDVQQDExZpMnAt
bmV0ZGIuaW5ub3ZhdGlvLm5vMRIwEAYDVQQKEwlJbm5vdmF0aW8xCzAJBgNVBAYT
Ak5PMCIYDzIwMTQwMTIxMDUzMzMxWhgPMjAyNDAxMTkwNTMzMzFaMEIxHzAdBgNV
BAMTFmkycC1uZXRkYi5pbm5vdmF0aW8ubm8xEjAQBgNVBAoTCUlubm92YXRpbzEL
MAkGA1UEBhMCTk8wggFSMA0GCSqGSIb3DQEBAQUAA4IBPwAwggE6AoIBMQC9WVet
EFeKAHmwgTUxJ/bRI4Gtjke3uj897eeZ15Y0SiqdHzypsEIWtXqx4G3W801xZzhv
UiAculvwRY4kpv3DnQE4sNTzbkAlvC6z4+CpFM2mhZ7o+YmozrIsNmQNCsvlxqJV
AD1mzqTFl/OB7LVtLmpSSd36IQFGmsh24XXa4pVH33e+NCZIGsdVwGsa4GoRuC9a
s/DiLI+x6zYRoY9cfOF2DuuOfKNMjSl65QUe4uHZCsRTb1q08NnPIidEFHr94kZH
Hph+MQs6MUVK1eT4yYt084S3cEWmWBQZVyAvWQ9q8EW+MoniOM7bBG2Bn9wu2F5x
kAKWTYfKSStW5CKSox9VSoopiUAtEIqhwgFGTISqhQyfOfyY97X2M47wvyWsl6dE
NxTgdvLD/o24rejBAgMBAAGjeDB2MAwGA1UdEwEB/wQCMAAwIQYDVR0RBBowGIIW
aTJwLW5ldGRiLmlubm92YXRpby5ubzATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNV
HQ8BAf8EBQMDByAAMB0GA1UdDgQWBBTaTuBpTAinNiT9PfKNRdIn3HrwxTANBgkq
hkiG9w0BAQsFAAOCATEAsYtojwAHiFwUqvCMIMJa5YN0Ms/QpjqZiENuGBpxvmVF
WVImX4s8K/qoBAKED8uKcRbMQd0FeDea7kMisJt5cblDzYuSv6wfeLXYkaT8/9H2
X1pXhO/eghJ2U42RTgBkaW3mCI8ohk/GehU4tEXnbWRPHt6XDoSYDJdf2X8BPcgB
ZE10owLCw9c80QTuU+LCvbt8/F2USyNplUrogJGThzxrxZvxjGq6EcDj0iA0RRoG
5CUNrCB+JgFc+4bagI3E5B0skk/wn3Nl7mM8/Nf8b1QENmc8eYBZx2InA9769DHL
tNxzvE+OeMNlKy4M9WvLieIh6KmpYhHBG8ubJT8X+bZpJkh4rH6RzVYiXeCpX2iL
eoeSriGO0+8CJTBRbd5cXYd/COT0iAomMTelhcGVTA==
-----END CERTIFICATE-----

View File

@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDhTCCAm2gAwIBAgIJAPVgXcMcr3zqMA0GCSqGSIb3DQEBBQUAMFkxCzAJBgNV
BAYTAkVVMQ8wDQYDVQQIDAZFdXJvcGUxDDAKBgNVBAoMA0kyUDETMBEGA1UECwwK
T3V0cHJveGllczEWMBQGA1UEAwwNaTJwLmZlYXJlZC5ldTAeFw0xMjEwMjkxNzMw
MDZaFw0yMTAxMTUxNzMwMDZaMFkxCzAJBgNVBAYTAkVVMQ8wDQYDVQQIDAZFdXJv
cGUxDDAKBgNVBAoMA0kyUDETMBEGA1UECwwKT3V0cHJveGllczEWMBQGA1UEAwwN
aTJwLmZlYXJlZC5ldTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOUh
y2+6Q4RO+b5WPXX/cZ/9fiI7aWGe/C7z0083HOEqnkgGCYgxFWUCed6/eZbYoZ7/
PV1BAuEereNwTp+Ov7fQB2H73O9sSAEejW6O4C2PZiZWaPxpZiTJNENbLOZxJnIN
+fSqmA5pqvGkYAJ2heZH4v4tayun7Vib58GWuizhzJ4EvhOrOrLq/YHrxMn++r4e
kNNbq4QzWpfxNa7ocDY9OJh5qFzuc+6wKj1m1syK6euDqs5d6X+y0aDTMgRxey2b
tkmNx9wC0flLg1oMcv9o1zN+dENy7Inkd/SqbSjLUqDTJzdq6xURVsgLoV63pb6r
B4gbGIlriYWK/mOPTTkCAwEAAaNQME4wHQYDVR0OBBYEFOI94JZ3Rb2RVmr8QjOp
u3KfVSrNMB8GA1UdIwQYMBaAFOI94JZ3Rb2RVmr8QjOpu3KfVSrNMAwGA1UdEwQF
MAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAD7bI05zg9nf9qanq4ZNw/rvEzYQRBmy
MqzZjcwBMGvbcEbS+zYAdAkfxmN3l/AT4I4z138Om0ud4ZJUQTVlRsJkMlmLD4Rt
Jbi2rl7mrY7Qupgu5hvgH+ZaEWr7LTq+tFjPycRS+zijw9NToKeAsgEex9zYIOYD
BxDUn/trvyA41ItvegWh803IsZUBb45Via+bopid9aFFkejRrck9hhcQ6fVh2yju
nuVwHrxNvGc0NmmJ7zI+nPESFS+TAYbWXikDhc5Vtyiuoz47WZU1cgXYYMejK4WA
+3GLvei7qKm4GOJSg7BngF5Iyj/n7ML1rBqTlN3KA1YOgpGCwJlKzto=
-----END CERTIFICATE-----

View File

@ -0,0 +1,23 @@
-----BEGIN CERTIFICATE-----
MIIDvjCCAyegAwIBAgICZhcwDQYJKoZIhvcNAQEFBQAwdTELMAkGA1UEBhMCVVMx
DTALBgNVBAgMBG5vbmUxDTALBgNVBAcMBG5vbmUxDTALBgNVBAoMBG5vbmUxDTAL
BgNVBAsMBG5vbmUxFTATBgNVBAMMDGkycC5tb29vLmNvbTETMBEGCSqGSIb3DQEJ
ARYEbm9uZTAeFw0xMTEwMjMyMTM2NDFaFw0xOTEwMjMyMTM2NDFaMGYxCzAJBgNV
BAYTAlVTMQ0wCwYDVQQIDARub25lMQ0wCwYDVQQKDARub25lMQ0wCwYDVQQLDARu
b25lMRUwEwYDVQQDDAxpMnAubW9vby5jb20xEzARBgkqhkiG9w0BCQEWBG5vbmUw
ggGPMA0GCSqGSIb3DQEBAQUAA4IBfAAwggF3AoIBbgMG1O7HRVa7UoiKbQTmKy5m
x79Na8vjD3etcOwfc4TSenQFvn+GbAWkJwKpM8uvOcgj1CxNeHWdSaeTFH1OwJsw
vl3leJ7clMdo3hpQDhPeGzBLyOiWwFHVn15YKa9xcM7S9Op5Q6rKBHUyyx1vGSz+
/NBmkktpI6rcGFfP3ISRL0auR+db+adWv4TS6W8YiwQIVZNbSlKP6FNO9Mv1kxQZ
KoHPn8vT/LtAh1fcI6ryBuy3F5oHfbGumIwsS5dpowryFxQzwg5vtMA7AMCMKyXv
hP/W6OuaaEP5MCIxkWjQs35gOYa8eF1dLoy3AD9yVVhoNrA8Bc5FnVFJ32Qv7agy
qRY85cXBA6hT/Qzs/wWwp7WrrnZuifaSv/u/Ayi5vX42/bf86PSM2IRNIESoA98A
NFz4U2KGq9s1K2JbkQmnFy8IU0w7CMq6PvNEm/uNjSk6OE1rcCXML+EuX0zmXy8d
PjRbLzC9csSg2CqMtQIDAQABo3sweTAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQf
Fh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUdjuOczdG
hUpYzH0UXqKrOleT8GkwHwYDVR0jBBgwFoAU+SKWC49cM5sCodv89AFin3pkS0Yw
DQYJKoZIhvcNAQEFBQADgYEAKYyWlDIStjjbn/ZzVScKR174I8whTbdqrX/vp9dr
2hMv5m4F+aswX4Jr58WneKg2LvRaL6xEhoL7OAQ6aB/7xVSpDjIrrBLZd513NAam
X6bOPYJ6IH7Vw9ClFY3AlfzsNlgRMXno7rySKKzhg24kusNwKDH2yCphZy4BgjMn
y6A=
-----END CERTIFICATE-----

View File

@ -0,0 +1,25 @@
-----BEGIN CERTIFICATE-----
MIIESzCCAzOgAwIBAgIJALGqvElYEEqyMA0GCSqGSIb3DQEBBQUAMIG7MQswCQYD
VQQGEwJERTEaMBgGA1UECAwRaWViOW9vcG8ubW9vby5jb20xGjAYBgNVBAcMEWll
Yjlvb3BvLm1vb28uY29tMRowGAYDVQQKDBFpZWI5b29wby5tb29vLmNvbTEaMBgG
A1UECwwRaWViOW9vcG8ubW9vby5jb20xGjAYBgNVBAMMEWllYjlvb3BvLm1vb28u
Y29tMSAwHgYJKoZIhvcNAQkBFhFpZWI5b29wby5tb29vLmNvbTAeFw0xNDA0MTMx
NDI3MThaFw0zNDA0MDgxNDI3MThaMIG7MQswCQYDVQQGEwJERTEaMBgGA1UECAwR
aWViOW9vcG8ubW9vby5jb20xGjAYBgNVBAcMEWllYjlvb3BvLm1vb28uY29tMRow
GAYDVQQKDBFpZWI5b29wby5tb29vLmNvbTEaMBgGA1UECwwRaWViOW9vcG8ubW9v
by5jb20xGjAYBgNVBAMMEWllYjlvb3BvLm1vb28uY29tMSAwHgYJKoZIhvcNAQkB
FhFpZWI5b29wby5tb29vLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
ggEBAN1CusoQ3OxJFTytbVMe7LPY8lnR7GIIQt5eCs0XTLl3ECclET1KLRo1c8Mv
vj5AwDRvL3Kw/oeDS+QvSRhgxG3lJ8i0soWDC/1LIX1NS/ZXG7hbycZ7YqAovCxU
958WPjUios73sAWUJrI8BbWFTYPWBB5lbyTaCooxtf6k7yB+CSwZnstEP/lbPNkf
Iupj+9B18ba21D2kQqZXyQRLX02d/rXq963BSkPX14Dxa7abw4lgDltvh2CzcoQH
VbQh3eGfPIIQGfvAAVEGsoy9fFt+xOUxp3KO7Y1VMKzegWqa2vBtWK+2nhpHfswq
eaE1qeVh12cG5kaVNP0o0hOUxkECAwEAAaNQME4wHQYDVR0OBBYEFIQ5U4EKfr0t
4L+RFe/RBFcDVoijMB8GA1UdIwQYMBaAFIQ5U4EKfr0t4L+RFe/RBFcDVoijMAwG
A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAGlNHzxF9tbS/Xr8jGpSdRLm
oIIoTr7+dIfFIy2TMAow0+gx1qEtWASUYIwbCP5mdMfhUjL8POfSwtTvgjw3LMoM
2zsGBvgGWs6VJbMPAgkgfsyjdJTM/IOiJtze6degn2bdZvAr1XiInLGMRMko7WyE
QQ1WJPcTUt8rNYVaCkq3CYioIlkb/C6M6ObHGSia0kwoZa6grD3CNUCa/c/dlFKO
E06qN72fsYihp0ghHkBaCxb+YfYpIAPIpfuuTSGkVYyjpY0a8EQ1JlzbJctQkX5r
sd9jfHsrZriAGCzdsL/diG8bUi9Yex/G0ip8GGEuHs5e2bJ6+7O/mPlL6SL/0tI=
-----END CERTIFICATE-----

View File

@ -0,0 +1,33 @@
-----BEGIN CERTIFICATE-----
MIIFqTCCA5GgAwIBAgIJAPsJOCng4aEOMA0GCSqGSIb3DQEBBQUAMGsxCzAJBgNV
BAYTAk5PMQ0wCwYDVQQIDARPc2xvMQ4wDAYDVQQHDAVKYXBhbjEMMAoGA1UECgwD
STJQMRMwEQYDVQQLDApJMlAgUmVzZWVkMRowGAYDVQQDDBFqcC5yZXNlZWQuaTJw
Mi5ubzAeFw0xNDA2MjgyMDQ3MThaFw0yNDA2MjUyMDQ3MThaMGsxCzAJBgNVBAYT
Ak5PMQ0wCwYDVQQIDARPc2xvMQ4wDAYDVQQHDAVKYXBhbjEMMAoGA1UECgwDSTJQ
MRMwEQYDVQQLDApJMlAgUmVzZWVkMRowGAYDVQQDDBFqcC5yZXNlZWQuaTJwMi5u
bzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALlZBIbb4MHqLPpUy298
PG5z7RFo4bO7CxW2LO8DgE93KNbdkZuYpt6KcAW/gkDfKCiXTxDjyqmzZeBeIbcS
ea96jFc/pTknkiiSm9NX4ATcyRwvKXn6DR/iOiofP3G/sEkxgIdv3BgEYsF5jPQY
KefG0Jvl612cIX+1jC3lRRePYPUZnWxIuokXglLApeyhNzTK/KHIsLzR+56ScltM
pwGlroTky5ekPx4ZnJCxI+qFXWcgqdoNixPUkOtceYm7u5gd1D+mQDuCB5zfB+Pw
Iy9BTARot9M3fMrcRRVfEIGWwN1+bRnZvr0A/sqYMUqGPiUVOMi/mzyqZVQ14CGy
0idRhUKdOb/HNSmcep0Jwp0cP+VD/nCNU4JLptTLdErpTJrmDn+zkuQPPSMTzTWg
Fh3ktRsJ5zKfrnrBGxeKbciZgRkVHyWpv3+0AgbD8C3HvDj4qpkIO6D2gf+TREyM
frDXN6luqkThQcUFv+huMaL3Iul2doYqw5YPAdel6/cCD12n/FoEj5UJ47O/77DA
ITYfKCFRKDh/Ew7Ih3bH66uNaUUp+a0Sd6fNXLmWWr7gcn5B5CvrXhjPPror+Xyz
EZVByPTTfU1BkMQuxv20GG65M9g9wtXrD79N8di2wOfr4EG5i6L9ZvNTThwgWGGd
9f745WCnPL/ulLT9Glnlfk01AgMBAAGjUDBOMB0GA1UdDgQWBBR5Ed38JID7+JwB
hpOLi1Xt1ORyoTAfBgNVHSMEGDAWgBR5Ed38JID7+JwBhpOLi1Xt1ORyoTAMBgNV
HRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4ICAQBRT/ourx4xT0n/qHF1vy8Ii02v
Hg5lHmzmiVn/3/S4q+t4HrOF6MSjjvVzVNk6JIIYb3+hwGUO31OLNZX60JfSMW+C
ffPiaNDLm/xE4yt4B/QZZDs0kv0RMjGau6k2XJPGvxVNl6LhM6LLvzMEtOGzBr0J
Ai4ZU+WqHk3nnXYHbg7C7Iuu8CT8tBpkaeW/VEN82dcLEulHFxA5Ia6HlwqrgvIW
w77oaOhOh5LKkdS8uHx4OUP8Mv25H2cMBblUdubbeqREyOGRGTkVXfRekD8K95ol
PfT9PhnhUXKRaSwy0nRqvRMiwk/CyIJTbMMflDET1P785diSvtJP0fOhkev9Uprz
FvLsPUTvANUz+vd2KJiLuGbR/d/LaJm18vdWx13vKVO60TBnyFQDS4RZbeuNp73v
x5fPTmiiPZYZj13m2xyes7SXJqhVbms0F39soThpuYrjCaHXyFgqah27+9Ivmbhu
EefPgLmkOx3v0kSfXdJYdji/mrKxERmqT1L34U6M32tCoSbjO7lakAV2opisbHEw
EehCuI83cGp/m3z8yjMoWV8Z4VoB+qMzxzgrXc5C2lxYAT4JggAV2SGcY9MszETI
/S7y5UypV4rutyJvHFrlJZKtp2B7Xi8N8n2NG1WGppYh9UShbFhDaVIMQpjZokAg
MHk7ixe0rSbnHcNF8g==
-----END CERTIFICATE-----

View File

@ -0,0 +1,18 @@
-----BEGIN CERTIFICATE-----
MIIC0DCCAjmgAwIBAgIJANdBFbakbhhOMA0GCSqGSIb3DQEBBQUAMIGAMQswCQYD
VQQGEwJOTzENMAsGA1UECAwET3NsbzENMAsGA1UEBwwET3NsbzEMMAoGA1UECgwD
STJQMQwwCgYDVQQLDANJMlAxFjAUBgNVBAMMDW5ldGRiLmkycDIubm8xHzAdBgkq
hkiG9w0BCQEWEG1lZWhAaTJwbWFpbC5vcmcwHhcNMTIxMDIzMDExMjIyWhcNMTkx
MDIyMDExMjIyWjCBgDELMAkGA1UEBhMCTk8xDTALBgNVBAgMBE9zbG8xDTALBgNV
BAcMBE9zbG8xDDAKBgNVBAoMA0kyUDEMMAoGA1UECwwDSTJQMRYwFAYDVQQDDA1u
ZXRkYi5pMnAyLm5vMR8wHQYJKoZIhvcNAQkBFhBtZWVoQGkycG1haWwub3JnMIGf
MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDCDvmjTpff6/XpiNuqoa9ZEKMlyq1o
kas9fHwnZax/0QTM3xusSQQ9DzeVMSx1ueYxhTZ6VLmE1mTr0aIndzugxGK/g85H
Y+cUl3nw7+5gLPMCUrKAXqQokE3mYxSNY3AUeend7nmHvm9iciw4+Sa2+6ROvQQy
kD31CEN6/I04rwIDAQABo1AwTjAdBgNVHQ4EFgQUV83dJhEcLbfJ+uh+MDYNPdah
RoQwHwYDVR0jBBgwFoAUV83dJhEcLbfJ+uh+MDYNPdahRoQwDAYDVR0TBAUwAwEB
/zANBgkqhkiG9w0BAQUFAAOBgQBQQlJym7mUMUM2ryKu20z2PSUzFyq5U4rWHeo3
elbNaTsFBwi+Ot/Lg/A5I4V8gywH1fBTG5bYKDUYvWohz1qIg66G57B1zT1zK9yh
Byz9go44M3y1/kXXSsJlY9llG9DDicr1y6LfldwZJ5zFAd3iiB8D8UadP5YLqb7v
wb1F1g==
-----END CERTIFICATE-----

View File

@ -0,0 +1,24 @@
-----BEGIN CERTIFICATE-----
MIID7TCCAtWgAwIBAgIJAOHakoadaLRiMA0GCSqGSIb3DQEBBQUAMIGMMQswCQYD
VQQGEwJBVDEQMA4GA1UECAwHQXVzdHJpYTENMAsGA1UEBwwER3JhejEMMAoGA1UE
CgwDSTJQMQ8wDQYDVQQLDAZSZXNlZWQxHjAcBgNVBAMMFXJlc2VlZC5pMnAtcHJv
amVrdC5kZTEdMBsGCSqGSIb3DQEJARYOcmVzZWVkQGkycDIuZGUwHhcNMTQwNTEw
MTAxOTM3WhcNMjQwNTA3MTAxOTM3WjCBjDELMAkGA1UEBhMCQVQxEDAOBgNVBAgM
B0F1c3RyaWExDTALBgNVBAcMBEdyYXoxDDAKBgNVBAoMA0kyUDEPMA0GA1UECwwG
UmVzZWVkMR4wHAYDVQQDDBVyZXNlZWQuaTJwLXByb2pla3QuZGUxHTAbBgkqhkiG
9w0BCQEWDnJlc2VlZEBpMnAyLmRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
CgKCAQEA8t5igIeAUZVX9k/A2gudRWVfToIV4yvlxmnH9UTJ8DTkWfHGbY9MmW2+
b0ZdvIZDcgg1nvcLEKqCDQnIp3wLGdM8fdVSXqxA1dLyHdk6IrGVqb60qpGENeIc
EHiUeB1g0KqP4kLcj2sNlo+Vupjnu7qS8v0/LfZ3fq2m4vtx8dYnvo+JIzGL9K0f
/DOil8QIcdTZupzMbXd6P936Blm/1RdbW/uKROOuuYE38NwYOUCq2/Nd+T86S5DD
9wQBjy0U+9nNayWf6BOSuP6m2mxx/pA1CvKRq7CzI0Gqjo2Msd+i0dTL2WIO2JDp
5uykZ0GabRW3UrMEuyrzzK6U2RZ1dQIDAQABo1AwTjAdBgNVHQ4EFgQUIejD2MMl
6PpcCernYd3ku3sEWfswHwYDVR0jBBgwFoAUIejD2MMl6PpcCernYd3ku3sEWfsw
DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAupUg3ZTBSE7iRebjcZ+y
zgnRaClmgrv8Mpa1/weTuXKhJZ65k6+G5mplI5hN/crKi/3b6oyfRrYhgdTdb0rD
2CbrhBkPGGlubhkjkxWjAhibzU6Kt3a7WOjykGnslpCZhwS/hiVB7ZE2JGdphFld
aJTKt12CytyP3GyIQyyX7O2t92dk8cW4tlxRVpaPNr59lk0V50qpvNmNyhxv3yDz
taop/etfjHStq1YrltHWH0d4Dxy8ubb7nV19uvPcE0+MrR2xm7jvOBfGjAf1bQ7Z
rk7RMHio4xWFJZO7TSzL5/8EH2jX6ZqpH+hZ6sV8TmzuRWsPkm0doXWr+HBZ/gMt
5w==
-----END CERTIFICATE-----

View File

@ -0,0 +1,20 @@
-----BEGIN CERTIFICATE-----
MIIDRDCCAiwCCQDCm/Zrmali9zANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJB
VTETMBEGA1UECBMKU29tZS1TdGF0ZTELMAkGA1UEBxMCSEgxDDAKBgNVBAoTA0ky
UDEPMA0GA1UECxMGcmVzZWVkMRQwEgYDVQQDEwtyZXNlZWQuaW5mbzAeFw0xMjEw
MjcxODU3NDNaFw0xNjEyMDUxODU3NDNaMGQxCzAJBgNVBAYTAkFVMRMwEQYDVQQI
EwpTb21lLVN0YXRlMQswCQYDVQQHEwJISDEMMAoGA1UEChMDSTJQMQ8wDQYDVQQL
EwZyZXNlZWQxFDASBgNVBAMTC3Jlc2VlZC5pbmZvMIIBIjANBgkqhkiG9w0BAQEF
AAOCAQ8AMIIBCgKCAQEAt9nz0iUvjdX4Hkhfk0FbBOeEP4i/FG3V4VrEdQfcviSF
XgzGYeRtGsvrFWP/5+6bcGnOkIy/jrKJfij3AjKJh8gTzqiNNNnV8VcHwFSNp+hZ
D4BM+UHPACV1Pjd3HQe6f0+LvcTs3HQgIkNkwUyqRuXOm/5Mk6SWSu1740aSwHCj
Kk0x1FByzI0YBvXCPX6TVk6sJqKkQyLzK0CSGSeqUq8GvGCq+jT9k62Su7ooxCwi
GzxaFjMdVYxuI8cuT5Cni+SUw1Ia8vhESnIy6slwzk37xNI80VuMvRT6rD2KcXDH
mK7ml1qL0rJWoF5AE+x/nen4V41mouv1W9rk3wTlTQIDAQABMA0GCSqGSIb3DQEB
BQUAA4IBAQAr6RBviBDW4bnPDTcdtstTDdaYX9yzoh+zzeGB0dUR26GKoOjpSItb
B9nrsW1eJ2wbblfGBUoXhcmNByKHXXHejMhmurHjdei2BuLbTsknN8DPKXu5UF9z
cg4cKQkxgzXOcNYlaF4+sfwFXDHJ4we/8vduVgkyo8R66543/Sh/nIMvq2slRT4w
wIBOVcMb2XxlbdwHW9XALAz9sto+4GH9GAC24f8ngluOpHijMnOOIo4dHibQ5hM9
KcDpHezP0ugMTAxS2NmtVahwAqa2IjpqR7aEQ2wLvxQzDqrXo93L93+b2FKRUQXH
Duud/n/w0kVV3DaIGikOsJayoanR+9HD
-----END CERTIFICATE-----

View File

@ -0,0 +1,33 @@
-----BEGIN CERTIFICATE-----
MIIFsTCCA5mgAwIBAgIJANgzPow6thRuMA0GCSqGSIb3DQEBBQUAMG8xCzAJBgNV
BAYTAk5PMQ0wCwYDVQQIDARPc2xvMQswCQYDVQQHDAJVSzETMBEGA1UECgwKSTJQ
IFJlc2VlZDETMBEGA1UECwwKSTJQIFJlc2VlZDEaMBgGA1UEAwwRdWsucmVzZWVk
LmkycDIubm8wHhcNMTQwNjI4MjA0OTA3WhcNMjQwNjI1MjA0OTA3WjBvMQswCQYD
VQQGEwJOTzENMAsGA1UECAwET3NsbzELMAkGA1UEBwwCVUsxEzARBgNVBAoMCkky
UCBSZXNlZWQxEzARBgNVBAsMCkkyUCBSZXNlZWQxGjAYBgNVBAMMEXVrLnJlc2Vl
ZC5pMnAyLm5vMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxlVlXWn5
Ham6ZqM6FkH6ZoXeXbncY/PnF669mCPcrPH56V2xZXwpeCXHWfu7YiHuhXXZSmzP
zwRrawHZTJulHt4e6j27JnDuEj69gmFpyi4B1djQ0kav0aJeagwCPG2do/UD7Cbr
4nITkU4CifLe47IUW/2K/EBI6bZGsRIDHJ3A+fAQmLnvehEkpvLN+cvtkpJOtZYx
6WvbwLsirkISnaio4//UY8M4poIu9mSG5pvNLagn9uoRPUSuj8jDEysB1Nmh12Zu
gFnt2XcxQB9/0krB5GnDTodrgfsz/UPbk44l4kFmQoLv5ACFndH69RKftogisauj
VVUrqCL3l9TcNsx8GLqZkeWhCwdZycZFjBhK01zihTYPEiU2HXfCNWhzLqxrM2Hh
r1ci+56fyNdn/ssO4o3hrGaWPDiayiHlEGEJxaG/ueKX2c3c0UJKkIGBPTEcdBjW
q42n/7EhY/ISaieQXPRK+gVm18I1OlGUH5FEYELO20bL88J8pr/bYuJyJnC8fiMP
YzKZuiVhey6dPr0zZgNDHyRbOlZqQllzKd1wbzbE4xqdUZfBWYwtRpdOJKDw4eoi
M69TwPQFfudeiudnMcR1gN37OkxS7UTEdsYIB5urgLb6qQD+tYFsxpcVPkedJw62
3TobhZjucaEZWzePd4u9faT9mQBXBAgY6VcCAwEAAaNQME4wHQYDVR0OBBYEFDTN
QRqhzaLc6XX2gFg26K//e0+8MB8GA1UdIwQYMBaAFDTNQRqhzaLc6XX2gFg26K//
e0+8MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggIBACcJ99Z45ghglvL3
/yMnx6IkOSneEm2/ADQoOabBQSC2grRAMBescKUiqpgbpBFalIPbPJUVrlH9tXYB
izNhqWETBY2tNy7AEHcJcCsAFuC2gOhaFH7FLgPA8V5IJmZ+McjB8REyowcN+CP4
GDY8s5/yr9S3HpKLD80UV18UX/j5m4b6I1w61QceMOSt6ahTtlnyvNBonFW94L1c
RmkdbhxYWn2eeUas62Q/+9bjr24E0weDKqopa3bbO7MWJ3mKkS4rua42j8GG3Q3q
UWPGh4zm+2+Ncjmz0Ho73RyYDDcp9IjwlAEv+NW86rz/5Pdkhoy+SzQwFYAwNgaQ
FRKb6ltpslxmu3tUdZ7Ydrj6MBGQyH2gRVm9qByro7WGI4UsyzsjP009Iu6dbhdC
2ddTGMisXF3dOmdRWh8dlggmW6gV4iaVgZkzLtrc9S0SK66utKMVXa4EUTm6XogX
F5ImPnVzIMo2qF2pP31aGDzKqJF3GNjGj+xHRVau5whz0a4ESY6V14PLTEL4Vc/H
J9uLCySifvqN+jzs5iY2QvNXjg2zPaTJbnjxxpYQJVSQHX6SyRcszhChqQzxnbyo
+S19BRclqzufRq6pp6VcOiID0BB7qPcrUHM9h1ingMXcZZlGBgHew9cY7tb5TAox
o+aTNc4k/7E543FVbs40dpOD2Fcr
-----END CERTIFICATE-----

View File

@ -0,0 +1,20 @@
-----BEGIN CERTIFICATE-----
MIIDUDCCAjgCCQCkTcCJMdZV7zANBgkqhkiG9w0BAQUFADBqMQswCQYDVQQGEwJO
TzENMAsGA1UECAwET3NsbzENMAsGA1UEBwwET3NsbzENMAsGA1UECgwET3NsbzES
MBAGA1UECwwJTm9yZGNsb3VkMRowGAYDVQQDDBF1cy5yZXNlZWQuaTJwMi5ubzAe
Fw0xNDA2MjcyMjQxMjFaFw0yNDA2MjQyMjQxMjFaMGoxCzAJBgNVBAYTAk5PMQ0w
CwYDVQQIDARPc2xvMQ0wCwYDVQQHDARPc2xvMQ0wCwYDVQQKDARPc2xvMRIwEAYD
VQQLDAlOb3JkY2xvdWQxGjAYBgNVBAMMEXVzLnJlc2VlZC5pMnAyLm5vMIIBIjAN
BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAomVoBEc53jzy3xGMfgRaKyX6MaGG
KAmwu0uMTX6bVzGjy56JMMq3luoxOrpvgrNZF52lu7i36Tejo0HM75AHoea1es55
DNLmrlDeqzlBU2WibOnizbB8G+tlMEbx8eAGAWk/Wv/vH8CAKmxjImslmbajzZC2
LEH7inp3J5T2sVV7zmXeL9OEPKNyohbu6Mrno2IAlEOr8cu+lWAaFWzpknnR1gBX
NkB/8+7vK5Fq4MT7B0qnXPxmaWDbUOepPPni8u+2L9+qt19vZH4/6KNuH7xd7JLz
FfIdol6jy2cBQyAK7cVKWDHNk7ceB4Dl0mjBDbBIRTtLK+rfdnVmfWn8aQIDAQAB
MA0GCSqGSIb3DQEBBQUAA4IBAQCQH4QJMp5xneh2ah7fiuVdtKbiv6QNunRz7nb/
mWYyqmBX7EHL8jOG5qmPELDgDt58HmnaYMo05nEJb9JhAoviEDXSYw0s6eN4n4nc
MKqgR/HLLSiXPwT+Wi1MI57OYim5AFTUCYTSaWFUT+dZKYb0QPE1XjGpQXi3ppsJ
3TJG71tOzJmZT6vRPmdTHJO70v6ZEhr5w4SiGx07gNmcgO8WRyb5ajOwSHiGKrj6
UsuRNhtCyZaAEmelR9mfKBR1J2Nb+9jTz6mJtpT82WY3bst6mFk+A+mMWBQy7Hjt
gpdSDBCcFx9if+AKINGLgFvFKV2q8UzbfXms19NsVt9Hu7W3
-----END CERTIFICATE-----

View File

@ -0,0 +1,41 @@
-----BEGIN CERTIFICATE-----
MIIHPTCCBSWgAwIBAgIBADANBgkqhkiG9w0BAQQFADB5MRAwDgYDVQQKEwdSb290
IENBMR4wHAYDVQQLExVodHRwOi8vd3d3LmNhY2VydC5vcmcxIjAgBgNVBAMTGUNB
IENlcnQgU2lnbmluZyBBdXRob3JpdHkxITAfBgkqhkiG9w0BCQEWEnN1cHBvcnRA
Y2FjZXJ0Lm9yZzAeFw0wMzAzMzAxMjI5NDlaFw0zMzAzMjkxMjI5NDlaMHkxEDAO
BgNVBAoTB1Jvb3QgQ0ExHjAcBgNVBAsTFWh0dHA6Ly93d3cuY2FjZXJ0Lm9yZzEi
MCAGA1UEAxMZQ0EgQ2VydCBTaWduaW5nIEF1dGhvcml0eTEhMB8GCSqGSIb3DQEJ
ARYSc3VwcG9ydEBjYWNlcnQub3JnMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
CgKCAgEAziLA4kZ97DYoB1CW8qAzQIxL8TtmPzHlawI229Z89vGIj053NgVBlfkJ
8BLPRoZzYLdufujAWGSuzbCtRRcMY/pnCujW0r8+55jE8Ez64AO7NV1sId6eINm6
zWYyN3L69wj1x81YyY7nDl7qPv4coRQKFWyGhFtkZip6qUtTefWIonvuLwphK42y
fk1WpRPs6tqSnqxEQR5YYGUFZvjARL3LlPdCfgv3ZWiYUQXw8wWRBB0bF4LsyFe7
w2t6iPGwcswlWyCR7BYCEo8y6RcYSNDHBS4CMEK4JZwFaz+qOqfrU0j36NK2B5jc
G8Y0f3/JHIJ6BVgrCFvzOKKrF11myZjXnhCLotLddJr3cQxyYN/Nb5gznZY0dj4k
epKwDpUeb+agRThHqtdB7Uq3EvbXG4OKDy7YCbZZ16oE/9KTfWgu3YtLq1i6L43q
laegw1SJpfvbi1EinbLDvhG+LJGGi5Z4rSDTii8aP8bQUWWHIbEZAWV/RRyH9XzQ
QUxPKZgh/TMfdQwEUfoZd9vUFBzugcMd9Zi3aQaRIt0AUMyBMawSB3s42mhb5ivU
fslfrejrckzzAeVLIL+aplfKkQABi6F1ITe1Yw1nPkZPcCBnzsXWWdsC4PDSy826
YreQQejdIOQpvGQpQsgi3Hia/0PsmBsJUUtaWsJx8cTLc6nloQsCAwEAAaOCAc4w
ggHKMB0GA1UdDgQWBBQWtTIb1Mfz4OaO873SsDrusjkY0TCBowYDVR0jBIGbMIGY
gBQWtTIb1Mfz4OaO873SsDrusjkY0aF9pHsweTEQMA4GA1UEChMHUm9vdCBDQTEe
MBwGA1UECxMVaHR0cDovL3d3dy5jYWNlcnQub3JnMSIwIAYDVQQDExlDQSBDZXJ0
IFNpZ25pbmcgQXV0aG9yaXR5MSEwHwYJKoZIhvcNAQkBFhJzdXBwb3J0QGNhY2Vy
dC5vcmeCAQAwDwYDVR0TAQH/BAUwAwEB/zAyBgNVHR8EKzApMCegJaAjhiFodHRw
czovL3d3dy5jYWNlcnQub3JnL3Jldm9rZS5jcmwwMAYJYIZIAYb4QgEEBCMWIWh0
dHBzOi8vd3d3LmNhY2VydC5vcmcvcmV2b2tlLmNybDA0BglghkgBhvhCAQgEJxYl
aHR0cDovL3d3dy5jYWNlcnQub3JnL2luZGV4LnBocD9pZD0xMDBWBglghkgBhvhC
AQ0ESRZHVG8gZ2V0IHlvdXIgb3duIGNlcnRpZmljYXRlIGZvciBGUkVFIGhlYWQg
b3ZlciB0byBodHRwOi8vd3d3LmNhY2VydC5vcmcwDQYJKoZIhvcNAQEEBQADggIB
ACjH7pyCArpcgBLKNQodgW+JapnM8mgPf6fhjViVPr3yBsOQWqy1YPaZQwGjiHCc
nWKdpIevZ1gNMDY75q1I08t0AoZxPuIrA2jxNGJARjtT6ij0rPtmlVOKTV39O9lg
18p5aTuxZZKmxoGCXJzN600BiqXfEVWqFcofN8CCmHBh22p8lqOOLlQ+TyGpkO/c
gr/c6EWtTZBzCDyUZbAEmXZ/4rzCahWqlwQ3JNgelE5tDlG+1sSPypZt90Pf6DBl
Jzt7u0NDY8RD97LsaMzhGY4i+5jhe1o+ATc7iwiwovOVThrLm82asduycPAtStvY
sONvRUgzEv/+PDIqVPfE94rwiCPCR/5kenHA0R6mY7AHfqQv0wGP3J8rtsYIqQ+T
SCX8Ev2fQtzzxD72V7DX3WnRBnc0CkvSyqD/HMaMyRa+xMwyN2hzXwj7UfdJUzYF
CpUCTPJ5GhD22Dp1nPMd8aINcGeGG7MW9S/lpOt5hvk9C8JzC6WZrG/8Z7jlLwum
GCSNe9FINSkYQKyTYOGWhlC0elnYjyELn8+CkcY7v2vcB5G5l1YjqrZslMZIBjzk
zk6q5PYvCdxTby78dOs6Y5nCpqyJvKeyRKANihDjbPIky/qbn3BHLt4Ui9SyIAmW
omTxJBzcoTWcFbLUvFUufQb1nA5V9FrWk9p2rSVzTMVD
-----END CERTIFICATE-----

18
filelist.mk Normal file
View File

@ -0,0 +1,18 @@
CPP_FILES := CryptoConst.cpp base64.cpp NTCPSession.cpp RouterInfo.cpp Transports.cpp \
RouterContext.cpp NetDb.cpp LeaseSet.cpp Tunnel.cpp TunnelEndpoint.cpp TunnelGateway.cpp \
TransitTunnel.cpp I2NPProtocol.cpp Log.cpp Garlic.cpp HTTPServer.cpp Streaming.cpp Identity.cpp \
SSU.cpp util.cpp Reseed.cpp DaemonLinux.cpp SSUData.cpp i2p.cpp aes.cpp SOCKS.cpp UPnP.cpp \
TunnelPool.cpp HTTPProxy.cpp AddressBook.cpp Daemon.cpp
H_FILES := CryptoConst.h base64.h NTCPSession.h RouterInfo.h Transports.h \
RouterContext.h NetDb.h LeaseSet.h Tunnel.h TunnelEndpoint.h TunnelGateway.h \
TransitTunnel.h I2NPProtocol.h Log.h Garlic.h HTTPServer.h Streaming.h Identity.h \
SSU.h util.h Reseed.h DaemonLinux.h SSUData.h i2p.h aes.h SOCKS.h UPnP.h TunnelPool.h \
HTTPProxy.h AddressBook.h Daemon.h
OBJECTS = $(addprefix obj/, $(notdir $(CPP_FILES:.cpp=.o)))

View File

@ -5,12 +5,14 @@
int main( int argc, char* argv[] )
{
Daemon.init(argc, argv);
Daemon.start();
if (Daemon.start())
{
while (Daemon.running)
{
//TODO Meeh: Find something better to do here.
std::this_thread::sleep_for (std::chrono::seconds(1));
}
}
Daemon.stop();
return EXIT_SUCCESS;
}