mirror of
https://github.com/PurpleI2P/i2pd
synced 2024-11-13 01:20:22 +03:00
commit
ccc24337be
@ -17,7 +17,6 @@ addons:
|
|||||||
- libboost-date-time-dev
|
- libboost-date-time-dev
|
||||||
- libboost-filesystem-dev
|
- libboost-filesystem-dev
|
||||||
- libboost-program-options-dev
|
- libboost-program-options-dev
|
||||||
- libboost-regex-dev
|
|
||||||
- libboost-system-dev
|
- libboost-system-dev
|
||||||
- libboost-thread-dev
|
- libboost-thread-dev
|
||||||
- libminiupnpc-dev
|
- libminiupnpc-dev
|
||||||
|
@ -159,7 +159,7 @@ namespace client
|
|||||||
|
|
||||||
int AddressBookFilesystemStorage::Save (const std::map<std::string, i2p::data::IdentHash>& addresses)
|
int AddressBookFilesystemStorage::Save (const std::map<std::string, i2p::data::IdentHash>& addresses)
|
||||||
{
|
{
|
||||||
if (addresses.size() == 0) {
|
if (addresses.empty()) {
|
||||||
LogPrint(eLogWarning, "Addressbook: not saving empty addressbook");
|
LogPrint(eLogWarning, "Addressbook: not saving empty addressbook");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
86
ChangeLog
Normal file
86
ChangeLog
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
# for this file format description,
|
||||||
|
# see https://github.com/olivierlacan/keep-a-changelog
|
||||||
|
|
||||||
|
## [2.8.0] - UNRELEASED
|
||||||
|
### Changed
|
||||||
|
- Proxy refactoring & speedup
|
||||||
|
- I2PControl refactoring & fixes (proper jsonrpc responses on errors)
|
||||||
|
- boost::regex no more needed
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- initscripts: added openrc one, in sysv-ish make I2PD_PORT optional
|
||||||
|
|
||||||
|
## [2.7.0] - 2016-05-18
|
||||||
|
### Added
|
||||||
|
- Precomputed El-Gamal/DH tables
|
||||||
|
- Configurable limit of transit tunnels
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Speed-up of assymetric crypto for non-x64 platforms
|
||||||
|
- Refactoring of web-console
|
||||||
|
|
||||||
|
## [2.6.0] - 2016-03-31
|
||||||
|
### Added
|
||||||
|
- Gracefull shutdown on SIGINT
|
||||||
|
- Numeric bandwidth limits (was: by router class)
|
||||||
|
- Jumpservices in web-console
|
||||||
|
- Logging to syslog
|
||||||
|
- Tray icon for windows application
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Logs refactoring
|
||||||
|
- Improved statistics in web-console
|
||||||
|
|
||||||
|
### Deprecated:
|
||||||
|
- Renamed main/tunnels config files (will use old, if found, but emits warning)
|
||||||
|
|
||||||
|
## [2.5.1] - 2016-03-10
|
||||||
|
### Fixed
|
||||||
|
- Doesn't create ~/.i2pd dir if missing
|
||||||
|
|
||||||
|
## [2.5.0] - 2016-03-04
|
||||||
|
### Added
|
||||||
|
- IRC server tunnels
|
||||||
|
- SOCKS outproxy support
|
||||||
|
- Support for gzipped addressbook updates
|
||||||
|
- Support for router families
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Shared RTT/RTO between streams
|
||||||
|
- Filesystem work refactoring
|
||||||
|
|
||||||
|
## [2.4.0] - 2016-02-03
|
||||||
|
### Added
|
||||||
|
- X-I2P-* headers for server http-tunnels
|
||||||
|
- I2CP options for I2P tunnels
|
||||||
|
- Show I2P tunnels in webconsole
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Refactoring of cmdline/config parsing
|
||||||
|
|
||||||
|
## [2.3.0] - 2016-01-12
|
||||||
|
### Added
|
||||||
|
- Support for new router bandwidth class codes (P and X)
|
||||||
|
- I2PControl supports external webui
|
||||||
|
- Added --pidfile and --notransit parameters
|
||||||
|
- Ability to specify signature type for i2p tunnel
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Fixed multiple floodfill-related bugs
|
||||||
|
- New webconsole layout
|
||||||
|
|
||||||
|
## [2.2.0] - 2015-12-22
|
||||||
|
### Added
|
||||||
|
- Ability to connect to router without ip via introducer
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Persist temporary encryption keys for local destinations
|
||||||
|
- Performance improvements for EdDSA
|
||||||
|
- New addressbook structure
|
||||||
|
|
||||||
|
## [2.1.0] - 2015-11-12
|
||||||
|
### Added
|
||||||
|
- Implementation of EdDSA
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- EdDSA is default signature type for new RouterInfos
|
@ -114,6 +114,24 @@ namespace client
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// I2CP
|
||||||
|
bool i2cp; i2p::config::GetOption("i2cp.enabled", i2cp);
|
||||||
|
if (i2cp)
|
||||||
|
{
|
||||||
|
std::string i2cpAddr; i2p::config::GetOption("i2cp.address", i2cpAddr);
|
||||||
|
uint16_t i2cpPort; i2p::config::GetOption("i2cp.port", i2cpPort);
|
||||||
|
LogPrint(eLogInfo, "Clients: starting I2CP at ", i2cpAddr, ":", i2cpPort);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
m_I2CPServer = new I2CPServer (i2cpAddr, i2cpPort);
|
||||||
|
m_I2CPServer->Start ();
|
||||||
|
}
|
||||||
|
catch (std::exception& e)
|
||||||
|
{
|
||||||
|
LogPrint(eLogError, "Clients: Exception in I2CP: ", e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
m_AddressBook.StartResolvers ();
|
m_AddressBook.StartResolvers ();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,6 +183,14 @@ namespace client
|
|||||||
m_BOBCommandChannel = nullptr;
|
m_BOBCommandChannel = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_I2CPServer)
|
||||||
|
{
|
||||||
|
LogPrint(eLogInfo, "Clients: stopping I2CP");
|
||||||
|
m_I2CPServer->Stop ();
|
||||||
|
delete m_I2CPServer;
|
||||||
|
m_I2CPServer = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
LogPrint(eLogInfo, "Clients: stopping AddressBook");
|
LogPrint(eLogInfo, "Clients: stopping AddressBook");
|
||||||
m_AddressBook.Stop ();
|
m_AddressBook.Stop ();
|
||||||
for (auto it: m_Destinations)
|
for (auto it: m_Destinations)
|
||||||
|
@ -178,6 +178,13 @@ namespace config {
|
|||||||
("bob.port", value<uint16_t>()->default_value(2827), "BOB listen port")
|
("bob.port", value<uint16_t>()->default_value(2827), "BOB listen port")
|
||||||
;
|
;
|
||||||
|
|
||||||
|
options_description i2cp("I2CP options");
|
||||||
|
i2cp.add_options()
|
||||||
|
("i2cp.enabled", value<bool>()->default_value(false), "Enable or disable I2CP")
|
||||||
|
("i2cp.address", value<std::string>()->default_value("127.0.0.1"), "I2CP listen address")
|
||||||
|
("i2cp.port", value<uint16_t>()->default_value(7654), "I2CP listen port")
|
||||||
|
;
|
||||||
|
|
||||||
options_description i2pcontrol("I2PControl options");
|
options_description i2pcontrol("I2PControl options");
|
||||||
i2pcontrol.add_options()
|
i2pcontrol.add_options()
|
||||||
("i2pcontrol.enabled", value<bool>()->default_value(false), "Enable or disable I2P Control Protocol")
|
("i2pcontrol.enabled", value<bool>()->default_value(false), "Enable or disable I2P Control Protocol")
|
||||||
@ -207,6 +214,7 @@ namespace config {
|
|||||||
.add(socksproxy)
|
.add(socksproxy)
|
||||||
.add(sam)
|
.add(sam)
|
||||||
.add(bob)
|
.add(bob)
|
||||||
|
.add(i2cp)
|
||||||
.add(i2pcontrol)
|
.add(i2pcontrol)
|
||||||
.add(precomputation)
|
.add(precomputation)
|
||||||
;
|
;
|
||||||
|
4
Config.h
4
Config.h
@ -68,7 +68,7 @@ namespace config {
|
|||||||
* @param value Variable where to store option
|
* @param value Variable where to store option
|
||||||
* @return this function returns false if parameter not found
|
* @return this function returns false if parameter not found
|
||||||
*
|
*
|
||||||
* @example uint16_t port; GetOption("sam.port", port);
|
* Example: uint16_t port; GetOption("sam.port", port);
|
||||||
*/
|
*/
|
||||||
template<typename T>
|
template<typename T>
|
||||||
bool GetOption(const char *name, T& value) {
|
bool GetOption(const char *name, T& value) {
|
||||||
@ -84,7 +84,7 @@ namespace config {
|
|||||||
* @param value New parameter value
|
* @param value New parameter value
|
||||||
* @return true if value set up successful, false otherwise
|
* @return true if value set up successful, false otherwise
|
||||||
*
|
*
|
||||||
* @example uint16_t port = 2827; SetOption("bob.port", port);
|
* Example: uint16_t port = 2827; SetOption("bob.port", port);
|
||||||
*/
|
*/
|
||||||
template<typename T>
|
template<typename T>
|
||||||
bool SetOption(const char *name, const T& value) {
|
bool SetOption(const char *name, const T& value) {
|
||||||
|
@ -45,10 +45,10 @@ namespace i2p
|
|||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
Daemon_Singleton::Daemon_Singleton() : running(1), d(*new Daemon_Singleton_Private()) {};
|
Daemon_Singleton::Daemon_Singleton() : isDaemon(false), running(true), d(*new Daemon_Singleton_Private()) {}
|
||||||
Daemon_Singleton::~Daemon_Singleton() {
|
Daemon_Singleton::~Daemon_Singleton() {
|
||||||
delete &d;
|
delete &d;
|
||||||
};
|
}
|
||||||
|
|
||||||
bool Daemon_Singleton::IsService () const
|
bool Daemon_Singleton::IsService () const
|
||||||
{
|
{
|
||||||
|
2
Daemon.h
2
Daemon.h
@ -22,9 +22,7 @@ namespace i2p
|
|||||||
virtual bool stop();
|
virtual bool stop();
|
||||||
virtual void run () {};
|
virtual void run () {};
|
||||||
|
|
||||||
bool isLogging;
|
|
||||||
bool isDaemon;
|
bool isDaemon;
|
||||||
|
|
||||||
bool running;
|
bool running;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -45,7 +45,7 @@ namespace i2p
|
|||||||
{
|
{
|
||||||
bool DaemonLinux::start()
|
bool DaemonLinux::start()
|
||||||
{
|
{
|
||||||
if (isDaemon == 1)
|
if (isDaemon)
|
||||||
{
|
{
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
pid = fork();
|
pid = fork();
|
||||||
@ -73,10 +73,10 @@ namespace i2p
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// close stdin/stdout/stderr descriptors
|
// point std{in,out,err} descriptors to /dev/null
|
||||||
freopen("/dev/null", "r", stdin);
|
stdin = freopen("/dev/null", "r", stdin);
|
||||||
freopen("/dev/null", "w", stdout);
|
stdout = freopen("/dev/null", "w", stdout);
|
||||||
freopen("/dev/null", "w", stderr);
|
stderr = freopen("/dev/null", "w", stderr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pidfile
|
// Pidfile
|
||||||
|
@ -45,7 +45,7 @@ namespace i2p
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isDaemon == 1)
|
if (isDaemon)
|
||||||
{
|
{
|
||||||
LogPrint(eLogDebug, "Daemon: running as service");
|
LogPrint(eLogDebug, "Daemon: running as service");
|
||||||
I2PService service(SERVICE_NAME);
|
I2PService service(SERVICE_NAME);
|
||||||
|
@ -130,10 +130,6 @@ namespace client
|
|||||||
if (!m_IsRunning)
|
if (!m_IsRunning)
|
||||||
{
|
{
|
||||||
m_IsRunning = true;
|
m_IsRunning = true;
|
||||||
if (m_IsPublic)
|
|
||||||
PersistTemporaryKeys ();
|
|
||||||
else
|
|
||||||
i2p::crypto::GenerateElGamalKeyPair(m_EncryptionPrivateKey, m_EncryptionPublicKey);
|
|
||||||
m_Pool->SetLocalDestination (shared_from_this ());
|
m_Pool->SetLocalDestination (shared_from_this ());
|
||||||
m_Pool->SetActive (true);
|
m_Pool->SetActive (true);
|
||||||
m_Thread = new std::thread (std::bind (&LeaseSetDestination::Run, shared_from_this ()));
|
m_Thread = new std::thread (std::bind (&LeaseSetDestination::Run, shared_from_this ()));
|
||||||
@ -204,14 +200,21 @@ namespace client
|
|||||||
return m_LeaseSet;
|
return m_LeaseSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LeaseSetDestination::SetLeaseSet (i2p::data::LocalLeaseSet * newLeaseSet)
|
||||||
|
{
|
||||||
|
m_LeaseSet.reset (newLeaseSet);
|
||||||
|
if (m_IsPublic)
|
||||||
|
{
|
||||||
|
m_PublishVerificationTimer.cancel ();
|
||||||
|
Publish ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void LeaseSetDestination::UpdateLeaseSet ()
|
void LeaseSetDestination::UpdateLeaseSet ()
|
||||||
{
|
{
|
||||||
int numTunnels = m_Pool->GetNumInboundTunnels () + 2; // 2 backup tunnels
|
int numTunnels = m_Pool->GetNumInboundTunnels () + 2; // 2 backup tunnels
|
||||||
if (numTunnels > i2p::data::MAX_NUM_LEASES) numTunnels = i2p::data::MAX_NUM_LEASES; // 16 tunnels maximum
|
if (numTunnels > i2p::data::MAX_NUM_LEASES) numTunnels = i2p::data::MAX_NUM_LEASES; // 16 tunnels maximum
|
||||||
auto leaseSet = new i2p::data::LocalLeaseSet (GetIdentity (), GetEncryptionPublicKey (),
|
CreateNewLeaseSet (m_Pool->GetInboundTunnels (numTunnels));
|
||||||
m_Pool->GetInboundTunnels (numTunnels));
|
|
||||||
Sign (leaseSet->GetBuffer (), leaseSet->GetBufferLen () - leaseSet->GetSignatureLen (), leaseSet->GetSignature ()); // TODO
|
|
||||||
m_LeaseSet.reset (leaseSet);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LeaseSetDestination::SubmitSessionKey (const uint8_t * key, const uint8_t * tag)
|
bool LeaseSetDestination::SubmitSessionKey (const uint8_t * key, const uint8_t * tag)
|
||||||
@ -391,11 +394,6 @@ namespace client
|
|||||||
{
|
{
|
||||||
i2p::garlic::GarlicDestination::SetLeaseSetUpdated ();
|
i2p::garlic::GarlicDestination::SetLeaseSetUpdated ();
|
||||||
UpdateLeaseSet ();
|
UpdateLeaseSet ();
|
||||||
if (m_IsPublic)
|
|
||||||
{
|
|
||||||
m_PublishVerificationTimer.cancel ();
|
|
||||||
Publish ();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LeaseSetDestination::Publish ()
|
void LeaseSetDestination::Publish ()
|
||||||
@ -642,36 +640,16 @@ namespace client
|
|||||||
else
|
else
|
||||||
it++;
|
it++;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void LeaseSetDestination::PersistTemporaryKeys ()
|
|
||||||
{
|
|
||||||
std::string ident = GetIdentHash().ToBase32();
|
|
||||||
std::string path = i2p::fs::DataDirPath("destinations", (ident + ".dat"));
|
|
||||||
std::ifstream f(path, std::ifstream::binary);
|
|
||||||
|
|
||||||
if (f) {
|
|
||||||
f.read ((char *)m_EncryptionPublicKey, 256);
|
|
||||||
f.read ((char *)m_EncryptionPrivateKey, 256);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
LogPrint (eLogInfo, "Destination: Creating new temporary keys for address ", ident, ".b32.i2p");
|
|
||||||
i2p::crypto::GenerateElGamalKeyPair(m_EncryptionPrivateKey, m_EncryptionPublicKey);
|
|
||||||
|
|
||||||
std::ofstream f1 (path, std::ofstream::binary | std::ofstream::out);
|
|
||||||
if (f1) {
|
|
||||||
f1.write ((char *)m_EncryptionPublicKey, 256);
|
|
||||||
f1.write ((char *)m_EncryptionPrivateKey, 256);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
LogPrint(eLogError, "Destinations: Can't save keys to ", path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ClientDestination::ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map<std::string, std::string> * params):
|
ClientDestination::ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map<std::string, std::string> * params):
|
||||||
LeaseSetDestination (isPublic, params),
|
LeaseSetDestination (isPublic, params),
|
||||||
m_Keys (keys), m_DatagramDestination (nullptr)
|
m_Keys (keys), m_DatagramDestination (nullptr)
|
||||||
{
|
{
|
||||||
|
if (isPublic)
|
||||||
|
PersistTemporaryKeys ();
|
||||||
|
else
|
||||||
|
i2p::crypto::GenerateElGamalKeyPair(m_EncryptionPrivateKey, m_EncryptionPublicKey);
|
||||||
if (isPublic)
|
if (isPublic)
|
||||||
LogPrint (eLogInfo, "Destination: Local address ", GetIdentHash().ToBase32 (), " created");
|
LogPrint (eLogInfo, "Destination: Local address ", GetIdentHash().ToBase32 (), " created");
|
||||||
}
|
}
|
||||||
@ -840,5 +818,37 @@ namespace client
|
|||||||
ret.push_back (it1.second);
|
ret.push_back (it1.second);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ClientDestination::PersistTemporaryKeys ()
|
||||||
|
{
|
||||||
|
std::string ident = GetIdentHash().ToBase32();
|
||||||
|
std::string path = i2p::fs::DataDirPath("destinations", (ident + ".dat"));
|
||||||
|
std::ifstream f(path, std::ifstream::binary);
|
||||||
|
|
||||||
|
if (f) {
|
||||||
|
f.read ((char *)m_EncryptionPublicKey, 256);
|
||||||
|
f.read ((char *)m_EncryptionPrivateKey, 256);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LogPrint (eLogInfo, "Destination: Creating new temporary keys for address ", ident, ".b32.i2p");
|
||||||
|
i2p::crypto::GenerateElGamalKeyPair(m_EncryptionPrivateKey, m_EncryptionPublicKey);
|
||||||
|
|
||||||
|
std::ofstream f1 (path, std::ofstream::binary | std::ofstream::out);
|
||||||
|
if (f1) {
|
||||||
|
f1.write ((char *)m_EncryptionPublicKey, 256);
|
||||||
|
f1.write ((char *)m_EncryptionPrivateKey, 256);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LogPrint(eLogError, "Destinations: Can't save keys to ", path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClientDestination::CreateNewLeaseSet (std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels)
|
||||||
|
{
|
||||||
|
auto leaseSet = new i2p::data::LocalLeaseSet (GetIdentity (), m_EncryptionPublicKey, tunnels);
|
||||||
|
// sign
|
||||||
|
Sign (leaseSet->GetBuffer (), leaseSet->GetBufferLen () - leaseSet->GetSignatureLen (), leaseSet->GetSignature ()); // TODO
|
||||||
|
SetLeaseSet (leaseSet);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,10 +81,6 @@ namespace client
|
|||||||
bool RequestDestination (const i2p::data::IdentHash& dest, RequestComplete requestComplete = nullptr);
|
bool RequestDestination (const i2p::data::IdentHash& dest, RequestComplete requestComplete = nullptr);
|
||||||
void CancelDestinationRequest (const i2p::data::IdentHash& dest);
|
void CancelDestinationRequest (const i2p::data::IdentHash& dest);
|
||||||
|
|
||||||
// implements LocalDestination
|
|
||||||
const uint8_t * GetEncryptionPrivateKey () const { return m_EncryptionPrivateKey; };
|
|
||||||
const uint8_t * GetEncryptionPublicKey () const { return m_EncryptionPublicKey; };
|
|
||||||
|
|
||||||
// implements GarlicDestination
|
// implements GarlicDestination
|
||||||
std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet ();
|
std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet ();
|
||||||
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const { return m_Pool; }
|
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const { return m_Pool; }
|
||||||
@ -98,8 +94,10 @@ namespace client
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
void SetLeaseSet (i2p::data::LocalLeaseSet * newLeaseSet);
|
||||||
// I2CP
|
// I2CP
|
||||||
virtual void HandleDataMessage (const uint8_t * buf, size_t len) = 0;
|
virtual void HandleDataMessage (const uint8_t * buf, size_t len) = 0;
|
||||||
|
virtual void CreateNewLeaseSet (std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels) = 0;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
@ -117,13 +115,9 @@ namespace client
|
|||||||
void HandleRequestTimoutTimer (const boost::system::error_code& ecode, const i2p::data::IdentHash& dest);
|
void HandleRequestTimoutTimer (const boost::system::error_code& ecode, const i2p::data::IdentHash& dest);
|
||||||
void HandleCleanupTimer (const boost::system::error_code& ecode);
|
void HandleCleanupTimer (const boost::system::error_code& ecode);
|
||||||
void CleanupRemoteLeaseSets ();
|
void CleanupRemoteLeaseSets ();
|
||||||
|
|
||||||
void PersistTemporaryKeys ();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
uint8_t m_EncryptionPublicKey[256], m_EncryptionPrivateKey[256];
|
|
||||||
|
|
||||||
volatile bool m_IsRunning;
|
volatile bool m_IsRunning;
|
||||||
std::thread * m_Thread;
|
std::thread * m_Thread;
|
||||||
boost::asio::io_service m_Service;
|
boost::asio::io_service m_Service;
|
||||||
@ -156,7 +150,8 @@ namespace client
|
|||||||
bool Stop ();
|
bool Stop ();
|
||||||
|
|
||||||
const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; };
|
const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; };
|
||||||
|
void Sign (const uint8_t * buf, int len, uint8_t * signature) const { m_Keys.Sign (buf, len, signature); };
|
||||||
|
|
||||||
// streaming
|
// streaming
|
||||||
std::shared_ptr<i2p::stream::StreamingDestination> CreateStreamingDestination (int port, bool gzip = true); // additional
|
std::shared_ptr<i2p::stream::StreamingDestination> CreateStreamingDestination (int port, bool gzip = true); // additional
|
||||||
std::shared_ptr<i2p::stream::StreamingDestination> GetStreamingDestination (int port = 0) const;
|
std::shared_ptr<i2p::stream::StreamingDestination> GetStreamingDestination (int port = 0) const;
|
||||||
@ -166,28 +161,31 @@ namespace client
|
|||||||
void AcceptStreams (const i2p::stream::StreamingDestination::Acceptor& acceptor);
|
void AcceptStreams (const i2p::stream::StreamingDestination::Acceptor& acceptor);
|
||||||
void StopAcceptingStreams ();
|
void StopAcceptingStreams ();
|
||||||
bool IsAcceptingStreams () const;
|
bool IsAcceptingStreams () const;
|
||||||
|
|
||||||
// datagram
|
// datagram
|
||||||
i2p::datagram::DatagramDestination * GetDatagramDestination () const { return m_DatagramDestination; };
|
i2p::datagram::DatagramDestination * GetDatagramDestination () const { return m_DatagramDestination; };
|
||||||
i2p::datagram::DatagramDestination * CreateDatagramDestination ();
|
i2p::datagram::DatagramDestination * CreateDatagramDestination ();
|
||||||
|
|
||||||
// implements LocalDestination
|
// implements LocalDestination
|
||||||
std::shared_ptr<const i2p::data::IdentityEx> GetIdentity () const { return m_Keys.GetPublic (); };
|
const uint8_t * GetEncryptionPrivateKey () const { return m_EncryptionPrivateKey; };
|
||||||
void Sign (const uint8_t * buf, int len, uint8_t * signature) const { m_Keys.Sign (buf, len, signature); };
|
std::shared_ptr<const i2p::data::IdentityEx> GetIdentity () const { return m_Keys.GetPublic (); };
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
// I2CP
|
// I2CP
|
||||||
void HandleDataMessage (const uint8_t * buf, size_t len);
|
void HandleDataMessage (const uint8_t * buf, size_t len);
|
||||||
|
void CreateNewLeaseSet (std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
std::shared_ptr<ClientDestination> GetSharedFromThis ()
|
std::shared_ptr<ClientDestination> GetSharedFromThis ()
|
||||||
{ return std::static_pointer_cast<ClientDestination>(shared_from_this ()); }
|
{ return std::static_pointer_cast<ClientDestination>(shared_from_this ()); }
|
||||||
|
void PersistTemporaryKeys ();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
i2p::data::PrivateKeys m_Keys;
|
i2p::data::PrivateKeys m_Keys;
|
||||||
|
uint8_t m_EncryptionPublicKey[256], m_EncryptionPrivateKey[256];
|
||||||
|
|
||||||
std::shared_ptr<i2p::stream::StreamingDestination> m_StreamingDestination; // default
|
std::shared_ptr<i2p::stream::StreamingDestination> m_StreamingDestination; // default
|
||||||
std::map<uint16_t, std::shared_ptr<i2p::stream::StreamingDestination> > m_StreamingDestinationsByPorts;
|
std::map<uint16_t, std::shared_ptr<i2p::stream::StreamingDestination> > m_StreamingDestinationsByPorts;
|
||||||
|
12
HTTP.cpp
12
HTTP.cpp
@ -253,21 +253,12 @@ namespace http {
|
|||||||
if (pos >= eoh)
|
if (pos >= eoh)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
auto it = headers.find("Host");
|
|
||||||
if (it != headers.end ()) {
|
|
||||||
host = it->second;
|
|
||||||
} else if (version == "HTTP/1.1") {
|
|
||||||
return -1; /* 'Host' header required for HTTP/1.1 */
|
|
||||||
} else if (url.host != "") {
|
|
||||||
host = url.host;
|
|
||||||
}
|
|
||||||
return eoh + strlen(HTTP_EOH);
|
return eoh + strlen(HTTP_EOH);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string HTTPReq::to_string() {
|
std::string HTTPReq::to_string() {
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << method << " " << uri << " " << version << CRLF;
|
ss << method << " " << uri << " " << version << CRLF;
|
||||||
ss << "Host: " << host << CRLF;
|
|
||||||
for (auto & h : headers) {
|
for (auto & h : headers) {
|
||||||
ss << h.first << ": " << h.second << CRLF;
|
ss << h.first << ": " << h.second << CRLF;
|
||||||
}
|
}
|
||||||
@ -406,11 +397,10 @@ namespace http {
|
|||||||
|
|
||||||
bool MergeChunkedResponse (std::istream& in, std::ostream& out) {
|
bool MergeChunkedResponse (std::istream& in, std::ostream& out) {
|
||||||
std::string hexLen;
|
std::string hexLen;
|
||||||
long int len;
|
|
||||||
while (!in.eof ()) {
|
while (!in.eof ()) {
|
||||||
std::getline (in, hexLen);
|
std::getline (in, hexLen);
|
||||||
errno = 0;
|
errno = 0;
|
||||||
len = strtoul(hexLen.c_str(), (char **) NULL, 16);
|
long int len = strtoul(hexLen.c_str(), (char **) NULL, 16);
|
||||||
if (errno != 0)
|
if (errno != 0)
|
||||||
return false; /* conversion error */
|
return false; /* conversion error */
|
||||||
if (len == 0)
|
if (len == 0)
|
||||||
|
17
HTTP.h
17
HTTP.h
@ -38,7 +38,7 @@ namespace http {
|
|||||||
* @brief Tries to parse url from string
|
* @brief Tries to parse url from string
|
||||||
* @return true on success, false on invalid url
|
* @return true on success, false on invalid url
|
||||||
*/
|
*/
|
||||||
bool parse (const char *str, size_t len = 0);
|
bool parse (const char *str, std::size_t len = 0);
|
||||||
bool parse (const std::string& url);
|
bool parse (const std::string& url);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -69,7 +69,6 @@ namespace http {
|
|||||||
std::string version;
|
std::string version;
|
||||||
std::string method;
|
std::string method;
|
||||||
std::string uri;
|
std::string uri;
|
||||||
std::string host;
|
|
||||||
|
|
||||||
HTTPReq (): version("HTTP/1.0"), method("GET"), uri("/") {};
|
HTTPReq (): version("HTTP/1.0"), method("GET"), uri("/") {};
|
||||||
|
|
||||||
@ -89,10 +88,12 @@ namespace http {
|
|||||||
std::string version;
|
std::string version;
|
||||||
std::string status;
|
std::string status;
|
||||||
unsigned short int code;
|
unsigned short int code;
|
||||||
/** simplifies response generation
|
/**
|
||||||
* If this variable is set:
|
* @brief Simplifies response generation
|
||||||
* a) Content-Length header will be added if missing
|
*
|
||||||
* b) contents of body will be included in response
|
* If this variable is set, on @a to_string() call:
|
||||||
|
* * Content-Length header will be added if missing,
|
||||||
|
* * contents of @a body will be included in generated response
|
||||||
*/
|
*/
|
||||||
std::string body;
|
std::string body;
|
||||||
|
|
||||||
@ -108,9 +109,9 @@ namespace http {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Serialize HTTP response to string
|
* @brief Serialize HTTP response to string
|
||||||
* @note If version is set to HTTP/1.1, and Date header is missing,
|
* @note If @a version is set to HTTP/1.1, and Date header is missing,
|
||||||
* it will be generated based on current time and added to headers
|
* it will be generated based on current time and added to headers
|
||||||
* @note If body member is set and Content-Length header is missing,
|
* @note If @a body is set and Content-Length header is missing,
|
||||||
* this header will be added, based on body's length
|
* this header will be added, based on body's length
|
||||||
*/
|
*/
|
||||||
std::string to_string();
|
std::string to_string();
|
||||||
|
@ -1,16 +1,8 @@
|
|||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <boost/lexical_cast.hpp>
|
#include <boost/lexical_cast.hpp>
|
||||||
#include <boost/regex.hpp>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <memory>
|
|
||||||
#include <set>
|
|
||||||
#include <boost/asio.hpp>
|
|
||||||
#include <mutex>
|
|
||||||
|
|
||||||
#include "I2PService.h"
|
|
||||||
#include "Destination.h"
|
|
||||||
#include "HTTPProxy.h"
|
#include "HTTPProxy.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "Identity.h"
|
#include "Identity.h"
|
||||||
@ -20,6 +12,7 @@
|
|||||||
#include "I2PEndian.h"
|
#include "I2PEndian.h"
|
||||||
#include "I2PTunnel.h"
|
#include "I2PTunnel.h"
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
|
#include "HTTP.h"
|
||||||
|
|
||||||
namespace i2p
|
namespace i2p
|
||||||
{
|
{
|
||||||
@ -43,7 +36,7 @@ namespace proxy
|
|||||||
void HandleSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered);
|
void HandleSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered);
|
||||||
void Terminate();
|
void Terminate();
|
||||||
void AsyncSockRead();
|
void AsyncSockRead();
|
||||||
void HTTPRequestFailed(const char *message);
|
void HTTPRequestFailed(/*std::string message*/);
|
||||||
void RedirectToJumpService();
|
void RedirectToJumpService();
|
||||||
void ExtractRequest();
|
void ExtractRequest();
|
||||||
bool IsI2PAddress();
|
bool IsI2PAddress();
|
||||||
@ -56,7 +49,6 @@ namespace proxy
|
|||||||
uint8_t m_http_buff[http_buffer_size];
|
uint8_t m_http_buff[http_buffer_size];
|
||||||
std::shared_ptr<boost::asio::ip::tcp::socket> m_sock;
|
std::shared_ptr<boost::asio::ip::tcp::socket> m_sock;
|
||||||
std::string m_request; //Data left to be sent
|
std::string m_request; //Data left to be sent
|
||||||
std::string m_Response;
|
|
||||||
std::string m_url; //URL
|
std::string m_url; //URL
|
||||||
std::string m_method; //Method
|
std::string m_method; //Method
|
||||||
std::string m_version; //HTTP version
|
std::string m_version; //HTTP version
|
||||||
@ -99,17 +91,10 @@ namespace proxy
|
|||||||
|
|
||||||
/* All hope is lost beyond this point */
|
/* All hope is lost beyond this point */
|
||||||
//TODO: handle this apropriately
|
//TODO: handle this apropriately
|
||||||
void HTTPProxyHandler::HTTPRequestFailed(const char *message)
|
void HTTPProxyHandler::HTTPRequestFailed(/*HTTPProxyHandler::errTypes error*/)
|
||||||
{
|
{
|
||||||
std::size_t size = std::strlen(message);
|
static std::string response = "HTTP/1.0 500 Internal Server Error\r\nContent-type: text/html\r\nContent-length: 0\r\n";
|
||||||
std::stringstream ss;
|
boost::asio::async_write(*m_sock, boost::asio::buffer(response,response.size()),
|
||||||
ss << "HTTP/1.0 500 Internal Server Error\r\n"
|
|
||||||
<< "Content-Type: text/plain\r\n";
|
|
||||||
ss << "Content-Length: " << std::to_string(size + 2) << "\r\n"
|
|
||||||
<< "\r\n"; /* end of headers */
|
|
||||||
ss << message << "\r\n";
|
|
||||||
m_Response = ss.str();
|
|
||||||
boost::asio::async_write(*m_sock, boost::asio::buffer(m_Response),
|
|
||||||
std::bind(&HTTPProxyHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1));
|
std::bind(&HTTPProxyHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,8 +105,7 @@ namespace proxy
|
|||||||
uint16_t httpPort; i2p::config::GetOption("http.port", httpPort);
|
uint16_t httpPort; i2p::config::GetOption("http.port", httpPort);
|
||||||
|
|
||||||
response << "HTTP/1.1 302 Found\r\nLocation: http://" << httpAddr << ":" << httpPort << "/?page=jumpservices&address=" << m_address << "\r\n\r\n";
|
response << "HTTP/1.1 302 Found\r\nLocation: http://" << httpAddr << ":" << httpPort << "/?page=jumpservices&address=" << m_address << "\r\n\r\n";
|
||||||
m_Response = response.str ();
|
boost::asio::async_write(*m_sock, boost::asio::buffer(response.str (),response.str ().length ()),
|
||||||
boost::asio::async_write(*m_sock, boost::asio::buffer(m_Response),
|
|
||||||
std::bind(&HTTPProxyHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1));
|
std::bind(&HTTPProxyHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,21 +117,13 @@ namespace proxy
|
|||||||
void HTTPProxyHandler::ExtractRequest()
|
void HTTPProxyHandler::ExtractRequest()
|
||||||
{
|
{
|
||||||
LogPrint(eLogDebug, "HTTPProxy: request: ", m_method, " ", m_url);
|
LogPrint(eLogDebug, "HTTPProxy: request: ", m_method, " ", m_url);
|
||||||
std::string server="";
|
i2p::http::URL url;
|
||||||
std::string port="80";
|
url.parse (m_url);
|
||||||
boost::regex rHTTP("http://(.*?)(:(\\d+))?(/.*)");
|
m_address = url.host;
|
||||||
boost::smatch m;
|
m_port = url.port;
|
||||||
std::string path;
|
m_path = url.path;
|
||||||
if(boost::regex_search(m_url, m, rHTTP, boost::match_extra))
|
if (!m_port) m_port = 80;
|
||||||
{
|
LogPrint(eLogDebug, "HTTPProxy: server: ", m_address, ", port: ", m_port, ", path: ", m_path);
|
||||||
server=m[1].str();
|
|
||||||
if (m[2].str() != "") port=m[3].str();
|
|
||||||
path=m[4].str();
|
|
||||||
}
|
|
||||||
LogPrint(eLogDebug, "HTTPProxy: server: ", server, ", port: ", port, ", path: ", path);
|
|
||||||
m_address = server;
|
|
||||||
m_port = boost::lexical_cast<int>(port);
|
|
||||||
m_path = path;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HTTPProxyHandler::ValidateHTTPRequest()
|
bool HTTPProxyHandler::ValidateHTTPRequest()
|
||||||
@ -155,7 +131,7 @@ namespace proxy
|
|||||||
if ( m_version != "HTTP/1.0" && m_version != "HTTP/1.1" )
|
if ( m_version != "HTTP/1.0" && m_version != "HTTP/1.1" )
|
||||||
{
|
{
|
||||||
LogPrint(eLogError, "HTTPProxy: unsupported version: ", m_version);
|
LogPrint(eLogError, "HTTPProxy: unsupported version: ", m_version);
|
||||||
HTTPRequestFailed("unsupported HTTP version");
|
HTTPRequestFailed(); //TODO: send right stuff
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -292,13 +268,13 @@ namespace proxy
|
|||||||
case '\n': EnterState(DONE); break;
|
case '\n': EnterState(DONE); break;
|
||||||
default:
|
default:
|
||||||
LogPrint(eLogError, "HTTPProxy: rejected invalid request ending with: ", ((int)*http_buff));
|
LogPrint(eLogError, "HTTPProxy: rejected invalid request ending with: ", ((int)*http_buff));
|
||||||
HTTPRequestFailed("rejected invalid request");
|
HTTPRequestFailed(); //TODO: add correct code
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
LogPrint(eLogError, "HTTPProxy: invalid state: ", m_state);
|
LogPrint(eLogError, "HTTPProxy: invalid state: ", m_state);
|
||||||
HTTPRequestFailed("invalid parser state");
|
HTTPRequestFailed(); //TODO: add correct code 500
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
http_buff++;
|
http_buff++;
|
||||||
@ -354,7 +330,7 @@ namespace proxy
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
LogPrint (eLogError, "HTTPProxy: error when creating the stream, check the previous warnings for more info");
|
LogPrint (eLogError, "HTTPProxy: error when creating the stream, check the previous warnings for more info");
|
||||||
HTTPRequestFailed("error when creating the stream, check logs");
|
HTTPRequestFailed(); // TODO: Send correct error message host unreachable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -367,5 +343,6 @@ namespace proxy
|
|||||||
{
|
{
|
||||||
return std::make_shared<HTTPProxyHandler> (this, socket);
|
return std::make_shared<HTTPProxyHandler> (this, socket);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,13 @@
|
|||||||
#ifndef HTTP_PROXY_H__
|
#ifndef HTTP_PROXY_H__
|
||||||
#define HTTP_PROXY_H__
|
#define HTTP_PROXY_H__
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <set>
|
||||||
|
#include <boost/asio.hpp>
|
||||||
|
#include <mutex>
|
||||||
|
#include "I2PService.h"
|
||||||
|
#include "Destination.h"
|
||||||
|
|
||||||
namespace i2p
|
namespace i2p
|
||||||
{
|
{
|
||||||
namespace proxy
|
namespace proxy
|
||||||
|
164
HTTPServer.cpp
164
HTTPServer.cpp
@ -27,156 +27,6 @@
|
|||||||
|
|
||||||
namespace i2p {
|
namespace i2p {
|
||||||
namespace http {
|
namespace http {
|
||||||
const char *itoopieImage =
|
|
||||||
"<img alt=\"ICToopie Icon\" src=\"data:image/png;base64,"
|
|
||||||
"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\" />";
|
|
||||||
|
|
||||||
const char *itoopieFavicon =
|
const char *itoopieFavicon =
|
||||||
"data:image/png;base64,"
|
"data:image/png;base64,"
|
||||||
"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv"
|
"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv"
|
||||||
@ -667,7 +517,7 @@ namespace http {
|
|||||||
i2p::config::GetOption("http.auth", needAuth);
|
i2p::config::GetOption("http.auth", needAuth);
|
||||||
i2p::config::GetOption("http.user", user);
|
i2p::config::GetOption("http.user", user);
|
||||||
i2p::config::GetOption("http.pass", pass);
|
i2p::config::GetOption("http.pass", pass);
|
||||||
};
|
}
|
||||||
|
|
||||||
void HTTPConnection::Receive ()
|
void HTTPConnection::Receive ()
|
||||||
{
|
{
|
||||||
@ -755,12 +605,14 @@ namespace http {
|
|||||||
|
|
||||||
// Html5 head start
|
// Html5 head start
|
||||||
ShowPageHead (s);
|
ShowPageHead (s);
|
||||||
if (req.uri.find("page=") != std::string::npos)
|
if (req.uri.find("page=") != std::string::npos) {
|
||||||
HandlePage (req, res, s);
|
HandlePage (req, res, s);
|
||||||
else if (req.uri.find("cmd=") != std::string::npos)
|
} else if (req.uri.find("cmd=") != std::string::npos) {
|
||||||
HandleCommand (req, res, s);
|
HandleCommand (req, res, s);
|
||||||
else
|
} else {
|
||||||
ShowStatus (s);
|
ShowStatus (s);
|
||||||
|
res.add_header("Refresh", "5");
|
||||||
|
}
|
||||||
ShowPageTail (s);
|
ShowPageTail (s);
|
||||||
|
|
||||||
res.code = 200;
|
res.code = 200;
|
||||||
@ -841,7 +693,9 @@ namespace http {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
s << "<b>SUCCESS</b>: Command accepted<br><br>\r\n";
|
s << "<b>SUCCESS</b>: Command accepted<br><br>\r\n";
|
||||||
s << "<a href=\"/?page=commands\">Back to commands list</a>";
|
s << "<a href=\"/?page=commands\">Back to commands list</a><br>\r\n";
|
||||||
|
s << "<p>You will be redirected in 5 seconds</b>";
|
||||||
|
res.add_header("Refresh", "5; url=/?page=commands");
|
||||||
}
|
}
|
||||||
|
|
||||||
void HTTPConnection::SendReply (HTTPRes& reply, std::string& content)
|
void HTTPConnection::SendReply (HTTPRes& reply, std::string& content)
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
namespace i2p {
|
namespace i2p {
|
||||||
namespace http {
|
namespace http {
|
||||||
extern const char *itoopieImage;
|
|
||||||
extern const char *itoopieFavicon;
|
extern const char *itoopieFavicon;
|
||||||
const size_t HTTP_CONNECTION_BUFFER_SIZE = 8192;
|
const size_t HTTP_CONNECTION_BUFFER_SIZE = 8192;
|
||||||
|
|
||||||
|
587
I2CP.cpp
587
I2CP.cpp
@ -1,29 +1,139 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2013-2016, The PurpleI2P Project
|
||||||
|
*
|
||||||
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
|
*
|
||||||
|
* See full license text in LICENSE file at top of project tree
|
||||||
|
*/
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <openssl/rand.h>
|
||||||
#include "I2PEndian.h"
|
#include "I2PEndian.h"
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
|
#include "Timestamp.h"
|
||||||
|
#include "LeaseSet.h"
|
||||||
|
#include "ClientContext.h"
|
||||||
|
#include "Transports.h"
|
||||||
|
#include "Signature.h"
|
||||||
#include "I2CP.h"
|
#include "I2CP.h"
|
||||||
|
|
||||||
|
|
||||||
namespace i2p
|
namespace i2p
|
||||||
{
|
{
|
||||||
namespace client
|
namespace client
|
||||||
{
|
{
|
||||||
|
|
||||||
I2CPDestination::I2CPDestination (I2CPSession& owner, std::shared_ptr<const i2p::data::IdentityEx> identity, bool isPublic):
|
I2CPDestination::I2CPDestination (std::shared_ptr<I2CPSession> owner, std::shared_ptr<const i2p::data::IdentityEx> identity, bool isPublic, const std::map<std::string, std::string>& params):
|
||||||
LeaseSetDestination (isPublic), m_Owner (owner), m_Identity (identity)
|
LeaseSetDestination (isPublic, ¶ms), m_Owner (owner), m_Identity (identity)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void I2CPDestination::SetEncryptionPrivateKey (const uint8_t * key)
|
||||||
|
{
|
||||||
|
memcpy (m_EncryptionPrivateKey, key, 256);
|
||||||
|
}
|
||||||
|
|
||||||
|
void I2CPDestination::HandleDataMessage (const uint8_t * buf, size_t len)
|
||||||
|
{
|
||||||
|
uint32_t length = bufbe32toh (buf);
|
||||||
|
if (length > len - 4) length = len - 4;
|
||||||
|
m_Owner->SendMessagePayloadMessage (buf + 4, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
void I2CPDestination::CreateNewLeaseSet (std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels)
|
||||||
|
{
|
||||||
|
i2p::data::LocalLeaseSet ls (m_Identity, m_EncryptionPrivateKey, tunnels); // we don't care about encryption key
|
||||||
|
m_LeaseSetExpirationTime = ls.GetExpirationTime ();
|
||||||
|
uint8_t * leases = ls.GetLeases ();
|
||||||
|
leases[-1] = tunnels.size ();
|
||||||
|
htobe16buf (leases - 3, m_Owner->GetSessionID ());
|
||||||
|
size_t l = 2/*sessionID*/ + 1/*num leases*/ + i2p::data::LEASE_SIZE*tunnels.size ();
|
||||||
|
m_Owner->SendI2CPMessage (I2CP_REQUEST_VARIABLE_LEASESET_MESSAGE, leases - 3, l);
|
||||||
|
}
|
||||||
|
|
||||||
|
void I2CPDestination::LeaseSetCreated (const uint8_t * buf, size_t len)
|
||||||
|
{
|
||||||
|
auto ls = new i2p::data::LocalLeaseSet (m_Identity, buf, len);
|
||||||
|
ls->SetExpirationTime (m_LeaseSetExpirationTime);
|
||||||
|
SetLeaseSet (ls);
|
||||||
|
}
|
||||||
|
|
||||||
|
void I2CPDestination::SendMsgTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint32_t nonce)
|
||||||
|
{
|
||||||
|
auto msg = NewI2NPMessage ();
|
||||||
|
uint8_t * buf = msg->GetPayload ();
|
||||||
|
htobe32buf (buf, len);
|
||||||
|
memcpy (buf + 4, payload, len);
|
||||||
|
msg->len += len + 4;
|
||||||
|
msg->FillI2NPMessageHeader (eI2NPData);
|
||||||
|
auto remote = FindLeaseSet (ident);
|
||||||
|
if (remote)
|
||||||
|
GetService ().post (std::bind (&I2CPDestination::SendMsg, GetSharedFromThis (), msg, remote));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto s = GetSharedFromThis ();
|
||||||
|
RequestDestination (ident,
|
||||||
|
[s, msg, nonce](std::shared_ptr<i2p::data::LeaseSet> ls)
|
||||||
|
{
|
||||||
|
if (ls)
|
||||||
|
{
|
||||||
|
bool sent = s->SendMsg (msg, ls);
|
||||||
|
s->m_Owner->SendMessageStatusMessage (nonce, sent ? eI2CPMessageStatusGuaranteedSuccess : eI2CPMessageStatusGuaranteedFailure);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
s->m_Owner->SendMessageStatusMessage (nonce, eI2CPMessageStatusNoLeaseSet);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool I2CPDestination::SendMsg (std::shared_ptr<I2NPMessage> msg, std::shared_ptr<const i2p::data::LeaseSet> remote)
|
||||||
|
{
|
||||||
|
auto outboundTunnel = GetTunnelPool ()->GetNextOutboundTunnel ();
|
||||||
|
auto leases = remote->GetNonExpiredLeases ();
|
||||||
|
if (!leases.empty () && outboundTunnel)
|
||||||
|
{
|
||||||
|
std::vector<i2p::tunnel::TunnelMessageBlock> msgs;
|
||||||
|
uint32_t i = rand () % leases.size ();
|
||||||
|
auto garlic = WrapMessage (remote, msg, true);
|
||||||
|
msgs.push_back (i2p::tunnel::TunnelMessageBlock
|
||||||
|
{
|
||||||
|
i2p::tunnel::eDeliveryTypeTunnel,
|
||||||
|
leases[i]->tunnelGateway, leases[i]->tunnelID,
|
||||||
|
garlic
|
||||||
|
});
|
||||||
|
outboundTunnel->SendTunnelDataMsg (msgs);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (outboundTunnel)
|
||||||
|
LogPrint (eLogWarning, "I2CP: Failed to send message. All leases expired");
|
||||||
|
else
|
||||||
|
LogPrint (eLogWarning, "I2CP: Failed to send message. No outbound tunnels");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
I2CPSession::I2CPSession (I2CPServer& owner, std::shared_ptr<boost::asio::ip::tcp::socket> socket):
|
I2CPSession::I2CPSession (I2CPServer& owner, std::shared_ptr<boost::asio::ip::tcp::socket> socket):
|
||||||
m_Owner (owner), m_Socket (socket),
|
m_Owner (owner), m_Socket (socket),
|
||||||
m_NextMessage (nullptr), m_NextMessageLen (0), m_NextMessageOffset (0)
|
m_NextMessage (nullptr), m_NextMessageLen (0), m_NextMessageOffset (0),
|
||||||
|
m_SessionID (0), m_MessageID (0), m_IsSendAccepted (true)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
I2CPSession::~I2CPSession ()
|
||||||
|
{
|
||||||
|
delete[] m_NextMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
void I2CPSession::Start ()
|
||||||
{
|
{
|
||||||
ReadProtocolByte ();
|
ReadProtocolByte ();
|
||||||
}
|
}
|
||||||
|
|
||||||
I2CPSession::~I2CPSession ()
|
void I2CPSession::Stop ()
|
||||||
{
|
{
|
||||||
delete[] m_NextMessage;
|
Terminate ();
|
||||||
}
|
}
|
||||||
|
|
||||||
void I2CPSession::ReadProtocolByte ()
|
void I2CPSession::ReadProtocolByte ()
|
||||||
@ -34,7 +144,7 @@ namespace client
|
|||||||
m_Socket->async_read_some (boost::asio::buffer (m_Buffer, 1),
|
m_Socket->async_read_some (boost::asio::buffer (m_Buffer, 1),
|
||||||
[s](const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
[s](const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
||||||
{
|
{
|
||||||
if (!ecode && bytes_transferred > 0 && s->m_Buffer[0] == I2CP_PRTOCOL_BYTE)
|
if (!ecode && bytes_transferred > 0 && s->m_Buffer[0] == I2CP_PROTOCOL_BYTE)
|
||||||
s->Receive ();
|
s->Receive ();
|
||||||
else
|
else
|
||||||
s->Terminate ();
|
s->Terminate ();
|
||||||
@ -54,25 +164,34 @@ namespace client
|
|||||||
Terminate ();
|
Terminate ();
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
size_t offset = 0;
|
size_t offset = 0; // from m_Buffer
|
||||||
if (m_NextMessage)
|
if (m_NextMessage)
|
||||||
{
|
{
|
||||||
if (m_NextMessageOffset + bytes_transferred <= m_NextMessageLen)
|
if (m_NextMessageOffset + bytes_transferred < m_NextMessageLen)
|
||||||
{
|
{
|
||||||
memcpy (m_NextMessage + m_NextMessageOffset, m_Buffer, bytes_transferred);
|
memcpy (m_NextMessage + m_NextMessageOffset, m_Buffer, bytes_transferred);
|
||||||
m_NextMessageOffset += bytes_transferred;
|
m_NextMessageOffset += bytes_transferred;
|
||||||
|
offset = bytes_transferred;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// m_NextMessage complete
|
||||||
offset = m_NextMessageLen - m_NextMessageOffset;
|
offset = m_NextMessageLen - m_NextMessageOffset;
|
||||||
memcpy (m_NextMessage + m_NextMessageOffset, m_Buffer, offset);
|
memcpy (m_NextMessage + m_NextMessageOffset, m_Buffer, offset);
|
||||||
HandleNextMessage (m_NextMessage);
|
HandleNextMessage (m_NextMessage);
|
||||||
delete[] m_NextMessage;
|
delete[] m_NextMessage;
|
||||||
|
m_NextMessage = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while (offset < bytes_transferred)
|
while (offset < bytes_transferred)
|
||||||
{
|
{
|
||||||
auto msgLen = bufbe32toh (m_Buffer + offset + I2CP_HEADER_LENGTH_OFFSET) + I2CP_HEADER_SIZE;
|
auto msgLen = bufbe32toh (m_Buffer + offset + I2CP_HEADER_LENGTH_OFFSET) + I2CP_HEADER_SIZE;
|
||||||
|
if (msgLen > 0xFFFF) // 64K
|
||||||
|
{
|
||||||
|
LogPrint (eLogError, "I2CP: message length ", msgLen, " exceeds 64K. Terminated");
|
||||||
|
Terminate ();
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (msgLen <= bytes_transferred - offset)
|
if (msgLen <= bytes_transferred - offset)
|
||||||
{
|
{
|
||||||
HandleNextMessage (m_Buffer + offset);
|
HandleNextMessage (m_Buffer + offset);
|
||||||
@ -102,17 +221,463 @@ namespace client
|
|||||||
|
|
||||||
void I2CPSession::Terminate ()
|
void I2CPSession::Terminate ()
|
||||||
{
|
{
|
||||||
|
if (m_Destination)
|
||||||
|
{
|
||||||
|
m_Destination->Stop ();
|
||||||
|
m_Destination = nullptr;
|
||||||
|
}
|
||||||
|
if (m_Socket)
|
||||||
|
{
|
||||||
|
m_Socket->close ();
|
||||||
|
m_Socket = nullptr;
|
||||||
|
}
|
||||||
|
m_Owner.RemoveSession (GetSessionID ());
|
||||||
|
}
|
||||||
|
|
||||||
|
void I2CPSession::SendI2CPMessage (uint8_t type, const uint8_t * payload, size_t len)
|
||||||
|
{
|
||||||
|
auto l = len + I2CP_HEADER_SIZE;
|
||||||
|
uint8_t * buf = new uint8_t[l];
|
||||||
|
htobe32buf (buf + I2CP_HEADER_LENGTH_OFFSET, len);
|
||||||
|
buf[I2CP_HEADER_TYPE_OFFSET] = type;
|
||||||
|
memcpy (buf + I2CP_HEADER_SIZE, payload, len);
|
||||||
|
boost::asio::async_write (*m_Socket, boost::asio::buffer (buf, l), boost::asio::transfer_all (),
|
||||||
|
std::bind(&I2CPSession::HandleI2CPMessageSent, shared_from_this (),
|
||||||
|
std::placeholders::_1, std::placeholders::_2, buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
void I2CPSession::HandleI2CPMessageSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, const uint8_t * buf)
|
||||||
|
{
|
||||||
|
delete[] buf;
|
||||||
|
if (ecode && ecode != boost::asio::error::operation_aborted)
|
||||||
|
Terminate ();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string I2CPSession::ExtractString (const uint8_t * buf, size_t len)
|
||||||
|
{
|
||||||
|
uint8_t l = buf[0];
|
||||||
|
if (l > len) l = len;
|
||||||
|
return std::string ((const char *)(buf + 1), l);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t I2CPSession::PutString (uint8_t * buf, size_t len, const std::string& str)
|
||||||
|
{
|
||||||
|
auto l = str.length ();
|
||||||
|
if (l + 1 >= len) l = len - 1;
|
||||||
|
if (l > 255) l = 255; // 1 byte max
|
||||||
|
buf[0] = l;
|
||||||
|
memcpy (buf + 1, str.c_str (), l);
|
||||||
|
return l + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void I2CPSession::ExtractMapping (const uint8_t * buf, size_t len, std::map<std::string, std::string>& mapping)
|
||||||
|
// TODO: move to Base.cpp
|
||||||
|
{
|
||||||
|
size_t offset = 0;
|
||||||
|
while (offset < len)
|
||||||
|
{
|
||||||
|
std::string param = ExtractString (buf + offset, len - offset);
|
||||||
|
offset += param.length ();
|
||||||
|
if (buf[offset] != '=')
|
||||||
|
{
|
||||||
|
LogPrint (eLogWarning, "I2CP: Unexpected character ", buf[offset], " instead '=' after ", param);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
offset++;
|
||||||
|
|
||||||
|
std::string value = ExtractString (buf + offset, len - offset);
|
||||||
|
offset += value.length ();
|
||||||
|
if (buf[offset] != ';')
|
||||||
|
{
|
||||||
|
LogPrint (eLogWarning, "I2CP: Unexpected character ", buf[offset], " instead ';' after ", value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
offset++;
|
||||||
|
mapping.insert (std::make_pair (param, value));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void I2CPSession::GetDateMessageHandler (const uint8_t * buf, size_t len)
|
void I2CPSession::GetDateMessageHandler (const uint8_t * buf, size_t len)
|
||||||
{
|
{
|
||||||
|
// get version
|
||||||
|
auto version = ExtractString (buf, len);
|
||||||
|
auto l = version.length () + 1 + 8;
|
||||||
|
uint8_t * payload = new uint8_t[l];
|
||||||
|
// set date
|
||||||
|
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||||
|
htobe64buf (payload, ts);
|
||||||
|
// echo vesrion back
|
||||||
|
PutString (payload + 8, l - 8, version);
|
||||||
|
SendI2CPMessage (I2CP_SET_DATE_MESSAGE, payload, l);
|
||||||
|
delete[] payload;
|
||||||
}
|
}
|
||||||
|
|
||||||
I2CPServer::I2CPServer (const std::string& interface, int port)
|
void I2CPSession::CreateSessionMessageHandler (const uint8_t * buf, size_t len)
|
||||||
|
{
|
||||||
|
RAND_bytes ((uint8_t *)&m_SessionID, 2);
|
||||||
|
auto identity = std::make_shared<i2p::data::IdentityEx>();
|
||||||
|
size_t offset = identity->FromBuffer (buf, len);
|
||||||
|
if (!offset)
|
||||||
|
{
|
||||||
|
LogPrint (eLogError, "I2CP: create session maformed identity");
|
||||||
|
SendSessionStatusMessage (3); // invalid
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint16_t optionsSize = bufbe16toh (buf + offset);
|
||||||
|
offset += 2;
|
||||||
|
if (optionsSize > len - offset)
|
||||||
|
{
|
||||||
|
LogPrint (eLogError, "I2CP: options size ", optionsSize, "exceeds message size");
|
||||||
|
SendSessionStatusMessage (3); // invalid
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::map<std::string, std::string> params;
|
||||||
|
ExtractMapping (buf + offset, optionsSize, params);
|
||||||
|
offset += optionsSize; // options
|
||||||
|
if (params[I2CP_PARAM_MESSAGE_RELIABILITY] == "none") m_IsSendAccepted = false;
|
||||||
|
|
||||||
|
offset += 8; // date
|
||||||
|
if (identity->Verify (buf, offset, buf + offset)) // signature
|
||||||
|
{
|
||||||
|
bool isPublic = true;
|
||||||
|
if (params[I2CP_PARAM_DONT_PUBLISH_LEASESET] == "true") isPublic = false;
|
||||||
|
if (!m_Destination)
|
||||||
|
{
|
||||||
|
m_Destination = std::make_shared<I2CPDestination>(shared_from_this (), identity, isPublic, params);
|
||||||
|
SendSessionStatusMessage (1); // created
|
||||||
|
LogPrint (eLogDebug, "I2CP: session ", m_SessionID, " created");
|
||||||
|
m_Destination->Start ();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LogPrint (eLogError, "I2CP: session already exists");
|
||||||
|
SendSessionStatusMessage (4); // refused
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LogPrint (eLogError, "I2CP: create session signature verification falied");
|
||||||
|
SendSessionStatusMessage (3); // invalid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void I2CPSession::DestroySessionMessageHandler (const uint8_t * buf, size_t len)
|
||||||
|
{
|
||||||
|
SendSessionStatusMessage (0); // destroy
|
||||||
|
LogPrint (eLogDebug, "I2CP: session ", m_SessionID, " destroyed");
|
||||||
|
if (m_Destination)
|
||||||
|
{
|
||||||
|
m_Destination->Stop ();
|
||||||
|
m_Destination = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void I2CPSession::ReconfigureSessionMessageHandler (const uint8_t * buf, size_t len)
|
||||||
|
{
|
||||||
|
// TODO: implement actual reconfiguration
|
||||||
|
SendSessionStatusMessage (2); // updated
|
||||||
|
}
|
||||||
|
|
||||||
|
void I2CPSession::SendSessionStatusMessage (uint8_t status)
|
||||||
|
{
|
||||||
|
uint8_t buf[3];
|
||||||
|
htobe16buf (buf, m_SessionID);
|
||||||
|
buf[2] = status;
|
||||||
|
SendI2CPMessage (I2CP_SESSION_STATUS_MESSAGE, buf, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
void I2CPSession::SendMessageStatusMessage (uint32_t nonce, I2CPMessageStatus status)
|
||||||
|
{
|
||||||
|
if (!nonce) return; // don't send status with zero nonce
|
||||||
|
uint8_t buf[15];
|
||||||
|
htobe16buf (buf, m_SessionID);
|
||||||
|
htobe32buf (buf + 2, m_MessageID++);
|
||||||
|
buf[6] = (uint8_t)status;
|
||||||
|
memset (buf + 7, 0, 4); // size
|
||||||
|
htobe32buf (buf + 11, nonce);
|
||||||
|
SendI2CPMessage (I2CP_MESSAGE_STATUS_MESSAGE, buf, 15);
|
||||||
|
}
|
||||||
|
|
||||||
|
void I2CPSession::CreateLeaseSetMessageHandler (const uint8_t * buf, size_t len)
|
||||||
|
{
|
||||||
|
uint16_t sessionID = bufbe16toh (buf);
|
||||||
|
if (sessionID == m_SessionID)
|
||||||
|
{
|
||||||
|
size_t offset = 2;
|
||||||
|
if (m_Destination)
|
||||||
|
{
|
||||||
|
offset += i2p::crypto::DSA_PRIVATE_KEY_LENGTH; // skip signing private key
|
||||||
|
// we always assume this field as 20 bytes (DSA) regardless actual size
|
||||||
|
// instead of
|
||||||
|
//offset += m_Destination->GetIdentity ()->GetSigningPrivateKeyLen ();
|
||||||
|
m_Destination->SetEncryptionPrivateKey (buf + offset);
|
||||||
|
offset += 256;
|
||||||
|
m_Destination->LeaseSetCreated (buf + offset, len - offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
LogPrint (eLogError, "I2CP: unexpected sessionID ", sessionID);
|
||||||
|
}
|
||||||
|
|
||||||
|
void I2CPSession::SendMessageMessageHandler (const uint8_t * buf, size_t len)
|
||||||
|
{
|
||||||
|
uint16_t sessionID = bufbe16toh (buf);
|
||||||
|
if (sessionID == m_SessionID)
|
||||||
|
{
|
||||||
|
size_t offset = 2;
|
||||||
|
if (m_Destination)
|
||||||
|
{
|
||||||
|
i2p::data::IdentityEx identity;
|
||||||
|
offset += identity.FromBuffer (buf + offset, len - offset);
|
||||||
|
uint32_t payloadLen = bufbe32toh (buf + offset);
|
||||||
|
offset += 4;
|
||||||
|
uint32_t nonce = bufbe32toh (buf + offset + payloadLen);
|
||||||
|
if (m_IsSendAccepted)
|
||||||
|
SendMessageStatusMessage (nonce, eI2CPMessageStatusAccepted); // accepted
|
||||||
|
m_Destination->SendMsgTo (buf + offset, payloadLen, identity.GetIdentHash (), nonce);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
LogPrint (eLogError, "I2CP: unexpected sessionID ", sessionID);
|
||||||
|
}
|
||||||
|
|
||||||
|
void I2CPSession::SendMessageExpiresMessageHandler (const uint8_t * buf, size_t len)
|
||||||
|
{
|
||||||
|
SendMessageMessageHandler (buf, len - 8); // ignore flags(2) and expiration(6)
|
||||||
|
}
|
||||||
|
|
||||||
|
void I2CPSession::HostLookupMessageHandler (const uint8_t * buf, size_t len)
|
||||||
|
{
|
||||||
|
uint16_t sessionID = bufbe16toh (buf);
|
||||||
|
if (sessionID == m_SessionID)
|
||||||
|
{
|
||||||
|
uint32_t requestID = bufbe32toh (buf + 2);
|
||||||
|
//uint32_t timeout = bufbe32toh (buf + 6);
|
||||||
|
i2p::data::IdentHash ident;
|
||||||
|
switch (buf[10])
|
||||||
|
{
|
||||||
|
case 0: // hash
|
||||||
|
ident = i2p::data::IdentHash (buf + 11);
|
||||||
|
break;
|
||||||
|
case 1: // address
|
||||||
|
{
|
||||||
|
auto name = ExtractString (buf + 11, len - 11);
|
||||||
|
if (!i2p::client::context.GetAddressBook ().GetIdentHash (name, ident))
|
||||||
|
{
|
||||||
|
LogPrint (eLogError, "I2CP: address ", name, " not found");
|
||||||
|
SendHostReplyMessage (requestID, nullptr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
LogPrint (eLogError, "I2CP: request type ", (int)buf[10], " is not supported");
|
||||||
|
SendHostReplyMessage (requestID, nullptr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_Destination)
|
||||||
|
{
|
||||||
|
auto ls = m_Destination->FindLeaseSet (ident);
|
||||||
|
if (ls)
|
||||||
|
SendHostReplyMessage (requestID, ls->GetIdentity ());
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto s = shared_from_this ();
|
||||||
|
m_Destination->RequestDestination (ident,
|
||||||
|
[s, requestID](std::shared_ptr<i2p::data::LeaseSet> leaseSet)
|
||||||
|
{
|
||||||
|
s->SendHostReplyMessage (requestID, leaseSet ? leaseSet->GetIdentity () : nullptr);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
SendHostReplyMessage (requestID, nullptr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
LogPrint (eLogError, "I2CP: unexpected sessionID ", sessionID);
|
||||||
|
}
|
||||||
|
|
||||||
|
void I2CPSession::SendHostReplyMessage (uint32_t requestID, std::shared_ptr<const i2p::data::IdentityEx> identity)
|
||||||
|
{
|
||||||
|
if (identity)
|
||||||
|
{
|
||||||
|
size_t l = identity->GetFullLen () + 7;
|
||||||
|
uint8_t * buf = new uint8_t[l];
|
||||||
|
htobe16buf (buf, m_SessionID);
|
||||||
|
htobe32buf (buf + 2, requestID);
|
||||||
|
buf[6] = 0; // result code
|
||||||
|
identity->ToBuffer (buf + 7, l - 7);
|
||||||
|
SendI2CPMessage (I2CP_HOST_REPLY_MESSAGE, buf, l);
|
||||||
|
delete[] buf;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
uint8_t buf[7];
|
||||||
|
htobe16buf (buf, m_SessionID);
|
||||||
|
htobe32buf (buf + 2, requestID);
|
||||||
|
buf[6] = 1; // result code
|
||||||
|
SendI2CPMessage (I2CP_HOST_REPLY_MESSAGE, buf, 7);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void I2CPSession::DestLookupMessageHandler (const uint8_t * buf, size_t len)
|
||||||
|
{
|
||||||
|
if (m_Destination)
|
||||||
|
{
|
||||||
|
auto ls = m_Destination->FindLeaseSet (buf);
|
||||||
|
if (ls)
|
||||||
|
{
|
||||||
|
auto l = ls->GetIdentity ()->GetFullLen ();
|
||||||
|
uint8_t * identBuf = new uint8_t[l];
|
||||||
|
ls->GetIdentity ()->ToBuffer (identBuf, l);
|
||||||
|
SendI2CPMessage (I2CP_DEST_REPLY_MESSAGE, identBuf, l);
|
||||||
|
delete[] identBuf;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto s = shared_from_this ();
|
||||||
|
i2p::data::IdentHash ident (buf);
|
||||||
|
m_Destination->RequestDestination (ident,
|
||||||
|
[s, ident](std::shared_ptr<i2p::data::LeaseSet> leaseSet)
|
||||||
|
{
|
||||||
|
if (leaseSet) // found
|
||||||
|
{
|
||||||
|
auto l = leaseSet->GetIdentity ()->GetFullLen ();
|
||||||
|
uint8_t * identBuf = new uint8_t[l];
|
||||||
|
leaseSet->GetIdentity ()->ToBuffer (identBuf, l);
|
||||||
|
s->SendI2CPMessage (I2CP_DEST_REPLY_MESSAGE, identBuf, l);
|
||||||
|
delete[] identBuf;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
s->SendI2CPMessage (I2CP_DEST_REPLY_MESSAGE, ident, 32); // not found
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
SendI2CPMessage (I2CP_DEST_REPLY_MESSAGE, buf, 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
void I2CPSession::GetBandwidthLimitsMessageHandler (const uint8_t * buf, size_t len)
|
||||||
|
{
|
||||||
|
uint8_t limits[64];
|
||||||
|
memset (limits, 0, 64);
|
||||||
|
htobe32buf (limits, i2p::transport::transports.GetInBandwidth ()); // inbound
|
||||||
|
htobe32buf (limits + 4, i2p::transport::transports.GetOutBandwidth ()); // outbound
|
||||||
|
SendI2CPMessage (I2CP_BANDWIDTH_LIMITS_MESSAGE, limits, 64);
|
||||||
|
}
|
||||||
|
|
||||||
|
void I2CPSession::SendMessagePayloadMessage (const uint8_t * payload, size_t len)
|
||||||
|
{
|
||||||
|
// we don't use SendI2CPMessage to eliminate additional copy
|
||||||
|
auto l = len + 10 + I2CP_HEADER_SIZE;
|
||||||
|
uint8_t * buf = new uint8_t[l];
|
||||||
|
htobe32buf (buf + I2CP_HEADER_LENGTH_OFFSET, len + 10);
|
||||||
|
buf[I2CP_HEADER_TYPE_OFFSET] = I2CP_MESSAGE_PAYLOAD_MESSAGE;
|
||||||
|
htobe16buf (buf + I2CP_HEADER_SIZE, m_SessionID);
|
||||||
|
htobe32buf (buf + I2CP_HEADER_SIZE + 2, m_MessageID++);
|
||||||
|
htobe32buf (buf + I2CP_HEADER_SIZE + 6, len);
|
||||||
|
memcpy (buf + I2CP_HEADER_SIZE + 10, payload, len);
|
||||||
|
boost::asio::async_write (*m_Socket, boost::asio::buffer (buf, l), boost::asio::transfer_all (),
|
||||||
|
std::bind(&I2CPSession::HandleI2CPMessageSent, shared_from_this (),
|
||||||
|
std::placeholders::_1, std::placeholders::_2, buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
I2CPServer::I2CPServer (const std::string& interface, int port):
|
||||||
|
m_IsRunning (false), m_Thread (nullptr),
|
||||||
|
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(interface), port))
|
||||||
{
|
{
|
||||||
memset (m_MessagesHandlers, 0, sizeof (m_MessagesHandlers));
|
memset (m_MessagesHandlers, 0, sizeof (m_MessagesHandlers));
|
||||||
m_MessagesHandlers[I2CP_GET_DATE_MESSAGE] = &I2CPSession::GetDateMessageHandler;
|
m_MessagesHandlers[I2CP_GET_DATE_MESSAGE] = &I2CPSession::GetDateMessageHandler;
|
||||||
|
m_MessagesHandlers[I2CP_CREATE_SESSION_MESSAGE] = &I2CPSession::CreateSessionMessageHandler;
|
||||||
|
m_MessagesHandlers[I2CP_DESTROY_SESSION_MESSAGE] = &I2CPSession::DestroySessionMessageHandler;
|
||||||
|
m_MessagesHandlers[I2CP_RECONFIGURE_SESSION_MESSAGE] = &I2CPSession::ReconfigureSessionMessageHandler;
|
||||||
|
m_MessagesHandlers[I2CP_CREATE_LEASESET_MESSAGE] = &I2CPSession::CreateLeaseSetMessageHandler;
|
||||||
|
m_MessagesHandlers[I2CP_SEND_MESSAGE_MESSAGE] = &I2CPSession::SendMessageMessageHandler;
|
||||||
|
m_MessagesHandlers[I2CP_SEND_MESSAGE_EXPIRES_MESSAGE] = &I2CPSession::SendMessageExpiresMessageHandler;
|
||||||
|
m_MessagesHandlers[I2CP_HOST_LOOKUP_MESSAGE] = &I2CPSession::HostLookupMessageHandler;
|
||||||
|
m_MessagesHandlers[I2CP_DEST_LOOKUP_MESSAGE] = &I2CPSession::DestLookupMessageHandler;
|
||||||
|
m_MessagesHandlers[I2CP_GET_BANDWIDTH_LIMITS_MESSAGE] = &I2CPSession::GetBandwidthLimitsMessageHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
I2CPServer::~I2CPServer ()
|
||||||
|
{
|
||||||
|
if (m_IsRunning)
|
||||||
|
Stop ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void I2CPServer::Start ()
|
||||||
|
{
|
||||||
|
Accept ();
|
||||||
|
m_IsRunning = true;
|
||||||
|
m_Thread = new std::thread (std::bind (&I2CPServer::Run, this));
|
||||||
|
}
|
||||||
|
|
||||||
|
void I2CPServer::Stop ()
|
||||||
|
{
|
||||||
|
m_IsRunning = false;
|
||||||
|
m_Acceptor.cancel ();
|
||||||
|
for (auto it: m_Sessions)
|
||||||
|
it.second->Stop ();
|
||||||
|
m_Sessions.clear ();
|
||||||
|
m_Service.stop ();
|
||||||
|
if (m_Thread)
|
||||||
|
{
|
||||||
|
m_Thread->join ();
|
||||||
|
delete m_Thread;
|
||||||
|
m_Thread = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void I2CPServer::Run ()
|
||||||
|
{
|
||||||
|
while (m_IsRunning)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
m_Service.run ();
|
||||||
|
}
|
||||||
|
catch (std::exception& ex)
|
||||||
|
{
|
||||||
|
LogPrint (eLogError, "I2CP: runtime exception: ", ex.what ());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void I2CPServer::Accept ()
|
||||||
|
{
|
||||||
|
auto newSocket = std::make_shared<boost::asio::ip::tcp::socket> (m_Service);
|
||||||
|
m_Acceptor.async_accept (*newSocket, std::bind (&I2CPServer::HandleAccept, this,
|
||||||
|
std::placeholders::_1, newSocket));
|
||||||
|
}
|
||||||
|
|
||||||
|
void I2CPServer::HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<boost::asio::ip::tcp::socket> socket)
|
||||||
|
{
|
||||||
|
if (!ecode && socket)
|
||||||
|
{
|
||||||
|
boost::system::error_code ec;
|
||||||
|
auto ep = socket->remote_endpoint (ec);
|
||||||
|
if (!ec)
|
||||||
|
{
|
||||||
|
LogPrint (eLogDebug, "I2CP: new connection from ", ep);
|
||||||
|
auto session = std::make_shared<I2CPSession>(*this, socket);
|
||||||
|
m_Sessions[session->GetSessionID ()] = session;
|
||||||
|
session->Start ();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
LogPrint (eLogError, "I2CP: incoming connection error ", ec.message ());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
LogPrint (eLogError, "I2CP: accept error: ", ecode.message ());
|
||||||
|
|
||||||
|
if (ecode != boost::asio::error::operation_aborted)
|
||||||
|
Accept ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void I2CPServer::RemoveSession (uint16_t sessionID)
|
||||||
|
{
|
||||||
|
m_Sessions.erase (sessionID);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
119
I2CP.h
119
I2CP.h
@ -1,9 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2013-2016, The PurpleI2P Project
|
||||||
|
*
|
||||||
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
|
*
|
||||||
|
* See full license text in LICENSE file at top of project tree
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef I2CP_H__
|
#ifndef I2CP_H__
|
||||||
#define I2CP_H__
|
#define I2CP_H__
|
||||||
|
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <thread>
|
||||||
|
#include <map>
|
||||||
#include <boost/asio.hpp>
|
#include <boost/asio.hpp>
|
||||||
#include "Destination.h"
|
#include "Destination.h"
|
||||||
|
|
||||||
@ -11,7 +21,7 @@ namespace i2p
|
|||||||
{
|
{
|
||||||
namespace client
|
namespace client
|
||||||
{
|
{
|
||||||
const uint8_t I2CP_PRTOCOL_BYTE = 0x2A;
|
const uint8_t I2CP_PROTOCOL_BYTE = 0x2A;
|
||||||
const size_t I2CP_SESSION_BUFFER_SIZE = 4096;
|
const size_t I2CP_SESSION_BUFFER_SIZE = 4096;
|
||||||
|
|
||||||
const size_t I2CP_HEADER_LENGTH_OFFSET = 0;
|
const size_t I2CP_HEADER_LENGTH_OFFSET = 0;
|
||||||
@ -19,27 +29,69 @@ namespace client
|
|||||||
const size_t I2CP_HEADER_SIZE = I2CP_HEADER_TYPE_OFFSET + 1;
|
const size_t I2CP_HEADER_SIZE = I2CP_HEADER_TYPE_OFFSET + 1;
|
||||||
|
|
||||||
const uint8_t I2CP_GET_DATE_MESSAGE = 32;
|
const uint8_t I2CP_GET_DATE_MESSAGE = 32;
|
||||||
|
const uint8_t I2CP_SET_DATE_MESSAGE = 33;
|
||||||
|
const uint8_t I2CP_CREATE_SESSION_MESSAGE = 1;
|
||||||
|
const uint8_t I2CP_RECONFIGURE_SESSION_MESSAGE = 2;
|
||||||
|
const uint8_t I2CP_SESSION_STATUS_MESSAGE = 20;
|
||||||
|
const uint8_t I2CP_DESTROY_SESSION_MESSAGE = 3;
|
||||||
|
const uint8_t I2CP_REQUEST_VARIABLE_LEASESET_MESSAGE = 37;
|
||||||
|
const uint8_t I2CP_CREATE_LEASESET_MESSAGE = 4;
|
||||||
|
const uint8_t I2CP_SEND_MESSAGE_MESSAGE = 5;
|
||||||
|
const uint8_t I2CP_SEND_MESSAGE_EXPIRES_MESSAGE = 36;
|
||||||
|
const uint8_t I2CP_MESSAGE_PAYLOAD_MESSAGE = 31;
|
||||||
|
const uint8_t I2CP_MESSAGE_STATUS_MESSAGE = 22;
|
||||||
|
const uint8_t I2CP_HOST_LOOKUP_MESSAGE = 38;
|
||||||
|
const uint8_t I2CP_HOST_REPLY_MESSAGE = 39;
|
||||||
|
const uint8_t I2CP_DEST_LOOKUP_MESSAGE = 34;
|
||||||
|
const uint8_t I2CP_DEST_REPLY_MESSAGE = 35;
|
||||||
|
const uint8_t I2CP_GET_BANDWIDTH_LIMITS_MESSAGE = 8;
|
||||||
|
const uint8_t I2CP_BANDWIDTH_LIMITS_MESSAGE = 23;
|
||||||
|
|
||||||
|
enum I2CPMessageStatus
|
||||||
|
{
|
||||||
|
eI2CPMessageStatusAccepted = 1,
|
||||||
|
eI2CPMessageStatusGuaranteedSuccess = 4,
|
||||||
|
eI2CPMessageStatusGuaranteedFailure = 5,
|
||||||
|
eI2CPMessageStatusNoLeaseSet = 21
|
||||||
|
};
|
||||||
|
|
||||||
|
// params
|
||||||
|
const char I2CP_PARAM_DONT_PUBLISH_LEASESET[] = "i2cp.dontPublishLeaseSet";
|
||||||
|
const char I2CP_PARAM_MESSAGE_RELIABILITY[] = "i2cp.messageReliability";
|
||||||
|
|
||||||
class I2CPSession;
|
class I2CPSession;
|
||||||
class I2CPDestination: public LeaseSetDestination
|
class I2CPDestination: public LeaseSetDestination
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
I2CPDestination (I2CPSession& owner, std::shared_ptr<const i2p::data::IdentityEx> identity, bool isPublic);
|
I2CPDestination (std::shared_ptr<I2CPSession> owner, std::shared_ptr<const i2p::data::IdentityEx> identity, bool isPublic, const std::map<std::string, std::string>& params);
|
||||||
|
|
||||||
|
void SetEncryptionPrivateKey (const uint8_t * key);
|
||||||
|
void LeaseSetCreated (const uint8_t * buf, size_t len); // called from I2CPSession
|
||||||
|
void SendMsgTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint32_t nonce); // called from I2CPSession
|
||||||
|
|
||||||
|
// implements LocalDestination
|
||||||
|
const uint8_t * GetEncryptionPrivateKey () const { return m_EncryptionPrivateKey; };
|
||||||
|
std::shared_ptr<const i2p::data::IdentityEx> GetIdentity () const { return m_Identity; };
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
// implements LocalDestination
|
|
||||||
std::shared_ptr<const i2p::data::IdentityEx> GetIdentity () const { return m_Identity; };
|
|
||||||
void Sign (const uint8_t * buf, int len, uint8_t * signature) const { /* TODO */};
|
|
||||||
|
|
||||||
// I2CP
|
// I2CP
|
||||||
void HandleDataMessage (const uint8_t * buf, size_t len) {};
|
void HandleDataMessage (const uint8_t * buf, size_t len);
|
||||||
|
void CreateNewLeaseSet (std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
I2CPSession& m_Owner;
|
std::shared_ptr<I2CPDestination> GetSharedFromThis ()
|
||||||
|
{ return std::static_pointer_cast<I2CPDestination>(shared_from_this ()); }
|
||||||
|
bool SendMsg (std::shared_ptr<I2NPMessage> msg, std::shared_ptr<const i2p::data::LeaseSet> remote);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
std::shared_ptr<I2CPSession> m_Owner;
|
||||||
std::shared_ptr<const i2p::data::IdentityEx> m_Identity;
|
std::shared_ptr<const i2p::data::IdentityEx> m_Identity;
|
||||||
|
uint8_t m_EncryptionPrivateKey[256];
|
||||||
|
uint64_t m_LeaseSetExpirationTime;
|
||||||
};
|
};
|
||||||
|
|
||||||
class I2CPServer;
|
class I2CPServer;
|
||||||
@ -50,8 +102,26 @@ namespace client
|
|||||||
I2CPSession (I2CPServer& owner, std::shared_ptr<boost::asio::ip::tcp::socket> socket);
|
I2CPSession (I2CPServer& owner, std::shared_ptr<boost::asio::ip::tcp::socket> socket);
|
||||||
~I2CPSession ();
|
~I2CPSession ();
|
||||||
|
|
||||||
|
void Start ();
|
||||||
|
void Stop ();
|
||||||
|
uint16_t GetSessionID () const { return m_SessionID; };
|
||||||
|
|
||||||
|
// called from I2CPDestination
|
||||||
|
void SendI2CPMessage (uint8_t type, const uint8_t * payload, size_t len);
|
||||||
|
void SendMessagePayloadMessage (const uint8_t * payload, size_t len);
|
||||||
|
void SendMessageStatusMessage (uint32_t nonce, I2CPMessageStatus status);
|
||||||
|
|
||||||
// message handlers
|
// message handlers
|
||||||
void GetDateMessageHandler (const uint8_t * buf, size_t len);
|
void GetDateMessageHandler (const uint8_t * buf, size_t len);
|
||||||
|
void CreateSessionMessageHandler (const uint8_t * buf, size_t len);
|
||||||
|
void DestroySessionMessageHandler (const uint8_t * buf, size_t len);
|
||||||
|
void ReconfigureSessionMessageHandler (const uint8_t * buf, size_t len);
|
||||||
|
void CreateLeaseSetMessageHandler (const uint8_t * buf, size_t len);
|
||||||
|
void SendMessageMessageHandler (const uint8_t * buf, size_t len);
|
||||||
|
void SendMessageExpiresMessageHandler (const uint8_t * buf, size_t len);
|
||||||
|
void HostLookupMessageHandler (const uint8_t * buf, size_t len);
|
||||||
|
void DestLookupMessageHandler (const uint8_t * buf, size_t len);
|
||||||
|
void GetBandwidthLimitsMessageHandler (const uint8_t * buf, size_t len);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
@ -60,6 +130,14 @@ namespace client
|
|||||||
void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||||
void HandleNextMessage (const uint8_t * buf);
|
void HandleNextMessage (const uint8_t * buf);
|
||||||
void Terminate ();
|
void Terminate ();
|
||||||
|
|
||||||
|
void HandleI2CPMessageSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, const uint8_t * buf);
|
||||||
|
std::string ExtractString (const uint8_t * buf, size_t len);
|
||||||
|
size_t PutString (uint8_t * buf, size_t len, const std::string& str);
|
||||||
|
void ExtractMapping (const uint8_t * buf, size_t len, std::map<std::string, std::string>& mapping);
|
||||||
|
|
||||||
|
void SendSessionStatusMessage (uint8_t status);
|
||||||
|
void SendHostReplyMessage (uint32_t requestID, std::shared_ptr<const i2p::data::IdentityEx> identity);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
@ -69,6 +147,9 @@ namespace client
|
|||||||
size_t m_NextMessageLen, m_NextMessageOffset;
|
size_t m_NextMessageLen, m_NextMessageOffset;
|
||||||
|
|
||||||
std::shared_ptr<I2CPDestination> m_Destination;
|
std::shared_ptr<I2CPDestination> m_Destination;
|
||||||
|
uint16_t m_SessionID;
|
||||||
|
uint32_t m_MessageID;
|
||||||
|
bool m_IsSendAccepted;
|
||||||
};
|
};
|
||||||
typedef void (I2CPSession::*I2CPMessageHandler)(const uint8_t * buf, size_t len);
|
typedef void (I2CPSession::*I2CPMessageHandler)(const uint8_t * buf, size_t len);
|
||||||
|
|
||||||
@ -77,10 +158,30 @@ namespace client
|
|||||||
public:
|
public:
|
||||||
|
|
||||||
I2CPServer (const std::string& interface, int port);
|
I2CPServer (const std::string& interface, int port);
|
||||||
|
~I2CPServer ();
|
||||||
|
|
||||||
|
void Start ();
|
||||||
|
void Stop ();
|
||||||
|
boost::asio::io_service& GetService () { return m_Service; };
|
||||||
|
|
||||||
|
void RemoveSession (uint16_t sessionID);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void Run ();
|
||||||
|
|
||||||
|
void Accept ();
|
||||||
|
void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<boost::asio::ip::tcp::socket> socket);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
I2CPMessageHandler m_MessagesHandlers[256];
|
I2CPMessageHandler m_MessagesHandlers[256];
|
||||||
|
std::map<uint16_t, std::shared_ptr<I2CPSession> > m_Sessions;
|
||||||
|
|
||||||
|
bool m_IsRunning;
|
||||||
|
std::thread * m_Thread;
|
||||||
|
boost::asio::io_service m_Service;
|
||||||
|
boost::asio::ip::tcp::acceptor m_Acceptor;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
#include "ClientContext.h"
|
#include "ClientContext.h"
|
||||||
#include "I2PService.h"
|
#include "I2PService.h"
|
||||||
|
|
||||||
|
|
||||||
namespace i2p
|
namespace i2p
|
||||||
{
|
{
|
||||||
namespace client
|
namespace client
|
||||||
@ -28,7 +27,7 @@ namespace client
|
|||||||
m_LocalDestination->CreateStream (streamRequestComplete, identHash, port);
|
m_LocalDestination->CreateStream (streamRequestComplete, identHash, port);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LogPrint (eLogWarning, "I2PService: Remote destination ", dest, " not found");
|
LogPrint (eLogWarning, "I2PService: Remote destination not found: ", dest);
|
||||||
streamRequestComplete (nullptr);
|
streamRequestComplete (nullptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -71,7 +70,7 @@ namespace client
|
|||||||
std::bind(&TCPIPPipe::HandleUpstreamReceived, shared_from_this(),
|
std::bind(&TCPIPPipe::HandleUpstreamReceived, shared_from_this(),
|
||||||
std::placeholders::_1, std::placeholders::_2));
|
std::placeholders::_1, std::placeholders::_2));
|
||||||
} else {
|
} else {
|
||||||
LogPrint(eLogError, "TCPIPPipe: no upstream socket for read");
|
LogPrint(eLogError, "TCPIPPipe: upstream receive: no socket");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,14 +81,14 @@ namespace client
|
|||||||
std::bind(&TCPIPPipe::HandleDownstreamReceived, shared_from_this(),
|
std::bind(&TCPIPPipe::HandleDownstreamReceived, shared_from_this(),
|
||||||
std::placeholders::_1, std::placeholders::_2));
|
std::placeholders::_1, std::placeholders::_2));
|
||||||
} else {
|
} else {
|
||||||
LogPrint(eLogError, "TCPIPPipe: no downstream socket for read");
|
LogPrint(eLogError, "TCPIPPipe: downstream receive: no socket");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TCPIPPipe::UpstreamWrite(const uint8_t * buf, size_t len)
|
void TCPIPPipe::UpstreamWrite(const uint8_t * buf, size_t len)
|
||||||
{
|
{
|
||||||
if (m_up) {
|
if (m_up) {
|
||||||
LogPrint(eLogDebug, "TCPIPPipe: write upstream ", (int)len);
|
LogPrint(eLogDebug, "TCPIPPipe: upstream: ", (int) len, " bytes written");
|
||||||
boost::asio::async_write(*m_up, boost::asio::buffer(buf, len),
|
boost::asio::async_write(*m_up, boost::asio::buffer(buf, len),
|
||||||
boost::asio::transfer_all(),
|
boost::asio::transfer_all(),
|
||||||
std::bind(&TCPIPPipe::HandleUpstreamWrite,
|
std::bind(&TCPIPPipe::HandleUpstreamWrite,
|
||||||
@ -97,14 +96,14 @@ namespace client
|
|||||||
std::placeholders::_1)
|
std::placeholders::_1)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
LogPrint(eLogError, "tcpip pipe upstream socket null");
|
LogPrint(eLogError, "TCPIPPipe: upstream write: no socket");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TCPIPPipe::DownstreamWrite(const uint8_t * buf, size_t len)
|
void TCPIPPipe::DownstreamWrite(const uint8_t * buf, size_t len)
|
||||||
{
|
{
|
||||||
if (m_down) {
|
if (m_down) {
|
||||||
LogPrint(eLogDebug, "TCPIPPipe: write downstream ", (int)len);
|
LogPrint(eLogDebug, "TCPIPPipe: downstream: ", (int) len, " bytes written");
|
||||||
boost::asio::async_write(*m_down, boost::asio::buffer(buf, len),
|
boost::asio::async_write(*m_down, boost::asio::buffer(buf, len),
|
||||||
boost::asio::transfer_all(),
|
boost::asio::transfer_all(),
|
||||||
std::bind(&TCPIPPipe::HandleDownstreamWrite,
|
std::bind(&TCPIPPipe::HandleDownstreamWrite,
|
||||||
@ -112,16 +111,16 @@ namespace client
|
|||||||
std::placeholders::_1)
|
std::placeholders::_1)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
LogPrint(eLogError, "tcpip pipe downstream socket null");
|
LogPrint(eLogError, "TCPIPPipe: downstream write: no socket");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void TCPIPPipe::HandleDownstreamReceived(const boost::system::error_code & ecode, std::size_t bytes_transfered)
|
void TCPIPPipe::HandleDownstreamReceived(const boost::system::error_code & ecode, std::size_t bytes_transfered)
|
||||||
{
|
{
|
||||||
LogPrint(eLogDebug, "TCPIPPipe downstream got ", (int) bytes_transfered);
|
LogPrint(eLogDebug, "TCPIPPipe: downstream: ", (int) bytes_transfered, " bytes received");
|
||||||
if (ecode) {
|
if (ecode) {
|
||||||
LogPrint(eLogError, "TCPIPPipe Downstream read error:" , ecode.message());
|
LogPrint(eLogError, "TCPIPPipe: downstream read error:" , ecode.message());
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
if (ecode != boost::asio::error::operation_aborted)
|
||||||
Terminate();
|
Terminate();
|
||||||
} else {
|
} else {
|
||||||
@ -135,7 +134,7 @@ namespace client
|
|||||||
|
|
||||||
void TCPIPPipe::HandleDownstreamWrite(const boost::system::error_code & ecode) {
|
void TCPIPPipe::HandleDownstreamWrite(const boost::system::error_code & ecode) {
|
||||||
if (ecode) {
|
if (ecode) {
|
||||||
LogPrint(eLogError, "TCPIPPipe Downstream write error:" , ecode.message());
|
LogPrint(eLogError, "TCPIPPipe: downstream write error:" , ecode.message());
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
if (ecode != boost::asio::error::operation_aborted)
|
||||||
Terminate();
|
Terminate();
|
||||||
}
|
}
|
||||||
@ -143,7 +142,7 @@ namespace client
|
|||||||
|
|
||||||
void TCPIPPipe::HandleUpstreamWrite(const boost::system::error_code & ecode) {
|
void TCPIPPipe::HandleUpstreamWrite(const boost::system::error_code & ecode) {
|
||||||
if (ecode) {
|
if (ecode) {
|
||||||
LogPrint(eLogError, "TCPIPPipe Upstream write error:" , ecode.message());
|
LogPrint(eLogError, "TCPIPPipe: upstream write error:" , ecode.message());
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
if (ecode != boost::asio::error::operation_aborted)
|
||||||
Terminate();
|
Terminate();
|
||||||
}
|
}
|
||||||
@ -151,9 +150,9 @@ namespace client
|
|||||||
|
|
||||||
void TCPIPPipe::HandleUpstreamReceived(const boost::system::error_code & ecode, std::size_t bytes_transfered)
|
void TCPIPPipe::HandleUpstreamReceived(const boost::system::error_code & ecode, std::size_t bytes_transfered)
|
||||||
{
|
{
|
||||||
LogPrint(eLogDebug, "TCPIPPipe upstream got ", (int) bytes_transfered);
|
LogPrint(eLogDebug, "TCPIPPipe: upstream ", (int)bytes_transfered, " bytes received");
|
||||||
if (ecode) {
|
if (ecode) {
|
||||||
LogPrint(eLogError, "TCPIPPipe Upstream read error:" , ecode.message());
|
LogPrint(eLogError, "TCPIPPipe: upstream read error:" , ecode.message());
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
if (ecode != boost::asio::error::operation_aborted)
|
||||||
Terminate();
|
Terminate();
|
||||||
} else {
|
} else {
|
||||||
@ -206,6 +205,5 @@ namespace client
|
|||||||
LogPrint (eLogError, "I2PService: ", GetName(), " closing socket on accept because: ", ecode.message ());
|
LogPrint (eLogError, "I2PService: ", GetName(), " closing socket on accept because: ", ecode.message ());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -179,9 +179,7 @@ namespace data
|
|||||||
|
|
||||||
virtual ~LocalDestination() {};
|
virtual ~LocalDestination() {};
|
||||||
virtual const uint8_t * GetEncryptionPrivateKey () const = 0;
|
virtual const uint8_t * GetEncryptionPrivateKey () const = 0;
|
||||||
virtual const uint8_t * GetEncryptionPublicKey () const = 0;
|
|
||||||
virtual std::shared_ptr<const IdentityEx> GetIdentity () const = 0;
|
virtual std::shared_ptr<const IdentityEx> GetIdentity () const = 0;
|
||||||
virtual void Sign (const uint8_t * buf, int len, uint8_t * signature) const = 0;
|
|
||||||
|
|
||||||
const IdentHash& GetIdentHash () const { return GetIdentity ()->GetIdentHash (); };
|
const IdentHash& GetIdentHash () const { return GetIdentity ()->GetIdentHash (); };
|
||||||
};
|
};
|
||||||
|
@ -213,6 +213,7 @@ namespace data
|
|||||||
m_Buffer[offset] = num;
|
m_Buffer[offset] = num;
|
||||||
offset++;
|
offset++;
|
||||||
// leases
|
// leases
|
||||||
|
m_Leases = m_Buffer + offset;
|
||||||
auto currentTime = i2p::util::GetMillisecondsSinceEpoch ();
|
auto currentTime = i2p::util::GetMillisecondsSinceEpoch ();
|
||||||
for (int i = 0; i < num; i++)
|
for (int i = 0; i < num; i++)
|
||||||
{
|
{
|
||||||
@ -231,6 +232,14 @@ namespace data
|
|||||||
// we don't sign it yet. must be signed later on
|
// we don't sign it yet. must be signed later on
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LocalLeaseSet::LocalLeaseSet (std::shared_ptr<const IdentityEx> identity, const uint8_t * buf, size_t len):
|
||||||
|
m_ExpirationTime (0), m_Identity (identity)
|
||||||
|
{
|
||||||
|
m_BufferLen = len;
|
||||||
|
m_Buffer = new uint8_t[m_BufferLen];
|
||||||
|
memcpy (m_Buffer, buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
bool LocalLeaseSet::IsExpired () const
|
bool LocalLeaseSet::IsExpired () const
|
||||||
{
|
{
|
||||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <set>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include "Identity.h"
|
#include "Identity.h"
|
||||||
|
|
||||||
@ -88,14 +89,19 @@ namespace data
|
|||||||
public:
|
public:
|
||||||
|
|
||||||
LocalLeaseSet (std::shared_ptr<const IdentityEx> identity, const uint8_t * encryptionPublicKey, std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels);
|
LocalLeaseSet (std::shared_ptr<const IdentityEx> identity, const uint8_t * encryptionPublicKey, std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels);
|
||||||
|
LocalLeaseSet (std::shared_ptr<const IdentityEx> identity, const uint8_t * buf, size_t len);
|
||||||
~LocalLeaseSet () { delete[] m_Buffer; };
|
~LocalLeaseSet () { delete[] m_Buffer; };
|
||||||
|
|
||||||
const uint8_t * GetBuffer () const { return m_Buffer; };
|
const uint8_t * GetBuffer () const { return m_Buffer; };
|
||||||
uint8_t * GetSignature () { return m_Buffer + m_BufferLen - GetSignatureLen (); };
|
uint8_t * GetSignature () { return m_Buffer + m_BufferLen - GetSignatureLen (); };
|
||||||
size_t GetBufferLen () const { return m_BufferLen; };
|
size_t GetBufferLen () const { return m_BufferLen; };
|
||||||
size_t GetSignatureLen () const { return m_Identity->GetSignatureLen (); };
|
size_t GetSignatureLen () const { return m_Identity->GetSignatureLen (); };
|
||||||
|
uint8_t * GetLeases () { return m_Leases; };
|
||||||
|
|
||||||
const IdentHash& GetIdentHash () const { return m_Identity->GetIdentHash (); };
|
const IdentHash& GetIdentHash () const { return m_Identity->GetIdentHash (); };
|
||||||
bool IsExpired () const;
|
bool IsExpired () const;
|
||||||
|
uint64_t GetExpirationTime () const { return m_ExpirationTime; };
|
||||||
|
void SetExpirationTime (uint64_t expirationTime) { m_ExpirationTime = expirationTime; };
|
||||||
bool operator== (const LeaseSet& other) const
|
bool operator== (const LeaseSet& other) const
|
||||||
{ return m_BufferLen == other.GetBufferLen () && !memcmp (other.GetBuffer (), other.GetBuffer (), m_BufferLen); };
|
{ return m_BufferLen == other.GetBufferLen () && !memcmp (other.GetBuffer (), other.GetBuffer (), m_BufferLen); };
|
||||||
|
|
||||||
@ -104,7 +110,7 @@ namespace data
|
|||||||
|
|
||||||
uint64_t m_ExpirationTime; // in milliseconds
|
uint64_t m_ExpirationTime; // in milliseconds
|
||||||
std::shared_ptr<const IdentityEx> m_Identity;
|
std::shared_ptr<const IdentityEx> m_Identity;
|
||||||
uint8_t * m_Buffer;
|
uint8_t * m_Buffer, * m_Leases;
|
||||||
size_t m_BufferLen;
|
size_t m_BufferLen;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
20
Log.cpp
20
Log.cpp
@ -12,7 +12,7 @@ namespace i2p {
|
|||||||
namespace log {
|
namespace log {
|
||||||
Log logger;
|
Log logger;
|
||||||
/**
|
/**
|
||||||
* @enum Maps our loglevel to their symbolic name
|
* @brief Maps our loglevel to their symbolic name
|
||||||
*/
|
*/
|
||||||
static const char * g_LogLevelStr[eNumLogLevels] =
|
static const char * g_LogLevelStr[eNumLogLevels] =
|
||||||
{
|
{
|
||||||
@ -56,7 +56,7 @@ namespace log {
|
|||||||
#endif
|
#endif
|
||||||
case eLogFile:
|
case eLogFile:
|
||||||
case eLogStream:
|
case eLogStream:
|
||||||
m_LogStream->flush();
|
if (m_LogStream) m_LogStream->flush();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
/* do nothing */
|
/* do nothing */
|
||||||
@ -107,10 +107,11 @@ namespace log {
|
|||||||
#endif
|
#endif
|
||||||
case eLogFile:
|
case eLogFile:
|
||||||
case eLogStream:
|
case eLogStream:
|
||||||
*m_LogStream << TimeAsString(msg->timestamp)
|
if (m_LogStream)
|
||||||
<< "@" << short_tid
|
*m_LogStream << TimeAsString(msg->timestamp)
|
||||||
<< "/" << g_LogLevelStr[msg->level]
|
<< "@" << short_tid
|
||||||
<< " - " << msg->text << std::endl;
|
<< "/" << g_LogLevelStr[msg->level]
|
||||||
|
<< " - " << msg->text << std::endl;
|
||||||
break;
|
break;
|
||||||
case eLogStdout:
|
case eLogStdout:
|
||||||
default:
|
default:
|
||||||
@ -130,10 +131,13 @@ namespace log {
|
|||||||
Process();
|
Process();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Log::SendTo (const std::string& path) {
|
void Log::SendTo (const std::string& path)
|
||||||
|
{
|
||||||
|
if (m_LogStream) m_LogStream = nullptr; // close previous
|
||||||
auto flags = std::ofstream::out | std::ofstream::app;
|
auto flags = std::ofstream::out | std::ofstream::app;
|
||||||
auto os = std::make_shared<std::ofstream> (path, flags);
|
auto os = std::make_shared<std::ofstream> (path, flags);
|
||||||
if (os->is_open ()) {
|
if (os->is_open ())
|
||||||
|
{
|
||||||
m_Logfile = path;
|
m_Logfile = path;
|
||||||
m_Destination = eLogFile;
|
m_Destination = eLogFile;
|
||||||
m_LogStream = os;
|
m_LogStream = os;
|
||||||
|
7
Log.h
7
Log.h
@ -86,7 +86,7 @@ namespace log {
|
|||||||
LogLevel GetLogLevel () { return m_MinLevel; };
|
LogLevel GetLogLevel () { return m_MinLevel; };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Sets minimal alloed level for log messages
|
* @brief Sets minimal allowed level for log messages
|
||||||
* @param level String with wanted minimal msg level
|
* @param level String with wanted minimal msg level
|
||||||
*/
|
*/
|
||||||
void SetLogLevel (const std::string& level);
|
void SetLogLevel (const std::string& level);
|
||||||
@ -101,7 +101,7 @@ namespace log {
|
|||||||
* @brief Sets log destination to given output stream
|
* @brief Sets log destination to given output stream
|
||||||
* @param os Output stream
|
* @param os Output stream
|
||||||
*/
|
*/
|
||||||
void SendTo (std::shared_ptr<std::ostream> s);
|
void SendTo (std::shared_ptr<std::ostream> os);
|
||||||
|
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
/**
|
/**
|
||||||
@ -129,7 +129,8 @@ namespace log {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @struct Log message container
|
* @struct LogMsg
|
||||||
|
* @brief Log message container
|
||||||
*
|
*
|
||||||
* We creating it somewhere with LogPrint(),
|
* We creating it somewhere with LogPrint(),
|
||||||
* then put in MsgQueue for later processing.
|
* then put in MsgQueue for later processing.
|
||||||
|
5
Makefile
5
Makefile
@ -76,6 +76,7 @@ $(ARLIB_CLIENT): $(patsubst %.cpp,obj/%.o,$(LIB_CLIENT_SRC))
|
|||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf obj
|
rm -rf obj
|
||||||
|
rm -rf docs/generated
|
||||||
$(RM) $(I2PD) $(SHLIB) $(ARLIB) $(SHLIB_CLIENT) $(ARLIB_CLIENT)
|
$(RM) $(I2PD) $(SHLIB) $(ARLIB) $(SHLIB_CLIENT) $(ARLIB_CLIENT)
|
||||||
|
|
||||||
strip: $(I2PD) $(SHLIB_CLIENT) $(SHLIB)
|
strip: $(I2PD) $(SHLIB_CLIENT) $(SHLIB)
|
||||||
@ -86,9 +87,13 @@ dist:
|
|||||||
git archive --format=tar.gz -9 --worktree-attributes \
|
git archive --format=tar.gz -9 --worktree-attributes \
|
||||||
--prefix=i2pd_$(LATEST_TAG)/ $(LATEST_TAG) -o i2pd_$(LATEST_TAG).tar.gz
|
--prefix=i2pd_$(LATEST_TAG)/ $(LATEST_TAG) -o i2pd_$(LATEST_TAG).tar.gz
|
||||||
|
|
||||||
|
doxygen:
|
||||||
|
doxygen -s docs/Doxyfile
|
||||||
|
|
||||||
.PHONY: all
|
.PHONY: all
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
.PHONY: deps
|
.PHONY: deps
|
||||||
|
.PHONY: doxygen
|
||||||
.PHONY: dist
|
.PHONY: dist
|
||||||
.PHONY: api
|
.PHONY: api
|
||||||
.PHONY: api_client
|
.PHONY: api_client
|
||||||
|
@ -9,4 +9,4 @@ CXXFLAGS = -O2
|
|||||||
NEEDED_CXXFLAGS = -std=c++11 -D_GLIBCXX_USE_NANOSLEEP=1
|
NEEDED_CXXFLAGS = -std=c++11 -D_GLIBCXX_USE_NANOSLEEP=1
|
||||||
INCFLAGS = -I/usr/include/ -I/usr/local/include/
|
INCFLAGS = -I/usr/include/ -I/usr/local/include/
|
||||||
LDFLAGS = -Wl,-rpath,/usr/local/lib -L/usr/local/lib
|
LDFLAGS = -Wl,-rpath,/usr/local/lib -L/usr/local/lib
|
||||||
LDLIBS = -lcrypto -lssl -lz -lboost_system -lboost_date_time -lboost_filesystem -lboost_regex -lboost_program_options -lpthread
|
LDLIBS = -lcrypto -lssl -lz -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread
|
||||||
|
@ -6,7 +6,7 @@ CXX = clang++
|
|||||||
CXXFLAGS = -g -Wall -std=c++11 -DMAC_OSX
|
CXXFLAGS = -g -Wall -std=c++11 -DMAC_OSX
|
||||||
INCFLAGS = -I${SSLROOT}/include -I${BOOSTROOT}/include
|
INCFLAGS = -I${SSLROOT}/include -I${BOOSTROOT}/include
|
||||||
LDFLAGS = -L${SSLROOT}/lib -L${BOOSTROOT}/lib
|
LDFLAGS = -L${SSLROOT}/lib -L${BOOSTROOT}/lib
|
||||||
LDLIBS = -lz -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_regex -lboost_program_options -lpthread
|
LDLIBS = -lz -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread
|
||||||
|
|
||||||
ifeq ($(USE_UPNP),1)
|
ifeq ($(USE_UPNP),1)
|
||||||
LDFLAGS += -ldl
|
LDFLAGS += -ldl
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# set defaults instead redefine
|
# set defaults instead redefine
|
||||||
CXXFLAGS ?= -g -Wall
|
CXXFLAGS ?= -g -Wall -Wextra -Wno-unused-parameter -pedantic
|
||||||
INCFLAGS ?=
|
INCFLAGS ?=
|
||||||
|
|
||||||
## NOTE: The NEEDED_CXXFLAGS are here so that custom CXXFLAGS can be specified at build time
|
## NOTE: The NEEDED_CXXFLAGS are here so that custom CXXFLAGS can be specified at build time
|
||||||
@ -32,7 +32,6 @@ ifeq ($(USE_STATIC),yes)
|
|||||||
LDLIBS = $(LIBDIR)/libboost_system.a
|
LDLIBS = $(LIBDIR)/libboost_system.a
|
||||||
LDLIBS += $(LIBDIR)/libboost_date_time.a
|
LDLIBS += $(LIBDIR)/libboost_date_time.a
|
||||||
LDLIBS += $(LIBDIR)/libboost_filesystem.a
|
LDLIBS += $(LIBDIR)/libboost_filesystem.a
|
||||||
LDLIBS += $(LIBDIR)/libboost_regex.a
|
|
||||||
LDLIBS += $(LIBDIR)/libboost_program_options.a
|
LDLIBS += $(LIBDIR)/libboost_program_options.a
|
||||||
LDLIBS += $(LIBDIR)/libcrypto.a
|
LDLIBS += $(LIBDIR)/libcrypto.a
|
||||||
LDLIBS += $(LIBDIR)/libssl.a
|
LDLIBS += $(LIBDIR)/libssl.a
|
||||||
@ -40,7 +39,7 @@ ifeq ($(USE_STATIC),yes)
|
|||||||
LDLIBS += -lpthread -static-libstdc++ -static-libgcc
|
LDLIBS += -lpthread -static-libstdc++ -static-libgcc
|
||||||
USE_AESNI := no
|
USE_AESNI := no
|
||||||
else
|
else
|
||||||
LDLIBS = -lcrypto -lssl -lz -lboost_system -lboost_date_time -lboost_filesystem -lboost_regex -lboost_program_options -lpthread
|
LDLIBS = -lcrypto -lssl -lz -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# UPNP Support (miniupnpc 1.5 or 1.6)
|
# UPNP Support (miniupnpc 1.5 or 1.6)
|
||||||
|
@ -13,7 +13,6 @@ LDLIBS = \
|
|||||||
-Wl,-Bstatic -lboost_system$(BOOST_SUFFIX) \
|
-Wl,-Bstatic -lboost_system$(BOOST_SUFFIX) \
|
||||||
-Wl,-Bstatic -lboost_date_time$(BOOST_SUFFIX) \
|
-Wl,-Bstatic -lboost_date_time$(BOOST_SUFFIX) \
|
||||||
-Wl,-Bstatic -lboost_filesystem$(BOOST_SUFFIX) \
|
-Wl,-Bstatic -lboost_filesystem$(BOOST_SUFFIX) \
|
||||||
-Wl,-Bstatic -lboost_regex$(BOOST_SUFFIX) \
|
|
||||||
-Wl,-Bstatic -lboost_program_options$(BOOST_SUFFIX) \
|
-Wl,-Bstatic -lboost_program_options$(BOOST_SUFFIX) \
|
||||||
-Wl,-Bstatic -lssl \
|
-Wl,-Bstatic -lssl \
|
||||||
-Wl,-Bstatic -lcrypto \
|
-Wl,-Bstatic -lcrypto \
|
||||||
|
@ -3,7 +3,7 @@ CXXFLAGS = -g -Wall -std=c++11 -DMAC_OSX
|
|||||||
#CXXFLAGS = -g -O2 -Wall -std=c++11
|
#CXXFLAGS = -g -O2 -Wall -std=c++11
|
||||||
INCFLAGS = -I/usr/local/include -I/usr/local/ssl/include
|
INCFLAGS = -I/usr/local/include -I/usr/local/ssl/include
|
||||||
LDFLAGS = -Wl,-rpath,/usr/local/lib -L/usr/local/lib -L/usr/local/ssl/lib
|
LDFLAGS = -Wl,-rpath,/usr/local/lib -L/usr/local/lib -L/usr/local/ssl/lib
|
||||||
LDLIBS = -lz -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_regex -lboost_program_options -lpthread
|
LDLIBS = -lz -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread
|
||||||
|
|
||||||
ifeq ($(USE_UPNP),1)
|
ifeq ($(USE_UPNP),1)
|
||||||
LDFLAGS += -ldl
|
LDFLAGS += -ldl
|
||||||
|
@ -849,7 +849,8 @@ namespace data
|
|||||||
template<typename Filter>
|
template<typename Filter>
|
||||||
std::shared_ptr<const RouterInfo> NetDb::GetRandomRouter (Filter filter) const
|
std::shared_ptr<const RouterInfo> NetDb::GetRandomRouter (Filter filter) const
|
||||||
{
|
{
|
||||||
if (!m_RouterInfos.size ()) return 0;
|
if (m_RouterInfos.empty())
|
||||||
|
return 0;
|
||||||
uint32_t ind = rand () % m_RouterInfos.size ();
|
uint32_t ind = rand () % m_RouterInfos.size ();
|
||||||
for (int j = 0; j < 2; j++)
|
for (int j = 0; j < 2; j++)
|
||||||
{
|
{
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <boost/regex.hpp>
|
|
||||||
#include <boost/asio.hpp>
|
#include <boost/asio.hpp>
|
||||||
#include <boost/asio/ssl.hpp>
|
#include <boost/asio/ssl.hpp>
|
||||||
#include <openssl/ssl.h>
|
#include <openssl/ssl.h>
|
||||||
|
@ -353,7 +353,7 @@ namespace data
|
|||||||
if (m_Caps & eReachable) caps += CAPS_FLAG_REACHABLE; // reachable
|
if (m_Caps & eReachable) caps += CAPS_FLAG_REACHABLE; // reachable
|
||||||
if (m_Caps & eUnreachable) caps += CAPS_FLAG_UNREACHABLE; // unreachable
|
if (m_Caps & eUnreachable) caps += CAPS_FLAG_UNREACHABLE; // unreachable
|
||||||
|
|
||||||
SetProperty ("caps", caps.c_str ());
|
SetProperty ("caps", caps);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RouterInfo::WriteToStream (std::ostream& s)
|
void RouterInfo::WriteToStream (std::ostream& s)
|
||||||
|
@ -232,6 +232,11 @@ namespace stream
|
|||||||
bool acknowledged = false;
|
bool acknowledged = false;
|
||||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||||
uint32_t ackThrough = packet->GetAckThrough ();
|
uint32_t ackThrough = packet->GetAckThrough ();
|
||||||
|
if (ackThrough > m_SequenceNumber)
|
||||||
|
{
|
||||||
|
LogPrint (eLogError, "Streaming: Unexpected ackThrough=", ackThrough, " > seqn=", m_SequenceNumber);
|
||||||
|
return;
|
||||||
|
}
|
||||||
int nackCount = packet->GetNACKCount ();
|
int nackCount = packet->GetNACKCount ();
|
||||||
for (auto it = m_SentPackets.begin (); it != m_SentPackets.end ();)
|
for (auto it = m_SentPackets.begin (); it != m_SentPackets.end ();)
|
||||||
{
|
{
|
||||||
@ -336,9 +341,9 @@ namespace stream
|
|||||||
htobe32buf (packet + size, m_SequenceNumber++);
|
htobe32buf (packet + size, m_SequenceNumber++);
|
||||||
size += 4; // sequenceNum
|
size += 4; // sequenceNum
|
||||||
if (isNoAck)
|
if (isNoAck)
|
||||||
htobe32buf (packet + size, m_LastReceivedSequenceNumber);
|
|
||||||
else
|
|
||||||
htobuf32 (packet + size, 0);
|
htobuf32 (packet + size, 0);
|
||||||
|
else
|
||||||
|
htobe32buf (packet + size, m_LastReceivedSequenceNumber);
|
||||||
size += 4; // ack Through
|
size += 4; // ack Through
|
||||||
packet[size] = 0;
|
packet[size] = 0;
|
||||||
size++; // NACK count
|
size++; // NACK count
|
||||||
@ -521,7 +526,7 @@ namespace stream
|
|||||||
size += 4; // receiveStreamID
|
size += 4; // receiveStreamID
|
||||||
htobe32buf (packet + size, m_SequenceNumber++);
|
htobe32buf (packet + size, m_SequenceNumber++);
|
||||||
size += 4; // sequenceNum
|
size += 4; // sequenceNum
|
||||||
htobe32buf (packet + size, m_LastReceivedSequenceNumber);
|
htobe32buf (packet + size, m_LastReceivedSequenceNumber >= 0 ? m_LastReceivedSequenceNumber : 0);
|
||||||
size += 4; // ack Through
|
size += 4; // ack Through
|
||||||
packet[size] = 0;
|
packet[size] = 0;
|
||||||
size++; // NACK count
|
size++; // NACK count
|
||||||
|
@ -606,7 +606,7 @@ namespace transport
|
|||||||
|
|
||||||
std::shared_ptr<const i2p::data::RouterInfo> Transports::GetRandomPeer () const
|
std::shared_ptr<const i2p::data::RouterInfo> Transports::GetRandomPeer () const
|
||||||
{
|
{
|
||||||
if (!m_Peers.size ()) return nullptr;
|
if (m_Peers.empty ()) return nullptr;
|
||||||
std::unique_lock<std::mutex> l(m_PeersMutex);
|
std::unique_lock<std::mutex> l(m_PeersMutex);
|
||||||
auto it = m_Peers.begin ();
|
auto it = m_Peers.begin ();
|
||||||
std::advance (it, rand () % m_Peers.size ());
|
std::advance (it, rand () % m_Peers.size ());
|
||||||
|
@ -358,7 +358,7 @@ namespace tunnel
|
|||||||
|
|
||||||
std::shared_ptr<OutboundTunnel> Tunnels::GetNextOutboundTunnel ()
|
std::shared_ptr<OutboundTunnel> Tunnels::GetNextOutboundTunnel ()
|
||||||
{
|
{
|
||||||
if (!m_OutboundTunnels.size ()) return nullptr;
|
if (m_OutboundTunnels.empty ()) return nullptr;
|
||||||
uint32_t ind = rand () % m_OutboundTunnels.size (), i = 0;
|
uint32_t ind = rand () % m_OutboundTunnels.size (), i = 0;
|
||||||
std::shared_ptr<OutboundTunnel> tunnel;
|
std::shared_ptr<OutboundTunnel> tunnel;
|
||||||
for (auto it: m_OutboundTunnels)
|
for (auto it: m_OutboundTunnels)
|
||||||
|
2
Tunnel.h
2
Tunnel.h
@ -73,7 +73,7 @@ namespace tunnel
|
|||||||
|
|
||||||
bool HandleTunnelBuildResponse (uint8_t * msg, size_t len);
|
bool HandleTunnelBuildResponse (uint8_t * msg, size_t len);
|
||||||
|
|
||||||
virtual void Print (std::stringstream& s) const {};
|
virtual void Print (std::stringstream&) const {};
|
||||||
|
|
||||||
// implements TunnelBase
|
// implements TunnelBase
|
||||||
void SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg);
|
void SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg);
|
||||||
|
@ -125,7 +125,7 @@ install:
|
|||||||
- cd %BOOST_ROOT%
|
- cd %BOOST_ROOT%
|
||||||
- if defined msvc if not exist "stage%bitness%\lib\%boostlib%boost_system-vc%msvc%0-mt%boostdbg%*" (
|
- if defined msvc if not exist "stage%bitness%\lib\%boostlib%boost_system-vc%msvc%0-mt%boostdbg%*" (
|
||||||
bootstrap > c:\projects\instdir\build_boost.log
|
bootstrap > c:\projects\instdir\build_boost.log
|
||||||
&& b2 toolset=msvc-%msvc%.0 %boost_variant% link=%type% runtime-link=%type% address-model=%bitness% --build-type=minimal --with-filesystem --with-program_options --with-regex --with-date_time --stagedir=stage%bitness% >> c:\projects\instdir\build_boost.log
|
&& b2 toolset=msvc-%msvc%.0 %boost_variant% link=%type% runtime-link=%type% address-model=%bitness% --build-type=minimal --with-filesystem --with-program_options --with-date_time --stagedir=stage%bitness% >> c:\projects\instdir\build_boost.log
|
||||||
|| type c:\projects\instdir\build_boost.log
|
|| type c:\projects\instdir\build_boost.log
|
||||||
)
|
)
|
||||||
- if defined msvc if not exist C:\stage\OpenSSL-Win%bitness%-vc%msvc%-%type%\ (
|
- if defined msvc if not exist C:\stage\OpenSSL-Win%bitness%-vc%msvc%-%type%\ (
|
||||||
|
@ -242,7 +242,7 @@ endif()
|
|||||||
|
|
||||||
target_link_libraries(i2pdclient libi2pd)
|
target_link_libraries(i2pdclient libi2pd)
|
||||||
|
|
||||||
find_package ( Boost COMPONENTS system filesystem regex program_options date_time REQUIRED )
|
find_package ( Boost COMPONENTS system filesystem program_options date_time REQUIRED )
|
||||||
if(NOT DEFINED Boost_INCLUDE_DIRS)
|
if(NOT DEFINED Boost_INCLUDE_DIRS)
|
||||||
message(SEND_ERROR "Boost is not found, or your boost version was bellow 1.46. Please download Boost!")
|
message(SEND_ERROR "Boost is not found, or your boost version was bellow 1.46. Please download Boost!")
|
||||||
endif()
|
endif()
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
FROM ubuntu
|
FROM ubuntu
|
||||||
|
|
||||||
RUN apt-get update && apt-get install -y libboost-dev libboost-filesystem-dev \
|
RUN apt-get update && apt-get install -y libboost-dev libboost-filesystem-dev \
|
||||||
libboost-program-options-dev libboost-regex-dev libboost-date-time-dev \
|
libboost-program-options-dev libboost-date-time-dev \
|
||||||
libssl-dev git build-essential
|
libssl-dev git build-essential
|
||||||
|
|
||||||
RUN git clone https://github.com/PurpleI2P/i2pd.git
|
RUN git clone https://github.com/PurpleI2P/i2pd.git
|
||||||
|
6
debian/changelog
vendored
6
debian/changelog
vendored
@ -1,3 +1,9 @@
|
|||||||
|
i2pd (2.7.0-1) unstable; urgency=low
|
||||||
|
|
||||||
|
* updated to version 2.7.0/0.9.25
|
||||||
|
|
||||||
|
-- hagen <hagen@i2pmail.org> Wed, 18 May 2016 01:11:04 +0000
|
||||||
|
|
||||||
i2pd (2.2.0-2) unstable; urgency=low
|
i2pd (2.2.0-2) unstable; urgency=low
|
||||||
|
|
||||||
* updated to version 2.2.0
|
* updated to version 2.2.0
|
||||||
|
1
debian/control
vendored
1
debian/control
vendored
@ -4,7 +4,6 @@ Priority: extra
|
|||||||
Maintainer: hagen <hagen@i2pmail.org>
|
Maintainer: hagen <hagen@i2pmail.org>
|
||||||
Build-Depends: debhelper (>= 9.0.0), dpkg-dev (>= 1.16.1~),
|
Build-Depends: debhelper (>= 9.0.0), dpkg-dev (>= 1.16.1~),
|
||||||
gcc (>= 4.7) | clang (>= 3.3),
|
gcc (>= 4.7) | clang (>= 3.3),
|
||||||
libboost-regex-dev,
|
|
||||||
libboost-system-dev (>= 1.46),
|
libboost-system-dev (>= 1.46),
|
||||||
libboost-date-time-dev,
|
libboost-date-time-dev,
|
||||||
libboost-filesystem-dev,
|
libboost-filesystem-dev,
|
||||||
|
1
debian/i2pd.default
vendored
1
debian/i2pd.default
vendored
@ -4,6 +4,7 @@
|
|||||||
I2PD_ENABLED="yes"
|
I2PD_ENABLED="yes"
|
||||||
|
|
||||||
# port to listen for incoming connections
|
# port to listen for incoming connections
|
||||||
|
# comment this line if you want to use value from config
|
||||||
I2PD_PORT="4567"
|
I2PD_PORT="4567"
|
||||||
|
|
||||||
# Additional options that are passed to the Daemon.
|
# Additional options that are passed to the Daemon.
|
||||||
|
6
debian/i2pd.init
vendored
6
debian/i2pd.init
vendored
@ -41,6 +41,10 @@ do_start()
|
|||||||
return 2
|
return 2
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ -n "$I2PD_PORT" ]; then
|
||||||
|
DAEMON_OPTS="--port $I2PD_PORT $DAEMON_OPTS"
|
||||||
|
fi
|
||||||
|
|
||||||
touch "$PIDFILE"
|
touch "$PIDFILE"
|
||||||
chown -f $USER:adm "$PIDFILE"
|
chown -f $USER:adm "$PIDFILE"
|
||||||
|
|
||||||
@ -51,7 +55,7 @@ do_start()
|
|||||||
|| return 1
|
|| return 1
|
||||||
start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --chuid "$USER" -- \
|
start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --chuid "$USER" -- \
|
||||||
--service --daemon --log=file --logfile=$LOGFILE --conf=$I2PCONF --tunconf=$TUNCONF \
|
--service --daemon --log=file --logfile=$LOGFILE --conf=$I2PCONF --tunconf=$TUNCONF \
|
||||||
--port=$I2PD_PORT $DAEMON_OPTS > /dev/null 2>&1 \
|
$DAEMON_OPTS > /dev/null 2>&1 \
|
||||||
|| return 2
|
|| return 2
|
||||||
return $?
|
return $?
|
||||||
}
|
}
|
||||||
|
41
debian/i2pd.openrc
vendored
Normal file
41
debian/i2pd.openrc
vendored
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#!/sbin/openrc-run
|
||||||
|
|
||||||
|
pidfile="/var/run/i2pd.pid"
|
||||||
|
logfile="/var/log/i2pd.log"
|
||||||
|
mainconf="/etc/i2pd/i2pd.conf"
|
||||||
|
tunconf="/etc/i2pd/tunnels.conf"
|
||||||
|
|
||||||
|
name="i2pd"
|
||||||
|
command="/usr/sbin/i2pd"
|
||||||
|
command_args="--service --daemon --log=file --logfile=$logfile --conf=$mainconf --tunconf=$tunconf --pidfile=$pidfile"
|
||||||
|
description="i2p router written in C++"
|
||||||
|
required_dirs="/var/lib/i2pd"
|
||||||
|
required_files="$mainconf"
|
||||||
|
start_stop_daemon_args="--chuid i2pd"
|
||||||
|
|
||||||
|
depend() {
|
||||||
|
need mountall
|
||||||
|
use net
|
||||||
|
after bootmisc
|
||||||
|
}
|
||||||
|
|
||||||
|
start_pre() {
|
||||||
|
if [ -r /etc/default/i2pd ]; then
|
||||||
|
. /etc/default/i2pd
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "x$I2PD_ENABLED" != "xyes" ]; then
|
||||||
|
ewarn "i2pd disabled in /etc/default/i2pd"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
checkpath -f -o i2pd:adm $logfile
|
||||||
|
checkpath -f -o i2pd:adm $pidfile
|
||||||
|
|
||||||
|
if [ -n "$I2PD_PORT" -a "$I2PD_PORT" -gt 0 ]; then
|
||||||
|
command_args="$command_args --port=$I2PD_PORT"
|
||||||
|
fi
|
||||||
|
if [ -n "$DAEMON_OPTS" ]; then
|
||||||
|
command_args="$command_args $DAEMON_OPTS"
|
||||||
|
fi
|
||||||
|
}
|
259
docs/Doxyfile
Normal file
259
docs/Doxyfile
Normal file
@ -0,0 +1,259 @@
|
|||||||
|
DOXYFILE_ENCODING = UTF-8
|
||||||
|
PROJECT_NAME = "i2pd"
|
||||||
|
PROJECT_NUMBER =
|
||||||
|
PROJECT_BRIEF = "load-balanced unspoofable packet switching network"
|
||||||
|
PROJECT_LOGO =
|
||||||
|
OUTPUT_DIRECTORY = docs/generated
|
||||||
|
CREATE_SUBDIRS = NO
|
||||||
|
ALLOW_UNICODE_NAMES = NO
|
||||||
|
OUTPUT_LANGUAGE = English
|
||||||
|
BRIEF_MEMBER_DESC = YES
|
||||||
|
REPEAT_BRIEF = YES
|
||||||
|
ABBREVIATE_BRIEF =
|
||||||
|
ALWAYS_DETAILED_SEC = NO
|
||||||
|
INLINE_INHERITED_MEMB = NO
|
||||||
|
FULL_PATH_NAMES = YES
|
||||||
|
STRIP_FROM_PATH =
|
||||||
|
STRIP_FROM_INC_PATH =
|
||||||
|
SHORT_NAMES = NO
|
||||||
|
JAVADOC_AUTOBRIEF = NO
|
||||||
|
QT_AUTOBRIEF = NO
|
||||||
|
MULTILINE_CPP_IS_BRIEF = NO
|
||||||
|
INHERIT_DOCS = YES
|
||||||
|
SEPARATE_MEMBER_PAGES = NO
|
||||||
|
TAB_SIZE = 4
|
||||||
|
ALIASES =
|
||||||
|
TCL_SUBST =
|
||||||
|
OPTIMIZE_OUTPUT_FOR_C = NO
|
||||||
|
OPTIMIZE_OUTPUT_JAVA = NO
|
||||||
|
OPTIMIZE_FOR_FORTRAN = NO
|
||||||
|
OPTIMIZE_OUTPUT_VHDL = NO
|
||||||
|
EXTENSION_MAPPING =
|
||||||
|
MARKDOWN_SUPPORT = YES
|
||||||
|
AUTOLINK_SUPPORT = YES
|
||||||
|
BUILTIN_STL_SUPPORT = NO
|
||||||
|
CPP_CLI_SUPPORT = NO
|
||||||
|
SIP_SUPPORT = NO
|
||||||
|
IDL_PROPERTY_SUPPORT = YES
|
||||||
|
DISTRIBUTE_GROUP_DOC = NO
|
||||||
|
SUBGROUPING = YES
|
||||||
|
INLINE_GROUPED_CLASSES = NO
|
||||||
|
INLINE_SIMPLE_STRUCTS = NO
|
||||||
|
TYPEDEF_HIDES_STRUCT = NO
|
||||||
|
LOOKUP_CACHE_SIZE = 0
|
||||||
|
EXTRACT_ALL = NO
|
||||||
|
EXTRACT_PRIVATE = NO
|
||||||
|
EXTRACT_PACKAGE = NO
|
||||||
|
EXTRACT_STATIC = NO
|
||||||
|
EXTRACT_LOCAL_CLASSES = YES
|
||||||
|
EXTRACT_LOCAL_METHODS = NO
|
||||||
|
EXTRACT_ANON_NSPACES = NO
|
||||||
|
HIDE_UNDOC_MEMBERS = NO
|
||||||
|
HIDE_UNDOC_CLASSES = NO
|
||||||
|
HIDE_FRIEND_COMPOUNDS = NO
|
||||||
|
HIDE_IN_BODY_DOCS = NO
|
||||||
|
INTERNAL_DOCS = NO
|
||||||
|
CASE_SENSE_NAMES = YES
|
||||||
|
HIDE_SCOPE_NAMES = NO
|
||||||
|
SHOW_INCLUDE_FILES = YES
|
||||||
|
SHOW_GROUPED_MEMB_INC = NO
|
||||||
|
FORCE_LOCAL_INCLUDES = NO
|
||||||
|
INLINE_INFO = YES
|
||||||
|
SORT_MEMBER_DOCS = YES
|
||||||
|
SORT_BRIEF_DOCS = NO
|
||||||
|
SORT_MEMBERS_CTORS_1ST = NO
|
||||||
|
SORT_GROUP_NAMES = NO
|
||||||
|
SORT_BY_SCOPE_NAME = NO
|
||||||
|
STRICT_PROTO_MATCHING = NO
|
||||||
|
GENERATE_TODOLIST = YES
|
||||||
|
GENERATE_TESTLIST = YES
|
||||||
|
GENERATE_BUGLIST = YES
|
||||||
|
GENERATE_DEPRECATEDLIST= YES
|
||||||
|
ENABLED_SECTIONS =
|
||||||
|
MAX_INITIALIZER_LINES = 30
|
||||||
|
SHOW_USED_FILES = YES
|
||||||
|
SHOW_FILES = YES
|
||||||
|
SHOW_NAMESPACES = YES
|
||||||
|
FILE_VERSION_FILTER =
|
||||||
|
LAYOUT_FILE =
|
||||||
|
CITE_BIB_FILES =
|
||||||
|
QUIET = YES
|
||||||
|
WARNINGS = YES
|
||||||
|
WARN_IF_UNDOCUMENTED = NO
|
||||||
|
WARN_IF_DOC_ERROR = YES
|
||||||
|
WARN_NO_PARAMDOC = NO
|
||||||
|
WARN_FORMAT = "$file:$line: $text"
|
||||||
|
WARN_LOGFILE =
|
||||||
|
INPUT =
|
||||||
|
INPUT_ENCODING = UTF-8
|
||||||
|
FILE_PATTERNS = *.cpp *.h
|
||||||
|
RECURSIVE = NO
|
||||||
|
EXCLUDE =
|
||||||
|
EXCLUDE_SYMLINKS = NO
|
||||||
|
EXCLUDE_PATTERNS =
|
||||||
|
EXCLUDE_SYMBOLS =
|
||||||
|
EXAMPLE_PATH =
|
||||||
|
EXAMPLE_PATTERNS =
|
||||||
|
EXAMPLE_RECURSIVE = NO
|
||||||
|
IMAGE_PATH =
|
||||||
|
INPUT_FILTER =
|
||||||
|
FILTER_PATTERNS =
|
||||||
|
FILTER_SOURCE_FILES = NO
|
||||||
|
FILTER_SOURCE_PATTERNS =
|
||||||
|
USE_MDFILE_AS_MAINPAGE =
|
||||||
|
SOURCE_BROWSER = NO
|
||||||
|
INLINE_SOURCES = NO
|
||||||
|
STRIP_CODE_COMMENTS = YES
|
||||||
|
REFERENCED_BY_RELATION = NO
|
||||||
|
REFERENCES_RELATION = NO
|
||||||
|
REFERENCES_LINK_SOURCE = YES
|
||||||
|
SOURCE_TOOLTIPS = YES
|
||||||
|
USE_HTAGS = NO
|
||||||
|
VERBATIM_HEADERS = NO
|
||||||
|
CLANG_ASSISTED_PARSING = NO
|
||||||
|
CLANG_OPTIONS =
|
||||||
|
ALPHABETICAL_INDEX = YES
|
||||||
|
COLS_IN_ALPHA_INDEX = 5
|
||||||
|
IGNORE_PREFIX =
|
||||||
|
GENERATE_HTML = YES
|
||||||
|
HTML_OUTPUT = html
|
||||||
|
HTML_FILE_EXTENSION = .html
|
||||||
|
HTML_HEADER =
|
||||||
|
HTML_FOOTER =
|
||||||
|
HTML_STYLESHEET =
|
||||||
|
HTML_EXTRA_STYLESHEET =
|
||||||
|
HTML_EXTRA_FILES =
|
||||||
|
HTML_COLORSTYLE_HUE = 220
|
||||||
|
HTML_COLORSTYLE_SAT = 100
|
||||||
|
HTML_COLORSTYLE_GAMMA = 80
|
||||||
|
HTML_TIMESTAMP = YES
|
||||||
|
HTML_DYNAMIC_SECTIONS = NO
|
||||||
|
HTML_INDEX_NUM_ENTRIES = 100
|
||||||
|
GENERATE_DOCSET = NO
|
||||||
|
DOCSET_FEEDNAME = "Doxygen generated docs"
|
||||||
|
DOCSET_BUNDLE_ID = org.doxygen.Project
|
||||||
|
DOCSET_PUBLISHER_ID = org.doxygen.Publisher
|
||||||
|
DOCSET_PUBLISHER_NAME = Publisher
|
||||||
|
GENERATE_HTMLHELP = NO
|
||||||
|
CHM_FILE =
|
||||||
|
HHC_LOCATION =
|
||||||
|
GENERATE_CHI = NO
|
||||||
|
CHM_INDEX_ENCODING =
|
||||||
|
BINARY_TOC = NO
|
||||||
|
TOC_EXPAND = NO
|
||||||
|
GENERATE_QHP = NO
|
||||||
|
QCH_FILE =
|
||||||
|
QHP_NAMESPACE = org.doxygen.Project
|
||||||
|
QHP_VIRTUAL_FOLDER = doc
|
||||||
|
QHP_CUST_FILTER_NAME =
|
||||||
|
QHP_CUST_FILTER_ATTRS =
|
||||||
|
QHP_SECT_FILTER_ATTRS =
|
||||||
|
QHG_LOCATION =
|
||||||
|
GENERATE_ECLIPSEHELP = NO
|
||||||
|
ECLIPSE_DOC_ID = org.doxygen.Project
|
||||||
|
DISABLE_INDEX = NO
|
||||||
|
GENERATE_TREEVIEW = NO
|
||||||
|
ENUM_VALUES_PER_LINE = 4
|
||||||
|
TREEVIEW_WIDTH = 250
|
||||||
|
EXT_LINKS_IN_WINDOW = NO
|
||||||
|
FORMULA_FONTSIZE = 10
|
||||||
|
FORMULA_TRANSPARENT = YES
|
||||||
|
USE_MATHJAX = NO
|
||||||
|
MATHJAX_FORMAT = HTML-CSS
|
||||||
|
MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
|
||||||
|
MATHJAX_EXTENSIONS =
|
||||||
|
MATHJAX_CODEFILE =
|
||||||
|
SEARCHENGINE = YES
|
||||||
|
SERVER_BASED_SEARCH = NO
|
||||||
|
EXTERNAL_SEARCH = NO
|
||||||
|
SEARCHENGINE_URL =
|
||||||
|
SEARCHDATA_FILE = searchdata.xml
|
||||||
|
EXTERNAL_SEARCH_ID =
|
||||||
|
EXTRA_SEARCH_MAPPINGS =
|
||||||
|
GENERATE_LATEX = NO
|
||||||
|
LATEX_OUTPUT = latex
|
||||||
|
LATEX_CMD_NAME = latex
|
||||||
|
MAKEINDEX_CMD_NAME = makeindex
|
||||||
|
COMPACT_LATEX = NO
|
||||||
|
PAPER_TYPE = a4
|
||||||
|
EXTRA_PACKAGES =
|
||||||
|
LATEX_HEADER =
|
||||||
|
LATEX_FOOTER =
|
||||||
|
LATEX_EXTRA_FILES =
|
||||||
|
PDF_HYPERLINKS = YES
|
||||||
|
USE_PDFLATEX = YES
|
||||||
|
LATEX_BATCHMODE = NO
|
||||||
|
LATEX_HIDE_INDICES = NO
|
||||||
|
LATEX_SOURCE_CODE = NO
|
||||||
|
LATEX_BIB_STYLE = plain
|
||||||
|
GENERATE_RTF = NO
|
||||||
|
RTF_OUTPUT = rtf
|
||||||
|
COMPACT_RTF = NO
|
||||||
|
RTF_HYPERLINKS = NO
|
||||||
|
RTF_STYLESHEET_FILE =
|
||||||
|
RTF_EXTENSIONS_FILE =
|
||||||
|
GENERATE_MAN = NO
|
||||||
|
MAN_OUTPUT = man
|
||||||
|
MAN_EXTENSION = .3
|
||||||
|
MAN_SUBDIR =
|
||||||
|
MAN_LINKS = NO
|
||||||
|
GENERATE_XML = NO
|
||||||
|
XML_OUTPUT = xml
|
||||||
|
XML_PROGRAMLISTING = YES
|
||||||
|
GENERATE_DOCBOOK = NO
|
||||||
|
DOCBOOK_OUTPUT = docbook
|
||||||
|
DOCBOOK_PROGRAMLISTING = NO
|
||||||
|
GENERATE_AUTOGEN_DEF = NO
|
||||||
|
GENERATE_PERLMOD = NO
|
||||||
|
PERLMOD_LATEX = NO
|
||||||
|
PERLMOD_PRETTY = YES
|
||||||
|
PERLMOD_MAKEVAR_PREFIX =
|
||||||
|
ENABLE_PREPROCESSING = YES
|
||||||
|
MACRO_EXPANSION = NO
|
||||||
|
EXPAND_ONLY_PREDEF = NO
|
||||||
|
SEARCH_INCLUDES = YES
|
||||||
|
INCLUDE_PATH =
|
||||||
|
INCLUDE_FILE_PATTERNS =
|
||||||
|
PREDEFINED =
|
||||||
|
EXPAND_AS_DEFINED =
|
||||||
|
SKIP_FUNCTION_MACROS = YES
|
||||||
|
TAGFILES =
|
||||||
|
GENERATE_TAGFILE =
|
||||||
|
ALLEXTERNALS = NO
|
||||||
|
EXTERNAL_GROUPS = YES
|
||||||
|
EXTERNAL_PAGES = YES
|
||||||
|
PERL_PATH = /usr/bin/perl
|
||||||
|
CLASS_DIAGRAMS = YES
|
||||||
|
MSCGEN_PATH =
|
||||||
|
DIA_PATH =
|
||||||
|
HIDE_UNDOC_RELATIONS = YES
|
||||||
|
HAVE_DOT = NO
|
||||||
|
DOT_NUM_THREADS = 0
|
||||||
|
DOT_FONTNAME = Helvetica
|
||||||
|
DOT_FONTSIZE = 10
|
||||||
|
DOT_FONTPATH =
|
||||||
|
CLASS_GRAPH = YES
|
||||||
|
COLLABORATION_GRAPH = YES
|
||||||
|
GROUP_GRAPHS = YES
|
||||||
|
UML_LOOK = NO
|
||||||
|
UML_LIMIT_NUM_FIELDS = 10
|
||||||
|
TEMPLATE_RELATIONS = NO
|
||||||
|
INCLUDE_GRAPH = YES
|
||||||
|
INCLUDED_BY_GRAPH = YES
|
||||||
|
CALL_GRAPH = NO
|
||||||
|
CALLER_GRAPH = NO
|
||||||
|
GRAPHICAL_HIERARCHY = YES
|
||||||
|
DIRECTORY_GRAPH = YES
|
||||||
|
DOT_IMAGE_FORMAT = png
|
||||||
|
INTERACTIVE_SVG = NO
|
||||||
|
DOT_PATH =
|
||||||
|
DOTFILE_DIRS =
|
||||||
|
MSCFILE_DIRS =
|
||||||
|
DIAFILE_DIRS =
|
||||||
|
PLANTUML_JAR_PATH =
|
||||||
|
DOT_GRAPH_MAX_NODES = 50
|
||||||
|
MAX_DOT_GRAPH_DEPTH = 0
|
||||||
|
DOT_TRANSPARENT = NO
|
||||||
|
DOT_MULTI_TARGETS = NO
|
||||||
|
GENERATE_LEGEND = YES
|
||||||
|
DOT_CLEANUP = YES
|
@ -22,7 +22,7 @@ Proceed with building Boost normal way, but let's define dedicated staging direc
|
|||||||
```sh
|
```sh
|
||||||
./bootstrap.sh
|
./bootstrap.sh
|
||||||
./b2 toolset=gcc-mingw target-os=windows variant=release link=static runtime-link=static address-model=64 \
|
./b2 toolset=gcc-mingw target-os=windows variant=release link=static runtime-link=static address-model=64 \
|
||||||
--build-type=minimal --with-filesystem --with-program_options --with-regex --with-date_time \
|
--build-type=minimal --with-filesystem --with-program_options --with-date_time \
|
||||||
--stagedir=stage-mingw-64
|
--stagedir=stage-mingw-64
|
||||||
cd ..
|
cd ..
|
||||||
```
|
```
|
||||||
|
@ -46,7 +46,6 @@ sudo apt-get install \
|
|||||||
libboost-date-time-dev \
|
libboost-date-time-dev \
|
||||||
libboost-filesystem-dev \
|
libboost-filesystem-dev \
|
||||||
libboost-program-options-dev \
|
libboost-program-options-dev \
|
||||||
libboost-regex-dev \
|
|
||||||
libboost-system-dev \
|
libboost-system-dev \
|
||||||
libboost-thread-dev \
|
libboost-thread-dev \
|
||||||
libssl-dev
|
libssl-dev
|
||||||
|
@ -110,11 +110,11 @@ prompt to build Boost) and run the following:
|
|||||||
|
|
||||||
cd C:\dev\boost
|
cd C:\dev\boost
|
||||||
bootstrap
|
bootstrap
|
||||||
b2 toolset=msvc-12.0 --build-type=complete --with-filesystem --with-program_options --with-regex --with-date_time
|
b2 toolset=msvc-12.0 --build-type=complete --with-filesystem --with-program_options --with-date_time
|
||||||
|
|
||||||
If you are on 64-bit Windows and you want to build 64-bit version as well
|
If you are on 64-bit Windows and you want to build 64-bit version as well
|
||||||
|
|
||||||
b2 toolset=msvc-12.0 --build-type=complete --stagedir=stage64 address-model=64 --with-filesystem --with-program_options --with-regex --with-date_time
|
b2 toolset=msvc-12.0 --build-type=complete --stagedir=stage64 address-model=64 --with-filesystem --with-program_options --with-date_time
|
||||||
|
|
||||||
After Boost is compiled, set the environment variable `BOOST_ROOT` to
|
After Boost is compiled, set the environment variable `BOOST_ROOT` to
|
||||||
the directory Boost was unpacked to, e.g., C:\dev\boost.
|
the directory Boost was unpacked to, e.g., C:\dev\boost.
|
||||||
|
@ -58,7 +58,11 @@ All options below still possible in cmdline, but better write it in config file:
|
|||||||
|
|
||||||
* --bob.address= - The address to listen on (BOB command channel)
|
* --bob.address= - The address to listen on (BOB command channel)
|
||||||
* --bob.port= - Port of BOB command channel. Usually 2827. BOB is off if not specified
|
* --bob.port= - Port of BOB command channel. Usually 2827. BOB is off if not specified
|
||||||
* --sam.enabled= - If BOB is enabled. false by default
|
* --bob.enabled= - If BOB is enabled. false by default
|
||||||
|
|
||||||
|
* --i2cp.address= - The address to listen on
|
||||||
|
* --i2cp.port= - Port of I2CP server. Usually 7654. IPCP is off if not specified
|
||||||
|
* --i2cp.enabled= - If I2CP is enabled. false by default. Other services don't requeire I2CP
|
||||||
|
|
||||||
* --i2pcontrol.address= - The address to listen on (I2P control service)
|
* --i2pcontrol.address= - The address to listen on (I2P control service)
|
||||||
* --i2pcontrol.port= - Port of I2P control service. Usually 7650. I2PControl is off if not specified
|
* --i2pcontrol.port= - Port of I2P control service. Usually 7650. I2PControl is off if not specified
|
||||||
|
BIN
docs/itoopieImage.png
Normal file
BIN
docs/itoopieImage.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.5 KiB |
1
stdafx.h
1
stdafx.h
@ -33,7 +33,6 @@
|
|||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
#include <boost/asio.hpp>
|
#include <boost/asio.hpp>
|
||||||
#include <boost/regex.hpp>
|
|
||||||
#include <boost/bind.hpp>
|
#include <boost/bind.hpp>
|
||||||
#include <boost/date_time/local_time/local_time.hpp>
|
#include <boost/date_time/local_time/local_time.hpp>
|
||||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
CXXFLAGS += -Wall -Wextra -pedantic -O0 -g -std=c++11 -D_GLIBCXX_USE_NANOSLEEP=1
|
CXXFLAGS += -Wall -Wextra -pedantic -O0 -g -std=c++11 -D_GLIBCXX_USE_NANOSLEEP=1
|
||||||
|
|
||||||
TESTS = test-http-url test-http-req test-http-res test-http-url_decode
|
TESTS = test-http-url test-http-req test-http-res test-http-url_decode \
|
||||||
|
test-http-merge_chunked
|
||||||
|
|
||||||
all: $(TESTS) run
|
all: $(TESTS) run
|
||||||
|
|
||||||
|
25
tests/test-http-merge_chunked.cpp
Normal file
25
tests/test-http-merge_chunked.cpp
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#include <cassert>
|
||||||
|
#include "../HTTP.h"
|
||||||
|
|
||||||
|
using namespace i2p::http;
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
const char *buf =
|
||||||
|
"4\r\n"
|
||||||
|
"HTTP\r\n"
|
||||||
|
"A\r\n"
|
||||||
|
" response \r\n"
|
||||||
|
"E\r\n"
|
||||||
|
"with \r\n"
|
||||||
|
"chunks.\r\n"
|
||||||
|
"0\r\n"
|
||||||
|
"\r\n"
|
||||||
|
;
|
||||||
|
std::stringstream in(buf);
|
||||||
|
std::stringstream out;
|
||||||
|
|
||||||
|
assert(MergeChunkedResponse(in, out) == true);
|
||||||
|
assert(out.str() == "HTTP response with \r\nchunks.");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
#define I2P_VERSION_MAJOR 0
|
#define I2P_VERSION_MAJOR 0
|
||||||
#define I2P_VERSION_MINOR 9
|
#define I2P_VERSION_MINOR 9
|
||||||
#define I2P_VERSION_MICRO 25
|
#define I2P_VERSION_MICRO 26
|
||||||
#define I2P_VERSION_PATCH 0
|
#define I2P_VERSION_PATCH 0
|
||||||
#define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO)
|
#define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user