mirror of
https://github.com/PurpleI2P/i2pd.git
synced 2025-03-07 06:09:42 +00:00
Compare commits
262 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ffaabe8674 | ||
|
|
0233ab4deb | ||
|
|
c9dc010c0b | ||
|
|
9fefbb0c4a | ||
|
|
eb9ea97e21 | ||
|
|
673b7a95b7 | ||
|
|
d5f27ecb0e | ||
|
|
8f8b928cc4 | ||
|
|
259a63e612 | ||
|
|
05c914156a | ||
|
|
f69884d573 | ||
|
|
8b8007695c | ||
|
|
ae442ee015 | ||
|
|
99b5f1b7b8 | ||
|
|
8071df0e68 | ||
|
|
63d6b23344 | ||
|
|
38d85a49e7 | ||
|
|
0edc149ecc | ||
|
|
10d6cd9896 | ||
|
|
6913da7efa | ||
|
|
34df1b1646 | ||
|
|
992603496e | ||
|
|
b9552c42f1 | ||
|
|
37e4dfc5d5 | ||
|
|
15b7284a8f | ||
|
|
b57a62fece | ||
|
|
9c7de5ad03 | ||
|
|
c065fae422 | ||
|
|
cfde1f8c27 | ||
|
|
c45f72a63e | ||
|
|
e1d9eca7bd | ||
|
|
2e66c4c9f5 | ||
|
|
9a19b5994b | ||
|
|
920586f56c | ||
|
|
919aa2895a | ||
|
|
75690598e3 | ||
|
|
ac2caf2787 | ||
|
|
5405876d84 | ||
|
|
4b9de0777b | ||
|
|
67492bf024 | ||
|
|
259baa0e84 | ||
|
|
dca48c7eec | ||
|
|
7386b0a523 | ||
|
|
d0e9fe1e3e | ||
|
|
2b7bab04dd | ||
|
|
ad5f890a1e | ||
|
|
fa191e2928 | ||
|
|
6d8a23ec16 | ||
|
|
12371650f9 | ||
|
|
79e1d54e4c | ||
|
|
447f5f69c9 | ||
|
|
e08a26d015 | ||
|
|
381f6b184e | ||
|
|
59681398cb | ||
|
|
adf887a06b | ||
|
|
42f70cd55d | ||
|
|
3704a4ff47 | ||
|
|
5b8d637f6a | ||
|
|
436621f79f | ||
|
|
739b6645f8 | ||
|
|
7a7ae4cc83 | ||
|
|
db83cbe58f | ||
|
|
87228429d6 | ||
|
|
2651723b50 | ||
|
|
b8a01d2ff1 | ||
|
|
5c20751937 | ||
|
|
06b0a50462 | ||
|
|
0d589895f6 | ||
|
|
230c2aaf26 | ||
|
|
1d8807a6ba | ||
|
|
81978b214c | ||
|
|
5699b7bae5 | ||
|
|
e726d216bb | ||
|
|
3480824290 | ||
|
|
c8b935151a | ||
|
|
5e5aefa290 | ||
|
|
0e14b54b6d | ||
|
|
c6ddae2d8e | ||
|
|
d092b21da7 | ||
|
|
a8061003dd | ||
|
|
50f0099645 | ||
|
|
c270687223 | ||
|
|
a92652f4ad | ||
|
|
006e4526e8 | ||
|
|
55dbbb3546 | ||
|
|
e4fe18e435 | ||
|
|
cea38549da | ||
|
|
0487e730ba | ||
|
|
8fdd7205d7 | ||
|
|
1d8d71cfb6 | ||
|
|
10bd017e57 | ||
|
|
70f39eb959 | ||
|
|
3a3b0cc847 | ||
|
|
65d721285b | ||
|
|
565f844b7f | ||
|
|
248992b27b | ||
|
|
bdd6037726 | ||
|
|
9d292bb6a4 | ||
|
|
12b9b49902 | ||
|
|
93b8bd7f02 | ||
|
|
cd8169c0a5 | ||
|
|
b4a9d4df8c | ||
|
|
d62525abb6 | ||
|
|
a4988fd7cb | ||
|
|
d91691c344 | ||
|
|
164d3566e3 | ||
|
|
058120d001 | ||
|
|
1dfa09cda9 | ||
|
|
913438e3ff | ||
|
|
1aa939ae73 | ||
|
|
a914608264 | ||
|
|
5d0852c1e2 | ||
|
|
e0e50faa47 | ||
|
|
f6721a2ced | ||
|
|
b1333b7d99 | ||
|
|
673a2acade | ||
|
|
752e74d33c | ||
|
|
6bacf94a62 | ||
|
|
336cd60920 | ||
|
|
76c9b66db4 | ||
|
|
0c5ca28a14 | ||
|
|
db63bb4495 | ||
|
|
34afb54c21 | ||
|
|
69888e148e | ||
|
|
98a55c0613 | ||
|
|
5425e9aee3 | ||
|
|
7fef5f5654 | ||
|
|
fc94e846a6 | ||
|
|
7d7bbf15bf | ||
|
|
8a545b98ec | ||
|
|
ecdb60b44e | ||
|
|
2eea85b786 | ||
|
|
87fd0e6f29 | ||
|
|
ea191afd9d | ||
|
|
89b624308e | ||
|
|
facdf0ca9c | ||
|
|
98484d54c0 | ||
|
|
ea31ca5ee8 | ||
|
|
6b5b9b3d62 | ||
|
|
975dab6d1d | ||
|
|
eaa7adc88c | ||
|
|
f76b014a52 | ||
|
|
8676a1b4ef | ||
|
|
e1eaa2097e | ||
|
|
6f2357c695 | ||
|
|
91427264c3 | ||
|
|
74aa961561 | ||
|
|
aa47e11471 | ||
|
|
89d69a5d5a | ||
|
|
3bbe1e9c0c | ||
|
|
6377631ae7 | ||
|
|
3562ac1438 | ||
|
|
e152785de9 | ||
|
|
dd259f1852 | ||
|
|
5001cea3a3 | ||
|
|
a4d586b24e | ||
|
|
46f927fc1b | ||
|
|
b83e7e6c5c | ||
|
|
5f463d5f6b | ||
|
|
2e301c2919 | ||
|
|
9526d42ec5 | ||
|
|
a566479ddb | ||
|
|
1bba0f6bb2 | ||
|
|
232d42881b | ||
|
|
abeaf76fe9 | ||
|
|
03d4584562 | ||
|
|
f2f5226ebb | ||
|
|
660860b92d | ||
|
|
c0a1a8b47c | ||
|
|
bd82e81e26 | ||
|
|
0a94df592c | ||
|
|
66506ea1ce | ||
|
|
7bff4db483 | ||
|
|
9208da8a50 | ||
|
|
70fcd93ca7 | ||
|
|
9ba9bd4415 | ||
|
|
480ce6f522 | ||
|
|
f1254fd5d4 | ||
|
|
10ebcff48e | ||
|
|
6ee227675a | ||
|
|
89059abe15 | ||
|
|
4503223a4e | ||
|
|
07c31a90f3 | ||
|
|
bbcb9af01f | ||
|
|
1cd415a3ae | ||
|
|
c344e75701 | ||
|
|
0305e4cf8a | ||
|
|
bc86b0345f | ||
|
|
8b0ce30dfc | ||
|
|
4b983300fe | ||
|
|
8829ebba6c | ||
|
|
11b90d2113 | ||
|
|
9d8d4c09c6 | ||
|
|
c90d5bb67c | ||
|
|
7263d9f03e | ||
|
|
d5e77e9bb2 | ||
|
|
1ecd5250fc | ||
|
|
44af5e04e4 | ||
|
|
4582a4fd95 | ||
|
|
2d513277f2 | ||
|
|
7934974d92 | ||
|
|
4dce35b1e6 | ||
|
|
e5f5f96771 | ||
|
|
d4a0076aba | ||
|
|
cd9cd84c5b | ||
|
|
93eca799dd | ||
|
|
34f090662a | ||
|
|
1a1d54387c | ||
|
|
9575f70f38 | ||
|
|
b4e9ed7d18 | ||
|
|
3d4e2a275c | ||
|
|
b526718846 | ||
|
|
a4883cfa15 | ||
|
|
a41f179785 | ||
|
|
bef628212e | ||
|
|
ef3030abe5 | ||
|
|
754ad20eff | ||
|
|
647175cf12 | ||
|
|
5f0a440f0a | ||
|
|
d68544038c | ||
|
|
df36b0eb7e | ||
|
|
4f4748b8df | ||
|
|
028a896303 | ||
|
|
578083df3e | ||
|
|
c5e1823f15 | ||
|
|
5f396d6311 | ||
|
|
5c64c2ff42 | ||
|
|
2dcb91b284 | ||
|
|
d708e7f682 | ||
|
|
a8a4ef82cd | ||
|
|
1286f1c968 | ||
|
|
9368a93279 | ||
|
|
143aaa2d28 | ||
|
|
b8dcdece38 | ||
|
|
be7f4c5da7 | ||
|
|
890807b8d7 | ||
|
|
8e1687e7b3 | ||
|
|
d8510ead43 | ||
|
|
c74db4b81c | ||
|
|
4ee9b4524d | ||
|
|
28cf351878 | ||
|
|
c5e2ec5e00 | ||
|
|
fe3ebc4c84 | ||
|
|
6688f9a5ef | ||
|
|
3167ae21b0 | ||
|
|
c40a463549 | ||
|
|
87a85fff08 | ||
|
|
b68381db58 | ||
|
|
25c1884961 | ||
|
|
9980df2c67 | ||
|
|
ed09c1171b | ||
|
|
c473b10667 | ||
|
|
c15e53e9c0 | ||
|
|
e9d3278fc5 | ||
|
|
b683c07d55 | ||
|
|
681f055b16 | ||
|
|
f4cb4c1756 | ||
|
|
1cc67bbbe8 | ||
|
|
0df0450107 | ||
|
|
cb324ca723 | ||
|
|
442a0c48e7 | ||
|
|
d97acacae6 |
2
.dir-locals.el
Normal file
2
.dir-locals.el
Normal file
@@ -0,0 +1,2 @@
|
||||
((c++-mode . ((indent-tabs-mode . t)))
|
||||
(c-mode . ((mode . c++))))
|
||||
@@ -842,7 +842,7 @@ namespace client
|
||||
else
|
||||
memset (response + 8, 0, 32); // not found
|
||||
memset (response + 40, 0, 4); // set expiration time to zero
|
||||
m_LocalDestination->GetDatagramDestination ()->SendDatagramTo (response, 44, from.GetIdentHash (), toPort, fromPort);
|
||||
m_LocalDestination->GetDatagramDestination ()->SendDatagramTo (response, 44, from.GetIdentHash(), toPort, fromPort);
|
||||
}
|
||||
|
||||
void AddressResolver::AddAddress (const std::string& name, const i2p::data::IdentHash& ident)
|
||||
|
||||
6
BOB.cpp
6
BOB.cpp
@@ -1,7 +1,7 @@
|
||||
#include <string.h>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include "Log.h"
|
||||
#include "ClientContext.h"
|
||||
#include "util.h"
|
||||
#include "BOB.h"
|
||||
|
||||
namespace i2p
|
||||
@@ -459,7 +459,7 @@ namespace client
|
||||
void BOBCommandSession::OutportCommandHandler (const char * operand, size_t len)
|
||||
{
|
||||
LogPrint (eLogDebug, "BOB: outport ", operand);
|
||||
m_OutPort = boost::lexical_cast<int>(operand);
|
||||
m_OutPort = std::stoi(operand);
|
||||
if (m_OutPort >= 0)
|
||||
SendReplyOK ("outbound port set");
|
||||
else
|
||||
@@ -476,7 +476,7 @@ namespace client
|
||||
void BOBCommandSession::InportCommandHandler (const char * operand, size_t len)
|
||||
{
|
||||
LogPrint (eLogDebug, "BOB: inport ", operand);
|
||||
m_InPort = boost::lexical_cast<int>(operand);
|
||||
m_InPort = std::stoi(operand);
|
||||
if (m_InPort >= 0)
|
||||
SendReplyOK ("inbound port set");
|
||||
else
|
||||
|
||||
69
BloomFilter.cpp
Normal file
69
BloomFilter.cpp
Normal file
@@ -0,0 +1,69 @@
|
||||
#include "BloomFilter.h"
|
||||
#include "I2PEndian.h"
|
||||
#include <array>
|
||||
#include <openssl/sha.h>
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
|
||||
/** @brief decaying bloom filter implementation */
|
||||
class DecayingBloomFilter : public IBloomFilter
|
||||
{
|
||||
public:
|
||||
|
||||
DecayingBloomFilter(const std::size_t size)
|
||||
{
|
||||
m_Size = size;
|
||||
m_Data = new uint8_t[size];
|
||||
}
|
||||
|
||||
/** @brief implements IBloomFilter::~IBloomFilter */
|
||||
~DecayingBloomFilter()
|
||||
{
|
||||
delete [] m_Data;
|
||||
}
|
||||
|
||||
/** @brief implements IBloomFilter::Add */
|
||||
bool Add(const uint8_t * data, std::size_t len)
|
||||
{
|
||||
std::size_t idx;
|
||||
uint8_t mask;
|
||||
Get(data, len, idx, mask);
|
||||
if(m_Data[idx] & mask) return false; // filter hit
|
||||
m_Data[idx] |= mask;
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @brief implements IBloomFilter::Decay */
|
||||
void Decay()
|
||||
{
|
||||
// reset bloom filter buffer
|
||||
memset(m_Data, 0, m_Size);
|
||||
}
|
||||
|
||||
private:
|
||||
/** @brief get bit index for for data */
|
||||
void Get(const uint8_t * data, std::size_t len, std::size_t & idx, uint8_t & bm)
|
||||
{
|
||||
bm = 1;
|
||||
uint8_t digest[32];
|
||||
// TODO: use blake2 because it's faster
|
||||
SHA256(data, len, digest);
|
||||
uint64_t i = buf64toh(digest);
|
||||
idx = i % m_Size;
|
||||
bm <<= (i % 8);
|
||||
}
|
||||
|
||||
uint8_t * m_Data;
|
||||
std::size_t m_Size;
|
||||
};
|
||||
|
||||
|
||||
BloomFilterPtr BloomFilter(std::size_t capacity)
|
||||
{
|
||||
return std::make_shared<DecayingBloomFilter>(capacity);
|
||||
}
|
||||
}
|
||||
}
|
||||
31
BloomFilter.h
Normal file
31
BloomFilter.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#ifndef BLOOM_FILTER_H_
|
||||
#define BLOOM_FILTER_H_
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
|
||||
/** @brief interface for bloom filter */
|
||||
struct IBloomFilter
|
||||
{
|
||||
|
||||
/** @brief destructor */
|
||||
virtual ~IBloomFilter() {};
|
||||
/** @brief add entry to bloom filter, return false if filter hit otherwise return true */
|
||||
virtual bool Add(const uint8_t * data, std::size_t len) = 0;
|
||||
/** @brief optionally decay old entries */
|
||||
virtual void Decay() = 0;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<IBloomFilter> BloomFilterPtr;
|
||||
|
||||
/** @brief create bloom filter */
|
||||
BloomFilterPtr BloomFilter(std::size_t capacity = 1024 * 8);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
42
ChangeLog
42
ChangeLog
@@ -1,6 +1,48 @@
|
||||
# for this file format description,
|
||||
# see https://github.com/olivierlacan/keep-a-changelog
|
||||
|
||||
## [2.11.0] - 2016-12-18
|
||||
### Added
|
||||
- Websockets support
|
||||
- Reseed through a floodfill
|
||||
- Tunnel configuration for HTTP and SOCKS proxy
|
||||
- Zero-hops tunnels for destinations
|
||||
- Multiple acceptors for SAM
|
||||
### Changed
|
||||
- Reseed servers list
|
||||
- DHT uses AVX if applicable
|
||||
- New logo
|
||||
- LeaseSet lookups
|
||||
### Fixed
|
||||
- HTTP Proxy connection reset for Windows
|
||||
- Crash upon SAM session termination
|
||||
- Can't connect to a destination for a longer time after restart
|
||||
- Mass packet loss for UDP tunnels
|
||||
|
||||
## [2.10.2] - 2016-12-04
|
||||
### Fixed
|
||||
- Fixes UPnP discovery bug, producing excessive CPU usage
|
||||
- Fixes sudden SSU thread stop for Windows.
|
||||
|
||||
## [2.10.1] - 2016-11-07
|
||||
### Fixed
|
||||
- Fixed some performance issues for Windows and Android
|
||||
|
||||
## [2.10.0] - 2016-10-17
|
||||
### Added
|
||||
- Datagram i2p tunnels
|
||||
- Unique local addresses for server tunnels
|
||||
- Configurable list of reseed servers and initial addressbook
|
||||
- Configurable netid
|
||||
- Initial iOS support
|
||||
|
||||
### Changed
|
||||
- Reduced file descriptiors usage
|
||||
- Strict reseed checks enabled by default
|
||||
|
||||
## Fixed
|
||||
- Multiple fixes in I2CP and BOB implementations
|
||||
|
||||
## [2.9.0] - 2016-08-12
|
||||
### Changed
|
||||
- Proxy refactoring & speedup
|
||||
|
||||
@@ -52,8 +52,12 @@ namespace client
|
||||
if (httpProxyKeys.length () > 0)
|
||||
{
|
||||
i2p::data::PrivateKeys keys;
|
||||
if(LoadPrivateKeys (keys, httpProxyKeys))
|
||||
localDestination = CreateNewLocalDestination (keys, false);
|
||||
if(LoadPrivateKeys (keys, httpProxyKeys, i2p::data::SIGNING_KEY_TYPE_DSA_SHA1))
|
||||
{
|
||||
std::map<std::string, std::string> params;
|
||||
ReadI2CPOptionsFromConfig ("httpproxy.", params);
|
||||
localDestination = CreateNewLocalDestination (keys, false, ¶ms);
|
||||
}
|
||||
else
|
||||
LogPrint(eLogError, "Clients: failed to load HTTP Proxy key");
|
||||
}
|
||||
@@ -65,8 +69,10 @@ namespace client
|
||||
}
|
||||
}
|
||||
|
||||
localDestination = nullptr;
|
||||
bool socksproxy; i2p::config::GetOption("socksproxy.enabled", socksproxy);
|
||||
if (socksproxy) {
|
||||
if (socksproxy)
|
||||
{
|
||||
std::string socksProxyKeys; i2p::config::GetOption("socksproxy.keys", socksProxyKeys);
|
||||
std::string socksProxyAddr; i2p::config::GetOption("socksproxy.address", socksProxyAddr);
|
||||
uint16_t socksProxyPort; i2p::config::GetOption("socksproxy.port", socksProxyPort);
|
||||
@@ -76,8 +82,14 @@ namespace client
|
||||
if (socksProxyKeys.length () > 0)
|
||||
{
|
||||
i2p::data::PrivateKeys keys;
|
||||
LoadPrivateKeys (keys, socksProxyKeys);
|
||||
localDestination = CreateNewLocalDestination (keys, false);
|
||||
if (LoadPrivateKeys (keys, socksProxyKeys, i2p::data::SIGNING_KEY_TYPE_DSA_SHA1))
|
||||
{
|
||||
std::map<std::string, std::string> params;
|
||||
ReadI2CPOptionsFromConfig ("socksproxy.", params);
|
||||
localDestination = CreateNewLocalDestination (keys, false, ¶ms);
|
||||
}
|
||||
else
|
||||
LogPrint(eLogError, "Clients: failed to load SOCKS Proxy key");
|
||||
}
|
||||
try {
|
||||
m_SocksProxy = new i2p::proxy::SOCKSProxy(socksProxyAddr, socksProxyPort, socksOutProxyAddr, socksOutProxyPort, localDestination);
|
||||
@@ -290,7 +302,7 @@ namespace client
|
||||
}
|
||||
return infos;
|
||||
}
|
||||
|
||||
|
||||
std::shared_ptr<ClientDestination> ClientContext::CreateNewLocalDestination (bool isPublic, i2p::data::SigningKeyType sigType,
|
||||
const std::map<std::string, std::string> * params)
|
||||
{
|
||||
@@ -360,6 +372,25 @@ namespace client
|
||||
options[I2CP_PARAM_INBOUND_TUNNELS_QUANTITY] = GetI2CPOption (section, I2CP_PARAM_INBOUND_TUNNELS_QUANTITY, DEFAULT_INBOUND_TUNNELS_QUANTITY);
|
||||
options[I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY] = GetI2CPOption (section, I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY, DEFAULT_OUTBOUND_TUNNELS_QUANTITY);
|
||||
options[I2CP_PARAM_TAGS_TO_SEND] = GetI2CPOption (section, I2CP_PARAM_TAGS_TO_SEND, DEFAULT_TAGS_TO_SEND);
|
||||
options[I2CP_PARAM_MIN_TUNNEL_LATENCY] = GetI2CPOption(section, I2CP_PARAM_MIN_TUNNEL_LATENCY, DEFAULT_MIN_TUNNEL_LATENCY);
|
||||
options[I2CP_PARAM_MAX_TUNNEL_LATENCY] = GetI2CPOption(section, I2CP_PARAM_MAX_TUNNEL_LATENCY, DEFAULT_MAX_TUNNEL_LATENCY);
|
||||
}
|
||||
|
||||
void ClientContext::ReadI2CPOptionsFromConfig (const std::string& prefix, std::map<std::string, std::string>& options) const
|
||||
{
|
||||
std::string value;
|
||||
if (i2p::config::GetOption(prefix + I2CP_PARAM_INBOUND_TUNNEL_LENGTH, value))
|
||||
options[I2CP_PARAM_INBOUND_TUNNEL_LENGTH] = value;
|
||||
if (i2p::config::GetOption(prefix + I2CP_PARAM_INBOUND_TUNNELS_QUANTITY, value))
|
||||
options[I2CP_PARAM_INBOUND_TUNNELS_QUANTITY] = value;
|
||||
if (i2p::config::GetOption(prefix + I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH, value))
|
||||
options[I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH] = value;
|
||||
if (i2p::config::GetOption(prefix + I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY, value))
|
||||
options[I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY] = value;
|
||||
if (i2p::config::GetOption(prefix + I2CP_PARAM_MIN_TUNNEL_LATENCY, value))
|
||||
options[I2CP_PARAM_MIN_TUNNEL_LATENCY] = value;
|
||||
if (i2p::config::GetOption(prefix + I2CP_PARAM_MAX_TUNNEL_LATENCY, value))
|
||||
options[I2CP_PARAM_MAX_TUNNEL_LATENCY] = value;
|
||||
}
|
||||
|
||||
void ClientContext::ReadTunnels ()
|
||||
|
||||
@@ -66,16 +66,18 @@ namespace client
|
||||
|
||||
AddressBook& GetAddressBook () { return m_AddressBook; };
|
||||
const SAMBridge * GetSAMBridge () const { return m_SamBridge; };
|
||||
const I2CPServer * GetI2CPServer () const { return m_I2CPServer; };
|
||||
|
||||
std::vector<std::shared_ptr<DatagramSessionInfo> > GetForwardInfosFor(const i2p::data::IdentHash & destination);
|
||||
|
||||
|
||||
private:
|
||||
|
||||
void ReadTunnels ();
|
||||
template<typename Section, typename Type>
|
||||
std::string GetI2CPOption (const Section& section, const std::string& name, const Type& value) const;
|
||||
template<typename Section>
|
||||
void ReadI2CPOptions (const Section& section, std::map<std::string, std::string>& options) const;
|
||||
void ReadI2CPOptions (const Section& section, std::map<std::string, std::string>& options) const;
|
||||
void ReadI2CPOptionsFromConfig (const std::string& prefix, std::map<std::string, std::string>& options) const;
|
||||
|
||||
void CleanupUDP(const boost::system::error_code & ecode);
|
||||
void ScheduleCleanupUDP();
|
||||
@@ -110,6 +112,7 @@ namespace client
|
||||
const decltype(m_ServerTunnels)& GetServerTunnels () const { return m_ServerTunnels; };
|
||||
const decltype(m_ClientForwards)& GetClientForwards () const { return m_ClientForwards; }
|
||||
const decltype(m_ServerForwards)& GetServerForwards () const { return m_ServerForwards; }
|
||||
const i2p::proxy::HTTPProxy * GetHttpProxy () const { return m_HttpProxy; }
|
||||
};
|
||||
|
||||
extern ClientContext context;
|
||||
|
||||
60
Config.cpp
60
Config.cpp
@@ -27,10 +27,6 @@ namespace config {
|
||||
variables_map m_Options;
|
||||
|
||||
void Init() {
|
||||
bool nat = true;
|
||||
#ifdef MESHNET
|
||||
nat = false;
|
||||
#endif
|
||||
|
||||
options_description general("General options");
|
||||
general.add_options()
|
||||
@@ -44,8 +40,10 @@ namespace config {
|
||||
("family", value<std::string>()->default_value(""), "Specify a family, router belongs to")
|
||||
("datadir", value<std::string>()->default_value(""), "Path to storage of i2pd data (RI, keys, peer profiles, ...)")
|
||||
("host", value<std::string>()->default_value("0.0.0.0"), "External IP")
|
||||
("ifname", value<std::string>()->default_value(""), "network interface to bind to")
|
||||
("nat", value<bool>()->zero_tokens()->default_value(nat), "should we assume we are behind NAT?")
|
||||
("ifname", value<std::string>()->default_value(""), "Network interface to bind to")
|
||||
("ifname4", value<std::string>()->default_value(""), "Network interface to bind to for ipv4")
|
||||
("ifname6", value<std::string>()->default_value(""), "Network interface to bind to for ipv6")
|
||||
("nat", value<bool>()->zero_tokens()->default_value(true), "Should we assume we are behind NAT?")
|
||||
("port", value<uint16_t>()->default_value(0), "Port to listen for incoming connections (default: auto)")
|
||||
("ipv4", value<bool>()->zero_tokens()->default_value(true), "Enable communication through ipv4")
|
||||
("ipv6", value<bool>()->zero_tokens()->default_value(false), "Enable communication through ipv6")
|
||||
@@ -55,17 +53,19 @@ namespace config {
|
||||
("notransit", value<bool>()->zero_tokens()->default_value(false), "Router will not accept transit tunnels at startup")
|
||||
("floodfill", value<bool>()->zero_tokens()->default_value(false), "Router will be floodfill")
|
||||
("bandwidth", value<std::string>()->default_value(""), "Bandwidth limit: integer in kbps or letters: L (32), O (256), P (2048), X (>9000)")
|
||||
("ntcp", value<bool>()->zero_tokens()->default_value(true), "enable ntcp transport")
|
||||
("ssu", value<bool>()->zero_tokens()->default_value(true), "enable ssu transport")
|
||||
("ntcp", value<bool>()->zero_tokens()->default_value(true), "Enable NTCP transport")
|
||||
("ssu", value<bool>()->zero_tokens()->default_value(true), "Enable SSU transport")
|
||||
#ifdef _WIN32
|
||||
("svcctl", value<std::string>()->default_value(""), "Windows service management ('install' or 'remove')")
|
||||
("insomnia", value<bool>()->zero_tokens()->default_value(false), "Prevent system from sleeping")
|
||||
("close", value<std::string>()->default_value("ask"), "Action on close: minimize, exit, ask") // TODO: add custom validator or something
|
||||
#endif
|
||||
;
|
||||
|
||||
|
||||
options_description limits("Limits options");
|
||||
limits.add_options()
|
||||
("limits.coresize", value<uint32_t>()->default_value(0), "Maximum size of corefile in Kb (0 - use system limit)")
|
||||
("limits.openfiles", value<uint16_t>()->default_value(0), "Maximum number of open files (0 - use system default)")
|
||||
("limits.transittunnels", value<uint16_t>()->default_value(2500), "Maximum active transit sessions (default:2500)")
|
||||
;
|
||||
|
||||
@@ -85,6 +85,12 @@ namespace config {
|
||||
("httpproxy.address", value<std::string>()->default_value("127.0.0.1"), "HTTP Proxy listen address")
|
||||
("httpproxy.port", value<uint16_t>()->default_value(4444), "HTTP Proxy listen port")
|
||||
("httpproxy.keys", value<std::string>()->default_value(""), "File to persist HTTP Proxy keys")
|
||||
("httpproxy.inbound.length", value<std::string>()->default_value("3"), "HTTP proxy inbound tunnel length")
|
||||
("httpproxy.outbound.length", value<std::string>()->default_value("3"), "HTTP proxy outbound tunnel length")
|
||||
("httpproxy.inbound.quantity", value<std::string>()->default_value("5"), "HTTP proxy inbound tunnels quantity")
|
||||
("httpproxy.outbound.quantity", value<std::string>()->default_value("5"), "HTTP proxy outbound tunnels quantity")
|
||||
("httpproxy.latency.min", value<std::string>()->default_value("0"), "HTTP proxy min latency for tunnels")
|
||||
("httpproxy.latency.max", value<std::string>()->default_value("0"), "HTTP proxy max latency for tunnels")
|
||||
;
|
||||
|
||||
options_description socksproxy("SOCKS Proxy options");
|
||||
@@ -93,7 +99,13 @@ namespace config {
|
||||
("socksproxy.address", value<std::string>()->default_value("127.0.0.1"), "SOCKS Proxy listen address")
|
||||
("socksproxy.port", value<uint16_t>()->default_value(4447), "SOCKS Proxy listen port")
|
||||
("socksproxy.keys", value<std::string>()->default_value(""), "File to persist SOCKS Proxy keys")
|
||||
("socksproxy.outproxy", value<std::string>()->default_value("127.0.0.1"), "Upstream outproxy address for SOCKS Proxy")
|
||||
("socksproxy.inbound.length", value<std::string>()->default_value("3"), "SOCKS proxy inbound tunnel length")
|
||||
("socksproxy.outbound.length", value<std::string>()->default_value("3"), "SOCKS proxy outbound tunnel length")
|
||||
("socksproxy.inbound.quantity", value<std::string>()->default_value("5"), "SOCKS proxy inbound tunnels quantity")
|
||||
("socksproxy.outbound.quantity", value<std::string>()->default_value("5"), "SOCKS proxy outbound tunnels quantity")
|
||||
("socksproxy.latency.min", value<std::string>()->default_value("0"), "SOCKS proxy min latency for tunnels")
|
||||
("socksproxy.latency.max", value<std::string>()->default_value("0"), "SOCKS proxy max latency for tunnels")
|
||||
("socksproxy.outproxy", value<std::string>()->default_value("127.0.0.1"), "Upstream outproxy address for SOCKS Proxy")
|
||||
("socksproxy.outproxyport", value<uint16_t>()->default_value(9050), "Upstream outproxy port for SOCKS Proxy")
|
||||
;
|
||||
|
||||
@@ -151,25 +163,21 @@ namespace config {
|
||||
|
||||
options_description reseed("Reseed options");
|
||||
reseed.add_options()
|
||||
("reseed.verify", value<bool>()->default_value(false), "Verify .su3 signature")
|
||||
("reseed.file", value<std::string>()->default_value(""), "Path to .su3 file")
|
||||
#ifdef MESHNET
|
||||
("reseed.urls", value<std::string>()->default_value("https://reseed.i2p.rocks:8443/"), "Reseed URLs, separated by comma")
|
||||
#else
|
||||
("reseed.verify", value<bool>()->default_value(false), "Verify .su3 signature")
|
||||
("reseed.floodfill", value<std::string>()->default_value(""), "Path to router info of floodfill to reseed from")
|
||||
("reseed.file", value<std::string>()->default_value(""), "Path to local .su3 file or HTTPS URL to reseed from")
|
||||
("reseed.urls", value<std::string>()->default_value(
|
||||
"https://reseed.i2p-projekt.de/,"
|
||||
"https://i2p.mooo.com/netDb/,"
|
||||
"https://netdb.i2p2.no/,"
|
||||
"https://us.reseed.i2p2.no:444/,"
|
||||
"https://uk.reseed.i2p2.no:444/,"
|
||||
"https://i2p.manas.ca:8443/,"
|
||||
// "https://uk.reseed.i2p2.no:444/," // mamoth's shit
|
||||
"https://i2p-0.manas.ca:8443/,"
|
||||
"https://reseed.i2p.vzaws.com:8443/,"
|
||||
"https://download.xxlspeed.com/,"
|
||||
"https://reseed-ru.lngserv.ru/,"
|
||||
"https://reseed.atomike.ninja/"
|
||||
), "Reseed URLs, separated by comma")
|
||||
#endif
|
||||
;
|
||||
|
||||
options_description addressbook("AddressBook options");
|
||||
@@ -182,10 +190,17 @@ namespace config {
|
||||
|
||||
options_description trust("Trust options");
|
||||
trust.add_options()
|
||||
("trust.enabled", value<bool>()->default_value(false), "enable explicit trust options")
|
||||
("trust.enabled", value<bool>()->default_value(false), "Enable explicit trust options")
|
||||
("trust.family", value<std::string>()->default_value(""), "Router Familiy to trust for first hops")
|
||||
("trust.hidden", value<bool>()->default_value(false), "should we hide our router from other routers?");
|
||||
|
||||
("trust.routers", value<std::string>()->default_value(""), "Only Connect to these routers")
|
||||
("trust.hidden", value<bool>()->default_value(false), "Should we hide our router from other routers?");
|
||||
|
||||
options_description websocket("Websocket Options");
|
||||
websocket.add_options()
|
||||
("websockets.enabled", value<bool>()->default_value(false), "enable websocket server")
|
||||
("websockets.address", value<std::string>()->default_value("127.0.0.1"), "address to bind websocket server on")
|
||||
("websockets.port", value<uint16_t>()->default_value(7666), "port to bind websocket server on");
|
||||
|
||||
m_OptionsDesc
|
||||
.add(general)
|
||||
.add(limits)
|
||||
@@ -200,7 +215,8 @@ namespace config {
|
||||
.add(precomputation)
|
||||
.add(reseed)
|
||||
.add(addressbook)
|
||||
.add(trust)
|
||||
.add(trust)
|
||||
.add(websocket)
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
8
Config.h
8
Config.h
@@ -78,6 +78,12 @@ namespace config {
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool GetOption(const std::string& name, T& value)
|
||||
{
|
||||
return GetOption (name.c_str (), value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set value of given parameter
|
||||
* @param name Name of settable parameter
|
||||
@@ -93,7 +99,7 @@ namespace config {
|
||||
m_Options.at(name).value() = value;
|
||||
notify(m_Options);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check is value explicitly given or default
|
||||
|
||||
133
Crypto.cpp
133
Crypto.cpp
@@ -224,13 +224,11 @@ namespace crypto
|
||||
|
||||
// DH
|
||||
|
||||
DHKeys::DHKeys (): m_IsUpdated (true)
|
||||
DHKeys::DHKeys ()
|
||||
{
|
||||
m_DH = DH_new ();
|
||||
m_DH->p = BN_dup (elgp);
|
||||
m_DH->g = BN_dup (elgg);
|
||||
m_DH->priv_key = NULL;
|
||||
m_DH->pub_key = NULL;
|
||||
DH_set0_pqg (m_DH, BN_dup (elgp), NULL, BN_dup (elgg));
|
||||
DH_set0_key (m_DH, NULL, NULL);
|
||||
}
|
||||
|
||||
DHKeys::~DHKeys ()
|
||||
@@ -238,41 +236,32 @@ namespace crypto
|
||||
DH_free (m_DH);
|
||||
}
|
||||
|
||||
void DHKeys::GenerateKeys (uint8_t * priv, uint8_t * pub)
|
||||
void DHKeys::GenerateKeys ()
|
||||
{
|
||||
if (m_DH->priv_key) { BN_free (m_DH->priv_key); m_DH->priv_key = NULL; };
|
||||
if (m_DH->pub_key) { BN_free (m_DH->pub_key); m_DH->pub_key = NULL; };
|
||||
BIGNUM * priv_key = NULL, * pub_key = NULL;
|
||||
#if !defined(__x86_64__) // use short exponent for non x64
|
||||
m_DH->priv_key = BN_new ();
|
||||
BN_rand (m_DH->priv_key, ELGAMAL_SHORT_EXPONENT_NUM_BITS, 0, 1);
|
||||
priv_key = BN_new ();
|
||||
BN_rand (priv_key, ELGAMAL_SHORT_EXPONENT_NUM_BITS, 0, 1);
|
||||
#endif
|
||||
if (g_ElggTable)
|
||||
{
|
||||
#if defined(__x86_64__)
|
||||
m_DH->priv_key = BN_new ();
|
||||
BN_rand (m_DH->priv_key, ELGAMAL_FULL_EXPONENT_NUM_BITS, 0, 1);
|
||||
priv_key = BN_new ();
|
||||
BN_rand (priv_key, ELGAMAL_FULL_EXPONENT_NUM_BITS, 0, 1);
|
||||
#endif
|
||||
auto ctx = BN_CTX_new ();
|
||||
m_DH->pub_key = ElggPow (m_DH->priv_key, g_ElggTable, ctx);
|
||||
pub_key = ElggPow (priv_key, g_ElggTable, ctx);
|
||||
DH_set0_key (m_DH, pub_key, priv_key);
|
||||
BN_CTX_free (ctx);
|
||||
}
|
||||
else
|
||||
{
|
||||
DH_set0_key (m_DH, NULL, priv_key);
|
||||
DH_generate_key (m_DH);
|
||||
|
||||
if (priv) bn2buf (m_DH->priv_key, priv, 256);
|
||||
if (pub) bn2buf (m_DH->pub_key, pub, 256);
|
||||
m_IsUpdated = true;
|
||||
}
|
||||
|
||||
const uint8_t * DHKeys::GetPublicKey ()
|
||||
{
|
||||
if (m_IsUpdated)
|
||||
{
|
||||
bn2buf (m_DH->pub_key, m_PublicKey, 256);
|
||||
BN_free (m_DH->pub_key); m_DH->pub_key = NULL;
|
||||
m_IsUpdated= false;
|
||||
DH_get0_key (m_DH, (const BIGNUM **)&pub_key, (const BIGNUM **)&priv_key);
|
||||
}
|
||||
return m_PublicKey;
|
||||
|
||||
bn2buf (pub_key, m_PublicKey, 256);
|
||||
}
|
||||
|
||||
void DHKeys::Agree (const uint8_t * pub, uint8_t * shared)
|
||||
@@ -319,12 +308,12 @@ namespace crypto
|
||||
BN_free (b1);
|
||||
}
|
||||
|
||||
void ElGamalEncryption::Encrypt (const uint8_t * data, int len, uint8_t * encrypted, bool zeroPadding) const
|
||||
void ElGamalEncryption::Encrypt (const uint8_t * data, uint8_t * encrypted, bool zeroPadding) const
|
||||
{
|
||||
// create m
|
||||
uint8_t m[255];
|
||||
m[0] = 0xFF;
|
||||
memcpy (m+33, data, len);
|
||||
memcpy (m+33, data, 222);
|
||||
SHA256 (m+33, 222, m+1);
|
||||
// calculate b = b1*m mod p
|
||||
BIGNUM * b = BN_new ();
|
||||
@@ -397,44 +386,68 @@ namespace crypto
|
||||
// HMAC
|
||||
const uint64_t IPAD = 0x3636363636363636;
|
||||
const uint64_t OPAD = 0x5C5C5C5C5C5C5C5C;
|
||||
|
||||
|
||||
#if defined(__AVX__)
|
||||
static const uint64_t ipads[] = { IPAD, IPAD, IPAD, IPAD };
|
||||
static const uint64_t opads[] = { OPAD, OPAD, OPAD, OPAD };
|
||||
#endif
|
||||
|
||||
void HMACMD5Digest (uint8_t * msg, size_t len, const MACKey& key, uint8_t * digest)
|
||||
// key is 32 bytes
|
||||
// digest is 16 bytes
|
||||
// block size is 64 bytes
|
||||
{
|
||||
uint64_t buf[256];
|
||||
uint64_t hash[12]; // 96 bytes
|
||||
#if defined(__AVX__) // for AVX
|
||||
__asm__
|
||||
(
|
||||
"vmovups %[key], %%ymm0 \n"
|
||||
"vmovups %[ipad], %%ymm1 \n"
|
||||
"vmovups %%ymm1, 32(%[buf]) \n"
|
||||
"vxorps %%ymm0, %%ymm1, %%ymm1 \n"
|
||||
"vmovups %%ymm1, (%[buf]) \n"
|
||||
"vmovups %[opad], %%ymm1 \n"
|
||||
"vmovups %%ymm1, 32(%[hash]) \n"
|
||||
"vxorps %%ymm0, %%ymm1, %%ymm1 \n"
|
||||
"vmovups %%ymm1, (%[hash]) \n"
|
||||
"vzeroall \n" // end of AVX
|
||||
"movups %%xmm0, 80(%[hash]) \n" // zero last 16 bytes
|
||||
:
|
||||
: [key]"m"(*(const uint8_t *)key), [ipad]"m"(*ipads), [opad]"m"(*opads),
|
||||
[buf]"r"(buf), [hash]"r"(hash)
|
||||
: "memory", "%xmm0" // TODO: change to %ymm0 later
|
||||
);
|
||||
#else
|
||||
// ikeypad
|
||||
buf[0] = key.GetLL ()[0] ^ IPAD;
|
||||
buf[1] = key.GetLL ()[1] ^ IPAD;
|
||||
buf[2] = key.GetLL ()[2] ^ IPAD;
|
||||
buf[3] = key.GetLL ()[3] ^ IPAD;
|
||||
buf[4] = IPAD;
|
||||
buf[5] = IPAD;
|
||||
buf[6] = IPAD;
|
||||
buf[7] = IPAD;
|
||||
buf[4] = IPAD;
|
||||
buf[5] = IPAD;
|
||||
buf[6] = IPAD;
|
||||
buf[7] = IPAD;
|
||||
// okeypad
|
||||
hash[0] = key.GetLL ()[0] ^ OPAD;
|
||||
hash[1] = key.GetLL ()[1] ^ OPAD;
|
||||
hash[2] = key.GetLL ()[2] ^ OPAD;
|
||||
hash[3] = key.GetLL ()[3] ^ OPAD;
|
||||
hash[4] = OPAD;
|
||||
hash[5] = OPAD;
|
||||
hash[6] = OPAD;
|
||||
hash[7] = OPAD;
|
||||
// fill last 16 bytes with zeros (first hash size assumed 32 bytes in I2P)
|
||||
memset (hash + 10, 0, 16);
|
||||
#endif
|
||||
|
||||
// concatenate with msg
|
||||
memcpy (buf + 8, msg, len);
|
||||
// calculate first hash
|
||||
uint8_t hash[16]; // MD5
|
||||
MD5((uint8_t *)buf, len + 64, hash);
|
||||
|
||||
// okeypad
|
||||
buf[0] = key.GetLL ()[0] ^ OPAD;
|
||||
buf[1] = key.GetLL ()[1] ^ OPAD;
|
||||
buf[2] = key.GetLL ()[2] ^ OPAD;
|
||||
buf[3] = key.GetLL ()[3] ^ OPAD;
|
||||
buf[4] = OPAD;
|
||||
buf[5] = OPAD;
|
||||
buf[6] = OPAD;
|
||||
buf[7] = OPAD;
|
||||
// copy first hash after okeypad
|
||||
memcpy (buf + 8, hash, 16);
|
||||
// fill next 16 bytes with zeros (first hash size assumed 32 bytes in I2P)
|
||||
memset (buf + 10, 0, 16);
|
||||
MD5((uint8_t *)buf, len + 64, (uint8_t *)(hash + 8)); // 16 bytes
|
||||
|
||||
// calculate digest
|
||||
MD5((uint8_t *)buf, 96, digest);
|
||||
MD5((uint8_t *)hash, 96, digest);
|
||||
}
|
||||
|
||||
// AES
|
||||
@@ -606,16 +619,16 @@ namespace crypto
|
||||
"jnz 1b \n"
|
||||
"movups %%xmm1, (%[iv]) \n"
|
||||
:
|
||||
: [iv]"r"(&m_LastBlock), [sched]"r"(m_ECBEncryption.GetKeySchedule ()),
|
||||
: [iv]"r"((uint8_t *)m_LastBlock), [sched]"r"(m_ECBEncryption.GetKeySchedule ()),
|
||||
[in]"r"(in), [out]"r"(out), [num]"r"(numBlocks)
|
||||
: "%xmm0", "%xmm1", "cc", "memory"
|
||||
);
|
||||
#else
|
||||
for (int i = 0; i < numBlocks; i++)
|
||||
{
|
||||
m_LastBlock ^= in[i];
|
||||
m_ECBEncryption.Encrypt (&m_LastBlock, &m_LastBlock);
|
||||
out[i] = m_LastBlock;
|
||||
*m_LastBlock.GetChipherBlock () ^= in[i];
|
||||
m_ECBEncryption.Encrypt (m_LastBlock.GetChipherBlock (), m_LastBlock.GetChipherBlock ());
|
||||
out[i] = *m_LastBlock.GetChipherBlock ();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -640,7 +653,7 @@ namespace crypto
|
||||
"movups %%xmm0, (%[out]) \n"
|
||||
"movups %%xmm0, (%[iv]) \n"
|
||||
:
|
||||
: [iv]"r"(&m_LastBlock), [sched]"r"(m_ECBEncryption.GetKeySchedule ()),
|
||||
: [iv]"r"((uint8_t *)m_LastBlock), [sched]"r"(m_ECBEncryption.GetKeySchedule ()),
|
||||
[in]"r"(in), [out]"r"(out)
|
||||
: "%xmm0", "%xmm1", "memory"
|
||||
);
|
||||
@@ -668,7 +681,7 @@ namespace crypto
|
||||
"jnz 1b \n"
|
||||
"movups %%xmm1, (%[iv]) \n"
|
||||
:
|
||||
: [iv]"r"(&m_IV), [sched]"r"(m_ECBDecryption.GetKeySchedule ()),
|
||||
: [iv]"r"((uint8_t *)m_IV), [sched]"r"(m_ECBDecryption.GetKeySchedule ()),
|
||||
[in]"r"(in), [out]"r"(out), [num]"r"(numBlocks)
|
||||
: "%xmm0", "%xmm1", "%xmm2", "cc", "memory"
|
||||
);
|
||||
@@ -677,8 +690,8 @@ namespace crypto
|
||||
{
|
||||
ChipherBlock tmp = in[i];
|
||||
m_ECBDecryption.Decrypt (in + i, out + i);
|
||||
out[i] ^= m_IV;
|
||||
m_IV = tmp;
|
||||
out[i] ^= *m_IV.GetChipherBlock ();
|
||||
*m_IV.GetChipherBlock () = tmp;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -702,7 +715,7 @@ namespace crypto
|
||||
"pxor %%xmm1, %%xmm0 \n"
|
||||
"movups %%xmm0, (%[out]) \n"
|
||||
:
|
||||
: [iv]"r"(&m_IV), [sched]"r"(m_ECBDecryption.GetKeySchedule ()),
|
||||
: [iv]"r"((uint8_t *)m_IV), [sched]"r"(m_ECBDecryption.GetKeySchedule ()),
|
||||
[in]"r"(in), [out]"r"(out)
|
||||
: "%xmm0", "%xmm1", "memory"
|
||||
);
|
||||
|
||||
93
Crypto.h
93
Crypto.h
@@ -7,7 +7,10 @@
|
||||
#include <openssl/dh.h>
|
||||
#include <openssl/aes.h>
|
||||
#include <openssl/dsa.h>
|
||||
#include <openssl/ecdsa.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/rand.h>
|
||||
|
||||
#include "Base.h"
|
||||
@@ -33,15 +36,14 @@ namespace crypto
|
||||
DHKeys ();
|
||||
~DHKeys ();
|
||||
|
||||
void GenerateKeys (uint8_t * priv = nullptr, uint8_t * pub = nullptr);
|
||||
const uint8_t * GetPublicKey ();
|
||||
void GenerateKeys ();
|
||||
const uint8_t * GetPublicKey () const { return m_PublicKey; };
|
||||
void Agree (const uint8_t * pub, uint8_t * shared);
|
||||
|
||||
private:
|
||||
|
||||
DH * m_DH;
|
||||
uint8_t m_PublicKey[256];
|
||||
bool m_IsUpdated;
|
||||
};
|
||||
|
||||
// ElGamal
|
||||
@@ -52,7 +54,7 @@ namespace crypto
|
||||
ElGamalEncryption (const uint8_t * key);
|
||||
~ElGamalEncryption ();
|
||||
|
||||
void Encrypt (const uint8_t * data, int len, uint8_t * encrypted, bool zeroPadding = false) const;
|
||||
void Encrypt (const uint8_t * data, uint8_t * encrypted, bool zeroPadding = false) const;
|
||||
|
||||
private:
|
||||
|
||||
@@ -74,7 +76,18 @@ namespace crypto
|
||||
|
||||
void operator^=(const ChipherBlock& other) // XOR
|
||||
{
|
||||
#if defined(__x86_64__) || defined(__SSE__) // for Intel x84 or with SSE
|
||||
#if defined(__AVX__) // AVX
|
||||
__asm__
|
||||
(
|
||||
"vmovups (%[buf]), %%xmm0 \n"
|
||||
"vmovups (%[other]), %%xmm1 \n"
|
||||
"vxorps %%xmm0, %%xmm1, %%xmm0 \n"
|
||||
"vmovups %%xmm0, (%[buf]) \n"
|
||||
:
|
||||
: [buf]"r"(buf), [other]"r"(other.buf)
|
||||
: "%xmm0", "%xmm1", "memory"
|
||||
);
|
||||
#elif defined(__SSE__) // SSE
|
||||
__asm__
|
||||
(
|
||||
"movups (%[buf]), %%xmm0 \n"
|
||||
@@ -110,7 +123,9 @@ namespace crypto
|
||||
|
||||
operator uint8_t * () { return m_Buf; };
|
||||
operator const uint8_t * () const { return m_Buf; };
|
||||
|
||||
ChipherBlock * GetChipherBlock () { return (ChipherBlock *)m_Buf; };
|
||||
const ChipherBlock * GetChipherBlock () const { return (const ChipherBlock *)m_Buf; };
|
||||
|
||||
private:
|
||||
|
||||
uint8_t m_UnalignedBuffer[sz + 15]; // up to 15 bytes alignment
|
||||
@@ -198,10 +213,10 @@ namespace crypto
|
||||
{
|
||||
public:
|
||||
|
||||
CBCEncryption () { memset (m_LastBlock.buf, 0, 16); };
|
||||
CBCEncryption () { memset ((uint8_t *)m_LastBlock, 0, 16); };
|
||||
|
||||
void SetKey (const AESKey& key) { m_ECBEncryption.SetKey (key); }; // 32 bytes
|
||||
void SetIV (const uint8_t * iv) { memcpy (m_LastBlock.buf, iv, 16); }; // 16 bytes
|
||||
void SetIV (const uint8_t * iv) { memcpy ((uint8_t *)m_LastBlock, iv, 16); }; // 16 bytes
|
||||
|
||||
void Encrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out);
|
||||
void Encrypt (const uint8_t * in, std::size_t len, uint8_t * out);
|
||||
@@ -209,7 +224,7 @@ namespace crypto
|
||||
|
||||
private:
|
||||
|
||||
ChipherBlock m_LastBlock;
|
||||
AESAlignedBuffer<16> m_LastBlock;
|
||||
|
||||
ECBEncryption m_ECBEncryption;
|
||||
};
|
||||
@@ -218,10 +233,10 @@ namespace crypto
|
||||
{
|
||||
public:
|
||||
|
||||
CBCDecryption () { memset (m_IV.buf, 0, 16); };
|
||||
CBCDecryption () { memset ((uint8_t *)m_IV, 0, 16); };
|
||||
|
||||
void SetKey (const AESKey& key) { m_ECBDecryption.SetKey (key); }; // 32 bytes
|
||||
void SetIV (const uint8_t * iv) { memcpy (m_IV.buf, iv, 16); }; // 16 bytes
|
||||
void SetIV (const uint8_t * iv) { memcpy ((uint8_t *)m_IV, iv, 16); }; // 16 bytes
|
||||
|
||||
void Decrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out);
|
||||
void Decrypt (const uint8_t * in, std::size_t len, uint8_t * out);
|
||||
@@ -229,7 +244,7 @@ namespace crypto
|
||||
|
||||
private:
|
||||
|
||||
ChipherBlock m_IV;
|
||||
AESAlignedBuffer<16> m_IV;
|
||||
ECBDecryption m_ECBDecryption;
|
||||
};
|
||||
|
||||
@@ -279,17 +294,51 @@ namespace crypto
|
||||
|
||||
void InitCrypto (bool precomputation);
|
||||
void TerminateCrypto ();
|
||||
|
||||
// take care about openssl version
|
||||
#include <openssl/opensslv.h>
|
||||
#if (OPENSSL_VERSION_NUMBER < 0x010100000) // 1.1.0
|
||||
// define getters and setters introduced in 1.1.0
|
||||
inline int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g) { d->p = p; d->q = q; d->g = g; return 1; }
|
||||
inline int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key) { d->pub_key = pub_key; d->priv_key = priv_key; return 1; }
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// take care about openssl version
|
||||
#include <openssl/opensslv.h>
|
||||
#if (OPENSSL_VERSION_NUMBER < 0x010100000) || defined(LIBRESSL_VERSION_NUMBER) // 1.1.0 or LibreSSL
|
||||
// define getters and setters introduced in 1.1.0
|
||||
inline int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g)
|
||||
{ d->p = p; d->q = q; d->g = g; return 1; }
|
||||
inline int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key)
|
||||
{ d->pub_key = pub_key; d->priv_key = priv_key; return 1; }
|
||||
inline void DSA_get0_key(const DSA *d, const BIGNUM **pub_key, const BIGNUM **priv_key)
|
||||
{ *pub_key = d->pub_key; *priv_key = d->priv_key; }
|
||||
inline int DSA_SIG_set0(DSA_SIG *sig, BIGNUM *r, BIGNUM *s)
|
||||
{ sig->r = r; sig->s = s; return 1; }
|
||||
inline void DSA_SIG_get0(const DSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps)
|
||||
{ *pr = sig->r; *ps = sig->s; }
|
||||
|
||||
inline int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s)
|
||||
{
|
||||
if (sig->r) BN_free (sig->r);
|
||||
if (sig->s) BN_free (sig->s);
|
||||
sig->r = r; sig->s = s; return 1;
|
||||
}
|
||||
inline void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps)
|
||||
{ *pr = sig->r; *ps = sig->s; }
|
||||
|
||||
inline int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d)
|
||||
{ r->n = n; r->e = e; r->d = d; return 1; }
|
||||
inline void RSA_get0_key(const RSA *r, const BIGNUM **n, const BIGNUM **e, const BIGNUM **d)
|
||||
{ *n = r->n; *e = r->e; *d = r->d; }
|
||||
|
||||
inline int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g)
|
||||
{ dh->p = p; dh->q = q; dh->g = g; return 1; }
|
||||
inline int DH_set0_key(DH *dh, BIGNUM *pub_key, BIGNUM *priv_key)
|
||||
{
|
||||
if (dh->pub_key) BN_free (dh->pub_key);
|
||||
if (dh->priv_key) BN_free (dh->priv_key);
|
||||
dh->pub_key = pub_key; dh->priv_key = priv_key; return 1;
|
||||
}
|
||||
inline void DH_get0_key(const DH *dh, const BIGNUM **pub_key, const BIGNUM **priv_key)
|
||||
{ *pub_key = dh->pub_key; *priv_key = dh->priv_key; }
|
||||
|
||||
inline RSA *EVP_PKEY_get0_RSA(EVP_PKEY *pkey)
|
||||
{ return pkey->pkey.rsa; }
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
74
Daemon.cpp
74
Daemon.cpp
@@ -25,6 +25,9 @@
|
||||
#include "UPnP.h"
|
||||
#include "util.h"
|
||||
|
||||
#include "Event.h"
|
||||
#include "Websocket.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace util
|
||||
@@ -38,6 +41,9 @@ namespace i2p
|
||||
std::unique_ptr<i2p::http::HTTPServer> httpServer;
|
||||
std::unique_ptr<i2p::client::I2PControlService> m_I2PControlService;
|
||||
std::unique_ptr<i2p::transport::UPnP> UPnP;
|
||||
#ifdef WITH_EVENTS
|
||||
std::unique_ptr<i2p::event::WebsocketServer> m_WebsocketServer;
|
||||
#endif
|
||||
};
|
||||
|
||||
Daemon_Singleton::Daemon_Singleton() : isDaemon(false), running(true), d(*new Daemon_Singleton_Private()) {}
|
||||
@@ -107,7 +113,6 @@ namespace i2p
|
||||
} else {
|
||||
// use stdout -- default
|
||||
}
|
||||
i2p::log::Logger().Ready();
|
||||
|
||||
LogPrint(eLogInfo, "i2pd v", VERSION, " starting");
|
||||
LogPrint(eLogDebug, "FS: main config file: ", config);
|
||||
@@ -115,6 +120,9 @@ namespace i2p
|
||||
|
||||
bool precomputation; i2p::config::GetOption("precomputation.elgamal", precomputation);
|
||||
i2p::crypto::InitCrypto (precomputation);
|
||||
|
||||
int netID; i2p::config::GetOption("netid", netID);
|
||||
i2p::context.SetNetID (netID);
|
||||
i2p::context.Init ();
|
||||
|
||||
bool ipv6; i2p::config::GetOption("ipv6", ipv6);
|
||||
@@ -191,12 +199,40 @@ namespace i2p
|
||||
{
|
||||
LogPrint(eLogInfo, "Daemon: explicit trust enabled");
|
||||
std::string fam; i2p::config::GetOption("trust.family", fam);
|
||||
std::string routers; i2p::config::GetOption("trust.routers", routers);
|
||||
bool restricted = false;
|
||||
if (fam.length() > 0)
|
||||
{
|
||||
LogPrint(eLogInfo, "Daemon: setting restricted routes to use family ", fam);
|
||||
i2p::transport::transports.RestrictRoutes({fam});
|
||||
} else
|
||||
LogPrint(eLogError, "Daemon: no family specified for restricted routes");
|
||||
std::set<std::string> fams;
|
||||
size_t pos = 0, comma;
|
||||
do
|
||||
{
|
||||
comma = fam.find (',', pos);
|
||||
fams.insert (fam.substr (pos, comma != std::string::npos ? comma - pos : std::string::npos));
|
||||
pos = comma + 1;
|
||||
}
|
||||
while (comma != std::string::npos);
|
||||
i2p::transport::transports.RestrictRoutesToFamilies(fams);
|
||||
restricted = fams.size() > 0;
|
||||
}
|
||||
if (routers.length() > 0) {
|
||||
std::set<i2p::data::IdentHash> idents;
|
||||
size_t pos = 0, comma;
|
||||
do
|
||||
{
|
||||
comma = routers.find (',', pos);
|
||||
i2p::data::IdentHash ident;
|
||||
ident.FromBase64 (routers.substr (pos, comma != std::string::npos ? comma - pos : std::string::npos));
|
||||
idents.insert (ident);
|
||||
pos = comma + 1;
|
||||
}
|
||||
while (comma != std::string::npos);
|
||||
LogPrint(eLogInfo, "Daemon: setting restricted routes to use ", idents.size(), " trusted routesrs");
|
||||
i2p::transport::transports.RestrictRoutesToRouters(idents);
|
||||
restricted = idents.size() > 0;
|
||||
}
|
||||
if(!restricted)
|
||||
LogPrint(eLogError, "Daemon: no trusted routers of families specififed");
|
||||
}
|
||||
bool hidden; i2p::config::GetOption("trust.hidden", hidden);
|
||||
if (hidden)
|
||||
@@ -209,6 +245,7 @@ namespace i2p
|
||||
|
||||
bool Daemon_Singleton::start()
|
||||
{
|
||||
i2p::log::Logger().Start();
|
||||
LogPrint(eLogInfo, "Daemon: starting NetDB");
|
||||
i2p::data::netdb.Start();
|
||||
|
||||
@@ -259,12 +296,27 @@ namespace i2p
|
||||
d.m_I2PControlService = std::unique_ptr<i2p::client::I2PControlService>(new i2p::client::I2PControlService (i2pcpAddr, i2pcpPort));
|
||||
d.m_I2PControlService->Start ();
|
||||
}
|
||||
#ifdef WITH_EVENTS
|
||||
|
||||
bool websocket; i2p::config::GetOption("websockets.enabled", websocket);
|
||||
if(websocket) {
|
||||
std::string websocketAddr; i2p::config::GetOption("websockets.address", websocketAddr);
|
||||
uint16_t websocketPort; i2p::config::GetOption("websockets.port", websocketPort);
|
||||
LogPrint(eLogInfo, "Daemon: starting Websocket server at ", websocketAddr, ":", websocketPort);
|
||||
d.m_WebsocketServer = std::unique_ptr<i2p::event::WebsocketServer>(new i2p::event::WebsocketServer (websocketAddr, websocketPort));
|
||||
d.m_WebsocketServer->Start();
|
||||
i2p::event::core.SetListener(d.m_WebsocketServer->ToListener());
|
||||
}
|
||||
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Daemon_Singleton::stop()
|
||||
{
|
||||
#ifdef WITH_EVENTS
|
||||
i2p::event::core.SetListener(nullptr);
|
||||
#endif
|
||||
LogPrint(eLogInfo, "Daemon: shutting down");
|
||||
LogPrint(eLogInfo, "Daemon: stopping Client");
|
||||
i2p::client::context.Stop();
|
||||
@@ -290,10 +342,18 @@ namespace i2p
|
||||
LogPrint(eLogInfo, "Daemon: stopping I2PControl");
|
||||
d.m_I2PControlService->Stop ();
|
||||
d.m_I2PControlService = nullptr;
|
||||
}
|
||||
}
|
||||
#ifdef WITH_EVENTS
|
||||
if (d.m_WebsocketServer) {
|
||||
LogPrint(eLogInfo, "Daemon: stopping Websocket server");
|
||||
d.m_WebsocketServer->Stop();
|
||||
d.m_WebsocketServer = nullptr;
|
||||
}
|
||||
#endif
|
||||
i2p::crypto::TerminateCrypto ();
|
||||
i2p::log::Logger().Stop();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
2
Daemon.h
2
Daemon.h
@@ -97,7 +97,7 @@ namespace i2p
|
||||
|
||||
public:
|
||||
|
||||
int gracefullShutdownInterval; // in seconds
|
||||
int gracefulShutdownInterval; // in seconds
|
||||
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
#include "Config.h"
|
||||
#include "FS.h"
|
||||
@@ -25,11 +26,11 @@ void handle_signal(int sig)
|
||||
i2p::client::context.ReloadConfig();
|
||||
break;
|
||||
case SIGINT:
|
||||
if (i2p::context.AcceptsTunnels () && !Daemon.gracefullShutdownInterval)
|
||||
if (i2p::context.AcceptsTunnels () && !Daemon.gracefulShutdownInterval)
|
||||
{
|
||||
i2p::context.SetAcceptsTunnels (false);
|
||||
Daemon.gracefullShutdownInterval = 10*60; // 10 minutes
|
||||
LogPrint(eLogInfo, "Graceful shutdown after ", Daemon.gracefullShutdownInterval, " seconds");
|
||||
Daemon.gracefulShutdownInterval = 10*60; // 10 minutes
|
||||
LogPrint(eLogInfo, "Graceful shutdown after ", Daemon.gracefulShutdownInterval, " seconds");
|
||||
}
|
||||
else
|
||||
Daemon.running = 0;
|
||||
@@ -75,12 +76,50 @@ namespace i2p
|
||||
return false;
|
||||
}
|
||||
|
||||
#if !defined(__OpenBSD__)
|
||||
// point std{in,out,err} descriptors to /dev/null
|
||||
stdin = freopen("/dev/null", "r", stdin);
|
||||
stdout = freopen("/dev/null", "w", stdout);
|
||||
stderr = freopen("/dev/null", "w", stderr);
|
||||
#endif
|
||||
}
|
||||
|
||||
// set proc limits
|
||||
struct rlimit limit;
|
||||
uint16_t nfiles; i2p::config::GetOption("limits.openfiles", nfiles);
|
||||
getrlimit(RLIMIT_NOFILE, &limit);
|
||||
if (nfiles == 0) {
|
||||
LogPrint(eLogInfo, "Daemon: using system limit in ", limit.rlim_cur, " max open files");
|
||||
} else if (nfiles <= limit.rlim_max) {
|
||||
limit.rlim_cur = nfiles;
|
||||
if (setrlimit(RLIMIT_NOFILE, &limit) == 0) {
|
||||
LogPrint(eLogInfo, "Daemon: set max number of open files to ",
|
||||
nfiles, " (system limit is ", limit.rlim_max, ")");
|
||||
} else {
|
||||
LogPrint(eLogError, "Daemon: can't set max number of open files: ", strerror(errno));
|
||||
}
|
||||
} else {
|
||||
LogPrint(eLogError, "Daemon: limits.openfiles exceeds system limit: ", limit.rlim_max);
|
||||
}
|
||||
uint32_t cfsize; i2p::config::GetOption("limits.coresize", cfsize);
|
||||
if (cfsize) // core file size set
|
||||
{
|
||||
cfsize *= 1024;
|
||||
getrlimit(RLIMIT_CORE, &limit);
|
||||
if (cfsize <= limit.rlim_max) {
|
||||
limit.rlim_cur = cfsize;
|
||||
if (setrlimit(RLIMIT_CORE, &limit) != 0) {
|
||||
LogPrint(eLogError, "Daemon: can't set max size of coredump: ", strerror(errno));
|
||||
} else if (cfsize == 0) {
|
||||
LogPrint(eLogInfo, "Daemon: coredumps disabled");
|
||||
} else {
|
||||
LogPrint(eLogInfo, "Daemon: set max size of core files to ", cfsize / 1024, "Kb");
|
||||
}
|
||||
} else {
|
||||
LogPrint(eLogError, "Daemon: limits.coresize exceeds system limit: ", limit.rlim_max);
|
||||
}
|
||||
}
|
||||
|
||||
// Pidfile
|
||||
// this code is c-styled and a bit ugly, but we need fd for locking pidfile
|
||||
std::string pidfile; i2p::config::GetOption("pidfile", pidfile);
|
||||
@@ -108,7 +147,7 @@ namespace i2p
|
||||
return false;
|
||||
}
|
||||
}
|
||||
gracefullShutdownInterval = 0; // not specified
|
||||
gracefulShutdownInterval = 0; // not specified
|
||||
|
||||
// Signal handler
|
||||
struct sigaction sa;
|
||||
@@ -135,10 +174,10 @@ namespace i2p
|
||||
while (running)
|
||||
{
|
||||
std::this_thread::sleep_for (std::chrono::seconds(1));
|
||||
if (gracefullShutdownInterval)
|
||||
if (gracefulShutdownInterval)
|
||||
{
|
||||
gracefullShutdownInterval--; // - 1 second
|
||||
if (gracefullShutdownInterval <= 0)
|
||||
gracefulShutdownInterval--; // - 1 second
|
||||
if (gracefulShutdownInterval <= 0)
|
||||
{
|
||||
LogPrint(eLogInfo, "Graceful shutdown");
|
||||
return;
|
||||
|
||||
382
Datagram.cpp
382
Datagram.cpp
@@ -22,9 +22,9 @@ namespace datagram
|
||||
{
|
||||
m_Sessions.clear();
|
||||
}
|
||||
|
||||
void DatagramDestination::SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint16_t fromPort, uint16_t toPort)
|
||||
{
|
||||
|
||||
void DatagramDestination::SendDatagramTo(const uint8_t * payload, size_t len, const i2p::data::IdentHash & identity, uint16_t fromPort, uint16_t toPort)
|
||||
{
|
||||
auto owner = m_Owner;
|
||||
std::vector<uint8_t> v(MAX_DATAGRAM_SIZE);
|
||||
uint8_t * buf = v.data();
|
||||
@@ -45,8 +45,7 @@ namespace datagram
|
||||
owner->Sign (buf1, len, signature);
|
||||
|
||||
auto msg = CreateDataMessage (buf, len + headerLen, fromPort, toPort);
|
||||
auto session = ObtainSession(ident);
|
||||
session->SendMsg(msg);
|
||||
ObtainSession(identity)->SendMsg(msg);
|
||||
}
|
||||
|
||||
|
||||
@@ -69,6 +68,8 @@ namespace datagram
|
||||
|
||||
if (verified)
|
||||
{
|
||||
auto h = identity.GetIdentHash();
|
||||
ObtainSession(h)->Ack();
|
||||
auto r = FindReceiver(toPort);
|
||||
if(r)
|
||||
r(identity, fromPort, toPort, buf + headerLen, len -headerLen);
|
||||
@@ -138,15 +139,15 @@ namespace datagram
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<DatagramSession> DatagramDestination::ObtainSession(const i2p::data::IdentHash & ident)
|
||||
std::shared_ptr<DatagramSession> DatagramDestination::ObtainSession(const i2p::data::IdentHash & identity)
|
||||
{
|
||||
std::shared_ptr<DatagramSession> session = nullptr;
|
||||
std::lock_guard<std::mutex> lock(m_SessionsMutex);
|
||||
auto itr = m_Sessions.find(ident);
|
||||
auto itr = m_Sessions.find(identity);
|
||||
if (itr == m_Sessions.end()) {
|
||||
// not found, create new session
|
||||
session = std::make_shared<DatagramSession>(m_Owner, ident);
|
||||
m_Sessions[ident] = session;
|
||||
session = std::make_shared<DatagramSession>(m_Owner, identity);
|
||||
m_Sessions[identity] = session;
|
||||
} else {
|
||||
session = itr->second;
|
||||
}
|
||||
@@ -164,13 +165,13 @@ namespace datagram
|
||||
}
|
||||
|
||||
DatagramSession::DatagramSession(i2p::client::ClientDestination * localDestination,
|
||||
const i2p::data::IdentHash & remoteIdent) :
|
||||
const i2p::data::IdentHash & remoteIdent) :
|
||||
m_LocalDestination(localDestination),
|
||||
m_RemoteIdentity(remoteIdent),
|
||||
m_LastUse(i2p::util::GetMillisecondsSinceEpoch ()),
|
||||
m_LastPathChange(0),
|
||||
m_LastSuccess(0)
|
||||
m_RemoteIdent(remoteIdent),
|
||||
m_SendQueueTimer(localDestination->GetService())
|
||||
{
|
||||
m_LastUse = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
ScheduleFlushSendQueue();
|
||||
}
|
||||
|
||||
void DatagramSession::SendMsg(std::shared_ptr<I2NPMessage> msg)
|
||||
@@ -184,262 +185,149 @@ namespace datagram
|
||||
DatagramSession::Info DatagramSession::GetSessionInfo() const
|
||||
{
|
||||
if(!m_RoutingSession)
|
||||
return DatagramSession::Info(nullptr, nullptr, m_LastUse, m_LastSuccess);
|
||||
return DatagramSession::Info(nullptr, nullptr, m_LastUse);
|
||||
|
||||
auto routingPath = m_RoutingSession->GetSharedRoutingPath();
|
||||
if (!routingPath)
|
||||
return DatagramSession::Info(nullptr, nullptr, m_LastUse, m_LastSuccess);
|
||||
return DatagramSession::Info(nullptr, nullptr, m_LastUse);
|
||||
auto lease = routingPath->remoteLease;
|
||||
auto tunnel = routingPath->outboundTunnel;
|
||||
if(lease)
|
||||
{
|
||||
if(tunnel)
|
||||
return DatagramSession::Info(lease->tunnelGateway, tunnel->GetEndpointIdentHash(), m_LastUse, m_LastSuccess);
|
||||
return DatagramSession::Info(lease->tunnelGateway, tunnel->GetEndpointIdentHash(), m_LastUse);
|
||||
else
|
||||
return DatagramSession::Info(lease->tunnelGateway, nullptr, m_LastUse, m_LastSuccess);
|
||||
return DatagramSession::Info(lease->tunnelGateway, nullptr, m_LastUse);
|
||||
}
|
||||
else if(tunnel)
|
||||
return DatagramSession::Info(nullptr, tunnel->GetEndpointIdentHash(), m_LastUse, m_LastSuccess);
|
||||
return DatagramSession::Info(nullptr, tunnel->GetEndpointIdentHash(), m_LastUse);
|
||||
else
|
||||
return DatagramSession::Info(nullptr, nullptr, m_LastUse, m_LastSuccess);
|
||||
return DatagramSession::Info(nullptr, nullptr, m_LastUse);
|
||||
}
|
||||
|
||||
void DatagramSession::Ack()
|
||||
{
|
||||
m_LastUse = i2p::util::GetMillisecondsSinceEpoch();
|
||||
auto path = GetSharedRoutingPath();
|
||||
if(path)
|
||||
path->updateTime = i2p::util::GetSecondsSinceEpoch ();
|
||||
}
|
||||
|
||||
std::shared_ptr<i2p::garlic::GarlicRoutingPath> DatagramSession::GetSharedRoutingPath ()
|
||||
{
|
||||
if(!m_RoutingSession) {
|
||||
if(!m_RemoteLeaseSet) {
|
||||
m_RemoteLeaseSet = m_LocalDestination->FindLeaseSet(m_RemoteIdent);
|
||||
}
|
||||
if(!m_RemoteLeaseSet) {
|
||||
// no remote lease set
|
||||
m_LocalDestination->RequestDestination(m_RemoteIdent, std::bind(&DatagramSession::HandleLeaseSetUpdated, this, std::placeholders::_1));
|
||||
return nullptr;
|
||||
}
|
||||
m_RoutingSession = m_LocalDestination->GetRoutingSession(m_RemoteLeaseSet, true);
|
||||
}
|
||||
auto path = m_RoutingSession->GetSharedRoutingPath();
|
||||
if(path) {
|
||||
if (m_CurrentOutboundTunnel && !m_CurrentOutboundTunnel->IsEstablished()) {
|
||||
// bad outbound tunnel, switch outbound tunnel
|
||||
m_CurrentOutboundTunnel = m_LocalDestination->GetTunnelPool()->GetNextOutboundTunnel(m_CurrentOutboundTunnel);
|
||||
path->outboundTunnel = m_CurrentOutboundTunnel;
|
||||
}
|
||||
if(m_CurrentRemoteLease && ! m_CurrentRemoteLease->ExpiresWithin(DATAGRAM_SESSION_LEASE_HANDOVER_WINDOW)) {
|
||||
// bad lease, switch to next one
|
||||
if(m_RemoteLeaseSet) {
|
||||
auto ls = m_RemoteLeaseSet->GetNonExpiredLeasesExcluding([&](const i2p::data::Lease& l) -> bool {
|
||||
return l.tunnelGateway == m_CurrentRemoteLease->tunnelGateway || l.endDate <= m_CurrentRemoteLease->endDate;
|
||||
});
|
||||
auto sz = ls.size();
|
||||
if (sz) {
|
||||
auto idx = rand() % sz;
|
||||
m_CurrentRemoteLease = ls[idx];
|
||||
}
|
||||
} else {
|
||||
// no remote lease set?
|
||||
LogPrint(eLogWarning, "DatagramSession: no cached remote lease set for ", m_RemoteIdent.ToBase32());
|
||||
}
|
||||
path->remoteLease = m_CurrentRemoteLease;
|
||||
}
|
||||
} else {
|
||||
// no current path, make one
|
||||
path = std::make_shared<i2p::garlic::GarlicRoutingPath>();
|
||||
// switch outbound tunnel if bad
|
||||
if(m_CurrentOutboundTunnel == nullptr || ! m_CurrentOutboundTunnel->IsEstablished()) {
|
||||
m_CurrentOutboundTunnel = m_LocalDestination->GetTunnelPool()->GetNextOutboundTunnel(m_CurrentOutboundTunnel);
|
||||
}
|
||||
// switch lease if bad
|
||||
if(m_CurrentRemoteLease == nullptr || m_CurrentRemoteLease->ExpiresWithin(DATAGRAM_SESSION_LEASE_HANDOVER_WINDOW)) {
|
||||
if(!m_RemoteLeaseSet) {
|
||||
m_RemoteLeaseSet = m_LocalDestination->FindLeaseSet(m_RemoteIdent);
|
||||
}
|
||||
if(m_RemoteLeaseSet) {
|
||||
// pick random next good lease
|
||||
auto ls = m_RemoteLeaseSet->GetNonExpiredLeasesExcluding([&] (const i2p::data::Lease & l) -> bool {
|
||||
if(m_CurrentRemoteLease)
|
||||
return l.tunnelGateway == m_CurrentRemoteLease->tunnelGateway;
|
||||
return false;
|
||||
});
|
||||
auto sz = ls.size();
|
||||
if(sz) {
|
||||
auto idx = rand() % sz;
|
||||
m_CurrentRemoteLease = ls[idx];
|
||||
}
|
||||
} else {
|
||||
// no remote lease set currently, bail
|
||||
LogPrint(eLogWarning, "DatagramSession: no remote lease set found for ", m_RemoteIdent.ToBase32());
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
path->outboundTunnel = m_CurrentOutboundTunnel;
|
||||
path->remoteLease = m_CurrentRemoteLease;
|
||||
m_RoutingSession->SetSharedRoutingPath(path);
|
||||
}
|
||||
return path;
|
||||
|
||||
}
|
||||
|
||||
void DatagramSession::HandleLeaseSetUpdated(std::shared_ptr<i2p::data::LeaseSet> ls)
|
||||
{
|
||||
if(!ls) return;
|
||||
// only update lease set if found and newer than previous lease set
|
||||
uint64_t oldExpire = 0;
|
||||
if(m_RemoteLeaseSet) oldExpire = m_RemoteLeaseSet->GetExpirationTime();
|
||||
if(ls && ls->GetExpirationTime() > oldExpire) m_RemoteLeaseSet = ls;
|
||||
}
|
||||
|
||||
void DatagramSession::HandleSend(std::shared_ptr<I2NPMessage> msg)
|
||||
{
|
||||
if(!m_RoutingSession)
|
||||
m_SendQueue.push_back(msg);
|
||||
// flush queue right away if full
|
||||
if(m_SendQueue.size() >= DATAGRAM_SEND_QUEUE_MAX_SIZE) FlushSendQueue();
|
||||
}
|
||||
|
||||
void DatagramSession::FlushSendQueue ()
|
||||
{
|
||||
|
||||
std::vector<i2p::tunnel::TunnelMessageBlock> send;
|
||||
auto routingPath = GetSharedRoutingPath();
|
||||
// if we don't have a routing path we will drop all queued messages
|
||||
if(routingPath && routingPath->outboundTunnel && routingPath->remoteLease)
|
||||
{
|
||||
// try to get one
|
||||
if(m_RemoteLeaseSet) m_RoutingSession = m_LocalDestination->GetRoutingSession(m_RemoteLeaseSet, true);
|
||||
else
|
||||
for (const auto & msg : m_SendQueue)
|
||||
{
|
||||
UpdateLeaseSet(msg);
|
||||
return;
|
||||
auto m = m_RoutingSession->WrapSingleMessage(msg);
|
||||
send.push_back(i2p::tunnel::TunnelMessageBlock{i2p::tunnel::eDeliveryTypeTunnel,routingPath->remoteLease->tunnelGateway, routingPath->remoteLease->tunnelID, m});
|
||||
}
|
||||
routingPath->outboundTunnel->SendTunnelDataMsg(send);
|
||||
}
|
||||
// do we have a routing session?
|
||||
if(m_RoutingSession)
|
||||
{
|
||||
// should we switch paths?
|
||||
if(ShouldUpdateRoutingPath ())
|
||||
{
|
||||
LogPrint(eLogDebug, "DatagramSession: try getting new routing path");
|
||||
// try switching paths
|
||||
auto path = GetNextRoutingPath();
|
||||
if(path)
|
||||
UpdateRoutingPath (path);
|
||||
else
|
||||
ResetRoutingPath();
|
||||
}
|
||||
auto routingPath = m_RoutingSession->GetSharedRoutingPath ();
|
||||
// make sure we have a routing path
|
||||
if (routingPath)
|
||||
{
|
||||
auto outboundTunnel = routingPath->outboundTunnel;
|
||||
if (outboundTunnel)
|
||||
{
|
||||
if(outboundTunnel->IsEstablished())
|
||||
{
|
||||
m_LastSuccess = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
// we have a routing path and routing session and the outbound tunnel we are using is good
|
||||
// wrap message with routing session and send down routing path's outbound tunnel wrapped for the IBGW
|
||||
auto m = m_RoutingSession->WrapSingleMessage(msg);
|
||||
routingPath->outboundTunnel->SendTunnelDataMsg({i2p::tunnel::TunnelMessageBlock{
|
||||
i2p::tunnel::eDeliveryTypeTunnel,
|
||||
routingPath->remoteLease->tunnelGateway, routingPath->remoteLease->tunnelID,
|
||||
m
|
||||
}});
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
auto now = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
// if this path looks dead reset the routing path since we didn't seem to be able to get a path in time
|
||||
if (m_LastPathChange && now - m_LastPathChange >= DATAGRAM_SESSION_PATH_TIMEOUT ) ResetRoutingPath();
|
||||
UpdateLeaseSet(msg);
|
||||
|
||||
m_SendQueue.clear();
|
||||
ScheduleFlushSendQueue();
|
||||
}
|
||||
|
||||
void DatagramSession::UpdateRoutingPath(const std::shared_ptr<i2p::garlic::GarlicRoutingPath> & path)
|
||||
void DatagramSession::ScheduleFlushSendQueue()
|
||||
{
|
||||
if(m_RoutingSession == nullptr && m_RemoteLeaseSet)
|
||||
m_RoutingSession = m_LocalDestination->GetRoutingSession(m_RemoteLeaseSet, true);
|
||||
if(!m_RoutingSession) return;
|
||||
// set routing path and update time we last updated the routing path
|
||||
m_RoutingSession->SetSharedRoutingPath (path);
|
||||
m_LastPathChange = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
}
|
||||
|
||||
bool DatagramSession::ShouldUpdateRoutingPath() const
|
||||
{
|
||||
bool dead = m_RoutingSession == nullptr || m_RoutingSession->GetSharedRoutingPath () == nullptr;
|
||||
auto now = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
// we need to rotate paths becuase the routing path is too old
|
||||
// if (now - m_LastPathChange >= DATAGRAM_SESSION_PATH_SWITCH_INTERVAL) return true;
|
||||
// too fast switching paths
|
||||
if (now - m_LastPathChange < DATAGRAM_SESSION_PATH_MIN_LIFETIME ) return false;
|
||||
// our path looks dead so we need to rotate paths
|
||||
if (now - m_LastSuccess >= DATAGRAM_SESSION_PATH_TIMEOUT) return !dead;
|
||||
// if we have a routing session and routing path we don't need to switch paths
|
||||
return dead;
|
||||
}
|
||||
|
||||
|
||||
bool DatagramSession::ShouldSwitchLease() const
|
||||
{
|
||||
std::shared_ptr<i2p::garlic::GarlicRoutingPath> routingPath = nullptr;
|
||||
std::shared_ptr<const i2p::data::Lease> currentLease = nullptr;
|
||||
if(m_RoutingSession)
|
||||
routingPath = m_RoutingSession->GetSharedRoutingPath ();
|
||||
if(routingPath)
|
||||
currentLease = routingPath->remoteLease;
|
||||
if(currentLease) // if we have a lease return true if it's about to expire otherwise return false
|
||||
return currentLease->ExpiresWithin( DATAGRAM_SESSION_LEASE_HANDOVER_WINDOW, DATAGRAM_SESSION_LEASE_HANDOVER_FUDGE );
|
||||
// we have no current lease, we should switch
|
||||
return currentLease == nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<i2p::garlic::GarlicRoutingPath> DatagramSession::GetNextRoutingPath()
|
||||
{
|
||||
std::shared_ptr<i2p::tunnel::OutboundTunnel> outboundTunnel = nullptr;
|
||||
std::shared_ptr<i2p::garlic::GarlicRoutingPath> routingPath = nullptr;
|
||||
// get existing routing path if we have one
|
||||
if(m_RoutingSession)
|
||||
routingPath = m_RoutingSession->GetSharedRoutingPath();
|
||||
// do we have an existing outbound tunnel and routing path?
|
||||
if(routingPath && routingPath->outboundTunnel)
|
||||
{
|
||||
// is the outbound tunnel we are using good?
|
||||
if (routingPath->outboundTunnel->IsEstablished())
|
||||
{
|
||||
// ya so let's stick with it
|
||||
outboundTunnel = routingPath->outboundTunnel;
|
||||
}
|
||||
else
|
||||
outboundTunnel = m_LocalDestination->GetTunnelPool()->GetNextOutboundTunnel(routingPath->outboundTunnel); // no so we'll switch outbound tunnels
|
||||
}
|
||||
// do we have an outbound tunnel that works already ?
|
||||
if(!outboundTunnel)
|
||||
outboundTunnel = m_LocalDestination->GetTunnelPool()->GetNextOutboundTunnel(); // no, let's get a new outbound tunnel as we probably just started
|
||||
|
||||
if(outboundTunnel)
|
||||
{
|
||||
std::shared_ptr<const i2p::data::Lease> lease = nullptr;
|
||||
// should we switch leases ?
|
||||
if (ShouldSwitchLease ())
|
||||
{
|
||||
// yes, get next available lease
|
||||
lease = GetNextLease();
|
||||
}
|
||||
else if (routingPath)
|
||||
{
|
||||
if(routingPath->remoteLease)
|
||||
{
|
||||
if(routingPath->remoteLease->ExpiresWithin(DATAGRAM_SESSION_LEASE_HANDOVER_WINDOW, DATAGRAM_SESSION_LEASE_HANDOVER_FUDGE))
|
||||
lease = GetNextLease();
|
||||
else
|
||||
lease = routingPath->remoteLease;
|
||||
}
|
||||
}
|
||||
else
|
||||
lease = GetNextLease();
|
||||
if(lease)
|
||||
{
|
||||
// we have a valid lease to use and an outbound tunnel
|
||||
// create new routing path
|
||||
uint32_t now = i2p::util::GetSecondsSinceEpoch();
|
||||
routingPath = std::make_shared<i2p::garlic::GarlicRoutingPath>(i2p::garlic::GarlicRoutingPath{
|
||||
outboundTunnel,
|
||||
lease,
|
||||
0,
|
||||
now,
|
||||
0
|
||||
});
|
||||
}
|
||||
else // we don't have a new routing path to give
|
||||
routingPath = nullptr;
|
||||
}
|
||||
return routingPath;
|
||||
}
|
||||
|
||||
void DatagramSession::ResetRoutingPath()
|
||||
{
|
||||
if(m_RoutingSession)
|
||||
{
|
||||
auto routingPath = m_RoutingSession->GetSharedRoutingPath();
|
||||
if(routingPath && routingPath->remoteLease) // we have a remote lease already specified and a routing path
|
||||
{
|
||||
// get outbound tunnel on this path
|
||||
auto outboundTunnel = routingPath->outboundTunnel;
|
||||
// is this outbound tunnel there and established
|
||||
if (outboundTunnel && outboundTunnel->IsEstablished())
|
||||
m_InvalidIBGW.push_back(routingPath->remoteLease->tunnelGateway); // yes, let's mark remote lease as dead because the outbound tunnel seems fine
|
||||
}
|
||||
// reset the routing path
|
||||
UpdateRoutingPath(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<const i2p::data::Lease> DatagramSession::GetNextLease()
|
||||
{
|
||||
auto now = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
std::shared_ptr<const i2p::data::Lease> next = nullptr;
|
||||
if(m_RemoteLeaseSet)
|
||||
{
|
||||
std::vector<i2p::data::IdentHash> exclude;
|
||||
for(const auto & ident : m_InvalidIBGW)
|
||||
exclude.push_back(ident);
|
||||
// find get all leases that are not in our ban list and are not going to expire within our lease set handover window + fudge
|
||||
auto leases = m_RemoteLeaseSet->GetNonExpiredLeasesExcluding( [&exclude, now] (const i2p::data::Lease & l) -> bool {
|
||||
if(exclude.size())
|
||||
{
|
||||
auto end = std::end(exclude);
|
||||
return std::find_if(exclude.begin(), end, [l, now] ( const i2p::data::IdentHash & ident) -> bool {
|
||||
return ident == l.tunnelGateway;
|
||||
}) != end;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
});
|
||||
if(leases.size())
|
||||
{
|
||||
// pick random valid next lease
|
||||
uint32_t idx = rand() % leases.size();
|
||||
next = leases[idx];
|
||||
}
|
||||
else
|
||||
LogPrint(eLogWarning, "DatagramDestination: no leases to use");
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
void DatagramSession::UpdateLeaseSet(std::shared_ptr<I2NPMessage> msg)
|
||||
{
|
||||
LogPrint(eLogInfo, "DatagramSession: updating lease set");
|
||||
m_LocalDestination->RequestDestination(m_RemoteIdentity, std::bind(&DatagramSession::HandleGotLeaseSet, this, std::placeholders::_1, msg));
|
||||
}
|
||||
|
||||
void DatagramSession::HandleGotLeaseSet(std::shared_ptr<const i2p::data::LeaseSet> remoteIdent, std::shared_ptr<I2NPMessage> msg)
|
||||
{
|
||||
if(remoteIdent)
|
||||
{
|
||||
// update routing session
|
||||
if(m_RoutingSession)
|
||||
m_RoutingSession = nullptr;
|
||||
m_RoutingSession = m_LocalDestination->GetRoutingSession(remoteIdent, true);
|
||||
// clear invalid IBGW as we have a new lease set
|
||||
m_InvalidIBGW.clear();
|
||||
m_RemoteLeaseSet = remoteIdent;
|
||||
// update routing path
|
||||
auto path = GetNextRoutingPath();
|
||||
if (path)
|
||||
UpdateRoutingPath(path);
|
||||
else
|
||||
ResetRoutingPath();
|
||||
// send the message that was queued if it was provided
|
||||
if(msg)
|
||||
HandleSend(msg);
|
||||
}
|
||||
boost::posix_time::milliseconds dlt(100);
|
||||
m_SendQueueTimer.expires_from_now(dlt);
|
||||
m_SendQueueTimer.async_wait([&](const boost::system::error_code & ec) { if(ec) return; FlushSendQueue(); });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
72
Datagram.h
72
Datagram.h
@@ -31,29 +31,33 @@ namespace datagram
|
||||
const uint64_t DATAGRAM_SESSION_LEASE_HANDOVER_FUDGE = 1000;
|
||||
// milliseconds minimum time between path switches
|
||||
const uint64_t DATAGRAM_SESSION_PATH_MIN_LIFETIME = 5 * 1000;
|
||||
// max 64 messages buffered in send queue for each datagram session
|
||||
const size_t DATAGRAM_SEND_QUEUE_MAX_SIZE = 64;
|
||||
|
||||
class DatagramSession
|
||||
{
|
||||
public:
|
||||
DatagramSession(i2p::client::ClientDestination * localDestination,
|
||||
const i2p::data::IdentHash & remoteIdent);
|
||||
const i2p::data::IdentHash & remoteIdent);
|
||||
|
||||
|
||||
/** @brief ack the garlic routing path */
|
||||
void Ack();
|
||||
|
||||
/** send an i2np message to remote endpoint for this session */
|
||||
void SendMsg(std::shared_ptr<I2NPMessage> msg);
|
||||
/** get the last time in milliseconds for when we used this datagram session */
|
||||
uint64_t LastActivity() const { return m_LastUse; }
|
||||
/** get the last time in milliseconds when we successfully sent data */
|
||||
uint64_t LastSuccess() const { return m_LastSuccess; }
|
||||
|
||||
struct Info
|
||||
{
|
||||
std::shared_ptr<const i2p::data::IdentHash> IBGW;
|
||||
std::shared_ptr<const i2p::data::IdentHash> OBEP;
|
||||
const uint64_t activity;
|
||||
const uint64_t success;
|
||||
Info() : IBGW(nullptr), OBEP(nullptr), activity(0), success(0) {}
|
||||
Info(const uint8_t * ibgw, const uint8_t * obep, const uint64_t a, const uint64_t s) :
|
||||
activity(a),
|
||||
success(s) {
|
||||
|
||||
Info() : IBGW(nullptr), OBEP(nullptr), activity(0) {}
|
||||
Info(const uint8_t * ibgw, const uint8_t * obep, const uint64_t a) :
|
||||
activity(a) {
|
||||
if(ibgw) IBGW = std::make_shared<i2p::data::IdentHash>(ibgw);
|
||||
else IBGW = nullptr;
|
||||
if(obep) OBEP = std::make_shared<i2p::data::IdentHash>(obep);
|
||||
@@ -63,44 +67,28 @@ namespace datagram
|
||||
|
||||
Info GetSessionInfo() const;
|
||||
|
||||
|
||||
private:
|
||||
|
||||
/** update our routing path we are using, mark that we have changed paths */
|
||||
void UpdateRoutingPath(const std::shared_ptr<i2p::garlic::GarlicRoutingPath> & path);
|
||||
void FlushSendQueue();
|
||||
void ScheduleFlushSendQueue();
|
||||
|
||||
/** return true if we should switch routing paths because of path lifetime or timeout otherwise false */
|
||||
bool ShouldUpdateRoutingPath() const;
|
||||
void HandleSend(std::shared_ptr<I2NPMessage> msg);
|
||||
|
||||
/** return true if we should switch the lease for out routing path otherwise return false */
|
||||
bool ShouldSwitchLease() const;
|
||||
|
||||
/** get next usable routing path, try reusing outbound tunnels */
|
||||
std::shared_ptr<i2p::garlic::GarlicRoutingPath> GetNextRoutingPath();
|
||||
/**
|
||||
* mark current routing path as invalid and clear it
|
||||
* if the outbound tunnel we were using was okay don't use the IBGW in the routing path's lease next time
|
||||
*/
|
||||
void ResetRoutingPath();
|
||||
std::shared_ptr<i2p::garlic::GarlicRoutingPath> GetSharedRoutingPath();
|
||||
|
||||
void HandleLeaseSetUpdated(std::shared_ptr<i2p::data::LeaseSet> ls);
|
||||
|
||||
/** get next usable lease, does not fetch or update if expired or have no lease set */
|
||||
std::shared_ptr<const i2p::data::Lease> GetNextLease();
|
||||
|
||||
void HandleSend(std::shared_ptr<I2NPMessage> msg);
|
||||
void HandleGotLeaseSet(std::shared_ptr<const i2p::data::LeaseSet> remoteIdent,
|
||||
std::shared_ptr<I2NPMessage> msg);
|
||||
void UpdateLeaseSet(std::shared_ptr<I2NPMessage> msg=nullptr);
|
||||
|
||||
private:
|
||||
i2p::client::ClientDestination * m_LocalDestination;
|
||||
i2p::data::IdentHash m_RemoteIdentity;
|
||||
std::shared_ptr<i2p::garlic::GarlicRoutingSession> m_RoutingSession;
|
||||
// Ident hash of IBGW that are invalid
|
||||
std::vector<i2p::data::IdentHash> m_InvalidIBGW;
|
||||
std::shared_ptr<const i2p::data::LeaseSet> m_RemoteLeaseSet;
|
||||
uint64_t m_LastUse;
|
||||
uint64_t m_LastPathChange;
|
||||
uint64_t m_LastSuccess;
|
||||
i2p::data::IdentHash m_RemoteIdent;
|
||||
std::shared_ptr<const i2p::data::LeaseSet> m_RemoteLeaseSet;
|
||||
std::shared_ptr<i2p::garlic::GarlicRoutingSession> m_RoutingSession;
|
||||
std::shared_ptr<const i2p::data::Lease> m_CurrentRemoteLease;
|
||||
std::shared_ptr<i2p::tunnel::OutboundTunnel> m_CurrentOutboundTunnel;
|
||||
boost::asio::deadline_timer m_SendQueueTimer;
|
||||
std::vector<std::shared_ptr<I2NPMessage> > m_SendQueue;
|
||||
uint64_t m_LastUse;
|
||||
|
||||
};
|
||||
|
||||
const size_t MAX_DATAGRAM_SIZE = 32768;
|
||||
@@ -112,9 +100,9 @@ namespace datagram
|
||||
|
||||
|
||||
DatagramDestination (std::shared_ptr<i2p::client::ClientDestination> owner);
|
||||
~DatagramDestination ();
|
||||
~DatagramDestination ();
|
||||
|
||||
void SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint16_t fromPort = 0, uint16_t toPort = 0);
|
||||
void SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash & ident, uint16_t fromPort = 0, uint16_t toPort = 0);
|
||||
void HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
|
||||
|
||||
void SetReceiver (const Receiver& receiver) { m_Receiver = receiver; };
|
||||
@@ -130,7 +118,7 @@ namespace datagram
|
||||
|
||||
private:
|
||||
|
||||
std::shared_ptr<DatagramSession> ObtainSession(const i2p::data::IdentHash & ident);
|
||||
std::shared_ptr<DatagramSession> ObtainSession(const i2p::data::IdentHash & ident);
|
||||
|
||||
std::shared_ptr<I2NPMessage> CreateDataMessage (const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort);
|
||||
|
||||
|
||||
272
Destination.cpp
272
Destination.cpp
@@ -1,6 +1,5 @@
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include "Crypto.h"
|
||||
#include "Log.h"
|
||||
#include "FS.h"
|
||||
@@ -14,100 +13,82 @@ namespace i2p
|
||||
namespace client
|
||||
{
|
||||
LeaseSetDestination::LeaseSetDestination (bool isPublic, const std::map<std::string, std::string> * params):
|
||||
m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service), m_IsPublic (isPublic),
|
||||
m_IsRunning (false), m_Thread (nullptr), m_IsPublic (isPublic),
|
||||
m_PublishReplyToken (0), m_PublishConfirmationTimer (m_Service),
|
||||
m_PublishVerificationTimer (m_Service), m_CleanupTimer (m_Service)
|
||||
{
|
||||
int inboundTunnelLen = DEFAULT_INBOUND_TUNNEL_LENGTH;
|
||||
int outboundTunnelLen = DEFAULT_OUTBOUND_TUNNEL_LENGTH;
|
||||
int inboundTunnelsQuantity = DEFAULT_INBOUND_TUNNELS_QUANTITY;
|
||||
int outboundTunnelsQuantity = DEFAULT_OUTBOUND_TUNNELS_QUANTITY;
|
||||
int inLen = DEFAULT_INBOUND_TUNNEL_LENGTH;
|
||||
int inQty = DEFAULT_INBOUND_TUNNELS_QUANTITY;
|
||||
int outLen = DEFAULT_OUTBOUND_TUNNEL_LENGTH;
|
||||
int outQty = DEFAULT_OUTBOUND_TUNNELS_QUANTITY;
|
||||
int numTags = DEFAULT_TAGS_TO_SEND;
|
||||
std::shared_ptr<std::vector<i2p::data::IdentHash> > explicitPeers;
|
||||
if (params)
|
||||
{
|
||||
auto it = params->find (I2CP_PARAM_INBOUND_TUNNEL_LENGTH);
|
||||
if (it != params->end ())
|
||||
{
|
||||
|
||||
int len = i2p::util::lexical_cast<int>(it->second, inboundTunnelLen);
|
||||
if (len >= 0)
|
||||
try {
|
||||
if (params) {
|
||||
auto it = params->find (I2CP_PARAM_INBOUND_TUNNEL_LENGTH);
|
||||
if (it != params->end ())
|
||||
inLen = std::stoi(it->second);
|
||||
it = params->find (I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH);
|
||||
if (it != params->end ())
|
||||
outLen = std::stoi(it->second);
|
||||
it = params->find (I2CP_PARAM_INBOUND_TUNNELS_QUANTITY);
|
||||
if (it != params->end ())
|
||||
inQty = std::stoi(it->second);
|
||||
it = params->find (I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY);
|
||||
if (it != params->end ())
|
||||
outQty = std::stoi(it->second);
|
||||
it = params->find (I2CP_PARAM_TAGS_TO_SEND);
|
||||
if (it != params->end ())
|
||||
numTags = std::stoi(it->second);
|
||||
LogPrint (eLogInfo, "Destination: parameters for tunnel set to: ", inQty, " inbound (", inLen, " hops), ", outQty, " outbound (", outLen, " hops), ", numTags, " tags");
|
||||
it = params->find (I2CP_PARAM_EXPLICIT_PEERS);
|
||||
if (it != params->end ())
|
||||
{
|
||||
inboundTunnelLen = len;
|
||||
explicitPeers = std::make_shared<std::vector<i2p::data::IdentHash> >();
|
||||
std::stringstream ss(it->second);
|
||||
std::string b64;
|
||||
while (std::getline (ss, b64, ','))
|
||||
{
|
||||
i2p::data::IdentHash ident;
|
||||
ident.FromBase64 (b64);
|
||||
explicitPeers->push_back (ident);
|
||||
LogPrint (eLogInfo, "Destination: Added to explicit peers list: ", b64);
|
||||
}
|
||||
}
|
||||
LogPrint (eLogInfo, "Destination: Inbound tunnel length set to ", inboundTunnelLen);
|
||||
}
|
||||
it = params->find (I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH);
|
||||
if (it != params->end ())
|
||||
{
|
||||
|
||||
int len = i2p::util::lexical_cast<int>(it->second, outboundTunnelLen);
|
||||
if (len >= 0)
|
||||
{
|
||||
outboundTunnelLen = len;
|
||||
}
|
||||
LogPrint (eLogInfo, "Destination: Outbound tunnel length set to ", outboundTunnelLen);
|
||||
}
|
||||
it = params->find (I2CP_PARAM_INBOUND_TUNNELS_QUANTITY);
|
||||
if (it != params->end ())
|
||||
{
|
||||
int quantity = i2p::util::lexical_cast<int>(it->second, inboundTunnelsQuantity);
|
||||
if (quantity > 0)
|
||||
{
|
||||
inboundTunnelsQuantity = quantity;
|
||||
LogPrint (eLogInfo, "Destination: Inbound tunnels quantity set to ", quantity);
|
||||
}
|
||||
}
|
||||
it = params->find (I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY);
|
||||
if (it != params->end ())
|
||||
{
|
||||
int quantity = i2p::util::lexical_cast<int>(it->second, outboundTunnelsQuantity);
|
||||
if (quantity > 0)
|
||||
{
|
||||
outboundTunnelsQuantity = quantity;
|
||||
LogPrint (eLogInfo, "Destination: Outbound tunnels quantity set to ", quantity);
|
||||
}
|
||||
}
|
||||
it = params->find (I2CP_PARAM_TAGS_TO_SEND);
|
||||
if (it != params->end ())
|
||||
{
|
||||
int tagsToSend = i2p::util::lexical_cast<int>(it->second, numTags);
|
||||
if (tagsToSend > 0)
|
||||
{
|
||||
numTags = tagsToSend;
|
||||
LogPrint (eLogInfo, "Destination: Tags to send set to ", tagsToSend);
|
||||
}
|
||||
}
|
||||
it = params->find (I2CP_PARAM_EXPLICIT_PEERS);
|
||||
if (it != params->end ())
|
||||
{
|
||||
explicitPeers = std::make_shared<std::vector<i2p::data::IdentHash> >();
|
||||
std::stringstream ss(it->second);
|
||||
std::string b64;
|
||||
while (std::getline (ss, b64, ','))
|
||||
{
|
||||
i2p::data::IdentHash ident;
|
||||
ident.FromBase64 (b64);
|
||||
explicitPeers->push_back (ident);
|
||||
}
|
||||
LogPrint (eLogInfo, "Destination: Explicit peers set to ", it->second);
|
||||
}
|
||||
}
|
||||
} catch (std::exception & ex) {
|
||||
LogPrint(eLogError, "Destination: unable to parse parameters for destination: ", ex.what());
|
||||
}
|
||||
SetNumTags (numTags);
|
||||
m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (inboundTunnelLen, outboundTunnelLen, inboundTunnelsQuantity, outboundTunnelsQuantity);
|
||||
m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (inLen, outLen, inQty, outQty);
|
||||
if (explicitPeers)
|
||||
m_Pool->SetExplicitPeers (explicitPeers);
|
||||
if(params)
|
||||
{
|
||||
auto itr = params->find(I2CP_PARAM_MAX_TUNNEL_LATENCY);
|
||||
if (itr != params->end()) {
|
||||
auto maxlatency = std::stoi(itr->second);
|
||||
itr = params->find(I2CP_PARAM_MIN_TUNNEL_LATENCY);
|
||||
if (itr != params->end()) {
|
||||
auto minlatency = std::stoi(itr->second);
|
||||
if ( minlatency > 0 && maxlatency > 0 ) {
|
||||
// set tunnel pool latency
|
||||
LogPrint(eLogInfo, "Destination: requiring tunnel latency [", minlatency, "ms, ", maxlatency, "ms]");
|
||||
m_Pool->RequireLatency(minlatency, maxlatency);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LeaseSetDestination::~LeaseSetDestination ()
|
||||
{
|
||||
if (m_IsRunning)
|
||||
Stop ();
|
||||
for (auto& it: m_LeaseSetRequests)
|
||||
if (it.second->requestComplete) it.second->requestComplete (nullptr);
|
||||
m_LeaseSetRequests.clear ();
|
||||
if (m_Pool)
|
||||
i2p::tunnel::tunnels.DeleteTunnelPool (m_Pool);
|
||||
for (auto& it: m_LeaseSetRequests)
|
||||
it.second->Complete (nullptr);
|
||||
}
|
||||
|
||||
void LeaseSetDestination::Run ()
|
||||
@@ -131,12 +112,12 @@ namespace client
|
||||
{
|
||||
m_IsRunning = true;
|
||||
m_Pool->SetLocalDestination (shared_from_this ());
|
||||
m_Pool->SetActive (true);
|
||||
m_Thread = new std::thread (std::bind (&LeaseSetDestination::Run, shared_from_this ()));
|
||||
|
||||
m_Pool->SetActive (true);
|
||||
m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT));
|
||||
m_CleanupTimer.async_wait (std::bind (&LeaseSetDestination::HandleCleanupTimer,
|
||||
shared_from_this (), std::placeholders::_1));
|
||||
shared_from_this (), std::placeholders::_1));
|
||||
m_Thread = new std::thread (std::bind (&LeaseSetDestination::Run, shared_from_this ()));
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
@@ -149,7 +130,8 @@ namespace client
|
||||
{
|
||||
m_CleanupTimer.cancel ();
|
||||
m_PublishConfirmationTimer.cancel ();
|
||||
m_PublishVerificationTimer.cancel ();
|
||||
m_PublishVerificationTimer.cancel ();
|
||||
|
||||
m_IsRunning = false;
|
||||
if (m_Pool)
|
||||
{
|
||||
@@ -163,6 +145,7 @@ namespace client
|
||||
delete m_Thread;
|
||||
m_Thread = 0;
|
||||
}
|
||||
CleanUp (); // GarlicDestination
|
||||
return true;
|
||||
}
|
||||
else
|
||||
@@ -202,7 +185,12 @@ namespace client
|
||||
return remoteLS;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "Destination: remote LeaseSet expired");
|
||||
std::lock_guard<std::mutex> lock(m_RemoteLeaseSetsMutex);
|
||||
m_RemoteLeaseSets.erase (ident);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -223,12 +211,16 @@ namespace client
|
||||
if (!m_Pool) return nullptr;
|
||||
if (!m_LeaseSet)
|
||||
UpdateLeaseSet ();
|
||||
std::lock_guard<std::mutex> l(m_LeaseSetMutex);
|
||||
return m_LeaseSet;
|
||||
}
|
||||
|
||||
void LeaseSetDestination::SetLeaseSet (i2p::data::LocalLeaseSet * newLeaseSet)
|
||||
{
|
||||
m_LeaseSet.reset (newLeaseSet);
|
||||
{
|
||||
std::lock_guard<std::mutex> l(m_LeaseSetMutex);
|
||||
m_LeaseSet.reset (newLeaseSet);
|
||||
}
|
||||
i2p::garlic::GarlicDestination::SetLeaseSetUpdated ();
|
||||
if (m_IsPublic)
|
||||
{
|
||||
@@ -302,46 +294,47 @@ namespace client
|
||||
LogPrint (eLogInfo, "Destination: Reply token is ignored for DatabaseStore");
|
||||
offset += 36;
|
||||
}
|
||||
i2p::data::IdentHash key (buf + DATABASE_STORE_KEY_OFFSET);
|
||||
std::shared_ptr<i2p::data::LeaseSet> leaseSet;
|
||||
if (buf[DATABASE_STORE_TYPE_OFFSET] == 1) // LeaseSet
|
||||
{
|
||||
LogPrint (eLogDebug, "Remote LeaseSet");
|
||||
LogPrint (eLogDebug, "Destination: Remote LeaseSet");
|
||||
std::lock_guard<std::mutex> lock(m_RemoteLeaseSetsMutex);
|
||||
auto it = m_RemoteLeaseSets.find (buf + DATABASE_STORE_KEY_OFFSET);
|
||||
auto it = m_RemoteLeaseSets.find (key);
|
||||
if (it != m_RemoteLeaseSets.end ())
|
||||
{
|
||||
leaseSet = it->second;
|
||||
if (leaseSet->IsNewer (buf + offset, len - offset))
|
||||
{
|
||||
leaseSet->Update (buf + offset, len - offset);
|
||||
if (leaseSet->IsValid ())
|
||||
LogPrint (eLogDebug, "Remote LeaseSet updated");
|
||||
if (leaseSet->IsValid () && leaseSet->GetIdentHash () == key)
|
||||
LogPrint (eLogDebug, "Destination: Remote LeaseSet updated");
|
||||
else
|
||||
{
|
||||
LogPrint (eLogDebug, "Remote LeaseSet update failed");
|
||||
LogPrint (eLogDebug, "Destination: Remote LeaseSet update failed");
|
||||
m_RemoteLeaseSets.erase (it);
|
||||
leaseSet = nullptr;
|
||||
}
|
||||
}
|
||||
else
|
||||
LogPrint (eLogDebug, "Remote LeaseSet is older. Not updated");
|
||||
LogPrint (eLogDebug, "Destination: Remote LeaseSet is older. Not updated");
|
||||
}
|
||||
else
|
||||
{
|
||||
leaseSet = std::make_shared<i2p::data::LeaseSet> (buf + offset, len - offset);
|
||||
if (leaseSet->IsValid ())
|
||||
if (leaseSet->IsValid () && leaseSet->GetIdentHash () == key)
|
||||
{
|
||||
if (leaseSet->GetIdentHash () != GetIdentHash ())
|
||||
{
|
||||
LogPrint (eLogDebug, "New remote LeaseSet added");
|
||||
m_RemoteLeaseSets[buf + DATABASE_STORE_KEY_OFFSET] = leaseSet;
|
||||
LogPrint (eLogDebug, "Destination: New remote LeaseSet added");
|
||||
m_RemoteLeaseSets[key] = leaseSet;
|
||||
}
|
||||
else
|
||||
LogPrint (eLogDebug, "Own remote LeaseSet dropped");
|
||||
LogPrint (eLogDebug, "Destination: Own remote LeaseSet dropped");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "New remote LeaseSet failed");
|
||||
LogPrint (eLogError, "Destination: New remote LeaseSet failed");
|
||||
leaseSet = nullptr;
|
||||
}
|
||||
}
|
||||
@@ -349,11 +342,11 @@ namespace client
|
||||
else
|
||||
LogPrint (eLogError, "Destination: Unexpected client's DatabaseStore type ", buf[DATABASE_STORE_TYPE_OFFSET], ", dropped");
|
||||
|
||||
auto it1 = m_LeaseSetRequests.find (buf + DATABASE_STORE_KEY_OFFSET);
|
||||
auto it1 = m_LeaseSetRequests.find (key);
|
||||
if (it1 != m_LeaseSetRequests.end ())
|
||||
{
|
||||
it1->second->requestTimeoutTimer.cancel ();
|
||||
if (it1->second->requestComplete) it1->second->requestComplete (leaseSet);
|
||||
if (it1->second) it1->second->Complete (leaseSet);
|
||||
m_LeaseSetRequests.erase (it1);
|
||||
}
|
||||
}
|
||||
@@ -371,29 +364,27 @@ namespace client
|
||||
if (request->excluded.size () < MAX_NUM_FLOODFILLS_PER_REQUEST)
|
||||
{
|
||||
for (int i = 0; i < num; i++)
|
||||
{
|
||||
i2p::data::IdentHash peerHash (buf + 33 + i*32);
|
||||
auto floodfill = i2p::data::netdb.FindRouter (peerHash);
|
||||
if (floodfill)
|
||||
{
|
||||
LogPrint (eLogInfo, "Destination: Requesting ", key.ToBase64 (), " at ", peerHash.ToBase64 ());
|
||||
if (SendLeaseSetRequest (key, floodfill, request))
|
||||
found = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogInfo, "Destination: Found new floodfill, request it"); // TODO: recheck this message
|
||||
i2p::data::netdb.RequestDestination (peerHash);
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
LogPrint (eLogError, "Destination: Suggested floodfills are not presented in netDb");
|
||||
}
|
||||
else
|
||||
LogPrint (eLogInfo, "Destination: ", key.ToBase64 (), " was not found on ", MAX_NUM_FLOODFILLS_PER_REQUEST, " floodfills");
|
||||
if (!found)
|
||||
{
|
||||
if (request->requestComplete) request->requestComplete (nullptr);
|
||||
i2p::data::IdentHash peerHash (buf + 33 + i*32);
|
||||
if (!request->excluded.count (peerHash) && !i2p::data::netdb.FindRouter (peerHash))
|
||||
{
|
||||
LogPrint (eLogInfo, "Destination: Found new floodfill, request it"); // TODO: recheck this message
|
||||
i2p::data::netdb.RequestDestination (peerHash);
|
||||
}
|
||||
}
|
||||
|
||||
auto floodfill = i2p::data::netdb.GetClosestFloodfill (key, request->excluded);
|
||||
if (floodfill)
|
||||
{
|
||||
LogPrint (eLogInfo, "Destination: Requesting ", key.ToBase64 (), " at ", floodfill->GetIdentHash ().ToBase64 ());
|
||||
if (SendLeaseSetRequest (key, floodfill, request))
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
LogPrint (eLogInfo, "Destination: ", key.ToBase64 (), " was not found on ", MAX_NUM_FLOODFILLS_PER_REQUEST, " floodfills");
|
||||
request->Complete (nullptr);
|
||||
m_LeaseSetRequests.erase (key);
|
||||
}
|
||||
}
|
||||
@@ -522,9 +513,9 @@ namespace client
|
||||
auto it = s->m_LeaseSetRequests.find (dest);
|
||||
if (it != s->m_LeaseSetRequests.end ())
|
||||
{
|
||||
auto requestComplete = it->second->requestComplete;
|
||||
auto requestComplete = it->second;
|
||||
s->m_LeaseSetRequests.erase (it);
|
||||
if (notify && requestComplete) requestComplete (nullptr);
|
||||
if (notify && requestComplete) requestComplete->Complete (nullptr);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -536,22 +527,31 @@ namespace client
|
||||
if (floodfill)
|
||||
{
|
||||
auto request = std::make_shared<LeaseSetRequest> (m_Service);
|
||||
request->requestComplete = requestComplete;
|
||||
if (requestComplete)
|
||||
request->requestComplete.push_back (requestComplete);
|
||||
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
auto ret = m_LeaseSetRequests.insert (std::pair<i2p::data::IdentHash, std::shared_ptr<LeaseSetRequest> >(dest,request));
|
||||
if (ret.second) // inserted
|
||||
{
|
||||
request->requestTime = ts;
|
||||
if (!SendLeaseSetRequest (dest, floodfill, request))
|
||||
{
|
||||
// request failed
|
||||
m_LeaseSetRequests.erase (dest);
|
||||
if (request->requestComplete) request->requestComplete (nullptr);
|
||||
m_LeaseSetRequests.erase (ret.first);
|
||||
if (requestComplete) requestComplete (nullptr);
|
||||
}
|
||||
}
|
||||
else // duplicate
|
||||
{
|
||||
LogPrint (eLogWarning, "Destination: Request of LeaseSet ", dest.ToBase64 (), " is pending already");
|
||||
// TODO: queue up requests
|
||||
if (request->requestComplete) request->requestComplete (nullptr);
|
||||
LogPrint (eLogInfo, "Destination: Request of LeaseSet ", dest.ToBase64 (), " is pending already");
|
||||
if (ts > ret.first->second->requestTime + MAX_LEASESET_REQUEST_TIMEOUT)
|
||||
{
|
||||
// something went wrong
|
||||
m_LeaseSetRequests.erase (ret.first);
|
||||
if (requestComplete) requestComplete (nullptr);
|
||||
}
|
||||
else if (requestComplete)
|
||||
ret.first->second->requestComplete.push_back (requestComplete);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -574,7 +574,6 @@ namespace client
|
||||
if (request->replyTunnel && request->outboundTunnel)
|
||||
{
|
||||
request->excluded.insert (nextFloodfill->GetIdentHash ());
|
||||
request->requestTime = i2p::util::GetSecondsSinceEpoch ();
|
||||
request->requestTimeoutTimer.cancel ();
|
||||
|
||||
uint8_t replyKey[32], replyTag[32];
|
||||
@@ -632,9 +631,9 @@ namespace client
|
||||
|
||||
if (done)
|
||||
{
|
||||
auto requestComplete = it->second->requestComplete;
|
||||
auto requestComplete = it->second;
|
||||
m_LeaseSetRequests.erase (it);
|
||||
if (requestComplete) requestComplete (nullptr);
|
||||
if (requestComplete) requestComplete->Complete (nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -706,18 +705,26 @@ namespace client
|
||||
{
|
||||
m_ReadyChecker.cancel();
|
||||
m_StreamingDestination->Stop ();
|
||||
//m_StreamingDestination->SetOwner (nullptr);
|
||||
m_StreamingDestination = nullptr;
|
||||
for (auto& it: m_StreamingDestinationsByPorts)
|
||||
{
|
||||
it.second->Stop ();
|
||||
if(m_DatagramDestination)
|
||||
delete m_DatagramDestination;
|
||||
m_DatagramDestination = nullptr;
|
||||
return true;
|
||||
//it.second->SetOwner (nullptr);
|
||||
}
|
||||
m_StreamingDestinationsByPorts.clear ();
|
||||
if (m_DatagramDestination)
|
||||
{
|
||||
delete m_DatagramDestination;
|
||||
m_DatagramDestination = nullptr;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef I2LUA
|
||||
void ClientDestination::Ready(ReadyPromise & p)
|
||||
{
|
||||
ScheduleCheckForReady(&p);
|
||||
@@ -741,6 +748,7 @@ namespace client
|
||||
else // we are not ready
|
||||
ScheduleCheckForReady(p);
|
||||
}
|
||||
#endif
|
||||
|
||||
void ClientDestination::HandleDataMessage (const uint8_t * buf, size_t len)
|
||||
{
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#ifdef I2LUA
|
||||
#include <future>
|
||||
#endif
|
||||
#include <boost/asio.hpp>
|
||||
#include "Identity.h"
|
||||
#include "TunnelPool.h"
|
||||
@@ -48,6 +50,12 @@ namespace client
|
||||
const char I2CP_PARAM_TAGS_TO_SEND[] = "crypto.tagsToSend";
|
||||
const int DEFAULT_TAGS_TO_SEND = 40;
|
||||
|
||||
// latency
|
||||
const char I2CP_PARAM_MIN_TUNNEL_LATENCY[] = "latency.min";
|
||||
const int DEFAULT_MIN_TUNNEL_LATENCY = 0;
|
||||
const char I2CP_PARAM_MAX_TUNNEL_LATENCY[] = "latency.max";
|
||||
const int DEFAULT_MAX_TUNNEL_LATENCY = 0;
|
||||
|
||||
typedef std::function<void (std::shared_ptr<i2p::stream::Stream> stream)> StreamRequestComplete;
|
||||
|
||||
class LeaseSetDestination: public i2p::garlic::GarlicDestination,
|
||||
@@ -61,9 +69,15 @@ namespace client
|
||||
std::set<i2p::data::IdentHash> excluded;
|
||||
uint64_t requestTime;
|
||||
boost::asio::deadline_timer requestTimeoutTimer;
|
||||
RequestComplete requestComplete;
|
||||
std::list<RequestComplete> requestComplete;
|
||||
std::shared_ptr<i2p::tunnel::OutboundTunnel> outboundTunnel;
|
||||
std::shared_ptr<i2p::tunnel::InboundTunnel> replyTunnel;
|
||||
|
||||
void Complete (std::shared_ptr<i2p::data::LeaseSet> ls)
|
||||
{
|
||||
for (auto& it: requestComplete) it (ls);
|
||||
requestComplete.clear ();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -123,12 +137,12 @@ namespace client
|
||||
volatile bool m_IsRunning;
|
||||
std::thread * m_Thread;
|
||||
boost::asio::io_service m_Service;
|
||||
boost::asio::io_service::work m_Work;
|
||||
mutable std::mutex m_RemoteLeaseSetsMutex;
|
||||
std::map<i2p::data::IdentHash, std::shared_ptr<i2p::data::LeaseSet> > m_RemoteLeaseSets;
|
||||
std::map<i2p::data::IdentHash, std::shared_ptr<LeaseSetRequest> > m_LeaseSetRequests;
|
||||
|
||||
std::shared_ptr<i2p::tunnel::TunnelPool> m_Pool;
|
||||
std::mutex m_LeaseSetMutex;
|
||||
std::shared_ptr<i2p::data::LocalLeaseSet> m_LeaseSet;
|
||||
bool m_IsPublic;
|
||||
uint32_t m_PublishReplyToken;
|
||||
@@ -140,23 +154,25 @@ namespace client
|
||||
|
||||
// for HTTP only
|
||||
int GetNumRemoteLeaseSets () const { return m_RemoteLeaseSets.size (); };
|
||||
const decltype(m_RemoteLeaseSets)& GetLeaseSets () const { return m_RemoteLeaseSets; };
|
||||
};
|
||||
|
||||
class ClientDestination: public LeaseSetDestination
|
||||
{
|
||||
public:
|
||||
#ifdef I2LUA
|
||||
// type for informing that a client destination is ready
|
||||
typedef std::promise<std::shared_ptr<ClientDestination> > ReadyPromise;
|
||||
// informs promise with shared_from_this() when this destination is ready to use
|
||||
// if cancelled before ready, informs promise with nullptr
|
||||
void Ready(ReadyPromise & p);
|
||||
#endif
|
||||
|
||||
ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map<std::string, std::string> * params = nullptr);
|
||||
~ClientDestination ();
|
||||
|
||||
bool Start ();
|
||||
bool Stop ();
|
||||
|
||||
// informs promise with shared_from_this() when this destination is ready to use
|
||||
// if cancelled before ready, informs promise with nullptr
|
||||
void Ready(ReadyPromise & p);
|
||||
|
||||
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); };
|
||||
@@ -191,10 +207,10 @@ namespace client
|
||||
std::shared_ptr<ClientDestination> GetSharedFromThis ()
|
||||
{ return std::static_pointer_cast<ClientDestination>(shared_from_this ()); }
|
||||
void PersistTemporaryKeys ();
|
||||
|
||||
#ifdef I2LUA
|
||||
void ScheduleCheckForReady(ReadyPromise * p);
|
||||
void HandleCheckForReady(const boost::system::error_code & ecode, ReadyPromise * p);
|
||||
|
||||
#endif
|
||||
private:
|
||||
|
||||
i2p::data::PrivateKeys m_Keys;
|
||||
|
||||
32
Event.cpp
Normal file
32
Event.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
#include "Event.h"
|
||||
#include "Log.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace event
|
||||
{
|
||||
#ifdef WITH_EVENTS
|
||||
EventCore core;
|
||||
#endif
|
||||
|
||||
void EventCore::SetListener(EventListener * l)
|
||||
{
|
||||
m_listener = l;
|
||||
LogPrint(eLogInfo, "Event: listener set");
|
||||
}
|
||||
|
||||
void EventCore::QueueEvent(const EventType & ev)
|
||||
{
|
||||
if(m_listener)
|
||||
m_listener->HandleEvent(ev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EmitEvent(const EventType & e)
|
||||
{
|
||||
#ifdef WITH_EVENTS
|
||||
i2p::event::core.QueueEvent(e);
|
||||
#endif
|
||||
}
|
||||
|
||||
37
Event.h
Normal file
37
Event.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#ifndef EVENT_H__
|
||||
#define EVENT_H__
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
|
||||
typedef std::map<std::string, std::string> EventType;
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace event
|
||||
{
|
||||
class EventListener {
|
||||
public:
|
||||
virtual ~EventListener() {};
|
||||
virtual void HandleEvent(const EventType & ev) = 0;
|
||||
};
|
||||
|
||||
class EventCore
|
||||
{
|
||||
public:
|
||||
void QueueEvent(const EventType & ev);
|
||||
void SetListener(EventListener * l);
|
||||
|
||||
private:
|
||||
EventListener * m_listener = nullptr;
|
||||
};
|
||||
#ifdef WITH_EVENTS
|
||||
extern EventCore core;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
void EmitEvent(const EventType & ev);
|
||||
|
||||
#endif
|
||||
34
FS.cpp
34
FS.cpp
@@ -46,8 +46,18 @@ namespace fs {
|
||||
}
|
||||
#if defined(WIN32) || defined(_WIN32)
|
||||
char localAppData[MAX_PATH];
|
||||
SHGetFolderPath(NULL, CSIDL_APPDATA, 0, NULL, localAppData);
|
||||
dataDir = std::string(localAppData) + "\\" + appName;
|
||||
// check executable directory first
|
||||
GetModuleFileName (NULL, localAppData, MAX_PATH);
|
||||
auto execPath = boost::filesystem::path(localAppData).parent_path();
|
||||
// if config file exists in .exe's folder use it
|
||||
if(boost::filesystem::exists(execPath/"i2pd.conf")) // TODO: magic string
|
||||
dataDir = execPath.string ();
|
||||
else
|
||||
{
|
||||
// otherwise %appdata%
|
||||
SHGetFolderPath(NULL, CSIDL_APPDATA, 0, NULL, localAppData);
|
||||
dataDir = std::string(localAppData) + "\\" + appName;
|
||||
}
|
||||
return;
|
||||
#elif defined(MAC_OSX)
|
||||
char *home = getenv("HOME");
|
||||
@@ -56,13 +66,15 @@ namespace fs {
|
||||
return;
|
||||
#else /* other unix */
|
||||
#if defined(ANDROID)
|
||||
if (boost::filesystem::exists("/sdcard"))
|
||||
{
|
||||
dataDir = "/sdcard/" + appName;
|
||||
const char * ext = getenv("EXTERNAL_STORAGE");
|
||||
if (!ext) ext = "/sdcard";
|
||||
if (boost::filesystem::exists(ext))
|
||||
{
|
||||
dataDir = std::string (ext) + "/" + appName;
|
||||
return;
|
||||
}
|
||||
// otherwise use /data/files
|
||||
#endif
|
||||
}
|
||||
// otherwise use /data/files
|
||||
#endif
|
||||
char *home = getenv("HOME");
|
||||
if (isService) {
|
||||
dataDir = "/var/lib/" + appName;
|
||||
@@ -112,10 +124,10 @@ namespace fs {
|
||||
|
||||
bool CreateDirectory (const std::string& path)
|
||||
{
|
||||
if (boost::filesystem::exists(path) &&
|
||||
if (boost::filesystem::exists(path) &&
|
||||
boost::filesystem::is_directory (boost::filesystem::status (path))) return true;
|
||||
return boost::filesystem::create_directory(path);
|
||||
}
|
||||
}
|
||||
|
||||
void HashedStorage::SetPlace(const std::string &path) {
|
||||
root = path + i2p::fs::dirSep + name;
|
||||
@@ -125,7 +137,7 @@ namespace fs {
|
||||
if (!boost::filesystem::exists(root)) {
|
||||
boost::filesystem::create_directories(root);
|
||||
}
|
||||
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
auto p = root + i2p::fs::dirSep + prefix1 + chars[i];
|
||||
if (boost::filesystem::exists(p))
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace data
|
||||
if (family) family[0] = 0;
|
||||
}
|
||||
auto pkey = X509_get_pubkey (cert);
|
||||
int keyType = EVP_PKEY_type(pkey->type);
|
||||
int keyType = EVP_PKEY_base_id (pkey);
|
||||
switch (keyType)
|
||||
{
|
||||
case EVP_PKEY_DSA:
|
||||
|
||||
103
Garlic.cpp
103
Garlic.cpp
@@ -20,7 +20,7 @@ namespace garlic
|
||||
std::shared_ptr<const i2p::data::RoutingDestination> destination, int numTags, bool attachLeaseSet):
|
||||
m_Owner (owner), m_Destination (destination), m_NumTags (numTags),
|
||||
m_LeaseSetUpdateStatus (attachLeaseSet ? eLeaseSetUpdated : eLeaseSetDoNotSend),
|
||||
m_ElGamalEncryption (new i2p::crypto::ElGamalEncryption (destination->GetEncryptionPublicKey ()))
|
||||
m_LeaseSetUpdateMsgID (0)
|
||||
{
|
||||
// create new session tags and session key
|
||||
RAND_bytes (m_SessionKey, 32);
|
||||
@@ -28,7 +28,7 @@ namespace garlic
|
||||
}
|
||||
|
||||
GarlicRoutingSession::GarlicRoutingSession (const uint8_t * sessionKey, const SessionTag& sessionTag):
|
||||
m_Owner (nullptr), m_Destination (nullptr), m_NumTags (1), m_LeaseSetUpdateStatus (eLeaseSetDoNotSend)
|
||||
m_Owner (nullptr), m_NumTags (1), m_LeaseSetUpdateStatus (eLeaseSetDoNotSend), m_LeaseSetUpdateMsgID (0)
|
||||
{
|
||||
memcpy (m_SessionKey, sessionKey, 32);
|
||||
m_Encryption.SetKey (m_SessionKey);
|
||||
@@ -83,6 +83,7 @@ namespace garlic
|
||||
if (msgID == m_LeaseSetUpdateMsgID)
|
||||
{
|
||||
m_LeaseSetUpdateStatus = eLeaseSetUpToDate;
|
||||
m_LeaseSetUpdateMsgID = 0;
|
||||
LogPrint (eLogInfo, "Garlic: LeaseSet update confirmed");
|
||||
}
|
||||
else
|
||||
@@ -92,32 +93,22 @@ namespace garlic
|
||||
void GarlicRoutingSession::TagsConfirmed (uint32_t msgID)
|
||||
{
|
||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
for (auto it = m_UnconfirmedTagsMsgs.begin (); it != m_UnconfirmedTagsMsgs.end ();)
|
||||
auto it = m_UnconfirmedTagsMsgs.find (msgID);
|
||||
if (it != m_UnconfirmedTagsMsgs.end ())
|
||||
{
|
||||
auto& tags = *it;
|
||||
if (tags->msgID == msgID)
|
||||
{
|
||||
if (ts < tags->tagsCreationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT)
|
||||
{
|
||||
for (int i = 0; i < tags->numTags; i++)
|
||||
m_SessionTags.push_back (tags->sessionTags[i]);
|
||||
}
|
||||
it = m_UnconfirmedTagsMsgs.erase (it);
|
||||
auto& tags = it->second;
|
||||
if (ts < tags->tagsCreationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT)
|
||||
{
|
||||
for (int i = 0; i < tags->numTags; i++)
|
||||
m_SessionTags.push_back (tags->sessionTags[i]);
|
||||
}
|
||||
else if (ts >= tags->tagsCreationTime + OUTGOING_TAGS_CONFIRMATION_TIMEOUT)
|
||||
{
|
||||
if (m_Owner)
|
||||
m_Owner->RemoveDeliveryStatusSession (tags->msgID);
|
||||
it = m_UnconfirmedTagsMsgs.erase (it);
|
||||
}
|
||||
else
|
||||
++it;
|
||||
m_UnconfirmedTagsMsgs.erase (it);
|
||||
}
|
||||
}
|
||||
|
||||
bool GarlicRoutingSession::CleanupExpiredTags ()
|
||||
{
|
||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
for (auto it = m_SessionTags.begin (); it != m_SessionTags.end ();)
|
||||
{
|
||||
if (ts >= it->creationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT)
|
||||
@@ -126,6 +117,12 @@ namespace garlic
|
||||
++it;
|
||||
}
|
||||
CleanupUnconfirmedTags ();
|
||||
if (m_LeaseSetUpdateMsgID && ts*1000LL > m_LeaseSetSubmissionTime + LEASET_CONFIRMATION_TIMEOUT)
|
||||
{
|
||||
if (m_Owner)
|
||||
m_Owner->RemoveDeliveryStatusSession (m_LeaseSetUpdateMsgID);
|
||||
m_LeaseSetUpdateMsgID = 0;
|
||||
}
|
||||
return !m_SessionTags.empty () || !m_UnconfirmedTagsMsgs.empty ();
|
||||
}
|
||||
|
||||
@@ -136,10 +133,10 @@ namespace garlic
|
||||
// delete expired unconfirmed tags
|
||||
for (auto it = m_UnconfirmedTagsMsgs.begin (); it != m_UnconfirmedTagsMsgs.end ();)
|
||||
{
|
||||
if (ts >= (*it)->tagsCreationTime + OUTGOING_TAGS_CONFIRMATION_TIMEOUT)
|
||||
if (ts >= it->second->tagsCreationTime + OUTGOING_TAGS_CONFIRMATION_TIMEOUT)
|
||||
{
|
||||
if (m_Owner)
|
||||
m_Owner->RemoveDeliveryStatusSession ((*it)->msgID);
|
||||
m_Owner->RemoveDeliveryStatusSession (it->first);
|
||||
it = m_UnconfirmedTagsMsgs.erase (it);
|
||||
ret = true;
|
||||
}
|
||||
@@ -190,7 +187,8 @@ namespace garlic
|
||||
RAND_bytes (elGamal.preIV, 32); // Pre-IV
|
||||
uint8_t iv[32]; // IV is first 16 bytes
|
||||
SHA256(elGamal.preIV, 32, iv);
|
||||
m_ElGamalEncryption->Encrypt ((uint8_t *)&elGamal, sizeof(elGamal), buf, true);
|
||||
i2p::crypto::ElGamalEncryption elGamalEncryption (m_Destination->GetEncryptionPublicKey ());
|
||||
elGamalEncryption.Encrypt ((uint8_t *)&elGamal, buf, true);
|
||||
m_Encryption.SetIV (iv);
|
||||
buf += 514;
|
||||
len += 514;
|
||||
@@ -276,7 +274,8 @@ namespace garlic
|
||||
if (newTags) // new tags created
|
||||
{
|
||||
newTags->msgID = msgID;
|
||||
m_UnconfirmedTagsMsgs.emplace_back (newTags);
|
||||
m_UnconfirmedTagsMsgs.insert (std::make_pair(msgID, std::unique_ptr<UnconfirmedTags>(newTags)));
|
||||
newTags = nullptr; // got acquired
|
||||
}
|
||||
m_Owner->DeliveryStatusSent (shared_from_this (), msgID);
|
||||
}
|
||||
@@ -286,6 +285,7 @@ namespace garlic
|
||||
// attach LeaseSet
|
||||
if (m_LeaseSetUpdateStatus == eLeaseSetUpdated)
|
||||
{
|
||||
if (m_LeaseSetUpdateMsgID) m_Owner->RemoveDeliveryStatusSession (m_LeaseSetUpdateMsgID); // remove previous
|
||||
m_LeaseSetUpdateStatus = eLeaseSetSubmitted;
|
||||
m_LeaseSetUpdateMsgID = msgID;
|
||||
m_LeaseSetSubmissionTime = ts;
|
||||
@@ -300,13 +300,14 @@ namespace garlic
|
||||
size += CreateGarlicClove (payload + size, msg, m_Destination ? m_Destination->IsDestination () : false);
|
||||
(*numCloves)++;
|
||||
}
|
||||
|
||||
memset (payload + size, 0, 3); // certificate of message
|
||||
size += 3;
|
||||
htobe32buf (payload + size, msgID); // MessageID
|
||||
size += 4;
|
||||
htobe64buf (payload + size, ts + 8000); // Expiration of message, 8 sec
|
||||
size += 8;
|
||||
|
||||
if (newTags) delete newTags; // not acquired, delete
|
||||
return size;
|
||||
}
|
||||
|
||||
@@ -314,7 +315,7 @@ namespace garlic
|
||||
{
|
||||
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 8000; // 8 sec
|
||||
size_t size = 0;
|
||||
if (isDestination && m_Destination)
|
||||
if (isDestination)
|
||||
{
|
||||
buf[size] = eGarlicDeliveryTypeDestination << 5;// delivery instructions flag destination
|
||||
size++;
|
||||
@@ -393,6 +394,12 @@ namespace garlic
|
||||
{
|
||||
}
|
||||
|
||||
void GarlicDestination::CleanUp ()
|
||||
{
|
||||
m_Sessions.clear ();
|
||||
m_DeliveryStatusSessions.clear ();
|
||||
m_Tags.clear ();
|
||||
}
|
||||
void GarlicDestination::AddSessionKey (const uint8_t * key, const uint8_t * tag)
|
||||
{
|
||||
if (key)
|
||||
@@ -623,42 +630,64 @@ namespace garlic
|
||||
LogPrint (eLogDebug, "Garlic: ", numExpiredTags, " tags expired for ", GetIdentHash().ToBase64 ());
|
||||
|
||||
// outgoing
|
||||
std::unique_lock<std::mutex> l(m_SessionsMutex);
|
||||
for (auto it = m_Sessions.begin (); it != m_Sessions.end ();)
|
||||
{
|
||||
it->second->GetSharedRoutingPath (); // delete shared path if necessary
|
||||
if (!it->second->CleanupExpiredTags ())
|
||||
std::unique_lock<std::mutex> l(m_SessionsMutex);
|
||||
for (auto it = m_Sessions.begin (); it != m_Sessions.end ();)
|
||||
{
|
||||
LogPrint (eLogInfo, "Routing session to ", it->first.ToBase32 (), " deleted");
|
||||
it = m_Sessions.erase (it);
|
||||
it->second->GetSharedRoutingPath (); // delete shared path if necessary
|
||||
if (!it->second->CleanupExpiredTags ())
|
||||
{
|
||||
LogPrint (eLogInfo, "Routing session to ", it->first.ToBase32 (), " deleted");
|
||||
it->second->SetOwner (nullptr);
|
||||
it = m_Sessions.erase (it);
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
// delivery status sessions
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_DeliveryStatusSessionsMutex);
|
||||
for (auto it = m_DeliveryStatusSessions.begin (); it != m_DeliveryStatusSessions.end (); )
|
||||
{
|
||||
if (it->second->GetOwner () != this)
|
||||
it = m_DeliveryStatusSessions.erase (it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GarlicDestination::RemoveDeliveryStatusSession (uint32_t msgID)
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_DeliveryStatusSessionsMutex);
|
||||
m_DeliveryStatusSessions.erase (msgID);
|
||||
}
|
||||
|
||||
void GarlicDestination::DeliveryStatusSent (GarlicRoutingSessionPtr session, uint32_t msgID)
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_DeliveryStatusSessionsMutex);
|
||||
m_DeliveryStatusSessions[msgID] = session;
|
||||
}
|
||||
|
||||
void GarlicDestination::HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
|
||||
{
|
||||
uint32_t msgID = bufbe32toh (msg->GetPayload ());
|
||||
GarlicRoutingSessionPtr session;
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_DeliveryStatusSessionsMutex);
|
||||
auto it = m_DeliveryStatusSessions.find (msgID);
|
||||
if (it != m_DeliveryStatusSessions.end ())
|
||||
{
|
||||
it->second->MessageConfirmed (msgID);
|
||||
session = it->second;
|
||||
m_DeliveryStatusSessions.erase (it);
|
||||
LogPrint (eLogDebug, "Garlic: message ", msgID, " acknowledged");
|
||||
}
|
||||
}
|
||||
if (session)
|
||||
{
|
||||
session->MessageConfirmed (msgID);
|
||||
LogPrint (eLogDebug, "Garlic: message ", msgID, " acknowledged");
|
||||
}
|
||||
}
|
||||
|
||||
void GarlicDestination::SetLeaseSetUpdated ()
|
||||
|
||||
13
Garlic.h
13
Garlic.h
@@ -105,10 +105,15 @@ namespace garlic
|
||||
if (m_LeaseSetUpdateStatus != eLeaseSetDoNotSend) m_LeaseSetUpdateStatus = eLeaseSetUpdated;
|
||||
};
|
||||
bool IsLeaseSetNonConfirmed () const { return m_LeaseSetUpdateStatus == eLeaseSetSubmitted; };
|
||||
|
||||
bool IsLeaseSetUpdated () const { return m_LeaseSetUpdateStatus == eLeaseSetUpdated; };
|
||||
uint64_t GetLeaseSetSubmissionTime () const { return m_LeaseSetSubmissionTime; }
|
||||
|
||||
std::shared_ptr<GarlicRoutingPath> GetSharedRoutingPath ();
|
||||
void SetSharedRoutingPath (std::shared_ptr<GarlicRoutingPath> path);
|
||||
|
||||
const GarlicDestination * GetOwner () const { return m_Owner; }
|
||||
void SetOwner (GarlicDestination * owner) { m_Owner = owner; }
|
||||
|
||||
private:
|
||||
|
||||
size_t CreateAESBlock (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg);
|
||||
@@ -123,17 +128,17 @@ namespace garlic
|
||||
|
||||
GarlicDestination * m_Owner;
|
||||
std::shared_ptr<const i2p::data::RoutingDestination> m_Destination;
|
||||
|
||||
i2p::crypto::AESKey m_SessionKey;
|
||||
std::list<SessionTag> m_SessionTags;
|
||||
int m_NumTags;
|
||||
std::list<std::unique_ptr<UnconfirmedTags> > m_UnconfirmedTagsMsgs;
|
||||
std::map<uint32_t, std::unique_ptr<UnconfirmedTags> > m_UnconfirmedTagsMsgs; // msgID->tags
|
||||
|
||||
LeaseSetUpdateStatus m_LeaseSetUpdateStatus;
|
||||
uint32_t m_LeaseSetUpdateMsgID;
|
||||
uint64_t m_LeaseSetSubmissionTime; // in milliseconds
|
||||
|
||||
i2p::crypto::CBCEncryption m_Encryption;
|
||||
std::unique_ptr<const i2p::crypto::ElGamalEncryption> m_ElGamalEncryption;
|
||||
|
||||
std::shared_ptr<GarlicRoutingPath> m_SharedRoutingPath;
|
||||
|
||||
@@ -151,6 +156,7 @@ namespace garlic
|
||||
GarlicDestination (): m_NumTags (32) {}; // 32 tags by default
|
||||
~GarlicDestination ();
|
||||
|
||||
void CleanUp ();
|
||||
void SetNumTags (int numTags) { m_NumTags = numTags; };
|
||||
std::shared_ptr<GarlicRoutingSession> GetRoutingSession (std::shared_ptr<const i2p::data::RoutingDestination> destination, bool attachLeaseSet);
|
||||
void CleanupExpiredTags ();
|
||||
@@ -190,6 +196,7 @@ namespace garlic
|
||||
// incoming
|
||||
std::map<SessionTag, std::shared_ptr<i2p::crypto::CBCDecryption>> m_Tags;
|
||||
// DeliveryStatus
|
||||
std::mutex m_DeliveryStatusSessionsMutex;
|
||||
std::map<uint32_t, GarlicRoutingSessionPtr> m_DeliveryStatusSessions; // msgID -> session
|
||||
|
||||
public:
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace proxy {
|
||||
|
||||
static const char *pageHead =
|
||||
"<head>\r\n"
|
||||
" <title>I2P HTTP proxy: error</title>\r\n"
|
||||
" <title>I2Pd HTTP proxy</title>\r\n"
|
||||
" <style type=\"text/css\">\r\n"
|
||||
" body { font: 100%/1.5em sans-serif; margin: 0; padding: 1.5em; background: #FAFAFA; color: #103456; }\r\n"
|
||||
" .header { font-size: 2.5em; text-align: center; margin: 1.5em 0; color: #894C84; }\r\n"
|
||||
@@ -60,6 +60,7 @@ namespace proxy {
|
||||
void HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream);
|
||||
/* error helpers */
|
||||
void GenericProxyError(const char *title, const char *description);
|
||||
void GenericProxyInfo(const char *title, const char *description);
|
||||
void HostNotFound(std::string & host);
|
||||
void SendProxyError(std::string & content);
|
||||
|
||||
@@ -107,6 +108,14 @@ namespace proxy {
|
||||
SendProxyError(content);
|
||||
}
|
||||
|
||||
void HTTPReqHandler::GenericProxyInfo(const char *title, const char *description) {
|
||||
std::stringstream ss;
|
||||
ss << "<h1>Proxy info: " << title << "</h1>\r\n";
|
||||
ss << "<p>" << description << "</p>\r\n";
|
||||
std::string content = ss.str();
|
||||
SendProxyError(content);
|
||||
}
|
||||
|
||||
void HTTPReqHandler::HostNotFound(std::string & host) {
|
||||
std::stringstream ss;
|
||||
ss << "<h1>Proxy error: Host not found</h1>\r\n"
|
||||
@@ -216,7 +225,7 @@ namespace proxy {
|
||||
std::stringstream ss;
|
||||
ss << "Host " << url.host << " added to router's addressbook from helper. "
|
||||
<< "Click <a href=\"" << full_url << "\">here</a> to proceed.";
|
||||
GenericProxyError("Addresshelper found", ss.str().c_str());
|
||||
GenericProxyInfo("Addresshelper found", ss.str().c_str());
|
||||
return true; /* request processed */
|
||||
}
|
||||
|
||||
|
||||
257
HTTPServer.cpp
257
HTTPServer.cpp
@@ -33,18 +33,21 @@ namespace i2p {
|
||||
namespace http {
|
||||
const char *itoopieFavicon =
|
||||
"data:image/png;base64,"
|
||||
"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv"
|
||||
"8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAYdEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My4wOGVynO"
|
||||
"EAAAIzSURBVDhPjZNdSFNhGMf3nm3n7OzMs+8JtfJGzdlgoPtoWBrkqc1OsLTMKEY3eZOQbbS6aBVYO"
|
||||
"oO8CKSLXEulQtZNahAM9Cq6lS533UUaeDEEKcN/79x7kbQT/eDhfPB7/u/7Poej08JqtXoEQbhoMpmG"
|
||||
"ZFn2stf/h8nEZ4aHue1SiWBlhSCV4n41NBifBINBjina8DyfzOUIVlcJtrYINjcJ3rw1oFAg4HnjHaZ"
|
||||
"p4/Ppv8zPH0G5XKZNPZibO4lKpYJ8vgOqqv+uKMq/d9Hfz/0sFr3w+/3IZt2YnbWhszOAxUUv0mkCs9"
|
||||
"ncyNT6hEL6dYBgY4Ngd5eger+zU7sODHA/mpubzUytj9FofLa0VGv4s9bWCCTJUGSaNvSzXT3stuHDM"
|
||||
"rc3xEqF4N2CERciURyyHfgqSZKPqfuxUMyC+OKcL4YHyl28nDFAPdqDZMcQ7tPnSfURUt0jMBgMH1nL"
|
||||
"fkRRDPvcLds3otfhbRTwasaE8b6He43VSrT3QW3tBT3iPdbyN3T7Ibsor988H8OxtiaMx2sB1aBbCRW"
|
||||
"R1hbQhbqYXh+6QkaJn8DZyzF09x6HeiaOTC6NK9cSsFqkb3aH3cLU+tCAx9l8FoXPBUy9n8LgyCCmS9"
|
||||
"MYez0Gm9P2iWna0GOcDp8KY2JhAsnbSQS6Ahh9OgrlklINeM40bWhAkBd4SLIEh8cBURLhOeiBIArVA"
|
||||
"U4yTRvJItk5PRehQVFaYfpbt9PBtTmdziaXyyUzjaHT/QZBQuKHAA0UxAAAAABJRU5ErkJggg==";
|
||||
"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACx"
|
||||
"jwv8YQUAAAAJcEhZcwAALiIAAC4iAari3ZIAAAAHdElNRQfgCQsUNSZrkhi1AAAAGXRFWHRTb2Z0"
|
||||
"d2FyZQBwYWludC5uZXQgNC4wLjEyQwRr7AAAAoJJREFUOE9jwAUqi4Q1oEwwcDTV1+5sETaBclGB"
|
||||
"vb09C5QJB6kWpvFQJoOCeLC5kmjEHCgXE2SlyETLi3h6QrkM4VL+ssWSCZUgtopITLKqaOotRTEn"
|
||||
"cbAkLqAkGtOqLBLVAWLXyWSVFkkmRiqLxuaqiWb/VBYJMAYrwgckJY25VEUzniqKhjU2y+RtCRSP"
|
||||
"6lUXy/1jIBV5tlYxZUaFVMq2NInwIi9hO8fSfOEAqDZUoCwal6MulvOvyS7gi69K4j9zxZT/m0ps"
|
||||
"/28ptvvvquXXryIa7QYMMdTwqi0WNtVi0GIDseXl7TnUxFKfnGlxAGp0+D8j2eH/8Ub7/9e7nf7X"
|
||||
"+Af/B7rwt6pI0h0l0WhQADOC9DBkhSirpImHNVZKp24ukkyoshGLnN8d5fA/y13t/44Kq/8hlnL/"
|
||||
"z7fZ/58f6vcxSNpbVUVFhV1RLNBVTsQzVYZPSwhsCAhkiIfpNMrkbO6TLf071Sfk/5ZSi/+7q6z/"
|
||||
"P5ns+v9mj/P/CpuI/20y+aeNGYxZoVoYGmsF3aFMBAAZlCwftnF9ke3//bU2//fXWP8/UGv731Am"
|
||||
"+V+DdNblSqnUYqhSTKAiYSOqJBrVqiaa+S3UNPr/gmyH/xuKXf63hnn/B8bIP0UxHfEyyeSNQKVM"
|
||||
"EB1AEB2twhcTLp+gIBJUoyKasEpVJHmqskh8qryovUG/ffCHHRU2q/Tk/YuB6eGPsbExa7ZkpLu1"
|
||||
"oLEcVDtuUCgV1w60rQzElpRUE1EVSX0BYidHiInXF4nagNhYQW60EF+ApH1ktni0A1SIITSUgVlZ"
|
||||
"JHYnlIsfzJjIp9xZKswL5YKBHL+coKJoRDaUSzoozxHVrygQU4JykQADAwAT5b1NHtwZugAAAABJ"
|
||||
"RU5ErkJggg==";
|
||||
|
||||
const char *cssStyles =
|
||||
"<style>\r\n"
|
||||
@@ -71,6 +74,7 @@ namespace http {
|
||||
const char HTTP_PAGE_TRANSPORTS[] = "transports";
|
||||
const char HTTP_PAGE_LOCAL_DESTINATIONS[] = "local_destinations";
|
||||
const char HTTP_PAGE_LOCAL_DESTINATION[] = "local_destination";
|
||||
const char HTTP_PAGE_I2CP_LOCAL_DESTINATION[] = "i2cp_local_destination";
|
||||
const char HTTP_PAGE_SAM_SESSIONS[] = "sam_sessions";
|
||||
const char HTTP_PAGE_SAM_SESSION[] = "sam_session";
|
||||
const char HTTP_PAGE_I2P_TUNNELS[] = "i2p_tunnels";
|
||||
@@ -86,7 +90,8 @@ namespace http {
|
||||
const char HTTP_PARAM_SAM_SESSION_ID[] = "id";
|
||||
const char HTTP_PARAM_ADDRESS[] = "address";
|
||||
|
||||
void ShowUptime (std::stringstream& s, int seconds) {
|
||||
static void ShowUptime (std::stringstream& s, int seconds)
|
||||
{
|
||||
int num;
|
||||
|
||||
if ((num = seconds / 86400) > 0) {
|
||||
@@ -104,7 +109,7 @@ namespace http {
|
||||
s << seconds << " seconds";
|
||||
}
|
||||
|
||||
void ShowTunnelDetails (std::stringstream& s, enum i2p::tunnel::TunnelState eState, int bytes)
|
||||
static void ShowTunnelDetails (std::stringstream& s, enum i2p::tunnel::TunnelState eState, int bytes)
|
||||
{
|
||||
std::string state;
|
||||
switch (eState) {
|
||||
@@ -121,7 +126,7 @@ namespace http {
|
||||
s << " " << (int) (bytes / 1024) << " KiB<br>\r\n";
|
||||
}
|
||||
|
||||
void ShowPageHead (std::stringstream& s)
|
||||
static void ShowPageHead (std::stringstream& s)
|
||||
{
|
||||
s <<
|
||||
"<!DOCTYPE html>\r\n"
|
||||
@@ -156,7 +161,7 @@ namespace http {
|
||||
"<div class=right>";
|
||||
}
|
||||
|
||||
void ShowPageTail (std::stringstream& s)
|
||||
static void ShowPageTail (std::stringstream& s)
|
||||
{
|
||||
s <<
|
||||
"</div></div>\r\n"
|
||||
@@ -164,12 +169,12 @@ namespace http {
|
||||
"</html>\r\n";
|
||||
}
|
||||
|
||||
void ShowError(std::stringstream& s, const std::string& string)
|
||||
static void ShowError(std::stringstream& s, const std::string& string)
|
||||
{
|
||||
s << "<b>ERROR:</b> " << string << "<br>\r\n";
|
||||
}
|
||||
|
||||
void ShowStatus (std::stringstream& s)
|
||||
static void ShowStatus (std::stringstream& s)
|
||||
{
|
||||
s << "<b>Uptime:</b> ";
|
||||
ShowUptime(s, i2p::context.GetUptime ());
|
||||
@@ -196,7 +201,7 @@ namespace http {
|
||||
}
|
||||
s << "<br>\r\n";
|
||||
#if (!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID))
|
||||
if (auto remains = Daemon.gracefullShutdownInterval) {
|
||||
if (auto remains = Daemon.gracefulShutdownInterval) {
|
||||
s << "<b>Stopping in:</b> ";
|
||||
s << remains << " seconds";
|
||||
s << "<br>\r\n";
|
||||
@@ -225,7 +230,7 @@ namespace http {
|
||||
else
|
||||
s << numKBytesSent / 1024 / 1024 << " GiB";
|
||||
s << " (" << (double) i2p::transport::transports.GetOutBandwidth () / 1024 << " KiB/s)<br>\r\n";
|
||||
s << "<b>Data path:</b> " << i2p::fs::GetDataDir() << "<br>\r\n<br>\r\n";
|
||||
s << "<b>Data path:</b> " << i2p::fs::GetDataDir() << "<br>\r\n";
|
||||
s << "<div class='slide'\r\n><label for='slide1'>Hidden content. Press on text to see.</label>\r\n<input type='checkbox' id='slide1'/>\r\n<p class='content'>\r\n";
|
||||
s << "<b>Router Ident:</b> " << i2p::context.GetRouterInfo().GetIdentHashBase64() << "<br>\r\n";
|
||||
s << "<b>Router Family:</b> " << i2p::context.GetRouterInfo().GetProperty("family") << "<br>\r\n";
|
||||
@@ -253,7 +258,7 @@ namespace http {
|
||||
s << address->host.to_string() << ":" << address->port << "<br>\r\n";
|
||||
}
|
||||
s << "</p>\r\n</div>\r\n";
|
||||
s << "<br>\r\n<b>Routers:</b> " << i2p::data::netdb.GetNumRouters () << " ";
|
||||
s << "<b>Routers:</b> " << i2p::data::netdb.GetNumRouters () << " ";
|
||||
s << "<b>Floodfills:</b> " << i2p::data::netdb.GetNumFloodfills () << " ";
|
||||
s << "<b>LeaseSets:</b> " << i2p::data::netdb.GetNumLeaseSets () << "<br>\r\n";
|
||||
|
||||
@@ -265,18 +270,75 @@ namespace http {
|
||||
s << "<b>Transit Tunnels:</b> " << std::to_string(transitTunnelCount) << "<br>\r\n";
|
||||
}
|
||||
|
||||
void ShowLocalDestinations (std::stringstream& s)
|
||||
static void ShowLocalDestinations (std::stringstream& s)
|
||||
{
|
||||
s << "<b>Local Destinations:</b><br>\r\n<br>\r\n";
|
||||
for (auto& it: i2p::client::context.GetDestinations ())
|
||||
{
|
||||
auto ident = it.second->GetIdentHash ();;
|
||||
auto ident = it.second->GetIdentHash ();
|
||||
s << "<a href=\"/?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
|
||||
s << i2p::client::context.GetAddressBook ().ToAddress(ident) << "</a><br>\r\n" << std::endl;
|
||||
}
|
||||
|
||||
auto i2cpServer = i2p::client::context.GetI2CPServer ();
|
||||
if (i2cpServer)
|
||||
{
|
||||
s << "<br><b>I2CP Local Destinations:</b><br>\r\n<br>\r\n";
|
||||
for (auto& it: i2cpServer->GetSessions ())
|
||||
{
|
||||
auto dest = it.second->GetDestination ();
|
||||
if (dest)
|
||||
{
|
||||
auto ident = dest->GetIdentHash ();
|
||||
s << "<a href=\"/?page=" << HTTP_PAGE_I2CP_LOCAL_DESTINATION << "&i2cp_id=" << it.first << "\">";
|
||||
s << i2p::client::context.GetAddressBook ().ToAddress(ident) << "</a><br>\r\n" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ShowLocalDestination (std::stringstream& s, const std::string& b32)
|
||||
static void ShowLeaseSetDestination (std::stringstream& s, std::shared_ptr<const i2p::client::LeaseSetDestination> dest)
|
||||
{
|
||||
s << "<b>Base64:</b><br>\r\n<textarea readonly=\"readonly\" cols=\"64\" rows=\"11\" wrap=\"on\">";
|
||||
s << dest->GetIdentity ()->ToBase64 () << "</textarea><br>\r\n<br>\r\n";
|
||||
s << "<b>LeaseSets:</b> <i>" << dest->GetNumRemoteLeaseSets () << "</i><br>\r\n";
|
||||
if(dest->GetNumRemoteLeaseSets())
|
||||
{
|
||||
s << "<div class='slide'\r\n><label for='slide1'>Hidden content. Press on text to see.</label>\r\n<input type='checkbox' id='slide1'/>\r\n<p class='content'>\r\n";
|
||||
for(auto& it: dest->GetLeaseSets ())
|
||||
s << it.second->GetIdentHash ().ToBase32 () << "<br>\r\n";
|
||||
s << "</p>\r\n</div>\r\n";
|
||||
}
|
||||
auto pool = dest->GetTunnelPool ();
|
||||
if (pool)
|
||||
{
|
||||
s << "<b>Inbound tunnels:</b><br>\r\n";
|
||||
for (auto & it : pool->GetInboundTunnels ()) {
|
||||
it->Print(s);
|
||||
if(it->LatencyIsKnown())
|
||||
s << " ( " << it->GetMeanLatency() << "ms )";
|
||||
ShowTunnelDetails(s, it->GetState (), it->GetNumReceivedBytes ());
|
||||
}
|
||||
s << "<br>\r\n";
|
||||
s << "<b>Outbound tunnels:</b><br>\r\n";
|
||||
for (auto & it : pool->GetOutboundTunnels ()) {
|
||||
it->Print(s);
|
||||
if(it->LatencyIsKnown())
|
||||
s << " ( " << it->GetMeanLatency() << "ms )";
|
||||
ShowTunnelDetails(s, it->GetState (), it->GetNumSentBytes ());
|
||||
}
|
||||
}
|
||||
s << "<br>\r\n";
|
||||
s << "<b>Tags</b><br>Incoming: " << dest->GetNumIncomingTags () << "<br>Outgoing:<br>" << std::endl;
|
||||
for (const auto& it: dest->GetSessions ())
|
||||
{
|
||||
s << i2p::client::context.GetAddressBook ().ToAddress(it.first) << " ";
|
||||
s << it.second->GetNumOutgoingTags () << "<br>" << std::endl;
|
||||
}
|
||||
s << "<br>" << std::endl;
|
||||
}
|
||||
|
||||
static void ShowLocalDestination (std::stringstream& s, const std::string& b32)
|
||||
{
|
||||
s << "<b>Local Destination:</b><br>\r\n<br>\r\n";
|
||||
i2p::data::IdentHash ident;
|
||||
@@ -284,44 +346,8 @@ namespace http {
|
||||
auto dest = i2p::client::context.FindLocalDestination (ident);
|
||||
if (dest)
|
||||
{
|
||||
s << "<b>Base64:</b><br>\r\n<textarea readonly=\"readonly\" cols=\"64\" rows=\"11\" wrap=\"on\">";
|
||||
s << dest->GetIdentity ()->ToBase64 () << "</textarea><br>\r\n<br>\r\n";
|
||||
s << "<b>LeaseSets:</b> <i>" << dest->GetNumRemoteLeaseSets () << "</i><br>\r\n";
|
||||
auto pool = dest->GetTunnelPool ();
|
||||
if (pool)
|
||||
{
|
||||
s << "<b>Inbound tunnels:</b><br>\r\n";
|
||||
for (auto & it : pool->GetInboundTunnels ()) {
|
||||
it->Print(s);
|
||||
ShowTunnelDetails(s, it->GetState (), it->GetNumReceivedBytes ());
|
||||
}
|
||||
s << "<br>\r\n";
|
||||
s << "<b>Outbound tunnels:</b><br>\r\n";
|
||||
for (auto & it : pool->GetOutboundTunnels ()) {
|
||||
it->Print(s);
|
||||
ShowTunnelDetails(s, it->GetState (), it->GetNumSentBytes ());
|
||||
}
|
||||
}
|
||||
s << "<br>\r\n";
|
||||
s << "<b>Tags</b><br>Incoming: " << dest->GetNumIncomingTags () << "<br>Outgoing:<br>" << std::endl;
|
||||
for (const auto& it: dest->GetSessions ())
|
||||
{
|
||||
s << i2p::client::context.GetAddressBook ().ToAddress(it.first) << " ";
|
||||
s << it.second->GetNumOutgoingTags () << "<br>" << std::endl;
|
||||
}
|
||||
s << "<br>" << std::endl;
|
||||
// s << "<br>\r\n<b>Streams:</b><br>\r\n";
|
||||
// for (auto it: dest->GetStreamingDestination ()->GetStreams ())
|
||||
// {
|
||||
// s << it.first << "->" << i2p::client::context.GetAddressBook ().ToAddress(it.second->GetRemoteIdentity ()) << " ";
|
||||
// s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
|
||||
// s << " [out:" << it.second->GetSendQueueSize () << "][in:" << it.second->GetReceiveQueueSize () << "]";
|
||||
// s << "[buf:" << it.second->GetSendBufferSize () << "]";
|
||||
// s << "[RTT:" << it.second->GetRTT () << "]";
|
||||
// s << "[Window:" << it.second->GetWindowSize () << "]";
|
||||
// s << "[Status:" << (int)it.second->GetStatus () << "]";
|
||||
// s << "<br>\r\n"<< std::endl;
|
||||
// }
|
||||
ShowLeaseSetDestination (s, dest);
|
||||
// show streams
|
||||
s << "<br>\r\n<table><caption>Streams</caption><tr>";
|
||||
s << "<th>StreamID</th>";
|
||||
s << "<th>Destination</th>";
|
||||
@@ -354,7 +380,23 @@ namespace http {
|
||||
}
|
||||
}
|
||||
|
||||
void ShowLeasesSets(std::stringstream& s)
|
||||
static void ShowI2CPLocalDestination (std::stringstream& s, const std::string& id)
|
||||
{
|
||||
auto i2cpServer = i2p::client::context.GetI2CPServer ();
|
||||
if (i2cpServer)
|
||||
{
|
||||
s << "<b>I2CP Local Destination:</b><br>\r\n<br>\r\n";
|
||||
auto it = i2cpServer->GetSessions ().find (std::stoi (id));
|
||||
if (it != i2cpServer->GetSessions ().end ())
|
||||
ShowLeaseSetDestination (s, it->second->GetDestination ());
|
||||
else
|
||||
ShowError(s, "I2CP session not found");
|
||||
}
|
||||
else
|
||||
ShowError(s, "I2CP is not enabled");
|
||||
}
|
||||
|
||||
static void ShowLeasesSets(std::stringstream& s)
|
||||
{
|
||||
s << "<div id='leasesets'><b>LeaseSets (click on to show info):</b></div><br>\r\n";
|
||||
int counter = 1;
|
||||
@@ -387,47 +429,51 @@ namespace http {
|
||||
// end for each lease set
|
||||
}
|
||||
|
||||
void ShowTunnels (std::stringstream& s)
|
||||
static void ShowTunnels (std::stringstream& s)
|
||||
{
|
||||
s << "<b>Queue size:</b> " << i2p::tunnel::tunnels.GetQueueSize () << "<br>\r\n";
|
||||
|
||||
s << "<b>Inbound tunnels:</b><br>\r\n";
|
||||
for (auto & it : i2p::tunnel::tunnels.GetInboundTunnels ()) {
|
||||
it->Print(s);
|
||||
if(it->LatencyIsKnown())
|
||||
s << " ( " << it->GetMeanLatency() << "ms )";
|
||||
ShowTunnelDetails(s, it->GetState (), it->GetNumReceivedBytes ());
|
||||
}
|
||||
s << "<br>\r\n";
|
||||
s << "<b>Outbound tunnels:</b><br>\r\n";
|
||||
for (auto & it : i2p::tunnel::tunnels.GetOutboundTunnels ()) {
|
||||
it->Print(s);
|
||||
if(it->LatencyIsKnown())
|
||||
s << " ( " << it->GetMeanLatency() << "ms )";
|
||||
ShowTunnelDetails(s, it->GetState (), it->GetNumSentBytes ());
|
||||
}
|
||||
s << "<br>\r\n";
|
||||
}
|
||||
|
||||
void ShowCommands (std::stringstream& s)
|
||||
static void ShowCommands (std::stringstream& s, uint32_t token)
|
||||
{
|
||||
/* commands */
|
||||
s << "<b>Router Commands</b><br>\r\n";
|
||||
s << " <a href=\"/?cmd=" << HTTP_COMMAND_RUN_PEER_TEST << "\">Run peer test</a><br>\r\n";
|
||||
s << " <a href=\"/?cmd=" << HTTP_COMMAND_RUN_PEER_TEST << "&token=" << token << "\">Run peer test</a><br>\r\n";
|
||||
//s << " <a href=\"/?cmd=" << HTTP_COMMAND_RELOAD_CONFIG << "\">Reload config</a><br>\r\n";
|
||||
if (i2p::context.AcceptsTunnels ())
|
||||
s << " <a href=\"/?cmd=" << HTTP_COMMAND_DISABLE_TRANSIT << "\">Decline transit tunnels</a><br>\r\n";
|
||||
s << " <a href=\"/?cmd=" << HTTP_COMMAND_DISABLE_TRANSIT << "&token=" << token << "\">Decline transit tunnels</a><br>\r\n";
|
||||
else
|
||||
s << " <a href=\"/?cmd=" << HTTP_COMMAND_ENABLE_TRANSIT << "\">Accept transit tunnels</a><br>\r\n";
|
||||
s << " <a href=\"/?cmd=" << HTTP_COMMAND_ENABLE_TRANSIT << "&token=" << token << "\">Accept transit tunnels</a><br>\r\n";
|
||||
#if (!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID))
|
||||
if (Daemon.gracefullShutdownInterval)
|
||||
s << " <a href=\"/?cmd=" << HTTP_COMMAND_SHUTDOWN_CANCEL << "\">Cancel gracefull shutdown</a><br>";
|
||||
if (Daemon.gracefulShutdownInterval)
|
||||
s << " <a href=\"/?cmd=" << HTTP_COMMAND_SHUTDOWN_CANCEL << "&token=" << token << "\">Cancel graceful shutdown</a><br>";
|
||||
else
|
||||
s << " <a href=\"/?cmd=" << HTTP_COMMAND_SHUTDOWN_START << "\">Start gracefull shutdown</a><br>\r\n";
|
||||
s << " <a href=\"/?cmd=" << HTTP_COMMAND_SHUTDOWN_START << "&token=" << token << "\">Start graceful shutdown</a><br>\r\n";
|
||||
#endif
|
||||
#ifdef WIN32_APP
|
||||
s << " <a href=\"/?cmd=" << HTTP_COMMAND_SHUTDOWN_START << "\">Gracefull shutdown</a><br>\r\n";
|
||||
s << " <a href=\"/?cmd=" << HTTP_COMMAND_SHUTDOWN_START << "&token=" << token << "\">Graceful shutdown</a><br>\r\n";
|
||||
#endif
|
||||
s << " <a href=\"/?cmd=" << HTTP_COMMAND_SHUTDOWN_NOW << "\">Force shutdown</a><br>\r\n";
|
||||
s << " <a href=\"/?cmd=" << HTTP_COMMAND_SHUTDOWN_NOW << "&token=" << token << "\">Force shutdown</a><br>\r\n";
|
||||
}
|
||||
|
||||
void ShowTransitTunnels (std::stringstream& s)
|
||||
static void ShowTransitTunnels (std::stringstream& s)
|
||||
{
|
||||
s << "<b>Transit tunnels:</b><br>\r\n<br>\r\n";
|
||||
for (const auto& it: i2p::tunnel::tunnels.GetTransitTunnels ())
|
||||
@@ -442,7 +488,7 @@ namespace http {
|
||||
}
|
||||
}
|
||||
|
||||
void ShowTransports (std::stringstream& s)
|
||||
static void ShowTransports (std::stringstream& s)
|
||||
{
|
||||
s << "<b>Transports:</b><br>\r\n<br>\r\n";
|
||||
auto ntcpServer = i2p::transport::transports.GetNTCPServer ();
|
||||
@@ -491,7 +537,7 @@ namespace http {
|
||||
}
|
||||
}
|
||||
|
||||
void ShowSAMSessions (std::stringstream& s)
|
||||
static void ShowSAMSessions (std::stringstream& s)
|
||||
{
|
||||
auto sam = i2p::client::context.GetSAMBridge ();
|
||||
if (!sam) {
|
||||
@@ -506,7 +552,7 @@ namespace http {
|
||||
}
|
||||
}
|
||||
|
||||
void ShowSAMSession (std::stringstream& s, const std::string& id)
|
||||
static void ShowSAMSession (std::stringstream& s, const std::string& id)
|
||||
{
|
||||
s << "<b>SAM Session:</b><br>\r\n<br>\r\n";
|
||||
auto sam = i2p::client::context.GetSAMBridge ();
|
||||
@@ -538,7 +584,7 @@ namespace http {
|
||||
}
|
||||
}
|
||||
|
||||
void ShowI2PTunnels (std::stringstream& s)
|
||||
static void ShowI2PTunnels (std::stringstream& s)
|
||||
{
|
||||
s << "<b>Client Tunnels:</b><br>\r\n<br>\r\n";
|
||||
for (auto& it: i2p::client::context.GetClientTunnels ())
|
||||
@@ -549,6 +595,15 @@ namespace http {
|
||||
s << i2p::client::context.GetAddressBook ().ToAddress(ident);
|
||||
s << "<br>\r\n"<< std::endl;
|
||||
}
|
||||
auto httpProxy = i2p::client::context.GetHttpProxy ();
|
||||
if (httpProxy)
|
||||
{
|
||||
auto& ident = httpProxy->GetLocalDestination ()->GetIdentHash();
|
||||
s << "<a href=\"/?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
|
||||
s << "HTTP Proxy" << "</a> ⇐ ";
|
||||
s << i2p::client::context.GetAddressBook ().ToAddress(ident);
|
||||
s << "<br>\r\n"<< std::endl;
|
||||
}
|
||||
s << "<br>\r\n<b>Server Tunnels:</b><br>\r\n<br>\r\n";
|
||||
for (auto& it: i2p::client::context.GetServerTunnels ())
|
||||
{
|
||||
@@ -654,11 +709,15 @@ namespace http {
|
||||
char b64_creds[64];
|
||||
std::size_t len = 0;
|
||||
len = i2p::data::ByteStreamToBase64((unsigned char *)expected.c_str(), expected.length(), b64_creds, sizeof(b64_creds));
|
||||
b64_creds[len] = '\0';
|
||||
expected = "Basic ";
|
||||
expected += b64_creds;
|
||||
if (provided == expected)
|
||||
return true;
|
||||
/* if we decoded properly then check credentials */
|
||||
if(len) {
|
||||
b64_creds[len] = '\0';
|
||||
expected = "Basic ";
|
||||
expected += b64_creds;
|
||||
return expected == provided;
|
||||
}
|
||||
/** we decoded wrong so it's not a correct login credential */
|
||||
return false;
|
||||
}
|
||||
|
||||
LogPrint(eLogWarning, "HTTPServer: auth failure from ", m_Socket->remote_endpoint().address ());
|
||||
@@ -697,6 +756,7 @@ namespace http {
|
||||
SendReply (res, content);
|
||||
}
|
||||
|
||||
std::map<uint32_t, uint32_t> HTTPConnection::m_Tokens;
|
||||
void HTTPConnection::HandlePage (const HTTPReq& req, HTTPRes& res, std::stringstream& s)
|
||||
{
|
||||
std::map<std::string, std::string> params;
|
||||
@@ -712,13 +772,28 @@ namespace http {
|
||||
else if (page == HTTP_PAGE_TUNNELS)
|
||||
ShowTunnels (s);
|
||||
else if (page == HTTP_PAGE_COMMANDS)
|
||||
ShowCommands (s);
|
||||
{
|
||||
uint32_t token;
|
||||
RAND_bytes ((uint8_t *)&token, 4);
|
||||
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
for (auto it = m_Tokens.begin (); it != m_Tokens.end (); )
|
||||
{
|
||||
if (ts > it->second + TOKEN_EXPIRATION_TIMEOUT)
|
||||
it = m_Tokens.erase (it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
m_Tokens[token] = ts;
|
||||
ShowCommands (s, token);
|
||||
}
|
||||
else if (page == HTTP_PAGE_TRANSIT_TUNNELS)
|
||||
ShowTransitTunnels (s);
|
||||
else if (page == HTTP_PAGE_LOCAL_DESTINATIONS)
|
||||
ShowLocalDestinations (s);
|
||||
else if (page == HTTP_PAGE_LOCAL_DESTINATION)
|
||||
ShowLocalDestination (s, params["b32"]);
|
||||
else if (page == HTTP_PAGE_I2CP_LOCAL_DESTINATION)
|
||||
ShowI2CPLocalDestination (s, params["i2cp_id"]);
|
||||
else if (page == HTTP_PAGE_SAM_SESSIONS)
|
||||
ShowSAMSessions (s);
|
||||
else if (page == HTTP_PAGE_SAM_SESSION)
|
||||
@@ -737,13 +812,19 @@ namespace http {
|
||||
void HTTPConnection::HandleCommand (const HTTPReq& req, HTTPRes& res, std::stringstream& s)
|
||||
{
|
||||
std::map<std::string, std::string> params;
|
||||
std::string cmd("");
|
||||
URL url;
|
||||
|
||||
url.parse(req.uri);
|
||||
url.parse_query(params);
|
||||
cmd = params["cmd"];
|
||||
|
||||
std::string token = params["token"];
|
||||
if (token.empty () || m_Tokens.find (std::stoi (token)) == m_Tokens.end ())
|
||||
{
|
||||
ShowError(s, "Invalid token");
|
||||
return;
|
||||
}
|
||||
|
||||
std::string cmd = params["cmd"];
|
||||
if (cmd == HTTP_COMMAND_RUN_PEER_TEST)
|
||||
i2p::transport::transports.PeerTest ();
|
||||
else if (cmd == HTTP_COMMAND_RELOAD_CONFIG)
|
||||
@@ -755,7 +836,7 @@ namespace http {
|
||||
else if (cmd == HTTP_COMMAND_SHUTDOWN_START) {
|
||||
i2p::context.SetAcceptsTunnels (false);
|
||||
#if (!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID))
|
||||
Daemon.gracefullShutdownInterval = 10*60;
|
||||
Daemon.gracefulShutdownInterval = 10*60;
|
||||
#endif
|
||||
#ifdef WIN32_APP
|
||||
i2p::win32::GracefulShutdown ();
|
||||
@@ -763,7 +844,7 @@ namespace http {
|
||||
} else if (cmd == HTTP_COMMAND_SHUTDOWN_CANCEL) {
|
||||
i2p::context.SetAcceptsTunnels (true);
|
||||
#if (!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID))
|
||||
Daemon.gracefullShutdownInterval = 0;
|
||||
Daemon.gracefulShutdownInterval = 0;
|
||||
#endif
|
||||
} else if (cmd == HTTP_COMMAND_SHUTDOWN_NOW) {
|
||||
Daemon.running = false;
|
||||
|
||||
20
HTTPServer.h
20
HTTPServer.h
@@ -1,10 +1,20 @@
|
||||
#ifndef HTTP_SERVER_H__
|
||||
#define HTTP_SERVER_H__
|
||||
|
||||
namespace i2p {
|
||||
namespace http {
|
||||
extern const char *itoopieFavicon;
|
||||
const size_t HTTP_CONNECTION_BUFFER_SIZE = 8192;
|
||||
#include <inttypes.h>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include <thread>
|
||||
#include <boost/asio.hpp>
|
||||
#include "HTTP.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace http
|
||||
{
|
||||
const size_t HTTP_CONNECTION_BUFFER_SIZE = 8192;
|
||||
const int TOKEN_EXPIRATION_TIMEOUT = 30; // in seconds
|
||||
|
||||
class HTTPConnection: public std::enable_shared_from_this<HTTPConnection>
|
||||
{
|
||||
@@ -35,6 +45,8 @@ namespace http {
|
||||
bool needAuth;
|
||||
std::string user;
|
||||
std::string pass;
|
||||
|
||||
static std::map<uint32_t, uint32_t> m_Tokens; // token->timestamp in seconds
|
||||
};
|
||||
|
||||
class HTTPServer
|
||||
|
||||
47
I2CP.cpp
47
I2CP.cpp
@@ -346,6 +346,7 @@ namespace client
|
||||
void I2CPSession::CreateSessionMessageHandler (const uint8_t * buf, size_t len)
|
||||
{
|
||||
RAND_bytes ((uint8_t *)&m_SessionID, 2);
|
||||
m_Owner.InsertSession (shared_from_this ());
|
||||
auto identity = std::make_shared<i2p::data::IdentityEx>();
|
||||
size_t offset = identity->FromBuffer (buf, len);
|
||||
if (!offset)
|
||||
@@ -460,23 +461,23 @@ namespace client
|
||||
{
|
||||
i2p::data::IdentityEx identity;
|
||||
size_t identsize = identity.FromBuffer (buf + offset, len - offset);
|
||||
if (identsize)
|
||||
{
|
||||
offset += identsize;
|
||||
uint32_t payloadLen = bufbe32toh (buf + offset);
|
||||
if (payloadLen + offset <= len)
|
||||
{
|
||||
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: cannot send message, too big");
|
||||
}
|
||||
else
|
||||
LogPrint(eLogError, "I2CP: invalid identity");
|
||||
if (identsize)
|
||||
{
|
||||
offset += identsize;
|
||||
uint32_t payloadLen = bufbe32toh (buf + offset);
|
||||
if (payloadLen + offset <= len)
|
||||
{
|
||||
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: cannot send message, too big");
|
||||
}
|
||||
else
|
||||
LogPrint(eLogError, "I2CP: invalid identity");
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -711,7 +712,6 @@ namespace client
|
||||
{
|
||||
LogPrint (eLogDebug, "I2CP: new connection from ", ep);
|
||||
auto session = std::make_shared<I2CPSession>(*this, socket);
|
||||
m_Sessions[session->GetSessionID ()] = session;
|
||||
session->Start ();
|
||||
}
|
||||
else
|
||||
@@ -724,6 +724,17 @@ namespace client
|
||||
Accept ();
|
||||
}
|
||||
|
||||
bool I2CPServer::InsertSession (std::shared_ptr<I2CPSession> session)
|
||||
{
|
||||
if (!session) return false;
|
||||
if (!m_Sessions.insert({session->GetSessionID (), session}).second)
|
||||
{
|
||||
LogPrint (eLogError, "I2CP: duplicate session id ", session->GetSessionID ());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void I2CPServer::RemoveSession (uint16_t sessionID)
|
||||
{
|
||||
m_Sessions.erase (sessionID);
|
||||
|
||||
5
I2CP.h
5
I2CP.h
@@ -112,6 +112,7 @@ namespace client
|
||||
void Start ();
|
||||
void Stop ();
|
||||
uint16_t GetSessionID () const { return m_SessionID; };
|
||||
std::shared_ptr<const I2CPDestination> GetDestination () const { return m_Destination; };
|
||||
|
||||
// called from I2CPDestination
|
||||
void SendI2CPMessage (uint8_t type, const uint8_t * payload, size_t len);
|
||||
@@ -173,6 +174,7 @@ namespace client
|
||||
void Stop ();
|
||||
boost::asio::io_service& GetService () { return m_Service; };
|
||||
|
||||
bool InsertSession (std::shared_ptr<I2CPSession> session);
|
||||
void RemoveSession (uint16_t sessionID);
|
||||
|
||||
private:
|
||||
@@ -196,6 +198,9 @@ namespace client
|
||||
public:
|
||||
|
||||
const decltype(m_MessagesHandlers)& GetMessagesHandlers () const { return m_MessagesHandlers; };
|
||||
|
||||
// for HTTP
|
||||
const decltype(m_Sessions)& GetSessions () const { return m_Sessions; };
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -547,7 +547,6 @@ namespace i2p
|
||||
uint8_t typeID = msg[I2NP_HEADER_TYPEID_OFFSET];
|
||||
uint32_t msgID = bufbe32toh (msg + I2NP_HEADER_MSGID_OFFSET);
|
||||
LogPrint (eLogDebug, "I2NP: msg received len=", len,", type=", (int)typeID, ", msgID=", (unsigned int)msgID);
|
||||
|
||||
uint8_t * buf = msg + I2NP_HEADER_SIZE;
|
||||
int size = bufbe16toh (msg + I2NP_HEADER_SIZE_OFFSET);
|
||||
switch (typeID)
|
||||
|
||||
@@ -38,6 +38,7 @@ namespace client
|
||||
}
|
||||
|
||||
inline std::shared_ptr<ClientDestination> GetLocalDestination () { return m_LocalDestination; }
|
||||
inline std::shared_ptr<const ClientDestination> GetLocalDestination () const { return m_LocalDestination; }
|
||||
inline void SetLocalDestination (std::shared_ptr<ClientDestination> dest) { m_LocalDestination = dest; }
|
||||
void CreateStream (StreamRequestComplete streamRequestComplete, const std::string& dest, int port = 0);
|
||||
|
||||
|
||||
@@ -92,7 +92,9 @@ namespace client
|
||||
m_Stream->Close ();
|
||||
m_Stream.reset ();
|
||||
}
|
||||
m_Socket->shutdown(boost::asio::ip::tcp::socket::shutdown_send); // avoid RST
|
||||
m_Socket->close ();
|
||||
|
||||
Done(shared_from_this ());
|
||||
}
|
||||
|
||||
@@ -107,9 +109,11 @@ namespace client
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint (eLogError, "I2PTunnel: read error: ", ecode.message ());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
LogPrint (eLogError, "I2PTunnel: read error: ", ecode.message ());
|
||||
Terminate ();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -173,9 +177,13 @@ namespace client
|
||||
{
|
||||
if (bytes_transferred > 0)
|
||||
Write (m_StreamBuffer, bytes_transferred); // postpone termination
|
||||
else
|
||||
else if (ecode == boost::asio::error::timed_out && m_Stream->IsOpen ())
|
||||
StreamReceive ();
|
||||
else
|
||||
Terminate ();
|
||||
}
|
||||
}
|
||||
else
|
||||
Terminate ();
|
||||
}
|
||||
else
|
||||
Write (m_StreamBuffer, bytes_transferred);
|
||||
@@ -236,8 +244,8 @@ namespace client
|
||||
if (line == "\r") endOfHeader = true;
|
||||
else
|
||||
{
|
||||
if (line.find ("Host:") != std::string::npos)
|
||||
m_OutHeader << "Host: " << m_Host << "\r\n";
|
||||
if (m_Host.length () > 0 && line.find ("Host:") != std::string::npos)
|
||||
m_OutHeader << "Host: " << m_Host << "\r\n"; // override host
|
||||
else
|
||||
m_OutHeader << line << "\n";
|
||||
}
|
||||
@@ -273,26 +281,24 @@ namespace client
|
||||
|
||||
void I2PTunnelConnectionIRC::Write (const uint8_t * buf, size_t len)
|
||||
{
|
||||
if (m_NeedsWebIrc) {
|
||||
m_OutPacket.str ("");
|
||||
if (m_NeedsWebIrc)
|
||||
{
|
||||
m_NeedsWebIrc = false;
|
||||
m_OutPacket.str ("");
|
||||
m_OutPacket << "WEBIRC " << this->m_WebircPass << " cgiirc " << context.GetAddressBook ().ToAddress (m_From->GetIdentHash ()) << " 127.0.0.1\n";
|
||||
I2PTunnelConnection::Write ((uint8_t *)m_OutPacket.str ().c_str (), m_OutPacket.str ().length ());
|
||||
m_OutPacket << "WEBIRC " << m_WebircPass << " cgiirc " << context.GetAddressBook ().ToAddress (m_From->GetIdentHash ()) << " 127.0.0.1\n";
|
||||
}
|
||||
|
||||
std::string line;
|
||||
m_OutPacket.str ("");
|
||||
m_InPacket.clear ();
|
||||
m_InPacket.write ((const char *)buf, len);
|
||||
|
||||
while (!m_InPacket.eof () && !m_InPacket.fail ())
|
||||
{
|
||||
std::string line;
|
||||
std::getline (m_InPacket, line);
|
||||
if (line.length () == 0 && m_InPacket.eof ()) {
|
||||
if (line.length () == 0 && m_InPacket.eof ())
|
||||
m_InPacket.str ("");
|
||||
}
|
||||
auto pos = line.find ("USER");
|
||||
if (pos != std::string::npos && pos == 0)
|
||||
if (!pos) // start of line
|
||||
{
|
||||
pos = line.find (" ");
|
||||
pos++;
|
||||
@@ -302,9 +308,9 @@ namespace client
|
||||
m_OutPacket << line.substr (0, pos);
|
||||
m_OutPacket << context.GetAddressBook ().ToAddress (m_From->GetIdentHash ());
|
||||
m_OutPacket << line.substr (nextpos) << '\n';
|
||||
} else {
|
||||
}
|
||||
else
|
||||
m_OutPacket << line << '\n';
|
||||
}
|
||||
}
|
||||
I2PTunnelConnection::Write ((uint8_t *)m_OutPacket.str ().c_str (), m_OutPacket.str ().length ());
|
||||
}
|
||||
@@ -501,7 +507,7 @@ namespace client
|
||||
int port, std::shared_ptr<ClientDestination> localDestination,
|
||||
const std::string& host, int inport, bool gzip):
|
||||
I2PServerTunnel (name, address, port, localDestination, inport, gzip),
|
||||
m_Host (host.length () > 0 ? host : address)
|
||||
m_Host (host)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -540,9 +546,13 @@ namespace client
|
||||
void I2PUDPServerTunnel::ExpireStale(const uint64_t delta) {
|
||||
std::lock_guard<std::mutex> lock(m_SessionsMutex);
|
||||
uint64_t now = i2p::util::GetMillisecondsSinceEpoch();
|
||||
std::remove_if(m_Sessions.begin(), m_Sessions.end(), [now, delta](const UDPSession * u) -> bool {
|
||||
return now - u->LastActivity >= delta;
|
||||
});
|
||||
auto itr = m_Sessions.begin();
|
||||
while(itr != m_Sessions.end()) {
|
||||
if(now - (*itr)->LastActivity >= delta )
|
||||
itr = m_Sessions.erase(itr);
|
||||
else
|
||||
++itr;
|
||||
}
|
||||
}
|
||||
|
||||
UDPSession * I2PUDPServerTunnel::ObtainUDPSession(const i2p::data::IdentityEx& from, uint16_t localPort, uint16_t remotePort)
|
||||
|
||||
25
Identity.cpp
25
Identity.cpp
@@ -385,7 +385,14 @@ namespace data
|
||||
else
|
||||
{
|
||||
delete verifier;
|
||||
while (!m_Verifier) ; // spin lock
|
||||
int count = 0;
|
||||
while (!m_Verifier && count < 500) // 5 seconds
|
||||
{
|
||||
std::this_thread::sleep_for (std::chrono::milliseconds(10));
|
||||
count++;
|
||||
}
|
||||
if (!m_Verifier)
|
||||
LogPrint (eLogError, "Identity: couldn't get verifier in 5 seconds");
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -481,7 +488,7 @@ namespace data
|
||||
switch (m_Public->GetSigningKeyType ())
|
||||
{
|
||||
case SIGNING_KEY_TYPE_DSA_SHA1:
|
||||
m_Signer.reset (new i2p::crypto::DSASigner (m_SigningPrivateKey));
|
||||
m_Signer.reset (new i2p::crypto::DSASigner (m_SigningPrivateKey, m_Public->GetStandardIdentity ().signingKey));
|
||||
break;
|
||||
case SIGNING_KEY_TYPE_ECDSA_SHA256_P256:
|
||||
m_Signer.reset (new i2p::crypto::ECDSAP256Signer (m_SigningPrivateKey));
|
||||
@@ -586,11 +593,25 @@ namespace data
|
||||
XORMetric operator^(const IdentHash& key1, const IdentHash& key2)
|
||||
{
|
||||
XORMetric m;
|
||||
#if defined(__AVX__) // for AVX
|
||||
__asm__
|
||||
(
|
||||
"vmovups %1, %%ymm0 \n"
|
||||
"vmovups %2, %%ymm1 \n"
|
||||
"vxorps %%ymm0, %%ymm1, %%ymm1 \n"
|
||||
"vmovups %%ymm1, %0 \n"
|
||||
: "=m"(*m.metric)
|
||||
: "m"(*key1), "m"(*key2)
|
||||
: "memory", "%xmm0", "%xmm1" // should be replaced by %ymm0/1 once supported by compiler
|
||||
);
|
||||
#else
|
||||
const uint64_t * hash1 = key1.GetLL (), * hash2 = key2.GetLL ();
|
||||
m.metric_ll[0] = hash1[0] ^ hash2[0];
|
||||
m.metric_ll[1] = hash1[1] ^ hash2[1];
|
||||
m.metric_ll[2] = hash1[2] ^ hash2[2];
|
||||
m.metric_ll[3] = hash1[3] ^ hash2[3];
|
||||
#endif
|
||||
|
||||
return m;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ namespace data
|
||||
if (m_StoreLeases)
|
||||
{
|
||||
auto ret = m_Leases.insert (std::make_shared<Lease>(lease));
|
||||
if (!ret.second) *(*ret.first) = lease; // update existing
|
||||
if (!ret.second) (*ret.first)->endDate = lease.endDate; // update existing
|
||||
(*ret.first)->isUpdated = true;
|
||||
// check if lease's gateway is in our netDb
|
||||
if (!netdb.FindRouter (lease.tunnelGateway))
|
||||
|
||||
114
Log.cpp
114
Log.cpp
@@ -22,6 +22,22 @@ namespace log {
|
||||
"debug" // eLogDebug
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Colorize log output -- array of terminal control sequences
|
||||
* @note Using ISO 6429 (ANSI) color sequences
|
||||
*/
|
||||
#ifdef _WIN32
|
||||
static const char *LogMsgColors[] = { "", "", "", "", "" };
|
||||
#else /* UNIX */
|
||||
static const char *LogMsgColors[] = {
|
||||
[eLogError] = "\033[1;31m", /* red */
|
||||
[eLogWarning] = "\033[1;33m", /* yellow */
|
||||
[eLogInfo] = "\033[1;36m", /* cyan */
|
||||
[eLogDebug] = "\033[1;34m", /* blue */
|
||||
[eNumLogLevels] = "\033[0m", /* reset */
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
/**
|
||||
* @brief Maps our log levels to syslog one
|
||||
@@ -42,13 +58,30 @@ namespace log {
|
||||
|
||||
Log::Log():
|
||||
m_Destination(eLogStdout), m_MinLevel(eLogInfo),
|
||||
m_LogStream (nullptr), m_Logfile(""), m_IsReady(false)
|
||||
m_LogStream (nullptr), m_Logfile(""), m_HasColors(true),
|
||||
m_IsRunning (false), m_Thread (nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
Log::~Log ()
|
||||
{
|
||||
switch (m_Destination) {
|
||||
delete m_Thread;
|
||||
}
|
||||
|
||||
void Log::Start ()
|
||||
{
|
||||
if (!m_IsRunning)
|
||||
{
|
||||
Reopen ();
|
||||
m_IsRunning = true;
|
||||
m_Thread = new std::thread (std::bind (&Log::Run, this));
|
||||
}
|
||||
}
|
||||
|
||||
void Log::Stop ()
|
||||
{
|
||||
switch (m_Destination)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
case eLogSyslog :
|
||||
closelog();
|
||||
@@ -62,7 +95,14 @@ namespace log {
|
||||
/* do nothing */
|
||||
break;
|
||||
}
|
||||
Process();
|
||||
m_IsRunning = false;
|
||||
m_Queue.WakeUp ();
|
||||
if (m_Thread)
|
||||
{
|
||||
m_Thread->join ();
|
||||
delete m_Thread;
|
||||
m_Thread = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Log::SetLogLevel (const std::string& level) {
|
||||
@@ -90,45 +130,52 @@ namespace log {
|
||||
* Unfortunately, with current startup process with late fork() this
|
||||
* will give us nothing but pain. Maybe later. See in NetDb as example.
|
||||
*/
|
||||
void Log::Process() {
|
||||
std::unique_lock<std::mutex> l(m_OutputLock);
|
||||
void Log::Process(std::shared_ptr<LogMsg> msg)
|
||||
{
|
||||
if (!msg) return;
|
||||
std::hash<std::thread::id> hasher;
|
||||
unsigned short short_tid;
|
||||
while (1) {
|
||||
auto msg = m_Queue.GetNextWithTimeout (1);
|
||||
if (!msg)
|
||||
break;
|
||||
short_tid = (short) (hasher(msg->tid) % 1000);
|
||||
switch (m_Destination) {
|
||||
short_tid = (short) (hasher(msg->tid) % 1000);
|
||||
switch (m_Destination) {
|
||||
#ifndef _WIN32
|
||||
case eLogSyslog:
|
||||
syslog(GetSyslogPrio(msg->level), "[%03u] %s", short_tid, msg->text.c_str());
|
||||
break;
|
||||
case eLogSyslog:
|
||||
syslog(GetSyslogPrio(msg->level), "[%03u] %s", short_tid, msg->text.c_str());
|
||||
break;
|
||||
#endif
|
||||
case eLogFile:
|
||||
case eLogStream:
|
||||
if (m_LogStream)
|
||||
*m_LogStream << TimeAsString(msg->timestamp)
|
||||
<< "@" << short_tid
|
||||
<< "/" << g_LogLevelStr[msg->level]
|
||||
<< " - " << msg->text << std::endl;
|
||||
break;
|
||||
case eLogStdout:
|
||||
default:
|
||||
std::cout << TimeAsString(msg->timestamp)
|
||||
case eLogFile:
|
||||
case eLogStream:
|
||||
if (m_LogStream)
|
||||
*m_LogStream << TimeAsString(msg->timestamp)
|
||||
<< "@" << short_tid
|
||||
<< "/" << g_LogLevelStr[msg->level]
|
||||
<< " - " << msg->text << std::endl;
|
||||
break;
|
||||
} // switch
|
||||
} // while
|
||||
break;
|
||||
case eLogStdout:
|
||||
default:
|
||||
std::cout << TimeAsString(msg->timestamp)
|
||||
<< "@" << short_tid
|
||||
<< "/" << LogMsgColors[msg->level] << g_LogLevelStr[msg->level] << LogMsgColors[eNumLogLevels]
|
||||
<< " - " << msg->text << std::endl;
|
||||
break;
|
||||
} // switch
|
||||
}
|
||||
|
||||
void Log::Append(std::shared_ptr<i2p::log::LogMsg> & msg) {
|
||||
void Log::Run ()
|
||||
{
|
||||
while (m_IsRunning)
|
||||
{
|
||||
std::shared_ptr<LogMsg> msg;
|
||||
while (msg = m_Queue.Get ())
|
||||
Process (msg);
|
||||
if (m_LogStream) m_LogStream->flush();
|
||||
if (m_IsRunning)
|
||||
m_Queue.Wait ();
|
||||
}
|
||||
}
|
||||
|
||||
void Log::Append(std::shared_ptr<i2p::log::LogMsg> & msg)
|
||||
{
|
||||
m_Queue.Put(msg);
|
||||
if (!m_IsReady)
|
||||
return;
|
||||
Process();
|
||||
}
|
||||
|
||||
void Log::SendTo (const std::string& path)
|
||||
@@ -138,6 +185,7 @@ namespace log {
|
||||
auto os = std::make_shared<std::ofstream> (path, flags);
|
||||
if (os->is_open ())
|
||||
{
|
||||
m_HasColors = false;
|
||||
m_Logfile = path;
|
||||
m_Destination = eLogFile;
|
||||
m_LogStream = os;
|
||||
@@ -147,12 +195,14 @@ namespace log {
|
||||
}
|
||||
|
||||
void Log::SendTo (std::shared_ptr<std::ostream> os) {
|
||||
m_HasColors = false;
|
||||
m_Destination = eLogStream;
|
||||
m_LogStream = os;
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
void Log::SendTo(const char *name, int facility) {
|
||||
m_HasColors = false;
|
||||
m_Destination = eLogSyslog;
|
||||
m_LogStream = nullptr;
|
||||
openlog(name, LOG_CONS | LOG_PID, facility);
|
||||
|
||||
43
Log.h
43
Log.h
@@ -16,6 +16,7 @@
|
||||
#include <sstream>
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include "Queue.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
@@ -40,17 +41,6 @@ enum LogType {
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef _WIN32
|
||||
const char LOG_COLOR_ERROR[] = "";
|
||||
const char LOG_COLOR_WARNING[] = "";
|
||||
const char LOG_COLOR_RESET[] = "";
|
||||
#else
|
||||
const char LOG_COLOR_ERROR[] = "\033[1;31m";
|
||||
const char LOG_COLOR_WARNING[] = "\033[1;33m";
|
||||
const char LOG_COLOR_RESET[] = "\033[0m";
|
||||
#endif
|
||||
|
||||
|
||||
namespace i2p {
|
||||
namespace log {
|
||||
|
||||
@@ -67,8 +57,9 @@ namespace log {
|
||||
std::time_t m_LastTimestamp;
|
||||
char m_LastDateTime[64];
|
||||
i2p::util::Queue<std::shared_ptr<LogMsg> > m_Queue;
|
||||
volatile bool m_IsReady;
|
||||
mutable std::mutex m_OutputLock;
|
||||
bool m_HasColors;
|
||||
volatile bool m_IsRunning;
|
||||
std::thread * m_Thread;
|
||||
|
||||
private:
|
||||
|
||||
@@ -76,10 +67,8 @@ namespace log {
|
||||
Log (const Log &);
|
||||
const Log& operator=(const Log&);
|
||||
|
||||
/**
|
||||
* @brief process stored messages in queue
|
||||
*/
|
||||
void Process ();
|
||||
void Run ();
|
||||
void Process (std::shared_ptr<LogMsg> msg);
|
||||
|
||||
/**
|
||||
* @brief Makes formatted string from unix timestamp
|
||||
@@ -97,6 +86,9 @@ namespace log {
|
||||
LogType GetLogType () { return m_Destination; };
|
||||
LogLevel GetLogLevel () { return m_MinLevel; };
|
||||
|
||||
void Start ();
|
||||
void Stop ();
|
||||
|
||||
/**
|
||||
* @brief Sets minimal allowed level for log messages
|
||||
* @param level String with wanted minimal msg level
|
||||
@@ -130,12 +122,6 @@ namespace log {
|
||||
*/
|
||||
void Append(std::shared_ptr<i2p::log::LogMsg> &);
|
||||
|
||||
/** @brief Allow log output */
|
||||
void Ready() { m_IsReady = true; }
|
||||
|
||||
/** @brief Flushes the output log stream */
|
||||
void Flush();
|
||||
|
||||
/** @brief Reopen log file */
|
||||
void Reopen();
|
||||
};
|
||||
@@ -152,13 +138,13 @@ namespace log {
|
||||
std::string text; /**< message text as single string */
|
||||
LogLevel level; /**< message level */
|
||||
std::thread::id tid; /**< id of thread that generated message */
|
||||
|
||||
|
||||
LogMsg (LogLevel lvl, std::time_t ts, const std::string & txt): timestamp(ts), text(txt), level(lvl) {};
|
||||
};
|
||||
|
||||
Log & Logger();
|
||||
} // log
|
||||
} // i2p
|
||||
}
|
||||
|
||||
/** internal usage only -- folding args array to single string */
|
||||
template<typename TValue>
|
||||
@@ -190,15 +176,8 @@ void LogPrint (LogLevel level, TArgs&&... args) noexcept
|
||||
// fold message to single string
|
||||
std::stringstream ss("");
|
||||
|
||||
if(level == eLogError) // if log level is ERROR color log message red
|
||||
ss << LOG_COLOR_ERROR;
|
||||
else if (level == eLogWarning) // if log level is WARN color log message yellow
|
||||
ss << LOG_COLOR_WARNING;
|
||||
LogPrint (ss, std::forward<TArgs>(args)...);
|
||||
|
||||
// reset color
|
||||
ss << LOG_COLOR_RESET;
|
||||
|
||||
auto msg = std::make_shared<i2p::log::LogMsg>(level, std::time(nullptr), ss.str());
|
||||
msg->tid = std::this_thread::get_id();
|
||||
log.Append(msg);
|
||||
|
||||
23
Makefile
23
Makefile
@@ -4,15 +4,21 @@ ARLIB := libi2pd.a
|
||||
SHLIB_CLIENT := libi2pdclient.so
|
||||
ARLIB_CLIENT := libi2pdclient.a
|
||||
I2PD := i2pd
|
||||
GREP := fgrep
|
||||
GREP := grep
|
||||
DEPS := obj/make.dep
|
||||
|
||||
include filelist.mk
|
||||
|
||||
USE_AESNI := yes
|
||||
USE_STATIC := no
|
||||
USE_MESHNET := no
|
||||
USE_UPNP := no
|
||||
USE_AESNI := yes
|
||||
USE_AVX := yes
|
||||
USE_STATIC := no
|
||||
USE_MESHNET := no
|
||||
USE_UPNP := no
|
||||
|
||||
ifeq ($(WEBSOCKETS),1)
|
||||
NEEDED_CXXFLAGS += -DWITH_EVENTS
|
||||
DAEMON_SRC += Websocket.cpp
|
||||
endif
|
||||
|
||||
ifeq ($(UNAME),Darwin)
|
||||
DAEMON_SRC += DaemonLinux.cpp
|
||||
@@ -21,7 +27,7 @@ ifeq ($(UNAME),Darwin)
|
||||
else
|
||||
include Makefile.osx
|
||||
endif
|
||||
else ifeq ($(shell echo $(UNAME) | $(GREP) -c FreeBSD),1)
|
||||
else ifeq ($(shell echo $(UNAME) | $(GREP) -Ec '(Free|Open)BSD'),1)
|
||||
DAEMON_SRC += DaemonLinux.cpp
|
||||
include Makefile.bsd
|
||||
else ifeq ($(UNAME),Linux)
|
||||
@@ -89,10 +95,15 @@ strip: $(I2PD) $(SHLIB_CLIENT) $(SHLIB)
|
||||
strip $^
|
||||
|
||||
LATEST_TAG=$(shell git describe --tags --abbrev=0 openssl)
|
||||
BRANCH=$(shell git rev-parse --abbrev-ref HEAD)
|
||||
dist:
|
||||
git archive --format=tar.gz -9 --worktree-attributes \
|
||||
--prefix=i2pd_$(LATEST_TAG)/ $(LATEST_TAG) -o i2pd_$(LATEST_TAG).tar.gz
|
||||
|
||||
last-dist:
|
||||
git archive --format=tar.gz -9 --worktree-attributes \
|
||||
--prefix=i2pd_$(LATEST_TAG)/ $(BRANCH) -o ../i2pd_$(LATEST_TAG).orig.tar.gz
|
||||
|
||||
doxygen:
|
||||
doxygen -s docs/Doxyfile
|
||||
|
||||
|
||||
@@ -39,24 +39,35 @@ ifeq ($(USE_STATIC),yes)
|
||||
LDLIBS += $(LIBDIR)/libssl.a
|
||||
LDLIBS += $(LIBDIR)/libcrypto.a
|
||||
LDLIBS += $(LIBDIR)/libz.a
|
||||
LDLIBS += -lpthread -static-libstdc++ -static-libgcc -lrt
|
||||
LDLIBS += -lpthread -static-libstdc++ -static-libgcc -lrt -ldl
|
||||
USE_AESNI := no
|
||||
else
|
||||
LDLIBS = -lcrypto -lssl -lz -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread
|
||||
endif
|
||||
|
||||
# UPNP Support (miniupnpc 1.5 or 1.6)
|
||||
# UPNP Support (miniupnpc 1.5 and higher)
|
||||
ifeq ($(USE_UPNP),yes)
|
||||
LDFLAGS += -lminiupnpc
|
||||
CXXFLAGS += -DUSE_UPNP
|
||||
ifeq ($(USE_STATIC),yes)
|
||||
LDLIBS += $(LIBDIR)/libminiupnpc.a
|
||||
else
|
||||
LDLIBS += -lminiupnpc
|
||||
endif
|
||||
endif
|
||||
|
||||
IS_64 := $(shell $(CXX) -dumpmachine 2>&1 | $(GREP) -c "64")
|
||||
ifeq ($(USE_AESNI),yes)
|
||||
ifeq ($(IS_64),1)
|
||||
#check if AES-NI is supported by CPU
|
||||
ifneq ($(shell grep -c aes /proc/cpuinfo),0)
|
||||
CPU_FLAGS = -maes -DAESNI
|
||||
ifneq ($(shell $(GREP) -c aes /proc/cpuinfo),0)
|
||||
CPU_FLAGS += -maes -DAESNI
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(USE_AVX),yes)
|
||||
#check if AVX supported by CPU
|
||||
ifneq ($(shell $(GREP) -c avx /proc/cpuinfo),0)
|
||||
CPU_FLAGS += -mavx
|
||||
endif
|
||||
endif
|
||||
|
||||
@@ -39,9 +39,13 @@ endif
|
||||
|
||||
# don't change following line to ifeq ($(USE_AESNI),yes) !!!
|
||||
ifeq ($(USE_AESNI),1)
|
||||
CPU_FLAGS = -maes -DAESNI
|
||||
CPU_FLAGS += -maes -DAESNI
|
||||
else
|
||||
CPU_FLAGS = -msse
|
||||
CPU_FLAGS += -msse
|
||||
endif
|
||||
|
||||
ifeq ($(USE_AVX),1)
|
||||
CPU_FLAGS += -mavx
|
||||
endif
|
||||
|
||||
ifeq ($(USE_ASLR),yes)
|
||||
|
||||
13
Makefile.osx
13
Makefile.osx
@@ -3,21 +3,26 @@ CXXFLAGS = -g -Wall -std=c++11 -DMAC_OSX
|
||||
#CXXFLAGS = -g -O2 -Wall -std=c++11
|
||||
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
|
||||
|
||||
ifeq ($(USE_STATIC),yes)
|
||||
LDLIBS = -lz -lcrypto -lssl /usr/local/lib/libboost_system.a /usr/local/lib/libboost_date_time.a /usr/local/lib/libboost_filesystem.a /usr/local/lib/libboost_program_options.a -lpthread
|
||||
else
|
||||
LDLIBS = -lz -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread
|
||||
endif
|
||||
|
||||
ifeq ($(USE_UPNP),yes)
|
||||
LDFLAGS += -ldl
|
||||
CXXFLAGS += -DUSE_UPNP
|
||||
endif
|
||||
|
||||
# OSX Notes
|
||||
# http://www.hutsby.net/2011/08/macs-with-aes-ni.html
|
||||
# Seems like all recent Mac's have AES-NI, after firmware upgrade 2.2
|
||||
# Found no good way to detect it from command line. TODO: Might be some osx sysinfo magic
|
||||
ifeq ($(USE_AESNI),yes)
|
||||
CXXFLAGS += -maes -DAESNI
|
||||
endif
|
||||
|
||||
ifeq ($(USE_AVX),yes)
|
||||
CXXFLAGS += -mavx
|
||||
endif
|
||||
|
||||
# Disabled, since it will be the default make rule. I think its better
|
||||
# to define the default rule in Makefile and not Makefile.<ostype> - torkel
|
||||
#install: all
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
#include "Transports.h"
|
||||
#include "NetDb.h"
|
||||
#include "NTCPSession.h"
|
||||
#ifdef WITH_EVENTS
|
||||
#include "Event.h"
|
||||
#endif
|
||||
|
||||
using namespace i2p::crypto;
|
||||
|
||||
@@ -534,11 +537,11 @@ namespace transport
|
||||
{
|
||||
boost::system::error_code ec;
|
||||
size_t moreBytes = m_Socket.available(ec);
|
||||
if (moreBytes)
|
||||
if (moreBytes && !ec)
|
||||
{
|
||||
if (moreBytes > NTCP_BUFFER_SIZE - m_ReceiveBufferOffset)
|
||||
moreBytes = NTCP_BUFFER_SIZE - m_ReceiveBufferOffset;
|
||||
moreBytes = m_Socket.read_some (boost::asio::buffer (m_ReceiveBuffer + m_ReceiveBufferOffset, moreBytes));
|
||||
moreBytes = m_Socket.read_some (boost::asio::buffer (m_ReceiveBuffer + m_ReceiveBufferOffset, moreBytes), ec);
|
||||
if (ec)
|
||||
{
|
||||
LogPrint (eLogInfo, "NTCP: Read more bytes error: ", ec.message ());
|
||||
@@ -571,40 +574,46 @@ namespace transport
|
||||
if (dataSize)
|
||||
{
|
||||
// new message
|
||||
if (dataSize + 16U > NTCP_MAX_MESSAGE_SIZE - 2) // + 6 + padding
|
||||
if (dataSize + 16U + 15U > NTCP_MAX_MESSAGE_SIZE - 2) // + 6 + padding
|
||||
{
|
||||
LogPrint (eLogError, "NTCP: data size ", dataSize, " exceeds max size");
|
||||
return false;
|
||||
}
|
||||
auto msg = (dataSize + 16U) <= I2NP_MAX_SHORT_MESSAGE_SIZE - 2 ? NewI2NPShortMessage () : NewI2NPMessage ();
|
||||
m_NextMessage = msg;
|
||||
memcpy (m_NextMessage->buf, buf, 16);
|
||||
m_NextMessage = (dataSize + 16U + 15U) <= I2NP_MAX_SHORT_MESSAGE_SIZE - 2 ? NewI2NPShortMessage () : NewI2NPMessage ();
|
||||
m_NextMessage->Align (16);
|
||||
m_NextMessage->offset += 2; // size field
|
||||
m_NextMessage->len = m_NextMessage->offset + dataSize;
|
||||
memcpy (m_NextMessage->GetBuffer () - 2, buf, 16);
|
||||
m_NextMessageOffset = 16;
|
||||
m_NextMessage->offset = 2; // size field
|
||||
m_NextMessage->len = dataSize + 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
// timestamp
|
||||
LogPrint (eLogDebug, "NTCP: Timestamp");
|
||||
int diff = (int)bufbe32toh (buf + 2) - (int)i2p::util::GetSecondsSinceEpoch ();
|
||||
LogPrint (eLogInfo, "NTCP: Timestamp. Time difference ", diff, " seconds");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else // message continues
|
||||
{
|
||||
m_Decryption.Decrypt (encrypted, m_NextMessage->buf + m_NextMessageOffset);
|
||||
m_Decryption.Decrypt (encrypted, m_NextMessage->GetBuffer () - 2 + m_NextMessageOffset);
|
||||
m_NextMessageOffset += 16;
|
||||
}
|
||||
|
||||
if (m_NextMessageOffset >= m_NextMessage->len + 4) // +checksum
|
||||
if (m_NextMessageOffset >= m_NextMessage->GetLength () + 2 + 4) // +checksum
|
||||
{
|
||||
// we have a complete I2NP message
|
||||
uint8_t checksum[4];
|
||||
htobe32buf (checksum, adler32 (adler32 (0, Z_NULL, 0), m_NextMessage->buf, m_NextMessageOffset - 4));
|
||||
if (!memcmp (m_NextMessage->buf + m_NextMessageOffset - 4, checksum, 4))
|
||||
htobe32buf (checksum, adler32 (adler32 (0, Z_NULL, 0), m_NextMessage->GetBuffer () - 2, m_NextMessageOffset - 4));
|
||||
if (!memcmp (m_NextMessage->GetBuffer () - 2 + m_NextMessageOffset - 4, checksum, 4))
|
||||
{
|
||||
if (!m_NextMessage->IsExpired ())
|
||||
{
|
||||
#ifdef WITH_EVENTS
|
||||
EmitEvent({{"type", "transport.recvmsg"} , {"ident", GetIdentHashBase64()}, {"number", "1"}});
|
||||
#endif
|
||||
m_Handler.PutNextMessage (m_NextMessage);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogInfo, "NTCP: message expired");
|
||||
}
|
||||
@@ -642,7 +651,7 @@ namespace transport
|
||||
sendBuffer = m_TimeSyncBuffer;
|
||||
len = 4;
|
||||
htobuf16(sendBuffer, 0);
|
||||
htobe32buf (sendBuffer + 2, time (0));
|
||||
htobe32buf (sendBuffer + 2, i2p::util::GetSecondsSinceEpoch ());
|
||||
}
|
||||
int rem = (len + 6) & 0x0F; // %16
|
||||
int padding = 0;
|
||||
@@ -795,6 +804,12 @@ namespace transport
|
||||
|
||||
void NTCPServer::Stop ()
|
||||
{
|
||||
{
|
||||
// we have to copy it because Terminate changes m_NTCPSessions
|
||||
auto ntcpSessions = m_NTCPSessions;
|
||||
for (auto& it: ntcpSessions)
|
||||
it.second->Terminate ();
|
||||
}
|
||||
m_NTCPSessions.clear ();
|
||||
|
||||
if (m_IsRunning)
|
||||
@@ -943,18 +958,31 @@ namespace transport
|
||||
{
|
||||
LogPrint (eLogDebug, "NTCP: Connecting to ", address ,":", port);
|
||||
m_Service.post([=]()
|
||||
{
|
||||
{
|
||||
if (this->AddNTCPSession (conn))
|
||||
{
|
||||
auto timer = std::make_shared<boost::asio::deadline_timer>(m_Service);
|
||||
timer->expires_from_now (boost::posix_time::seconds(NTCP_CONNECT_TIMEOUT));
|
||||
timer->async_wait ([conn](const boost::system::error_code& ecode)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
LogPrint (eLogInfo, "NTCP: Not connected in ", NTCP_CONNECT_TIMEOUT, " seconds");
|
||||
conn->Terminate ();
|
||||
}
|
||||
});
|
||||
conn->GetSocket ().async_connect (boost::asio::ip::tcp::endpoint (address, port),
|
||||
std::bind (&NTCPServer::HandleConnect, this, std::placeholders::_1, conn));
|
||||
std::bind (&NTCPServer::HandleConnect, this, std::placeholders::_1, conn, timer));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void NTCPServer::HandleConnect (const boost::system::error_code& ecode, std::shared_ptr<NTCPSession> conn)
|
||||
void NTCPServer::HandleConnect (const boost::system::error_code& ecode, std::shared_ptr<NTCPSession> conn, std::shared_ptr<boost::asio::deadline_timer> timer)
|
||||
{
|
||||
timer->cancel ();
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint (eLogError, "NTCP: Connect error ", ecode.message ());
|
||||
LogPrint (eLogInfo, "NTCP: Connect error ", ecode.message ());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
i2p::data::netdb.SetUnreachable (conn->GetRemoteIdentity ()->GetIdentHash (), true);
|
||||
conn->Terminate ();
|
||||
|
||||
@@ -36,6 +36,7 @@ namespace transport
|
||||
|
||||
const size_t NTCP_MAX_MESSAGE_SIZE = 16384;
|
||||
const size_t NTCP_BUFFER_SIZE = 4160; // fits 4 tunnel messages (4*1028)
|
||||
const int NTCP_CONNECT_TIMEOUT = 5; // 5 seconds
|
||||
const int NTCP_TERMINATION_TIMEOUT = 120; // 2 minutes
|
||||
const int NTCP_TERMINATION_CHECK_TIMEOUT = 30; // 30 seconds
|
||||
const size_t NTCP_DEFAULT_PHASE3_SIZE = 2/*size*/ + i2p::data::DEFAULT_IDENTITY_SIZE/*387*/ + 4/*ts*/ + 15/*padding*/ + 40/*signature*/; // 448
|
||||
@@ -153,7 +154,7 @@ namespace transport
|
||||
void HandleAccept (std::shared_ptr<NTCPSession> conn, const boost::system::error_code& error);
|
||||
void HandleAcceptV6 (std::shared_ptr<NTCPSession> conn, const boost::system::error_code& error);
|
||||
|
||||
void HandleConnect (const boost::system::error_code& ecode, std::shared_ptr<NTCPSession> conn);
|
||||
void HandleConnect (const boost::system::error_code& ecode, std::shared_ptr<NTCPSession> conn, std::shared_ptr<boost::asio::deadline_timer> timer);
|
||||
|
||||
// timer
|
||||
void ScheduleTermination ();
|
||||
|
||||
173
NetDb.cpp
173
NetDb.cpp
@@ -14,6 +14,7 @@
|
||||
#include "RouterContext.h"
|
||||
#include "Garlic.h"
|
||||
#include "NetDb.h"
|
||||
#include "Config.h"
|
||||
|
||||
using namespace i2p::transport;
|
||||
|
||||
@@ -23,7 +24,7 @@ namespace data
|
||||
{
|
||||
NetDb netdb;
|
||||
|
||||
NetDb::NetDb (): m_IsRunning (false), m_Thread (nullptr), m_Reseeder (nullptr), m_Storage("netDb", "r", "routerInfo-", "dat"), m_HiddenMode(false)
|
||||
NetDb::NetDb (): m_IsRunning (false), m_Thread (nullptr), m_Reseeder (nullptr), m_Storage("netDb", "r", "routerInfo-", "dat"), m_FloodfillBootstrap(nullptr), m_HiddenMode(false)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -117,7 +118,6 @@ namespace data
|
||||
{
|
||||
SaveUpdated ();
|
||||
ManageLeaseSets ();
|
||||
ManageLookupResponses ();
|
||||
}
|
||||
lastSave = ts;
|
||||
}
|
||||
@@ -126,10 +126,8 @@ namespace data
|
||||
i2p::context.CleanupDestination ();
|
||||
lastDestinationCleanup = ts;
|
||||
}
|
||||
// if we're in hidden mode don't publish or explore
|
||||
// if (m_HiddenMode) continue;
|
||||
|
||||
if (ts - lastPublish >= NETDB_PUBLISH_INTERVAL) // publish
|
||||
|
||||
if (ts - lastPublish >= NETDB_PUBLISH_INTERVAL && !m_HiddenMode) // publish
|
||||
{
|
||||
Publish ();
|
||||
lastPublish = ts;
|
||||
@@ -142,13 +140,16 @@ namespace data
|
||||
LogPrint(eLogError, "NetDb: no known routers, reseed seems to be totally failed");
|
||||
break;
|
||||
}
|
||||
else // we have peers now
|
||||
m_FloodfillBootstrap = nullptr;
|
||||
if (numRouters < 2500 || ts - lastExploratory >= 90)
|
||||
{
|
||||
numRouters = 800/numRouters;
|
||||
if (numRouters < 1) numRouters = 1;
|
||||
if (numRouters > 9) numRouters = 9;
|
||||
m_Requests.ManageRequests ();
|
||||
Explore (numRouters);
|
||||
m_Requests.ManageRequests ();
|
||||
if(!m_HiddenMode)
|
||||
Explore (numRouters);
|
||||
lastExploratory = ts;
|
||||
}
|
||||
}
|
||||
@@ -186,23 +187,34 @@ namespace data
|
||||
// TODO: check if floodfill has been changed
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogDebug, "NetDb: RouterInfo is older: ", ident.ToBase64());
|
||||
|
||||
updated = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
r = std::make_shared<RouterInfo> (buf, len);
|
||||
if (!r->IsUnreachable ())
|
||||
{
|
||||
LogPrint (eLogInfo, "NetDb: RouterInfo added: ", ident.ToBase64());
|
||||
bool inserted = false;
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_RouterInfosMutex);
|
||||
m_RouterInfos[r->GetIdentHash ()] = r;
|
||||
inserted = m_RouterInfos.insert ({r->GetIdentHash (), r}).second;
|
||||
}
|
||||
if (r->IsFloodfill () && r->IsReachable ()) // floodfill must be reachable
|
||||
if (inserted)
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_FloodfillsMutex);
|
||||
m_Floodfills.push_back (r);
|
||||
LogPrint (eLogInfo, "NetDb: RouterInfo added: ", ident.ToBase64());
|
||||
if (r->IsFloodfill () && r->IsReachable ()) // floodfill must be reachable
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_FloodfillsMutex);
|
||||
m_Floodfills.push_back (r);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "NetDb: Duplicated RouterInfo ", ident.ToBase64());
|
||||
updated = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -296,13 +308,62 @@ namespace data
|
||||
m_Reseeder = new Reseeder ();
|
||||
m_Reseeder->LoadCertificates (); // we need certificates for SU3 verification
|
||||
}
|
||||
int reseedRetries = 0;
|
||||
int reseedRetries = 0;
|
||||
|
||||
// try reseeding from floodfill first if specified
|
||||
std::string riPath;
|
||||
if(i2p::config::GetOption("reseed.floodfill", riPath)) {
|
||||
auto ri = std::make_shared<RouterInfo>(riPath);
|
||||
if (ri->IsFloodfill()) {
|
||||
const uint8_t * riData = ri->GetBuffer();
|
||||
int riLen = ri->GetBufferLen();
|
||||
if(!i2p::data::netdb.AddRouterInfo(riData, riLen)) {
|
||||
// bad router info
|
||||
LogPrint(eLogError, "NetDb: bad router info");
|
||||
return;
|
||||
}
|
||||
m_FloodfillBootstrap = ri;
|
||||
ReseedFromFloodfill(*ri);
|
||||
// don't try reseed servers if trying to boostrap from floodfill
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
while (reseedRetries < 10 && !m_Reseeder->ReseedNowSU3 ())
|
||||
reseedRetries++;
|
||||
if (reseedRetries >= 10)
|
||||
LogPrint (eLogWarning, "NetDb: failed to reseed after 10 attempts");
|
||||
}
|
||||
|
||||
void NetDb::ReseedFromFloodfill(const RouterInfo & ri, int numRouters, int numFloodfills)
|
||||
{
|
||||
LogPrint(eLogInfo, "NetDB: reseeding from floodfill ", ri.GetIdentHashBase64());
|
||||
std::vector<std::shared_ptr<i2p::I2NPMessage> > requests;
|
||||
|
||||
i2p::data::IdentHash ourIdent = i2p::context.GetIdentHash();
|
||||
i2p::data::IdentHash ih = ri.GetIdentHash();
|
||||
i2p::data::IdentHash randomIdent;
|
||||
|
||||
// make floodfill lookups
|
||||
while(numFloodfills > 0) {
|
||||
randomIdent.Randomize();
|
||||
auto msg = i2p::CreateRouterInfoDatabaseLookupMsg(randomIdent, ourIdent, 0, false);
|
||||
requests.push_back(msg);
|
||||
numFloodfills --;
|
||||
}
|
||||
|
||||
// make regular router lookups
|
||||
while(numRouters > 0) {
|
||||
randomIdent.Randomize();
|
||||
auto msg = i2p::CreateRouterInfoDatabaseLookupMsg(randomIdent, ourIdent, 0, true);
|
||||
requests.push_back(msg);
|
||||
numRouters --;
|
||||
}
|
||||
|
||||
// send them off
|
||||
i2p::transport::transports.SendMessages(ih, requests);
|
||||
}
|
||||
|
||||
bool NetDb::LoadRouterInfo (const std::string & path)
|
||||
{
|
||||
auto r = std::make_shared<RouterInfo>(path);
|
||||
@@ -499,6 +560,21 @@ namespace data
|
||||
m_Requests.RequestComplete (destination, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void NetDb::RequestDestinationFrom (const IdentHash& destination, const IdentHash & from, bool exploritory, RequestedDestination::RequestComplete requestComplete)
|
||||
{
|
||||
|
||||
auto dest = m_Requests.CreateRequest (destination, exploritory, requestComplete); // non-exploratory
|
||||
if (!dest)
|
||||
{
|
||||
LogPrint (eLogWarning, "NetDb: destination ", destination.ToBase64(), " is requested already");
|
||||
return;
|
||||
}
|
||||
LogPrint(eLogInfo, "NetDb: destination ", destination.ToBase64(), " being requested directly from ", from.ToBase64());
|
||||
// direct
|
||||
transports.SendMessage (from, dest->CreateRequestMessage (nullptr, nullptr));
|
||||
}
|
||||
|
||||
|
||||
void NetDb::HandleDatabaseStoreMsg (std::shared_ptr<const I2NPMessage> m)
|
||||
{
|
||||
@@ -621,7 +697,7 @@ namespace data
|
||||
if (!dest->IsExploratory ())
|
||||
{
|
||||
// reply to our destination. Try other floodfills
|
||||
if (outbound && inbound )
|
||||
if (outbound && inbound)
|
||||
{
|
||||
std::vector<i2p::tunnel::TunnelMessageBlock> msgs;
|
||||
auto count = dest->GetExcludedPeers ().size ();
|
||||
@@ -665,7 +741,7 @@ namespace data
|
||||
// no more requests for detination possible. delete it
|
||||
m_Requests.RequestComplete (ident, nullptr);
|
||||
}
|
||||
else
|
||||
else if(!m_FloodfillBootstrap)
|
||||
LogPrint (eLogWarning, "NetDb: requested destination for ", key, " not found");
|
||||
|
||||
// try responses
|
||||
@@ -682,7 +758,10 @@ namespace data
|
||||
{
|
||||
// router with ident not found or too old (1 hour)
|
||||
LogPrint (eLogDebug, "NetDb: found new/outdated router. Requesting RouterInfo ...");
|
||||
RequestDestination (router);
|
||||
if(m_FloodfillBootstrap)
|
||||
RequestDestinationFrom(router, m_FloodfillBootstrap->GetIdentHash(), true);
|
||||
else
|
||||
RequestDestination (router);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogDebug, "NetDb: [:|||:]");
|
||||
@@ -777,33 +856,17 @@ namespace data
|
||||
}
|
||||
|
||||
if (!replyMsg)
|
||||
{
|
||||
LogPrint (eLogWarning, "NetDb: Requested ", key, " not found, ", numExcluded, " peers excluded");
|
||||
// find or cleate response
|
||||
std::vector<IdentHash> closestFloodfills;
|
||||
bool found = false;
|
||||
if (!numExcluded)
|
||||
{
|
||||
auto it = m_LookupResponses.find (ident);
|
||||
if (it != m_LookupResponses.end ())
|
||||
{
|
||||
closestFloodfills = it->second.first;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
std::set<IdentHash> excludedRouters;
|
||||
const uint8_t * exclude_ident = excluded;
|
||||
for (int i = 0; i < numExcluded; i++)
|
||||
{
|
||||
excludedRouters.insert (exclude_ident);
|
||||
exclude_ident += 32;
|
||||
}
|
||||
closestFloodfills = GetClosestFloodfills (ident, 3, excludedRouters, true);
|
||||
if (!numExcluded) // save if no excluded
|
||||
m_LookupResponses[ident] = std::make_pair(closestFloodfills, i2p::util::GetSecondsSinceEpoch ());
|
||||
{
|
||||
std::set<IdentHash> excludedRouters;
|
||||
const uint8_t * exclude_ident = excluded;
|
||||
for (int i = 0; i < numExcluded; i++)
|
||||
{
|
||||
excludedRouters.insert (exclude_ident);
|
||||
exclude_ident += 32;
|
||||
}
|
||||
auto closestFloodfills = GetClosestFloodfills (ident, 3, excludedRouters, true);
|
||||
if (closestFloodfills.empty ())
|
||||
LogPrint (eLogWarning, "NetDb: Requested ", key, " not found, ", numExcluded, " peers excluded");
|
||||
replyMsg = CreateDatabaseSearchReply (ident, closestFloodfills);
|
||||
}
|
||||
}
|
||||
@@ -849,7 +912,6 @@ namespace data
|
||||
|
||||
uint8_t randomHash[32];
|
||||
std::vector<i2p::tunnel::TunnelMessageBlock> msgs;
|
||||
std::set<const RouterInfo *> floodfills;
|
||||
LogPrint (eLogInfo, "NetDb: exploring new ", numDestinations, " routers ...");
|
||||
for (int i = 0; i < numDestinations; i++)
|
||||
{
|
||||
@@ -861,9 +923,8 @@ namespace data
|
||||
return;
|
||||
}
|
||||
auto floodfill = GetClosestFloodfill (randomHash, dest->GetExcludedPeers ());
|
||||
if (floodfill && !floodfills.count (floodfill.get ())) // request floodfill only once
|
||||
if (floodfill)
|
||||
{
|
||||
floodfills.insert (floodfill.get ());
|
||||
if (i2p::transport::transports.IsConnected (floodfill->GetIdentHash ()))
|
||||
throughTunnels = false;
|
||||
if (throughTunnels)
|
||||
@@ -928,12 +989,12 @@ namespace data
|
||||
});
|
||||
}
|
||||
|
||||
std::shared_ptr<const RouterInfo> NetDb::GetRandomPeerTestRouter () const
|
||||
std::shared_ptr<const RouterInfo> NetDb::GetRandomPeerTestRouter (bool v4only) const
|
||||
{
|
||||
return GetRandomRouter (
|
||||
[](std::shared_ptr<const RouterInfo> router)->bool
|
||||
[v4only](std::shared_ptr<const RouterInfo> router)->bool
|
||||
{
|
||||
return !router->IsHidden () && router->IsPeerTesting ();
|
||||
return !router->IsHidden () && router->IsPeerTesting () && router->IsSSU (v4only);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1111,17 +1172,5 @@ namespace data
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
void NetDb::ManageLookupResponses ()
|
||||
{
|
||||
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
for (auto it = m_LookupResponses.begin (); it != m_LookupResponses.end ();)
|
||||
{
|
||||
if (ts > it->second.second + 180) // 3 minutes
|
||||
it = m_LookupResponses.erase (it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
14
NetDb.h
14
NetDb.h
@@ -8,7 +8,6 @@
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <future>
|
||||
|
||||
#include "Base.h"
|
||||
#include "Gzip.h"
|
||||
@@ -61,6 +60,7 @@ namespace data
|
||||
std::shared_ptr<RouterProfile> FindRouterProfile (const IdentHash& ident) const;
|
||||
|
||||
void RequestDestination (const IdentHash& destination, RequestedDestination::RequestComplete requestComplete = nullptr);
|
||||
void RequestDestinationFrom (const IdentHash& destination, const IdentHash & from, bool exploritory, RequestedDestination::RequestComplete requestComplete = nullptr);
|
||||
|
||||
void HandleDatabaseStoreMsg (std::shared_ptr<const I2NPMessage> msg);
|
||||
void HandleDatabaseSearchReplyMsg (std::shared_ptr<const I2NPMessage> msg);
|
||||
@@ -69,7 +69,7 @@ namespace data
|
||||
std::shared_ptr<const RouterInfo> GetRandomRouter () const;
|
||||
std::shared_ptr<const RouterInfo> GetRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith) const;
|
||||
std::shared_ptr<const RouterInfo> GetHighBandwidthRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith) const;
|
||||
std::shared_ptr<const RouterInfo> GetRandomPeerTestRouter () const;
|
||||
std::shared_ptr<const RouterInfo> GetRandomPeerTestRouter (bool v4only = true) const;
|
||||
std::shared_ptr<const RouterInfo> GetRandomIntroducer () const;
|
||||
std::shared_ptr<const RouterInfo> GetClosestFloodfill (const IdentHash& destination, const std::set<IdentHash>& excluded, bool closeThanUsOnly = false) const;
|
||||
std::vector<IdentHash> GetClosestFloodfills (const IdentHash& destination, size_t num,
|
||||
@@ -99,6 +99,9 @@ namespace data
|
||||
void VisitRouterInfos(RouterInfoVisitor v);
|
||||
/** visit N random router that match using filter, then visit them with a visitor, return number of RouterInfos that were visited */
|
||||
size_t VisitRandomRouterInfos(RouterInfoFilter f, RouterInfoVisitor v, size_t n);
|
||||
|
||||
void ClearRouterInfos () { m_RouterInfos.clear (); };
|
||||
|
||||
private:
|
||||
|
||||
void Load ();
|
||||
@@ -109,8 +112,9 @@ namespace data
|
||||
void Publish ();
|
||||
void ManageLeaseSets ();
|
||||
void ManageRequests ();
|
||||
void ManageLookupResponses ();
|
||||
|
||||
void ReseedFromFloodfill(const RouterInfo & ri, int numRouters=40, int numFloodfills=20);
|
||||
|
||||
template<typename Filter>
|
||||
std::shared_ptr<const RouterInfo> GetRandomRouter (Filter filter) const;
|
||||
|
||||
@@ -136,7 +140,9 @@ namespace data
|
||||
friend class NetDbRequests;
|
||||
NetDbRequests m_Requests;
|
||||
|
||||
std::map<IdentHash, std::pair<std::vector<IdentHash>, uint64_t> > m_LookupResponses; // ident->(closest FFs, timestamp)
|
||||
/** router info we are bootstrapping from or nullptr if we are not currently doing that*/
|
||||
std::shared_ptr<RouterInfo> m_FloodfillBootstrap;
|
||||
|
||||
|
||||
/** true if in hidden mode */
|
||||
bool m_HiddenMode;
|
||||
|
||||
@@ -11,10 +11,15 @@ namespace data
|
||||
std::shared_ptr<I2NPMessage> RequestedDestination::CreateRequestMessage (std::shared_ptr<const RouterInfo> router,
|
||||
std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel)
|
||||
{
|
||||
auto msg = i2p::CreateRouterInfoDatabaseLookupMsg (m_Destination,
|
||||
std::shared_ptr<I2NPMessage> msg;
|
||||
if(replyTunnel)
|
||||
msg = i2p::CreateRouterInfoDatabaseLookupMsg (m_Destination,
|
||||
replyTunnel->GetNextIdentHash (), replyTunnel->GetNextTunnelID (), m_IsExploratory,
|
||||
&m_ExcludedPeers);
|
||||
m_ExcludedPeers.insert (router->GetIdentHash ());
|
||||
else
|
||||
msg = i2p::CreateRouterInfoDatabaseLookupMsg(m_Destination, i2p::context.GetIdentHash(), 0, m_IsExploratory, &m_ExcludedPeers);
|
||||
if(router)
|
||||
m_ExcludedPeers.insert (router->GetIdentHash ());
|
||||
m_CreationTime = i2p::util::GetSecondsSinceEpoch ();
|
||||
return msg;
|
||||
}
|
||||
|
||||
48
Reseed.cpp
48
Reseed.cpp
@@ -41,19 +41,23 @@ namespace data
|
||||
std::string filename; i2p::config::GetOption("reseed.file", filename);
|
||||
if (filename.length() > 0) // reseed file is specified
|
||||
{
|
||||
if (filename.length() > 8 && filename.substr(0, 8) == "https://")
|
||||
{
|
||||
return ReseedFromSU3 (filename); // reseed from https URL
|
||||
} else {
|
||||
auto num = ProcessSU3File (filename.c_str ());
|
||||
if (num > 0) return num; // success
|
||||
LogPrint (eLogWarning, "Can't reseed from ", filename, " . Trying from hosts");
|
||||
}
|
||||
}
|
||||
auto ind = rand () % httpsReseedHostList.size ();
|
||||
std::string& reseedHost = httpsReseedHostList[ind];
|
||||
return ReseedFromSU3 (reseedHost);
|
||||
std::string reseedUrl = httpsReseedHostList[ind] + "i2pseeds.su3";
|
||||
return ReseedFromSU3 (reseedUrl);
|
||||
}
|
||||
|
||||
int Reseeder::ReseedFromSU3 (const std::string& host)
|
||||
int Reseeder::ReseedFromSU3 (const std::string& url)
|
||||
{
|
||||
std::string url = host + "i2pseeds.su3";
|
||||
LogPrint (eLogInfo, "Reseed: Downloading SU3 from ", host);
|
||||
LogPrint (eLogInfo, "Reseed: Downloading SU3 from ", url);
|
||||
std::string su3 = HttpsRequest (url);
|
||||
if (su3.length () > 0)
|
||||
{
|
||||
@@ -305,6 +309,34 @@ namespace data
|
||||
if (end - contentPos >= contentLength)
|
||||
break; // we are beyond contentLength
|
||||
}
|
||||
if (numFiles) // check if routers are not outdated
|
||||
{
|
||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
int numOutdated = 0;
|
||||
i2p::data::netdb.VisitRouterInfos (
|
||||
[&numOutdated, ts](std::shared_ptr<const RouterInfo> r)
|
||||
{
|
||||
if (r && ts > r->GetTimestamp () + 10*i2p::data::NETDB_MAX_EXPIRATION_TIMEOUT*1000LL) // 270 hours
|
||||
{
|
||||
LogPrint (eLogError, "Reseed: router ", r->GetIdentHash().ToBase64 (), " is outdated by ", (ts - r->GetTimestamp ())/1000LL/3600LL, " hours");
|
||||
numOutdated++;
|
||||
}
|
||||
});
|
||||
if (numOutdated > numFiles/2) // more than half
|
||||
{
|
||||
LogPrint (eLogError, "Reseed: mammoth's shit\n"
|
||||
" *_____*\n"
|
||||
" *_*****_*\n"
|
||||
" *_(O)_(O)_*\n"
|
||||
" **____V____**\n"
|
||||
" **_________**\n"
|
||||
" **_________**\n"
|
||||
" *_________*\n"
|
||||
" ***___***");
|
||||
i2p::data::netdb.ClearRouterInfos ();
|
||||
numFiles = 0;
|
||||
}
|
||||
}
|
||||
return numFiles;
|
||||
}
|
||||
|
||||
@@ -350,9 +382,11 @@ namespace data
|
||||
if (terminator) terminator[0] = 0;
|
||||
}
|
||||
// extract RSA key (we need n only, e = 65537)
|
||||
RSA * key = X509_get_pubkey (cert)->pkey.rsa;
|
||||
RSA * key = EVP_PKEY_get0_RSA (X509_get_pubkey (cert));
|
||||
const BIGNUM * n, * e, * d;
|
||||
RSA_get0_key(key, &n, &e, &d);
|
||||
PublicKey value;
|
||||
i2p::crypto::bn2buf (key->n, value, 512);
|
||||
i2p::crypto::bn2buf (n, value, 512);
|
||||
if (cn)
|
||||
m_SigningKeys[cn] = value;
|
||||
else
|
||||
|
||||
2
Reseed.h
2
Reseed.h
@@ -29,7 +29,7 @@ namespace data
|
||||
|
||||
void LoadCertificate (const std::string& filename);
|
||||
|
||||
int ReseedFromSU3 (const std::string& host);
|
||||
int ReseedFromSU3 (const std::string& url);
|
||||
int ProcessSU3File (const char * filename);
|
||||
int ProcessSU3Stream (std::istream& s);
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#include <fstream>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include "Config.h"
|
||||
#include "Crypto.h"
|
||||
#include "Timestamp.h"
|
||||
@@ -54,6 +53,8 @@ namespace i2p
|
||||
bool ipv6; i2p::config::GetOption("ipv6", ipv6);
|
||||
bool nat; i2p::config::GetOption("nat", nat);
|
||||
std::string ifname; i2p::config::GetOption("ifname", ifname);
|
||||
std::string ifname4; i2p::config::GetOption("ifname4", ifname4);
|
||||
std::string ifname6; i2p::config::GetOption("ifname6", ifname6);
|
||||
if (ipv4)
|
||||
{
|
||||
std::string host = "127.0.0.1";
|
||||
@@ -62,6 +63,10 @@ namespace i2p
|
||||
else if (!nat && !ifname.empty())
|
||||
/* bind to interface, we have no NAT so set external address too */
|
||||
host = i2p::util::net::GetInterfaceAddress(ifname, false).to_string(); // v4
|
||||
|
||||
if(ifname4.size())
|
||||
host = i2p::util::net::GetInterfaceAddress(ifname4, false).to_string();
|
||||
|
||||
routerInfo.AddSSUAddress (host.c_str(), port, routerInfo.GetIdentHash ());
|
||||
routerInfo.AddNTCPAddress (host.c_str(), port);
|
||||
}
|
||||
@@ -72,12 +77,16 @@ namespace i2p
|
||||
i2p::config::GetOption("host", host);
|
||||
else if (!ifname.empty())
|
||||
host = i2p::util::net::GetInterfaceAddress(ifname, true).to_string(); // v6
|
||||
|
||||
if(ifname6.size())
|
||||
host = i2p::util::net::GetInterfaceAddress(ifname6, true).to_string();
|
||||
|
||||
routerInfo.AddSSUAddress (host.c_str(), port, routerInfo.GetIdentHash ());
|
||||
routerInfo.AddNTCPAddress (host.c_str(), port);
|
||||
}
|
||||
|
||||
routerInfo.SetCaps (i2p::data::RouterInfo::eReachable |
|
||||
i2p::data::RouterInfo::eSSUTesting | i2p::data::RouterInfo::eSSUIntroducer); // LR, BC
|
||||
i2p::config::GetOption("netid", m_NetID);
|
||||
routerInfo.SetProperty ("netId", std::to_string (m_NetID));
|
||||
routerInfo.SetProperty ("router.version", I2P_VERSION);
|
||||
routerInfo.CreateBuffer (m_Keys);
|
||||
@@ -217,8 +226,8 @@ namespace i2p
|
||||
switch (type)
|
||||
{
|
||||
case low : /* not set */; break;
|
||||
case extra : caps |= i2p::data::RouterInfo::eExtraBandwidth; // no break here
|
||||
case high : caps |= i2p::data::RouterInfo::eHighBandwidth; break;
|
||||
case extra : caps |= i2p::data::RouterInfo::eExtraBandwidth; break;
|
||||
}
|
||||
m_RouterInfo.SetCaps (caps);
|
||||
UpdateRouterInfo ();
|
||||
@@ -358,8 +367,8 @@ namespace i2p
|
||||
if (m_IsFloodfill)
|
||||
{
|
||||
// update routers and leasesets
|
||||
m_RouterInfo.SetProperty (i2p::data::ROUTER_INFO_PROPERTY_LEASESETS, boost::lexical_cast<std::string>(i2p::data::netdb.GetNumLeaseSets ()));
|
||||
m_RouterInfo.SetProperty (i2p::data::ROUTER_INFO_PROPERTY_ROUTERS, boost::lexical_cast<std::string>(i2p::data::netdb.GetNumRouters ()));
|
||||
m_RouterInfo.SetProperty (i2p::data::ROUTER_INFO_PROPERTY_LEASESETS, std::to_string(i2p::data::netdb.GetNumLeaseSets ()));
|
||||
m_RouterInfo.SetProperty (i2p::data::ROUTER_INFO_PROPERTY_ROUTERS, std::to_string(i2p::data::netdb.GetNumRouters ()));
|
||||
UpdateRouterInfo ();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,19 +167,19 @@ namespace data
|
||||
{
|
||||
uint8_t supportedTransports = 0;
|
||||
bool isValidAddress = true;
|
||||
Address address;
|
||||
s.read ((char *)&address.cost, sizeof (address.cost));
|
||||
s.read ((char *)&address.date, sizeof (address.date));
|
||||
auto address = std::make_shared<Address>();
|
||||
s.read ((char *)&address->cost, sizeof (address->cost));
|
||||
s.read ((char *)&address->date, sizeof (address->date));
|
||||
char transportStyle[5];
|
||||
ReadString (transportStyle, 5, s);
|
||||
if (!strcmp (transportStyle, "NTCP"))
|
||||
address.transportStyle = eTransportNTCP;
|
||||
address->transportStyle = eTransportNTCP;
|
||||
else if (!strcmp (transportStyle, "SSU"))
|
||||
address.transportStyle = eTransportSSU;
|
||||
address->transportStyle = eTransportSSU;
|
||||
else
|
||||
address.transportStyle = eTransportUnknown;
|
||||
address.port = 0;
|
||||
address.mtu = 0;
|
||||
address->transportStyle = eTransportUnknown;
|
||||
address->port = 0;
|
||||
address->mtu = 0;
|
||||
uint16_t size, r = 0;
|
||||
s.read ((char *)&size, sizeof (size)); if (!s) return;
|
||||
size = be16toh (size);
|
||||
@@ -194,35 +194,35 @@ namespace data
|
||||
if (!strcmp (key, "host"))
|
||||
{
|
||||
boost::system::error_code ecode;
|
||||
address.host = boost::asio::ip::address::from_string (value, ecode);
|
||||
address->host = boost::asio::ip::address::from_string (value, ecode);
|
||||
if (ecode)
|
||||
{
|
||||
if (address.transportStyle == eTransportNTCP)
|
||||
if (address->transportStyle == eTransportNTCP)
|
||||
{
|
||||
supportedTransports |= eNTCPV4; // TODO:
|
||||
address.addressString = value;
|
||||
address->addressString = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
supportedTransports |= eSSUV4; // TODO:
|
||||
address.addressString = value;
|
||||
address->addressString = value;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// add supported protocol
|
||||
if (address.host.is_v4 ())
|
||||
supportedTransports |= (address.transportStyle == eTransportNTCP) ? eNTCPV4 : eSSUV4;
|
||||
if (address->host.is_v4 ())
|
||||
supportedTransports |= (address->transportStyle == eTransportNTCP) ? eNTCPV4 : eSSUV4;
|
||||
else
|
||||
supportedTransports |= (address.transportStyle == eTransportNTCP) ? eNTCPV6 : eSSUV6;
|
||||
supportedTransports |= (address->transportStyle == eTransportNTCP) ? eNTCPV6 : eSSUV6;
|
||||
}
|
||||
}
|
||||
else if (!strcmp (key, "port"))
|
||||
address.port = boost::lexical_cast<int>(value);
|
||||
address->port = boost::lexical_cast<int>(value);
|
||||
else if (!strcmp (key, "mtu"))
|
||||
address.mtu = boost::lexical_cast<int>(value);
|
||||
address->mtu = boost::lexical_cast<int>(value);
|
||||
else if (!strcmp (key, "key"))
|
||||
Base64ToByteStream (value, strlen (value), address.key, 32);
|
||||
Base64ToByteStream (value, strlen (value), address->key, 32);
|
||||
else if (!strcmp (key, "caps"))
|
||||
ExtractCaps (value);
|
||||
else if (key[0] == 'i')
|
||||
@@ -237,9 +237,9 @@ namespace data
|
||||
LogPrint (eLogError, "RouterInfo: Unexpected introducer's index ", index, " skipped");
|
||||
if (s) continue; else return;
|
||||
}
|
||||
if (index >= address.introducers.size ())
|
||||
address.introducers.resize (index + 1);
|
||||
Introducer& introducer = address.introducers.at (index);
|
||||
if (index >= address->introducers.size ())
|
||||
address->introducers.resize (index + 1);
|
||||
Introducer& introducer = address->introducers.at (index);
|
||||
if (!strcmp (key, "ihost"))
|
||||
{
|
||||
boost::system::error_code ecode;
|
||||
@@ -256,7 +256,7 @@ namespace data
|
||||
}
|
||||
if (isValidAddress)
|
||||
{
|
||||
addresses->push_back(std::make_shared<Address>(address));
|
||||
addresses->push_back(address);
|
||||
m_SupportedTransports |= supportedTransports;
|
||||
}
|
||||
}
|
||||
|
||||
159
SAM.cpp
159
SAM.cpp
@@ -3,12 +3,12 @@
|
||||
#ifdef _MSC_VER
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include "Base.h"
|
||||
#include "Identity.h"
|
||||
#include "Log.h"
|
||||
#include "Destination.h"
|
||||
#include "ClientContext.h"
|
||||
#include "util.h"
|
||||
#include "SAM.h"
|
||||
|
||||
namespace i2p
|
||||
@@ -54,11 +54,7 @@ namespace client
|
||||
case eSAMSocketTypeAcceptor:
|
||||
{
|
||||
if (m_Session)
|
||||
{
|
||||
m_Session->DelSocket (shared_from_this ());
|
||||
if (m_Session->localDestination)
|
||||
m_Session->localDestination->StopAcceptingStreams ();
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -289,6 +285,11 @@ namespace client
|
||||
dest->SetReceiver (std::bind (&SAMSocket::HandleI2PDatagramReceive, shared_from_this (),
|
||||
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5));
|
||||
}
|
||||
else
|
||||
{
|
||||
// start accepting streams because we're not a datagram session
|
||||
m_Session->localDestination->AcceptStreams (std::bind (&SAMSession::AcceptI2P, m_Session, std::placeholders::_1));
|
||||
}
|
||||
|
||||
if (m_Session->localDestination->IsReady ())
|
||||
SendSessionCreateReplyOk ();
|
||||
@@ -401,26 +402,31 @@ namespace client
|
||||
m_Session = m_Owner.FindSession (id);
|
||||
if (m_Session)
|
||||
{
|
||||
if (!m_Session->localDestination->IsAcceptingStreams ())
|
||||
{
|
||||
m_SocketType = eSAMSocketTypeAcceptor;
|
||||
m_Session->AddSocket (shared_from_this ());
|
||||
m_Session->localDestination->AcceptStreams (std::bind (&SAMSocket::HandleI2PAccept, shared_from_this (), std::placeholders::_1));
|
||||
SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false);
|
||||
}
|
||||
else
|
||||
SendMessageReply (SAM_STREAM_STATUS_I2P_ERROR, strlen(SAM_STREAM_STATUS_I2P_ERROR), true);
|
||||
m_SocketType = eSAMSocketTypeAcceptor;
|
||||
m_Session->AddSocket (shared_from_this ());
|
||||
SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false);
|
||||
}
|
||||
else
|
||||
SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true);
|
||||
}
|
||||
|
||||
void SAMSocket::Accept(std::shared_ptr<i2p::stream::Stream> stream)
|
||||
{
|
||||
if(stream) {
|
||||
m_SocketType = eSAMSocketTypeStream;
|
||||
HandleI2PAccept(stream);
|
||||
} else {
|
||||
SendMessageReply (SAM_STREAM_STATUS_I2P_ERROR, strlen(SAM_STREAM_STATUS_I2P_ERROR), true);
|
||||
auto s = shared_from_this ();
|
||||
m_Owner.GetService ().post ([s] { s->Terminate (); });
|
||||
}
|
||||
}
|
||||
size_t SAMSocket::ProcessDatagramSend (char * buf, size_t len, const char * data)
|
||||
{
|
||||
LogPrint (eLogDebug, "SAM: datagram send: ", buf, " ", len);
|
||||
std::map<std::string, std::string> params;
|
||||
ExtractParams (buf, params);
|
||||
size_t size = boost::lexical_cast<int>(params[SAM_PARAM_SIZE]), offset = data - buf;
|
||||
size_t size = std::stoi(params[SAM_PARAM_SIZE]), offset = data - buf;
|
||||
if (offset + size <= len)
|
||||
{
|
||||
if (m_Session)
|
||||
@@ -577,8 +583,8 @@ namespace client
|
||||
{
|
||||
if (!ecode)
|
||||
s->Receive ();
|
||||
else
|
||||
s->Terminate ();
|
||||
else
|
||||
s->m_Owner.GetService ().post ([s] { s->Terminate (); });
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -587,10 +593,28 @@ namespace client
|
||||
void SAMSocket::I2PReceive ()
|
||||
{
|
||||
if (m_Stream)
|
||||
m_Stream->AsyncReceive (boost::asio::buffer (m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE),
|
||||
std::bind (&SAMSocket::HandleI2PReceive, shared_from_this (),
|
||||
std::placeholders::_1, std::placeholders::_2),
|
||||
SAM_SOCKET_CONNECTION_MAX_IDLE);
|
||||
{
|
||||
if (m_Stream->GetStatus () == i2p::stream::eStreamStatusNew ||
|
||||
m_Stream->GetStatus () == i2p::stream::eStreamStatusOpen) // regular
|
||||
{
|
||||
m_Stream->AsyncReceive (boost::asio::buffer (m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE),
|
||||
std::bind (&SAMSocket::HandleI2PReceive, shared_from_this (),
|
||||
std::placeholders::_1, std::placeholders::_2),
|
||||
SAM_SOCKET_CONNECTION_MAX_IDLE);
|
||||
}
|
||||
else // closed by peer
|
||||
{
|
||||
// get remaning data
|
||||
auto len = m_Stream->ReadSome (m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE);
|
||||
if (len > 0) // still some data
|
||||
{
|
||||
boost::asio::async_write (m_Socket, boost::asio::buffer (m_StreamBuffer, len),
|
||||
std::bind (&SAMSocket::HandleWriteI2PData, shared_from_this (), std::placeholders::_1));
|
||||
}
|
||||
else // no more data
|
||||
Terminate ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SAMSocket::HandleI2PReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
||||
@@ -599,7 +623,21 @@ namespace client
|
||||
{
|
||||
LogPrint (eLogError, "SAM: stream read error: ", ecode.message ());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate ();
|
||||
{
|
||||
if (bytes_transferred > 0)
|
||||
boost::asio::async_write (m_Socket, boost::asio::buffer (m_StreamBuffer, bytes_transferred),
|
||||
std::bind (&SAMSocket::HandleWriteI2PData, shared_from_this (), std::placeholders::_1)); // postpone termination
|
||||
else
|
||||
{
|
||||
auto s = shared_from_this ();
|
||||
m_Owner.GetService ().post ([s] { s->Terminate (); });
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto s = shared_from_this ();
|
||||
m_Owner.GetService ().post ([s] { s->Terminate (); });
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -627,10 +665,6 @@ namespace client
|
||||
LogPrint (eLogDebug, "SAM: incoming I2P connection for session ", m_ID);
|
||||
m_Stream = stream;
|
||||
context.GetAddressBook ().InsertAddress (stream->GetRemoteIdentity ());
|
||||
auto session = m_Owner.FindSession (m_ID);
|
||||
if (session)
|
||||
session->localDestination->StopAcceptingStreams ();
|
||||
m_SocketType = eSAMSocketTypeStream;
|
||||
if (!m_IsSilent)
|
||||
{
|
||||
// get remote peer address
|
||||
@@ -672,26 +706,76 @@ namespace client
|
||||
}
|
||||
|
||||
SAMSession::SAMSession (std::shared_ptr<ClientDestination> dest):
|
||||
localDestination (dest)
|
||||
localDestination (dest),
|
||||
m_BacklogPumper(dest->GetService())
|
||||
{
|
||||
PumpBacklog();
|
||||
}
|
||||
|
||||
SAMSession::~SAMSession ()
|
||||
|
||||
void SAMSession::AcceptI2P(std::shared_ptr<i2p::stream::Stream> stream)
|
||||
{
|
||||
CloseStreams();
|
||||
i2p::client::context.DeleteLocalDestination (localDestination);
|
||||
if(!stream) return; // fail
|
||||
std::unique_lock<std::mutex> lock(m_SocketsMutex);
|
||||
if(m_Backlog.size() > SAM_MAX_ACCEPT_BACKLOG) {
|
||||
stream->Close();
|
||||
return;
|
||||
}
|
||||
m_Backlog.push_back(stream);
|
||||
}
|
||||
|
||||
void SAMSession::PumpBacklog()
|
||||
{
|
||||
// pump backlog every 100ms
|
||||
boost::posix_time::milliseconds dlt(100);
|
||||
m_BacklogPumper.expires_from_now(dlt);
|
||||
m_BacklogPumper.async_wait(std::bind(&SAMSession::HandlePumpBacklog, this, std::placeholders::_1));
|
||||
}
|
||||
|
||||
std::shared_ptr<SAMSocket> SAMSession::FindAcceptor()
|
||||
{
|
||||
for (auto & sock : m_Sockets) {
|
||||
auto t = sock->GetSocketType();
|
||||
if(t == eSAMSocketTypeAcceptor) {
|
||||
return sock;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void SAMSession::HandlePumpBacklog(const boost::system::error_code & ec)
|
||||
{
|
||||
if(ec) return;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_SocketsMutex);
|
||||
auto itr = m_Backlog.begin();
|
||||
while(itr != m_Backlog.end()) {
|
||||
auto sock = FindAcceptor();
|
||||
if (sock) {
|
||||
sock->Accept(*itr);
|
||||
itr = m_Backlog.erase(itr);
|
||||
} else {
|
||||
++itr;
|
||||
}
|
||||
}
|
||||
}
|
||||
PumpBacklog();
|
||||
}
|
||||
|
||||
void SAMSession::CloseStreams ()
|
||||
{
|
||||
{
|
||||
m_BacklogPumper.cancel();
|
||||
localDestination->GetService().post([&] () {
|
||||
std::lock_guard<std::mutex> lock(m_SocketsMutex);
|
||||
for (auto& sock : m_Sockets) {
|
||||
sock->CloseStream();
|
||||
}
|
||||
}
|
||||
// XXX: should this be done inside locked parts?
|
||||
m_Sockets.clear();
|
||||
for(auto & stream : m_Backlog) {
|
||||
stream->Close();
|
||||
}
|
||||
m_Sockets.clear();
|
||||
m_Backlog.clear();
|
||||
i2p::client::context.DeleteLocalDestination (localDestination);
|
||||
});
|
||||
}
|
||||
|
||||
SAMBridge::SAMBridge (const std::string& address, int port):
|
||||
@@ -793,7 +877,7 @@ namespace client
|
||||
auto it = params->find (SAM_PARAM_SIGNATURE_TYPE);
|
||||
if (it != params->end ())
|
||||
// TODO: extract string values
|
||||
signatureType = boost::lexical_cast<int> (it->second);
|
||||
signatureType = std::stoi(it->second);
|
||||
}
|
||||
localDestination = i2p::client::context.CreateNewLocalDestination (true, signatureType, params);
|
||||
}
|
||||
@@ -802,8 +886,9 @@ namespace client
|
||||
auto session = std::make_shared<SAMSession>(localDestination);
|
||||
std::unique_lock<std::mutex> l(m_SessionsMutex);
|
||||
auto ret = m_Sessions.insert (std::make_pair(id, session));
|
||||
if (!ret.second)
|
||||
if (!ret.second) {
|
||||
LogPrint (eLogWarning, "SAM: Session ", id, " already exists");
|
||||
}
|
||||
return ret.first->second;
|
||||
}
|
||||
return nullptr;
|
||||
|
||||
19
SAM.h
19
SAM.h
@@ -20,7 +20,8 @@ namespace client
|
||||
{
|
||||
const size_t SAM_SOCKET_BUFFER_SIZE = 8192;
|
||||
const int SAM_SOCKET_CONNECTION_MAX_IDLE = 3600; // in seconds
|
||||
const int SAM_SESSION_READINESS_CHECK_INTERVAL = 20; // in seconds
|
||||
const int SAM_SESSION_READINESS_CHECK_INTERVAL = 20; // in seconds
|
||||
const int SAM_MAX_ACCEPT_BACKLOG = 50;
|
||||
const char SAM_HANDSHAKE[] = "HELLO VERSION";
|
||||
const char SAM_HANDSHAKE_REPLY[] = "HELLO REPLY RESULT=OK VERSION=%s\n";
|
||||
const char SAM_HANDSHAKE_I2P_ERROR[] = "HELLO REPLY RESULT=I2P_ERROR\n";
|
||||
@@ -84,6 +85,8 @@ namespace client
|
||||
void SetSocketType (SAMSocketType socketType) { m_SocketType = socketType; };
|
||||
SAMSocketType GetSocketType () const { return m_SocketType; };
|
||||
|
||||
void Accept(std::shared_ptr<i2p::stream::Stream> stream);
|
||||
|
||||
private:
|
||||
|
||||
void Terminate ();
|
||||
@@ -134,6 +137,8 @@ namespace client
|
||||
struct SAMSession
|
||||
{
|
||||
std::shared_ptr<ClientDestination> localDestination;
|
||||
boost::asio::deadline_timer m_BacklogPumper;
|
||||
std::list<std::shared_ptr<i2p::stream::Stream> > m_Backlog;
|
||||
std::list<std::shared_ptr<SAMSocket> > m_Sockets;
|
||||
std::mutex m_SocketsMutex;
|
||||
|
||||
@@ -158,9 +163,15 @@ namespace client
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
SAMSession (std::shared_ptr<ClientDestination> dest);
|
||||
~SAMSession ();
|
||||
|
||||
SAMSession (std::shared_ptr<ClientDestination> dest);
|
||||
|
||||
void AcceptI2P(std::shared_ptr<i2p::stream::Stream> stream);
|
||||
|
||||
std::shared_ptr<SAMSocket> FindAcceptor();
|
||||
|
||||
void PumpBacklog();
|
||||
void HandlePumpBacklog(const boost::system::error_code & ec);
|
||||
|
||||
void CloseStreams ();
|
||||
};
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "I2PEndian.h"
|
||||
#include "I2PTunnel.h"
|
||||
#include "I2PService.h"
|
||||
#include "util.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
@@ -638,7 +639,7 @@ namespace proxy
|
||||
{
|
||||
LogPrint(eLogInfo, "SOCKS: forwarding to upstream");
|
||||
EnterState(UPSTREAM_RESOLVE);
|
||||
boost::asio::ip::tcp::resolver::query q(m_UpstreamProxyAddress,boost::lexical_cast<std::string>(m_UpstreamProxyPort) );
|
||||
boost::asio::ip::tcp::resolver::query q(m_UpstreamProxyAddress, std::to_string(m_UpstreamProxyPort));
|
||||
m_proxy_resolver.async_resolve(q, std::bind(&SOCKSHandler::HandleUpstreamResolved, shared_from_this(),
|
||||
std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
|
||||
112
SSU.cpp
112
SSU.cpp
@@ -20,11 +20,7 @@ namespace transport
|
||||
m_IntroducersUpdateTimer (m_Service), m_PeerTestsCleanupTimer (m_Service),
|
||||
m_TerminationTimer (m_Service), m_TerminationTimerV6 (m_ServiceV6)
|
||||
{
|
||||
m_SocketV6.open (boost::asio::ip::udp::v6());
|
||||
m_SocketV6.set_option (boost::asio::ip::v6_only (true));
|
||||
m_SocketV6.set_option (boost::asio::socket_base::receive_buffer_size (65535));
|
||||
m_SocketV6.set_option (boost::asio::socket_base::send_buffer_size (65535));
|
||||
m_SocketV6.bind (m_EndpointV6);
|
||||
OpenSocketV6 ();
|
||||
}
|
||||
|
||||
SSUServer::SSUServer (int port):
|
||||
@@ -32,27 +28,36 @@ namespace transport
|
||||
m_Thread (nullptr), m_ThreadV6 (nullptr), m_ReceiversThread (nullptr),
|
||||
m_Work (m_Service), m_WorkV6 (m_ServiceV6), m_ReceiversWork (m_ReceiversService),
|
||||
m_Endpoint (boost::asio::ip::udp::v4 (), port), m_EndpointV6 (boost::asio::ip::udp::v6 (), port),
|
||||
m_Socket (m_ReceiversService, m_Endpoint), m_SocketV6 (m_ReceiversService),
|
||||
m_Socket (m_ReceiversService), m_SocketV6 (m_ReceiversService),
|
||||
m_IntroducersUpdateTimer (m_Service), m_PeerTestsCleanupTimer (m_Service),
|
||||
m_TerminationTimer (m_Service), m_TerminationTimerV6 (m_ServiceV6)
|
||||
{
|
||||
|
||||
m_Socket.set_option (boost::asio::socket_base::receive_buffer_size (65535));
|
||||
m_Socket.set_option (boost::asio::socket_base::send_buffer_size (65535));
|
||||
OpenSocket ();
|
||||
if (context.SupportsV6 ())
|
||||
{
|
||||
m_SocketV6.open (boost::asio::ip::udp::v6());
|
||||
m_SocketV6.set_option (boost::asio::ip::v6_only (true));
|
||||
m_SocketV6.set_option (boost::asio::socket_base::receive_buffer_size (65535));
|
||||
m_SocketV6.set_option (boost::asio::socket_base::send_buffer_size (65535));
|
||||
m_SocketV6.bind (m_EndpointV6);
|
||||
}
|
||||
OpenSocketV6 ();
|
||||
}
|
||||
|
||||
SSUServer::~SSUServer ()
|
||||
{
|
||||
}
|
||||
|
||||
void SSUServer::OpenSocket ()
|
||||
{
|
||||
m_Socket.open (boost::asio::ip::udp::v4());
|
||||
m_Socket.set_option (boost::asio::socket_base::receive_buffer_size (65535));
|
||||
m_Socket.set_option (boost::asio::socket_base::send_buffer_size (65535));
|
||||
m_Socket.bind (m_Endpoint);
|
||||
}
|
||||
|
||||
void SSUServer::OpenSocketV6 ()
|
||||
{
|
||||
m_SocketV6.open (boost::asio::ip::udp::v6());
|
||||
m_SocketV6.set_option (boost::asio::ip::v6_only (true));
|
||||
m_SocketV6.set_option (boost::asio::socket_base::receive_buffer_size (65535));
|
||||
m_SocketV6.set_option (boost::asio::socket_base::send_buffer_size (65535));
|
||||
m_SocketV6.bind (m_EndpointV6);
|
||||
}
|
||||
|
||||
void SSUServer::Start ()
|
||||
{
|
||||
m_IsRunning = true;
|
||||
@@ -194,21 +199,40 @@ namespace transport
|
||||
|
||||
boost::system::error_code ec;
|
||||
size_t moreBytes = m_Socket.available(ec);
|
||||
while (moreBytes && packets.size () < 25)
|
||||
{
|
||||
packet = new SSUPacket ();
|
||||
packet->len = m_Socket.receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V4), packet->from);
|
||||
packets.push_back (packet);
|
||||
moreBytes = m_Socket.available();
|
||||
}
|
||||
if (!ec)
|
||||
{
|
||||
while (moreBytes && packets.size () < 25)
|
||||
{
|
||||
packet = new SSUPacket ();
|
||||
packet->len = m_Socket.receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V4), packet->from, 0, ec);
|
||||
if (!ec)
|
||||
{
|
||||
packets.push_back (packet);
|
||||
moreBytes = m_Socket.available(ec);
|
||||
if (ec) break;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "SSU: receive_from error: ", ec.message ());
|
||||
delete packet;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_Service.post (std::bind (&SSUServer::HandleReceivedPackets, this, packets, &m_Sessions));
|
||||
Receive ();
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "SSU: receive error: ", ecode.message ());
|
||||
delete packet;
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
LogPrint (eLogError, "SSU: receive error: ", ecode.message ());
|
||||
m_Socket.close ();
|
||||
OpenSocket ();
|
||||
Receive ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,22 +244,42 @@ namespace transport
|
||||
std::vector<SSUPacket *> packets;
|
||||
packets.push_back (packet);
|
||||
|
||||
size_t moreBytes = m_SocketV6.available ();
|
||||
while (moreBytes && packets.size () < 25)
|
||||
boost::system::error_code ec;
|
||||
size_t moreBytes = m_SocketV6.available (ec);
|
||||
if (!ec)
|
||||
{
|
||||
packet = new SSUPacket ();
|
||||
packet->len = m_SocketV6.receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V6), packet->from);
|
||||
packets.push_back (packet);
|
||||
moreBytes = m_SocketV6.available();
|
||||
while (moreBytes && packets.size () < 25)
|
||||
{
|
||||
packet = new SSUPacket ();
|
||||
packet->len = m_SocketV6.receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V6), packet->from, 0, ec);
|
||||
if (!ec)
|
||||
{
|
||||
packets.push_back (packet);
|
||||
moreBytes = m_SocketV6.available(ec);
|
||||
if (ec) break;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "SSU: v6 receive_from error: ", ec.message ());
|
||||
delete packet;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
m_ServiceV6.post (std::bind (&SSUServer::HandleReceivedPackets, this, packets, &m_SessionsV6));
|
||||
ReceiveV6 ();
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "SSU: v6 receive error: ", ecode.message ());
|
||||
delete packet;
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
LogPrint (eLogError, "SSU: v6 receive error: ", ecode.message ());
|
||||
m_SocketV6.close ();
|
||||
OpenSocketV6 ();
|
||||
ReceiveV6 ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -298,9 +342,9 @@ namespace transport
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void SSUServer::CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest)
|
||||
void SSUServer::CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest, bool v4only)
|
||||
{
|
||||
auto address = router->GetSSUAddress (!context.SupportsV6 ());
|
||||
auto address = router->GetSSUAddress (v4only || !context.SupportsV6 ());
|
||||
if (address)
|
||||
CreateSession (router, address->host, address->port, peerTest);
|
||||
else
|
||||
|
||||
4
SSU.h
4
SSU.h
@@ -42,7 +42,7 @@ namespace transport
|
||||
~SSUServer ();
|
||||
void Start ();
|
||||
void Stop ();
|
||||
void CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest = false);
|
||||
void CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest = false, bool v4only = false);
|
||||
void CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router,
|
||||
const boost::asio::ip::address& addr, int port, bool peerTest = false);
|
||||
void CreateDirectSession (std::shared_ptr<const i2p::data::RouterInfo> router, boost::asio::ip::udp::endpoint remoteEndpoint, bool peerTest);
|
||||
@@ -68,6 +68,8 @@ namespace transport
|
||||
|
||||
private:
|
||||
|
||||
void OpenSocket ();
|
||||
void OpenSocketV6 ();
|
||||
void Run ();
|
||||
void RunV6 ();
|
||||
void RunReceivers ();
|
||||
|
||||
10
SSUData.cpp
10
SSUData.cpp
@@ -5,6 +5,9 @@
|
||||
#include "NetDb.h"
|
||||
#include "SSU.h"
|
||||
#include "SSUData.h"
|
||||
#ifdef WITH_EVENTS
|
||||
#include "Event.h"
|
||||
#endif
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
@@ -234,8 +237,13 @@ namespace transport
|
||||
{
|
||||
m_ReceivedMessages.insert (msgID);
|
||||
m_LastMessageReceivedTime = i2p::util::GetSecondsSinceEpoch ();
|
||||
if (!msg->IsExpired ())
|
||||
if (!msg->IsExpired ())
|
||||
{
|
||||
#ifdef WITH_EVENTS
|
||||
EmitEvent({{"type", "transport.recvmsg"} , {"ident", m_Session.GetIdentHashBase64()}, {"number", "1"}});
|
||||
#endif
|
||||
m_Handler.PutNextMessage (msg);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogDebug, "SSU: message expired");
|
||||
}
|
||||
|
||||
@@ -944,7 +944,7 @@ namespace transport
|
||||
// existing test
|
||||
case ePeerTestParticipantAlice1:
|
||||
{
|
||||
if (m_State == eSessionStateEstablished)
|
||||
if (m_Server.GetPeerTestSession (nonce) == shared_from_this ()) // Alice-Bob
|
||||
{
|
||||
LogPrint (eLogDebug, "SSU: peer test from Bob. We are Alice");
|
||||
if (i2p::context.GetStatus () == eRouterStatusTesting) // still not OK
|
||||
@@ -953,6 +953,8 @@ namespace transport
|
||||
else
|
||||
{
|
||||
LogPrint (eLogDebug, "SSU: first peer test from Charlie. We are Alice");
|
||||
if (m_State == eSessionStateEstablished)
|
||||
LogPrint (eLogWarning, "SSU: first peer test from Charlie through established session. We are Alice");
|
||||
i2p::context.SetStatus (eRouterStatusOK);
|
||||
m_Server.UpdatePeerTest (nonce, ePeerTestParticipantAlice2);
|
||||
SendPeerTest (nonce, senderEndpoint.address (), senderEndpoint.port (), introKey, true, false); // to Charlie
|
||||
@@ -961,7 +963,7 @@ namespace transport
|
||||
}
|
||||
case ePeerTestParticipantAlice2:
|
||||
{
|
||||
if (m_State == eSessionStateEstablished)
|
||||
if (m_Server.GetPeerTestSession (nonce) == shared_from_this ()) // Alice-Bob
|
||||
LogPrint (eLogDebug, "SSU: peer test from Bob. We are Alice");
|
||||
else
|
||||
{
|
||||
@@ -1099,7 +1101,7 @@ namespace transport
|
||||
{
|
||||
// we are Alice
|
||||
LogPrint (eLogDebug, "SSU: sending peer test");
|
||||
auto address = i2p::context.GetRouterInfo ().GetSSUAddress (false);
|
||||
auto address = i2p::context.GetRouterInfo ().GetSSUAddress (i2p::context.SupportsV4 ());
|
||||
if (!address)
|
||||
{
|
||||
LogPrint (eLogInfo, "SSU is not supported. Can't send peer test");
|
||||
@@ -1109,7 +1111,7 @@ namespace transport
|
||||
RAND_bytes ((uint8_t *)&nonce, 4);
|
||||
if (!nonce) nonce = 1;
|
||||
m_IsPeerTest = false;
|
||||
m_Server.NewPeerTest (nonce, ePeerTestParticipantAlice1);
|
||||
m_Server.NewPeerTest (nonce, ePeerTestParticipantAlice1, shared_from_this ());
|
||||
SendPeerTest (nonce, boost::asio::ip::address(), 0, address->key, false, false); // address and port always zero for Alice
|
||||
}
|
||||
|
||||
|
||||
61
Signature.h
61
Signature.h
@@ -43,7 +43,7 @@ namespace crypto
|
||||
DSAVerifier (const uint8_t * signingKey)
|
||||
{
|
||||
m_PublicKey = CreateDSA ();
|
||||
m_PublicKey->pub_key = BN_bin2bn (signingKey, DSA_PUBLIC_KEY_LENGTH, NULL);
|
||||
DSA_set0_key (m_PublicKey, BN_bin2bn (signingKey, DSA_PUBLIC_KEY_LENGTH, NULL), NULL);
|
||||
}
|
||||
|
||||
~DSAVerifier ()
|
||||
@@ -58,8 +58,7 @@ namespace crypto
|
||||
SHA1 (buf, len, digest);
|
||||
// signature
|
||||
DSA_SIG * sig = DSA_SIG_new();
|
||||
sig->r = BN_bin2bn (signature, DSA_SIGNATURE_LENGTH/2, NULL);
|
||||
sig->s = BN_bin2bn (signature + DSA_SIGNATURE_LENGTH/2, DSA_SIGNATURE_LENGTH/2, NULL);
|
||||
DSA_SIG_set0 (sig, BN_bin2bn (signature, DSA_SIGNATURE_LENGTH/2, NULL), BN_bin2bn (signature + DSA_SIGNATURE_LENGTH/2, DSA_SIGNATURE_LENGTH/2, NULL));
|
||||
// DSA verification
|
||||
int ret = DSA_do_verify (digest, 20, sig, m_PublicKey);
|
||||
DSA_SIG_free(sig);
|
||||
@@ -78,10 +77,11 @@ namespace crypto
|
||||
{
|
||||
public:
|
||||
|
||||
DSASigner (const uint8_t * signingPrivateKey)
|
||||
DSASigner (const uint8_t * signingPrivateKey, const uint8_t * signingPublicKey)
|
||||
// openssl 1.1 always requires DSA public key even for signing
|
||||
{
|
||||
m_PrivateKey = CreateDSA ();
|
||||
m_PrivateKey->priv_key = BN_bin2bn (signingPrivateKey, DSA_PRIVATE_KEY_LENGTH, NULL);
|
||||
DSA_set0_key (m_PrivateKey, BN_bin2bn (signingPublicKey, DSA_PUBLIC_KEY_LENGTH, NULL), BN_bin2bn (signingPrivateKey, DSA_PRIVATE_KEY_LENGTH, NULL));
|
||||
}
|
||||
|
||||
~DSASigner ()
|
||||
@@ -94,8 +94,10 @@ namespace crypto
|
||||
uint8_t digest[20];
|
||||
SHA1 (buf, len, digest);
|
||||
DSA_SIG * sig = DSA_do_sign (digest, 20, m_PrivateKey);
|
||||
bn2buf (sig->r, signature, DSA_SIGNATURE_LENGTH/2);
|
||||
bn2buf (sig->s, signature + DSA_SIGNATURE_LENGTH/2, DSA_SIGNATURE_LENGTH/2);
|
||||
const BIGNUM * r, * s;
|
||||
DSA_SIG_get0 (sig, &r, &s);
|
||||
bn2buf (r, signature, DSA_SIGNATURE_LENGTH/2);
|
||||
bn2buf (s, signature + DSA_SIGNATURE_LENGTH/2, DSA_SIGNATURE_LENGTH/2);
|
||||
DSA_SIG_free(sig);
|
||||
}
|
||||
|
||||
@@ -108,10 +110,11 @@ namespace crypto
|
||||
{
|
||||
DSA * dsa = CreateDSA ();
|
||||
DSA_generate_key (dsa);
|
||||
bn2buf (dsa->priv_key, signingPrivateKey, DSA_PRIVATE_KEY_LENGTH);
|
||||
bn2buf (dsa->pub_key, signingPublicKey, DSA_PUBLIC_KEY_LENGTH);
|
||||
DSA_free (dsa);
|
||||
|
||||
const BIGNUM * pub_key, * priv_key;
|
||||
DSA_get0_key(dsa, &pub_key, &priv_key);
|
||||
bn2buf (priv_key, signingPrivateKey, DSA_PRIVATE_KEY_LENGTH);
|
||||
bn2buf (pub_key, signingPublicKey, DSA_PUBLIC_KEY_LENGTH);
|
||||
DSA_free (dsa);
|
||||
}
|
||||
|
||||
struct SHA256Hash
|
||||
@@ -152,9 +155,10 @@ namespace crypto
|
||||
ECDSAVerifier (const uint8_t * signingKey)
|
||||
{
|
||||
m_PublicKey = EC_KEY_new_by_curve_name (curve);
|
||||
EC_KEY_set_public_key_affine_coordinates (m_PublicKey,
|
||||
BN_bin2bn (signingKey, keyLen/2, NULL),
|
||||
BN_bin2bn (signingKey + keyLen/2, keyLen/2, NULL));
|
||||
BIGNUM * x = BN_bin2bn (signingKey, keyLen/2, NULL);
|
||||
BIGNUM * y = BN_bin2bn (signingKey + keyLen/2, keyLen/2, NULL);
|
||||
EC_KEY_set_public_key_affine_coordinates (m_PublicKey, x, y);
|
||||
BN_free (x); BN_free (y);
|
||||
}
|
||||
|
||||
~ECDSAVerifier ()
|
||||
@@ -167,8 +171,9 @@ namespace crypto
|
||||
uint8_t digest[Hash::hashLen];
|
||||
Hash::CalculateHash (buf, len, digest);
|
||||
ECDSA_SIG * sig = ECDSA_SIG_new();
|
||||
sig->r = BN_bin2bn (signature, GetSignatureLen ()/2, NULL);
|
||||
sig->s = BN_bin2bn (signature + GetSignatureLen ()/2, GetSignatureLen ()/2, NULL);
|
||||
auto r = BN_bin2bn (signature, GetSignatureLen ()/2, NULL);
|
||||
auto s = BN_bin2bn (signature + GetSignatureLen ()/2, GetSignatureLen ()/2, NULL);
|
||||
ECDSA_SIG_set0(sig, r, s);
|
||||
// ECDSA verification
|
||||
int ret = ECDSA_do_verify (digest, Hash::hashLen, sig, m_PublicKey);
|
||||
ECDSA_SIG_free(sig);
|
||||
@@ -205,9 +210,11 @@ namespace crypto
|
||||
uint8_t digest[Hash::hashLen];
|
||||
Hash::CalculateHash (buf, len, digest);
|
||||
ECDSA_SIG * sig = ECDSA_do_sign (digest, Hash::hashLen, m_PrivateKey);
|
||||
const BIGNUM * r, * s;
|
||||
ECDSA_SIG_get0 (sig, &r, &s);
|
||||
// signatureLen = keyLen
|
||||
bn2buf (sig->r, signature, keyLen/2);
|
||||
bn2buf (sig->s, signature + keyLen/2, keyLen/2);
|
||||
bn2buf (r, signature, keyLen/2);
|
||||
bn2buf (s, signature + keyLen/2, keyLen/2);
|
||||
ECDSA_SIG_free(sig);
|
||||
}
|
||||
|
||||
@@ -269,9 +276,7 @@ namespace crypto
|
||||
RSAVerifier (const uint8_t * signingKey)
|
||||
{
|
||||
m_PublicKey = RSA_new ();
|
||||
memset (m_PublicKey, 0, sizeof (RSA));
|
||||
m_PublicKey->e = BN_dup (GetRSAE ());
|
||||
m_PublicKey->n = BN_bin2bn (signingKey, keyLen, NULL);
|
||||
RSA_set0_key (m_PublicKey, BN_bin2bn (signingKey, keyLen, NULL) /* n */ , BN_dup (GetRSAE ()) /* d */, NULL);
|
||||
}
|
||||
|
||||
~RSAVerifier ()
|
||||
@@ -303,10 +308,8 @@ namespace crypto
|
||||
RSASigner (const uint8_t * signingPrivateKey)
|
||||
{
|
||||
m_PrivateKey = RSA_new ();
|
||||
memset (m_PrivateKey, 0, sizeof (RSA));
|
||||
m_PrivateKey->e = BN_dup (GetRSAE ());
|
||||
m_PrivateKey->n = BN_bin2bn (signingPrivateKey, keyLen, NULL);
|
||||
m_PrivateKey->d = BN_bin2bn (signingPrivateKey + keyLen, keyLen, NULL);
|
||||
RSA_set0_key (m_PrivateKey, BN_bin2bn (signingPrivateKey, keyLen, NULL), /* n */
|
||||
BN_dup (GetRSAE ()) /* e */, BN_bin2bn (signingPrivateKey + keyLen, keyLen, NULL) /* d */);
|
||||
}
|
||||
|
||||
~RSASigner ()
|
||||
@@ -332,9 +335,11 @@ namespace crypto
|
||||
RSA * rsa = RSA_new ();
|
||||
BIGNUM * e = BN_dup (GetRSAE ()); // make it non-const
|
||||
RSA_generate_key_ex (rsa, publicKeyLen*8, e, NULL);
|
||||
bn2buf (rsa->n, signingPrivateKey, publicKeyLen);
|
||||
bn2buf (rsa->d, signingPrivateKey + publicKeyLen, publicKeyLen);
|
||||
bn2buf (rsa->n, signingPublicKey, publicKeyLen);
|
||||
const BIGNUM * n, * d, * e1;
|
||||
RSA_get0_key (rsa, &n, &e1, &d);
|
||||
bn2buf (n, signingPrivateKey, publicKeyLen);
|
||||
bn2buf (d, signingPrivateKey + publicKeyLen, publicKeyLen);
|
||||
bn2buf (n, signingPublicKey, publicKeyLen);
|
||||
BN_free (e); // this e is not assigned to rsa->e
|
||||
RSA_free (rsa);
|
||||
}
|
||||
|
||||
@@ -611,22 +611,19 @@ namespace stream
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!m_CurrentOutboundTunnel) // first message to send
|
||||
if (!m_RoutingSession || !m_RoutingSession->GetOwner ()) // expired and detached
|
||||
m_RoutingSession = m_LocalDestination.GetOwner ()->GetRoutingSession (m_RemoteLeaseSet, true);
|
||||
if (!m_CurrentOutboundTunnel && m_RoutingSession) // first message to send
|
||||
{
|
||||
// try to get shared path first
|
||||
if (!m_RoutingSession)
|
||||
m_RoutingSession = m_LocalDestination.GetOwner ()->GetRoutingSession (m_RemoteLeaseSet, true);
|
||||
if (m_RoutingSession)
|
||||
{
|
||||
auto routingPath = m_RoutingSession->GetSharedRoutingPath ();
|
||||
if (routingPath)
|
||||
{
|
||||
m_CurrentOutboundTunnel = routingPath->outboundTunnel;
|
||||
m_CurrentRemoteLease = routingPath->remoteLease;
|
||||
m_RTT = routingPath->rtt;
|
||||
m_RTO = m_RTT*1.5; // TODO: implement it better
|
||||
}
|
||||
}
|
||||
auto routingPath = m_RoutingSession->GetSharedRoutingPath ();
|
||||
if (routingPath)
|
||||
{
|
||||
m_CurrentOutboundTunnel = routingPath->outboundTunnel;
|
||||
m_CurrentRemoteLease = routingPath->remoteLease;
|
||||
m_RTT = routingPath->rtt;
|
||||
m_RTO = m_RTT*1.5; // TODO: implement it better
|
||||
}
|
||||
}
|
||||
if (!m_CurrentOutboundTunnel || !m_CurrentOutboundTunnel->IsEstablished ())
|
||||
m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ()->GetTunnelPool ()->GetNewOutboundTunnel (m_CurrentOutboundTunnel);
|
||||
@@ -637,7 +634,8 @@ namespace stream
|
||||
}
|
||||
|
||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
if (!m_CurrentRemoteLease || ts >= m_CurrentRemoteLease->endDate - i2p::data::LEASE_ENDDATE_THRESHOLD)
|
||||
if (!m_CurrentRemoteLease || !m_CurrentRemoteLease->endDate || // excluded from LeaseSet
|
||||
ts >= m_CurrentRemoteLease->endDate - i2p::data::LEASE_ENDDATE_THRESHOLD)
|
||||
UpdateCurrentRemoteLease (true);
|
||||
if (m_CurrentRemoteLease && ts < m_CurrentRemoteLease->endDate + i2p::data::LEASE_ENDDATE_THRESHOLD)
|
||||
{
|
||||
@@ -656,16 +654,43 @@ namespace stream
|
||||
m_CurrentOutboundTunnel->SendTunnelDataMsg (msgs);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "Streaming: All leases are expired, sSID=", m_SendStreamID);
|
||||
{
|
||||
LogPrint (eLogWarning, "Streaming: Remote lease is not available, sSID=", m_SendStreamID);
|
||||
if (m_RoutingSession)
|
||||
m_RoutingSession->SetSharedRoutingPath (nullptr); // invalidate routing path
|
||||
}
|
||||
}
|
||||
|
||||
void Stream::SendUpdatedLeaseSet ()
|
||||
{
|
||||
if (m_RoutingSession)
|
||||
{
|
||||
if (m_RoutingSession->IsLeaseSetNonConfirmed ())
|
||||
{
|
||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
if (ts > m_RoutingSession->GetLeaseSetSubmissionTime () + i2p::garlic::LEASET_CONFIRMATION_TIMEOUT)
|
||||
{
|
||||
// LeaseSet was not confirmed, should try other tunnels
|
||||
LogPrint (eLogWarning, "Streaming: LeaseSet was not confrimed in ", i2p::garlic::LEASET_CONFIRMATION_TIMEOUT, " milliseconds. Trying to resubmit");
|
||||
m_RoutingSession->SetSharedRoutingPath (nullptr);
|
||||
m_CurrentOutboundTunnel = nullptr;
|
||||
m_CurrentRemoteLease = nullptr;
|
||||
SendQuickAck ();
|
||||
}
|
||||
}
|
||||
else if (m_RoutingSession->IsLeaseSetUpdated ())
|
||||
{
|
||||
LogPrint (eLogDebug, "Streaming: sending updated LeaseSet");
|
||||
SendQuickAck ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Stream::ScheduleResend ()
|
||||
{
|
||||
m_ResendTimer.cancel ();
|
||||
// check for invalid value
|
||||
if (m_RTO <= 0)
|
||||
m_RTO = 1;
|
||||
if (m_RTO <= 0) m_RTO = INITIAL_RTO;
|
||||
m_ResendTimer.expires_from_now (boost::posix_time::milliseconds(m_RTO));
|
||||
m_ResendTimer.async_wait (std::bind (&Stream::HandleResendTimer,
|
||||
shared_from_this (), std::placeholders::_1));
|
||||
@@ -760,7 +785,10 @@ namespace stream
|
||||
{
|
||||
m_RemoteLeaseSet = m_LocalDestination.GetOwner ()->FindLeaseSet (m_RemoteIdentity->GetIdentHash ());
|
||||
if (!m_RemoteLeaseSet)
|
||||
{
|
||||
LogPrint (eLogWarning, "Streaming: LeaseSet ", m_RemoteIdentity->GetIdentHash ().ToBase64 (), " not found");
|
||||
m_LocalDestination.GetOwner ()->RequestDestination (m_RemoteIdentity->GetIdentHash ()); // try to request for a next attempt
|
||||
}
|
||||
}
|
||||
if (m_RemoteLeaseSet)
|
||||
{
|
||||
@@ -797,17 +825,22 @@ namespace stream
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "Streaming: All remote leases are expired");
|
||||
m_RemoteLeaseSet = nullptr;
|
||||
m_CurrentRemoteLease = nullptr;
|
||||
// we have requested expired before, no need to do it twice
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "Streaming: Remote LeaseSet not found");
|
||||
m_CurrentRemoteLease = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
StreamingDestination::StreamingDestination (std::shared_ptr<i2p::client::ClientDestination> owner, uint16_t localPort, bool gzip):
|
||||
m_Owner (owner), m_LocalPort (localPort), m_Gzip (gzip),
|
||||
m_LastIncomingReceiveStreamID (0),
|
||||
m_PendingIncomingTimer (m_Owner->GetService ()),
|
||||
m_ConnTrackTimer(m_Owner->GetService()),
|
||||
m_ConnsPerMinute(DEFAULT_MAX_CONNS_PER_MIN),
|
||||
@@ -863,8 +896,15 @@ namespace stream
|
||||
{
|
||||
if (packet->IsSYN () && !packet->GetSeqn ()) // new incoming stream
|
||||
{
|
||||
auto incomingStream = CreateNewIncomingStream ();
|
||||
uint32_t receiveStreamID = packet->GetReceiveStreamID ();
|
||||
if (receiveStreamID == m_LastIncomingReceiveStreamID)
|
||||
{
|
||||
// already pending
|
||||
LogPrint(eLogWarning, "Streaming: Incoming streaming with rSID=", receiveStreamID, " already exists");
|
||||
delete packet; // drop it, because previous should be connected
|
||||
return;
|
||||
}
|
||||
auto incomingStream = CreateNewIncomingStream ();
|
||||
incomingStream->HandleNextPacket (packet); // SYN
|
||||
auto ident = incomingStream->GetRemoteIdentity();
|
||||
if(ident)
|
||||
@@ -878,6 +918,8 @@ namespace stream
|
||||
return;
|
||||
}
|
||||
}
|
||||
m_LastIncomingReceiveStreamID = receiveStreamID;
|
||||
|
||||
// handle saved packets if any
|
||||
{
|
||||
auto it = m_SavedPackets.find (receiveStreamID);
|
||||
|
||||
26
Streaming.h
26
Streaming.h
@@ -51,6 +51,7 @@ namespace stream
|
||||
const int INITIAL_RTO = 9000; // in milliseconds
|
||||
const size_t MAX_PENDING_INCOMING_BACKLOG = 128;
|
||||
const int PENDING_INCOMING_TIMEOUT = 10; // in seconds
|
||||
const int MAX_RECEIVE_TIMEOUT = 30; // in seconds
|
||||
|
||||
/** i2cp option for limiting inbound stremaing connections */
|
||||
const char I2CP_PARAM_STREAMING_MAX_CONNS_PER_MIN[] = "maxconns";
|
||||
@@ -161,6 +162,7 @@ namespace stream
|
||||
void SendClose ();
|
||||
bool SendPacket (Packet * packet);
|
||||
void SendPackets (const std::vector<Packet *>& packets);
|
||||
void SendUpdatedLeaseSet ();
|
||||
|
||||
void SavePacket (Packet * packet);
|
||||
void ProcessPacket (Packet * packet);
|
||||
@@ -170,7 +172,7 @@ namespace stream
|
||||
void UpdateCurrentRemoteLease (bool expired = false);
|
||||
|
||||
template<typename Buffer, typename ReceiveHandler>
|
||||
void HandleReceiveTimer (const boost::system::error_code& ecode, const Buffer& buffer, ReceiveHandler handler);
|
||||
void HandleReceiveTimer (const boost::system::error_code& ecode, const Buffer& buffer, ReceiveHandler handler, int remainingTimeout);
|
||||
|
||||
void ScheduleResend ();
|
||||
void HandleResendTimer (const boost::system::error_code& ecode);
|
||||
@@ -222,6 +224,7 @@ namespace stream
|
||||
void ResetAcceptor ();
|
||||
bool IsAcceptorSet () const { return m_Acceptor != nullptr; };
|
||||
std::shared_ptr<i2p::client::ClientDestination> GetOwner () const { return m_Owner; };
|
||||
void SetOwner (std::shared_ptr<i2p::client::ClientDestination> owner) { m_Owner = owner; };
|
||||
uint16_t GetLocalPort () const { return m_LocalPort; };
|
||||
|
||||
void HandleDataMessagePayload (const uint8_t * buf, size_t len);
|
||||
@@ -251,6 +254,7 @@ namespace stream
|
||||
std::mutex m_StreamsMutex;
|
||||
std::map<uint32_t, std::shared_ptr<Stream> > m_Streams; // sendStreamID->stream
|
||||
Acceptor m_Acceptor;
|
||||
uint32_t m_LastIncomingReceiveStreamID;
|
||||
std::list<std::shared_ptr<Stream> > m_PendingIncomingStreams;
|
||||
boost::asio::deadline_timer m_PendingIncomingTimer;
|
||||
std::map<uint32_t, std::list<Packet *> > m_SavedPackets; // receiveStreamID->packets, arrived before SYN
|
||||
@@ -282,18 +286,19 @@ namespace stream
|
||||
m_Service.post ([=](void)
|
||||
{
|
||||
if (!m_ReceiveQueue.empty () || m_Status == eStreamStatusReset)
|
||||
s->HandleReceiveTimer (boost::asio::error::make_error_code (boost::asio::error::operation_aborted), buffer, handler);
|
||||
s->HandleReceiveTimer (boost::asio::error::make_error_code (boost::asio::error::operation_aborted), buffer, handler, 0);
|
||||
else
|
||||
{
|
||||
s->m_ReceiveTimer.expires_from_now (boost::posix_time::seconds(timeout));
|
||||
int t = (timeout > MAX_RECEIVE_TIMEOUT) ? MAX_RECEIVE_TIMEOUT : timeout;
|
||||
s->m_ReceiveTimer.expires_from_now (boost::posix_time::seconds(t));
|
||||
s->m_ReceiveTimer.async_wait ([=](const boost::system::error_code& ecode)
|
||||
{ s->HandleReceiveTimer (ecode, buffer, handler); });
|
||||
{ s->HandleReceiveTimer (ecode, buffer, handler, timeout - t); });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template<typename Buffer, typename ReceiveHandler>
|
||||
void Stream::HandleReceiveTimer (const boost::system::error_code& ecode, const Buffer& buffer, ReceiveHandler handler)
|
||||
void Stream::HandleReceiveTimer (const boost::system::error_code& ecode, const Buffer& buffer, ReceiveHandler handler, int remainingTimeout)
|
||||
{
|
||||
size_t received = ConcatenatePackets (boost::asio::buffer_cast<uint8_t *>(buffer), boost::asio::buffer_size(buffer));
|
||||
if (received > 0)
|
||||
@@ -307,8 +312,17 @@ namespace stream
|
||||
handler (boost::asio::error::make_error_code (boost::asio::error::operation_aborted), 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// timeout expired
|
||||
handler (boost::asio::error::make_error_code (boost::asio::error::timed_out), received);
|
||||
if (remainingTimeout <= 0)
|
||||
handler (boost::asio::error::make_error_code (boost::asio::error::timed_out), received);
|
||||
else
|
||||
{
|
||||
// itermediate iterrupt
|
||||
SendUpdatedLeaseSet (); // send our leaseset if applicable
|
||||
AsyncReceive (buffer, handler, remainingTimeout);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
8
Tag.h
8
Tag.h
@@ -11,6 +11,7 @@
|
||||
|
||||
#include <boost/static_assert.hpp>
|
||||
#include <string.h>
|
||||
#include <openssl/rand.h>
|
||||
#include "Base.h"
|
||||
|
||||
namespace i2p {
|
||||
@@ -49,7 +50,12 @@ public:
|
||||
{
|
||||
memset(m_Buf, c, sz);
|
||||
}
|
||||
|
||||
|
||||
void Randomize()
|
||||
{
|
||||
RAND_bytes(m_Buf, sz);
|
||||
}
|
||||
|
||||
std::string ToBase64 () const
|
||||
{
|
||||
char str[sz*2];
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <boost/asio.hpp>
|
||||
#include "Log.h"
|
||||
#include "I2PEndian.h"
|
||||
#include "Timestamp.h"
|
||||
|
||||
@@ -8,7 +9,7 @@ namespace i2p
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
std::chrono::system_clock::duration g_TimeOffset = std::chrono::system_clock::duration::zero ();
|
||||
static int64_t g_TimeOffset = 0; // in seconds
|
||||
|
||||
void SyncTimeWithNTP (const std::string& address)
|
||||
{
|
||||
@@ -23,25 +24,34 @@ namespace util
|
||||
socket.open (boost::asio::ip::udp::v4 (), ec);
|
||||
if (!ec)
|
||||
{
|
||||
uint8_t request[48];// 48 bytes NTP request
|
||||
memset (request, 0, 48);
|
||||
request[0] = 0x80; // client mode, version 0
|
||||
uint8_t * response = new uint8_t[1500]; // MTU
|
||||
uint8_t buf[48];// 48 bytes NTP request/response
|
||||
memset (buf, 0, 48);
|
||||
htobe32buf (buf, (3 << 27) | (3 << 24)); // RFC 4330
|
||||
size_t len = 0;
|
||||
try
|
||||
{
|
||||
socket.send_to (boost::asio::buffer (request, 48), ep);
|
||||
len = socket.receive_from (boost::asio::buffer (response, 1500), ep);
|
||||
socket.send_to (boost::asio::buffer (buf, 48), ep);
|
||||
int i = 0;
|
||||
while (!socket.available() && i < 10) // 10 seconds max
|
||||
{
|
||||
std::this_thread::sleep_for (std::chrono::seconds(1));
|
||||
i++;
|
||||
}
|
||||
if (socket.available ())
|
||||
len = socket.receive_from (boost::asio::buffer (buf, 48), ep);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
LogPrint (eLogError, "NTP error: ", e.what ());
|
||||
}
|
||||
if (len >= 8)
|
||||
{
|
||||
uint32_t ts = bufbe32toh (response + 4);
|
||||
auto ourTs = GetSecondsSinceEpoch ();
|
||||
uint32_t ts = bufbe32toh (buf + 32);
|
||||
if (ts > 2208988800U) ts -= 2208988800U; // 1/1/1970 from 1/1/1900
|
||||
g_TimeOffset = ts - ourTs;
|
||||
LogPrint (eLogInfo, address, " time offset from system time is ", g_TimeOffset, " seconds");
|
||||
}
|
||||
delete[] response;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,8 +8,6 @@ namespace i2p
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
extern std::chrono::system_clock::duration g_TimeOffset;
|
||||
|
||||
inline uint64_t GetMillisecondsSinceEpoch ()
|
||||
{
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
|
||||
@@ -64,6 +64,8 @@ namespace transport
|
||||
|
||||
virtual ~TransportSession () {};
|
||||
virtual void Done () = 0;
|
||||
|
||||
std::string GetIdentHashBase64() const { return m_RemoteIdentity ? m_RemoteIdentity->GetIdentHash().ToBase64() : ""; }
|
||||
|
||||
std::shared_ptr<const i2p::data::IdentityEx> GetRemoteIdentity () { return m_RemoteIdentity; };
|
||||
void SetRemoteIdentity (std::shared_ptr<const i2p::data::IdentityEx> ident) { m_RemoteIdentity = ident; };
|
||||
|
||||
195
Transports.cpp
195
Transports.cpp
@@ -4,6 +4,11 @@
|
||||
#include "I2NPProtocol.h"
|
||||
#include "NetDb.h"
|
||||
#include "Transports.h"
|
||||
#include "Config.h"
|
||||
#ifdef WITH_EVENTS
|
||||
#include "Event.h"
|
||||
#include "util.h"
|
||||
#endif
|
||||
|
||||
using namespace i2p::data;
|
||||
|
||||
@@ -43,11 +48,22 @@ namespace transport
|
||||
{
|
||||
while (m_IsRunning)
|
||||
{
|
||||
int num;
|
||||
while ((num = m_QueueSize - m_Queue.size ()) > 0)
|
||||
int num, total = 0;
|
||||
while ((num = m_QueueSize - (int)m_Queue.size ()) > 0 && total < 20)
|
||||
{
|
||||
CreateDHKeysPairs (num);
|
||||
std::unique_lock<std::mutex> l(m_AcquiredMutex);
|
||||
m_Acquired.wait (l); // wait for element gets aquired
|
||||
total += num;
|
||||
}
|
||||
if (total >= 20)
|
||||
{
|
||||
LogPrint (eLogWarning, "Transports: ", total, " DH keys generated at the time");
|
||||
std::this_thread::sleep_for (std::chrono::seconds(1)); // take a break
|
||||
}
|
||||
else
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_AcquiredMutex);
|
||||
m_Acquired.wait (l); // wait for element gets aquired
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +71,6 @@ namespace transport
|
||||
{
|
||||
if (num > 0)
|
||||
{
|
||||
i2p::crypto::DHKeys dh;
|
||||
for (int i = 0; i < num; i++)
|
||||
{
|
||||
auto pair = std::make_shared<i2p::crypto::DHKeys> ();
|
||||
@@ -93,7 +108,8 @@ namespace transport
|
||||
Transports transports;
|
||||
|
||||
Transports::Transports ():
|
||||
m_IsOnline (true), m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service), m_PeerCleanupTimer (m_Service),
|
||||
m_IsOnline (true), m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service),
|
||||
m_PeerCleanupTimer (m_Service), m_PeerTestTimer (m_Service),
|
||||
m_NTCPServer (nullptr), m_SSUServer (nullptr), m_DHKeysPairSupplier (5), // 5 pre-generated keys
|
||||
m_TotalSentBytes(0), m_TotalReceivedBytes(0), m_InBandwidth (0), m_OutBandwidth (0),
|
||||
m_LastInBandwidthUpdateBytes (0), m_LastOutBandwidthUpdateBytes (0), m_LastBandwidthUpdateTime (0)
|
||||
@@ -153,11 +169,14 @@ namespace transport
|
||||
}
|
||||
m_PeerCleanupTimer.expires_from_now (boost::posix_time::seconds(5*SESSION_CREATION_TIMEOUT));
|
||||
m_PeerCleanupTimer.async_wait (std::bind (&Transports::HandlePeerCleanupTimer, this, std::placeholders::_1));
|
||||
m_PeerTestTimer.expires_from_now (boost::posix_time::minutes(PEER_TEST_INTERVAL));
|
||||
m_PeerTestTimer.async_wait (std::bind (&Transports::HandlePeerTestTimer, this, std::placeholders::_1));
|
||||
}
|
||||
|
||||
void Transports::Stop ()
|
||||
{
|
||||
m_PeerCleanupTimer.cancel ();
|
||||
m_PeerTestTimer.cancel ();
|
||||
m_Peers.clear ();
|
||||
if (m_SSUServer)
|
||||
{
|
||||
@@ -229,6 +248,9 @@ namespace transport
|
||||
|
||||
void Transports::SendMessages (const i2p::data::IdentHash& ident, const std::vector<std::shared_ptr<i2p::I2NPMessage> >& msgs)
|
||||
{
|
||||
#ifdef WITH_EVENTS
|
||||
EmitEvent({{"type" , "transport.sendmsg"}, {"ident", ident.ToBase64()}, {"number", std::to_string(msgs.size())}});
|
||||
#endif
|
||||
m_Service.post (std::bind (&Transports::PostMessages, this, ident, msgs));
|
||||
}
|
||||
|
||||
@@ -238,9 +260,11 @@ namespace transport
|
||||
{
|
||||
// we send it to ourself
|
||||
for (auto& it: msgs)
|
||||
i2p::HandleI2NPMessage (it);
|
||||
m_LoopbackHandler.PutNextMessage (it);
|
||||
m_LoopbackHandler.Flush ();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if(RoutesRestricted() && ! IsRestrictedPeer(ident)) return;
|
||||
auto it = m_Peers.find (ident);
|
||||
if (it == m_Peers.end ())
|
||||
{
|
||||
@@ -494,17 +518,23 @@ namespace transport
|
||||
|
||||
void Transports::DetectExternalIP ()
|
||||
{
|
||||
if (RoutesRestricted())
|
||||
{
|
||||
LogPrint(eLogInfo, "Transports: restricted routes enabled, not detecting ip");
|
||||
i2p::context.SetStatus (eRouterStatusOK);
|
||||
return;
|
||||
}
|
||||
if (m_SSUServer)
|
||||
{
|
||||
#ifndef MESHNET
|
||||
i2p::context.SetStatus (eRouterStatusTesting);
|
||||
#endif
|
||||
|
||||
bool nat; i2p::config::GetOption("nat", nat);
|
||||
bool isv4 = i2p::context.SupportsV4 ();
|
||||
if (nat && isv4)
|
||||
i2p::context.SetStatus (eRouterStatusTesting);
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
auto router = i2p::data::netdb.GetRandomPeerTestRouter ();
|
||||
if (router && router->IsSSU (!context.SupportsV6 ()))
|
||||
m_SSUServer->CreateSession (router, true); // peer test
|
||||
auto router = i2p::data::netdb.GetRandomPeerTestRouter (isv4); // v4 only if v4
|
||||
if (router)
|
||||
m_SSUServer->CreateSession (router, true, isv4); // peer test
|
||||
else
|
||||
{
|
||||
// if not peer test capable routers found pick any
|
||||
@@ -520,22 +550,25 @@ namespace transport
|
||||
|
||||
void Transports::PeerTest ()
|
||||
{
|
||||
if (RoutesRestricted() || !i2p::context.SupportsV4 ()) return;
|
||||
if (m_SSUServer)
|
||||
{
|
||||
{
|
||||
bool statusChanged = false;
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
auto router = i2p::data::netdb.GetRandomPeerTestRouter ();
|
||||
if (router && router->IsSSU (!context.SupportsV6 ()))
|
||||
auto router = i2p::data::netdb.GetRandomPeerTestRouter (true); // v4 only
|
||||
if (router)
|
||||
{
|
||||
if (!statusChanged)
|
||||
{
|
||||
statusChanged = true;
|
||||
i2p::context.SetStatus (eRouterStatusTesting); // first time only
|
||||
}
|
||||
m_SSUServer->CreateSession (router, true); // peer test
|
||||
m_SSUServer->CreateSession (router, true, true); // peer test v4
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!statusChanged)
|
||||
LogPrint (eLogWarning, "Can't find routers for peer test");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -559,6 +592,9 @@ namespace transport
|
||||
auto it = m_Peers.find (ident);
|
||||
if (it != m_Peers.end ())
|
||||
{
|
||||
#ifdef WITH_EVENTS
|
||||
EmitEvent({{"type" , "transport.connected"}, {"ident", ident.ToBase64()}, {"inbound", "false"}});
|
||||
#endif
|
||||
bool sendDatabaseStore = true;
|
||||
if (it->second.delayedMessages.size () > 0)
|
||||
{
|
||||
@@ -578,20 +614,32 @@ namespace transport
|
||||
}
|
||||
else // incoming connection
|
||||
{
|
||||
if(RoutesRestricted() && ! IsRestrictedPeer(ident)) {
|
||||
// not trusted
|
||||
LogPrint(eLogWarning, "Transports: closing untrusted inbound connection from ", ident.ToBase64());
|
||||
session->Done();
|
||||
return;
|
||||
}
|
||||
#ifdef WITH_EVENTS
|
||||
EmitEvent({{"type" , "transport.connected"}, {"ident", ident.ToBase64()}, {"inbound", "true"}});
|
||||
#endif
|
||||
session->SendI2NPMessages ({ CreateDatabaseStoreMsg () }); // send DatabaseStore
|
||||
std::unique_lock<std::mutex> l(m_PeersMutex);
|
||||
m_Peers.insert (std::make_pair (ident, Peer{ 0, nullptr, { session }, i2p::util::GetSecondsSinceEpoch (), {} }));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void Transports::PeerDisconnected (std::shared_ptr<TransportSession> session)
|
||||
{
|
||||
m_Service.post([session, this]()
|
||||
{
|
||||
{
|
||||
auto remoteIdentity = session->GetRemoteIdentity ();
|
||||
if (!remoteIdentity) return;
|
||||
auto ident = remoteIdentity->GetIdentHash ();
|
||||
#ifdef WITH_EVENTS
|
||||
EmitEvent({{"type" , "transport.disconnected"}, {"ident", ident.ToBase64()}});
|
||||
#endif
|
||||
auto it = m_Peers.find (ident);
|
||||
if (it != m_Peers.end ())
|
||||
{
|
||||
@@ -647,6 +695,16 @@ namespace transport
|
||||
}
|
||||
}
|
||||
|
||||
void Transports::HandlePeerTestTimer (const boost::system::error_code& ecode)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
PeerTest ();
|
||||
m_PeerTestTimer.expires_from_now (boost::posix_time::minutes(PEER_TEST_INTERVAL));
|
||||
m_PeerTestTimer.async_wait (std::bind (&Transports::HandlePeerTestTimer, this, std::placeholders::_1));
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<const i2p::data::RouterInfo> Transports::GetRandomPeer () const
|
||||
{
|
||||
if (m_Peers.empty ()) return nullptr;
|
||||
@@ -655,30 +713,79 @@ namespace transport
|
||||
std::advance (it, rand () % m_Peers.size ());
|
||||
return it != m_Peers.end () ? it->second.router : nullptr;
|
||||
}
|
||||
void Transports::RestrictRoutes(std::vector<std::string> families)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_FamilyMutex);
|
||||
m_TrustedFamilies.clear();
|
||||
for ( const auto& fam : families )
|
||||
m_TrustedFamilies.push_back(fam);
|
||||
}
|
||||
void Transports::RestrictRoutesToFamilies(std::set<std::string> families)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_FamilyMutex);
|
||||
m_TrustedFamilies.clear();
|
||||
for ( const auto& fam : families )
|
||||
m_TrustedFamilies.push_back(fam);
|
||||
}
|
||||
|
||||
bool Transports::RoutesRestricted() const {
|
||||
std::lock_guard<std::mutex> lock(m_FamilyMutex);
|
||||
return m_TrustedFamilies.size() > 0;
|
||||
}
|
||||
void Transports::RestrictRoutesToRouters(std::set<i2p::data::IdentHash> routers)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_TrustedRoutersMutex);
|
||||
m_TrustedRouters.clear();
|
||||
for (const auto & ri : routers )
|
||||
m_TrustedRouters.push_back(ri);
|
||||
}
|
||||
|
||||
bool Transports::RoutesRestricted() const {
|
||||
std::unique_lock<std::mutex> famlock(m_FamilyMutex);
|
||||
std::unique_lock<std::mutex> routerslock(m_TrustedRoutersMutex);
|
||||
return m_TrustedFamilies.size() > 0 || m_TrustedRouters.size() > 0;
|
||||
}
|
||||
|
||||
/** XXX: if routes are not restricted this dies */
|
||||
std::shared_ptr<const i2p::data::RouterInfo> Transports::GetRestrictedPeer() const {
|
||||
std::string fam;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_FamilyMutex);
|
||||
// TODO: random family (?)
|
||||
fam = m_TrustedFamilies[0];
|
||||
}
|
||||
boost::to_lower(fam);
|
||||
return i2p::data::netdb.GetRandomRouterInFamily(fam);
|
||||
}
|
||||
/** XXX: if routes are not restricted this dies */
|
||||
std::shared_ptr<const i2p::data::RouterInfo> Transports::GetRestrictedPeer() const
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> l(m_FamilyMutex);
|
||||
std::string fam;
|
||||
auto sz = m_TrustedFamilies.size();
|
||||
if(sz > 1)
|
||||
{
|
||||
auto it = m_TrustedFamilies.begin ();
|
||||
std::advance(it, rand() % sz);
|
||||
fam = *it;
|
||||
boost::to_lower(fam);
|
||||
}
|
||||
else if (sz == 1)
|
||||
{
|
||||
fam = m_TrustedFamilies[0];
|
||||
}
|
||||
if (fam.size())
|
||||
return i2p::data::netdb.GetRandomRouterInFamily(fam);
|
||||
}
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_TrustedRoutersMutex);
|
||||
auto sz = m_TrustedRouters.size();
|
||||
if (sz)
|
||||
{
|
||||
if(sz == 1)
|
||||
return i2p::data::netdb.FindRouter(m_TrustedRouters[0]);
|
||||
auto it = m_TrustedRouters.begin();
|
||||
std::advance(it, rand() % sz);
|
||||
return i2p::data::netdb.FindRouter(*it);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Transports::IsRestrictedPeer(const i2p::data::IdentHash & ih) const
|
||||
{
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_TrustedRoutersMutex);
|
||||
for (const auto & r : m_TrustedRouters )
|
||||
if ( r == ih ) return true;
|
||||
}
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_FamilyMutex);
|
||||
auto ri = i2p::data::netdb.FindRouter(ih);
|
||||
for (const auto & fam : m_TrustedFamilies)
|
||||
if(ri->IsFamily(fam)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
24
Transports.h
24
Transports.h
@@ -66,6 +66,7 @@ namespace transport
|
||||
};
|
||||
|
||||
const size_t SESSION_CREATION_TIMEOUT = 10; // in seconds
|
||||
const int PEER_TEST_INTERVAL = 71; // in minutes
|
||||
const int MAX_NUM_DELAYED_MESSAGES = 50;
|
||||
class Transports
|
||||
{
|
||||
@@ -110,7 +111,11 @@ namespace transport
|
||||
/** do we want to use restricted routes? */
|
||||
bool RoutesRestricted() const;
|
||||
/** restrict routes to use only these router families for first hops */
|
||||
void RestrictRoutes(std::vector<std::string> families);
|
||||
void RestrictRoutesToFamilies(std::set<std::string> families);
|
||||
/** restrict routes to use only these routers for first hops */
|
||||
void RestrictRoutesToRouters(std::set<i2p::data::IdentHash> routers);
|
||||
|
||||
bool IsRestrictedPeer(const i2p::data::IdentHash & ident) const;
|
||||
|
||||
void PeerTest ();
|
||||
|
||||
@@ -123,7 +128,8 @@ namespace transport
|
||||
void PostCloseSession (std::shared_ptr<const i2p::data::RouterInfo> router);
|
||||
bool ConnectToPeer (const i2p::data::IdentHash& ident, Peer& peer);
|
||||
void HandlePeerCleanupTimer (const boost::system::error_code& ecode);
|
||||
|
||||
void HandlePeerTestTimer (const boost::system::error_code& ecode);
|
||||
|
||||
void NTCPResolve (const std::string& addr, const i2p::data::IdentHash& ident);
|
||||
void HandleNTCPResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::iterator it,
|
||||
i2p::data::IdentHash ident, std::shared_ptr<boost::asio::ip::tcp::resolver> resolver);
|
||||
@@ -140,7 +146,7 @@ namespace transport
|
||||
std::thread * m_Thread;
|
||||
boost::asio::io_service m_Service;
|
||||
boost::asio::io_service::work m_Work;
|
||||
boost::asio::deadline_timer m_PeerCleanupTimer;
|
||||
boost::asio::deadline_timer m_PeerCleanupTimer, m_PeerTestTimer;
|
||||
|
||||
NTCPServer * m_NTCPServer;
|
||||
SSUServer * m_SSUServer;
|
||||
@@ -154,9 +160,15 @@ namespace transport
|
||||
uint64_t m_LastInBandwidthUpdateBytes, m_LastOutBandwidthUpdateBytes;
|
||||
uint64_t m_LastBandwidthUpdateTime;
|
||||
|
||||
/** which router families to trust for first hops */
|
||||
std::vector<std::string> m_TrustedFamilies;
|
||||
mutable std::mutex m_FamilyMutex;
|
||||
/** which router families to trust for first hops */
|
||||
std::vector<std::string> m_TrustedFamilies;
|
||||
mutable std::mutex m_FamilyMutex;
|
||||
|
||||
/** which routers for first hop to trust */
|
||||
std::vector<i2p::data::IdentHash> m_TrustedRouters;
|
||||
mutable std::mutex m_TrustedRoutersMutex;
|
||||
|
||||
i2p::I2NPMessagesHandler m_LoopbackHandler;
|
||||
|
||||
public:
|
||||
|
||||
|
||||
86
Tunnel.cpp
86
Tunnel.cpp
@@ -11,12 +11,41 @@
|
||||
#include "Transports.h"
|
||||
#include "NetDb.h"
|
||||
#include "Tunnel.h"
|
||||
#include "TunnelPool.h"
|
||||
#ifdef WITH_EVENTS
|
||||
#include "Event.h"
|
||||
#endif
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace tunnel
|
||||
{
|
||||
|
||||
void TunnelLatency::AddSample(Sample s)
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_access);
|
||||
m_samples.push_back(s);
|
||||
}
|
||||
|
||||
bool TunnelLatency::HasSamples() const
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_access);
|
||||
return m_samples.size() > 0;
|
||||
}
|
||||
|
||||
TunnelLatency::Latency TunnelLatency::GetMeanLatency() const
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_access);
|
||||
if (m_samples.size() > 0) {
|
||||
Latency l = 0;
|
||||
for(auto s : m_samples)
|
||||
l += s;
|
||||
return l / m_samples.size();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Tunnel::Tunnel (std::shared_ptr<const TunnelConfig> config):
|
||||
TunnelBase (config->GetTunnelID (), config->GetNextTunnelID (), config->GetNextIdentHash ()),
|
||||
m_Config (config), m_Pool (nullptr), m_State (eTunnelStatePending), m_IsRecreated (false)
|
||||
@@ -29,12 +58,14 @@ namespace tunnel
|
||||
|
||||
void Tunnel::Build (uint32_t replyMsgID, std::shared_ptr<OutboundTunnel> outboundTunnel)
|
||||
{
|
||||
#ifdef WITH_EVENTS
|
||||
std::string peers = i2p::context.GetIdentity()->GetIdentHash().ToBase64();
|
||||
#endif
|
||||
auto numHops = m_Config->GetNumHops ();
|
||||
int numRecords = numHops <= STANDARD_NUM_RECORDS ? STANDARD_NUM_RECORDS : numHops;
|
||||
auto msg = NewI2NPShortMessage ();
|
||||
*msg->GetPayload () = numRecords;
|
||||
msg->len += numRecords*TUNNEL_BUILD_RECORD_SIZE + 1;
|
||||
|
||||
// shuffle records
|
||||
std::vector<int> recordIndicies;
|
||||
for (int i = 0; i < numRecords; i++) recordIndicies.push_back(i);
|
||||
@@ -55,8 +86,14 @@ namespace tunnel
|
||||
hop->CreateBuildRequestRecord (records + idx*TUNNEL_BUILD_RECORD_SIZE, msgID);
|
||||
hop->recordIndex = idx;
|
||||
i++;
|
||||
#ifdef WITH_EVENTS
|
||||
peers += ":" + hop->ident->GetIdentHash().ToBase64();
|
||||
#endif
|
||||
hop = hop->next;
|
||||
}
|
||||
#ifdef WITH_EVENTS
|
||||
EmitTunnelEvent("tunnel.build", this, peers);
|
||||
#endif
|
||||
// fill up fake records with random data
|
||||
for (int i = numHops; i < numRecords; i++)
|
||||
{
|
||||
@@ -150,6 +187,12 @@ namespace tunnel
|
||||
return established;
|
||||
}
|
||||
|
||||
bool Tunnel::LatencyFitsRange(uint64_t lower, uint64_t upper) const
|
||||
{
|
||||
auto latency = GetMeanLatency();
|
||||
return latency >= lower && latency <= upper;
|
||||
}
|
||||
|
||||
void Tunnel::EncryptTunnelMsg (std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out)
|
||||
{
|
||||
const uint8_t * inPayload = in->GetPayload () + 4;
|
||||
@@ -182,12 +225,22 @@ namespace tunnel
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Tunnel::SetState(TunnelState state)
|
||||
{
|
||||
m_State = state;
|
||||
#ifdef WITH_EVENTS
|
||||
EmitTunnelEvent("tunnel.state", this, state);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void Tunnel::PrintHops (std::stringstream& s) const
|
||||
{
|
||||
for (auto& it: m_Hops)
|
||||
// hops are in inverted order, we must print in direct order
|
||||
for (auto it = m_Hops.rbegin (); it != m_Hops.rend (); it++)
|
||||
{
|
||||
s << " ⇒ ";
|
||||
s << i2p::data::GetIdentHashAbbreviation (it->ident->GetIdentHash ());
|
||||
s << i2p::data::GetIdentHashAbbreviation ((*it)->ident->GetIdentHash ());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -581,6 +634,9 @@ namespace tunnel
|
||||
hop = hop->next;
|
||||
}
|
||||
}
|
||||
#ifdef WITH_EVENTS
|
||||
EmitTunnelEvent("tunnel.state", tunnel.get(), eTunnelStateBuildFailed);
|
||||
#endif
|
||||
// delete
|
||||
it = pendingTunnels.erase (it);
|
||||
m_NumFailedTunnelCreations++;
|
||||
@@ -590,6 +646,9 @@ namespace tunnel
|
||||
break;
|
||||
case eTunnelStateBuildFailed:
|
||||
LogPrint (eLogDebug, "Tunnel: pending build request ", it->first, " failed, deleted");
|
||||
#ifdef WITH_EVENTS
|
||||
EmitTunnelEvent("tunnel.state", tunnel.get(), eTunnelStateBuildFailed);
|
||||
#endif
|
||||
it = pendingTunnels.erase (it);
|
||||
m_NumFailedTunnelCreations++;
|
||||
break;
|
||||
@@ -640,11 +699,13 @@ namespace tunnel
|
||||
}
|
||||
}
|
||||
|
||||
if (m_OutboundTunnels.size () < 5)
|
||||
if (m_OutboundTunnels.size () < 3)
|
||||
{
|
||||
// trying to create one more oubound tunnel
|
||||
auto inboundTunnel = GetNextInboundTunnel ();
|
||||
auto router = i2p::data::netdb.GetRandomRouter ();
|
||||
auto router = i2p::transport::transports.RoutesRestricted() ?
|
||||
i2p::transport::transports.GetRestrictedPeer() :
|
||||
i2p::data::netdb.GetRandomRouter ();
|
||||
if (!inboundTunnel || !router) return;
|
||||
LogPrint (eLogDebug, "Tunnel: creating one hop outbound tunnel");
|
||||
CreateTunnel<OutboundTunnel> (
|
||||
@@ -684,6 +745,8 @@ namespace tunnel
|
||||
|
||||
if (ts + TUNNEL_EXPIRATION_THRESHOLD > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT)
|
||||
tunnel->SetState (eTunnelStateExpiring);
|
||||
else // we don't need to cleanup expiring tunnels
|
||||
tunnel->Cleanup ();
|
||||
}
|
||||
it++;
|
||||
}
|
||||
@@ -697,16 +760,18 @@ namespace tunnel
|
||||
CreateZeroHopsOutboundTunnel ();
|
||||
if (!m_ExploratoryPool)
|
||||
{
|
||||
m_ExploratoryPool = CreateTunnelPool (2, 2, 5, 5); // 2-hop exploratory, 5 tunnels
|
||||
m_ExploratoryPool = CreateTunnelPool (2, 2, 3, 3); // 2-hop exploratory, 3 tunnels
|
||||
m_ExploratoryPool->SetLocalDestination (i2p::context.GetSharedDestination ());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_OutboundTunnels.empty () || m_InboundTunnels.size () < 5)
|
||||
if (m_OutboundTunnels.empty () || m_InboundTunnels.size () < 3)
|
||||
{
|
||||
// trying to create one more inbound tunnel
|
||||
auto router = i2p::data::netdb.GetRandomRouter ();
|
||||
auto router = i2p::transport::transports.RoutesRestricted() ?
|
||||
i2p::transport::transports.GetRestrictedPeer() :
|
||||
i2p::data::netdb.GetRandomRouter ();
|
||||
if (!router) {
|
||||
LogPrint (eLogWarning, "Tunnel: can't find any router, skip creating tunnel");
|
||||
return;
|
||||
@@ -731,7 +796,10 @@ namespace tunnel
|
||||
it = m_TransitTunnels.erase (it);
|
||||
}
|
||||
else
|
||||
{
|
||||
tunnel->Cleanup ();
|
||||
it++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -771,7 +839,7 @@ namespace tunnel
|
||||
|
||||
std::shared_ptr<InboundTunnel> Tunnels::CreateInboundTunnel (std::shared_ptr<TunnelConfig> config, std::shared_ptr<OutboundTunnel> outboundTunnel)
|
||||
{
|
||||
if (config)
|
||||
if (config)
|
||||
return CreateTunnel<InboundTunnel>(config, outboundTunnel);
|
||||
else
|
||||
return CreateZeroHopsInboundTunnel ();
|
||||
|
||||
77
Tunnel.h
77
Tunnel.h
@@ -19,11 +19,49 @@
|
||||
#include "TunnelGateway.h"
|
||||
#include "TunnelBase.h"
|
||||
#include "I2NPProtocol.h"
|
||||
#include "Event.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace tunnel
|
||||
{
|
||||
{
|
||||
|
||||
template<typename TunnelT>
|
||||
static void EmitTunnelEvent(const std::string & ev, const TunnelT & t)
|
||||
{
|
||||
#ifdef WITH_EVENTS
|
||||
EmitEvent({{"type", ev}, {"tid", std::to_string(t->GetTunnelID())}});
|
||||
#else
|
||||
(void) ev;
|
||||
(void) t;
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename TunnelT, typename T>
|
||||
static void EmitTunnelEvent(const std::string & ev, TunnelT * t, const T & val)
|
||||
{
|
||||
#ifdef WITH_EVENTS
|
||||
EmitEvent({{"type", ev}, {"tid", std::to_string(t->GetTunnelID())}, {"value", std::to_string(val)}, {"inbound", std::to_string(t->IsInbound())}});
|
||||
#else
|
||||
(void) ev;
|
||||
(void) t;
|
||||
(void) val;
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename TunnelT>
|
||||
static void EmitTunnelEvent(const std::string & ev, TunnelT * t, const std::string & val)
|
||||
{
|
||||
#ifdef WITH_EVENTS
|
||||
EmitEvent({{"type", ev}, {"tid", std::to_string(t->GetTunnelID())}, {"value", val}, {"inbound", std::to_string(t->IsInbound())}});
|
||||
#else
|
||||
(void) ev;
|
||||
(void) t;
|
||||
(void) val;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
const int TUNNEL_EXPIRATION_TIMEOUT = 660; // 11 minutes
|
||||
const int TUNNEL_EXPIRATION_THRESHOLD = 60; // 1 minute
|
||||
const int TUNNEL_RECREATION_THRESHOLD = 90; // 1.5 minutes
|
||||
@@ -40,6 +78,21 @@ namespace tunnel
|
||||
eTunnelStateFailed,
|
||||
eTunnelStateExpiring
|
||||
};
|
||||
|
||||
/** @brief for storing latency history */
|
||||
struct TunnelLatency
|
||||
{
|
||||
typedef uint64_t Sample;
|
||||
typedef uint64_t Latency;
|
||||
|
||||
|
||||
void AddSample(Sample s);
|
||||
bool HasSamples() const;
|
||||
Latency GetMeanLatency() const;
|
||||
|
||||
std::vector<Sample> m_samples;
|
||||
mutable std::mutex m_access;
|
||||
};
|
||||
|
||||
class OutboundTunnel;
|
||||
class InboundTunnel;
|
||||
@@ -62,12 +115,13 @@ namespace tunnel
|
||||
std::vector<std::shared_ptr<const i2p::data::IdentityEx> > GetPeers () const;
|
||||
std::vector<std::shared_ptr<const i2p::data::IdentityEx> > GetInvertedPeers () const;
|
||||
TunnelState GetState () const { return m_State; };
|
||||
void SetState (TunnelState state) { m_State = state; };
|
||||
void SetState (TunnelState state);
|
||||
bool IsEstablished () const { return m_State == eTunnelStateEstablished; };
|
||||
bool IsFailed () const { return m_State == eTunnelStateFailed; };
|
||||
bool IsRecreated () const { return m_IsRecreated; };
|
||||
void SetIsRecreated () { m_IsRecreated = true; };
|
||||
|
||||
virtual bool IsInbound() const = 0;
|
||||
|
||||
std::shared_ptr<TunnelPool> GetTunnelPool () const { return m_Pool; };
|
||||
void SetTunnelPool (std::shared_ptr<TunnelPool> pool) { m_Pool = pool; };
|
||||
|
||||
@@ -79,6 +133,14 @@ namespace tunnel
|
||||
void SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg);
|
||||
void EncryptTunnelMsg (std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out);
|
||||
|
||||
/** @brief add latency sample */
|
||||
void AddLatencySample(const uint64_t ms) { m_Latency.AddSample(ms); }
|
||||
/** @brief get this tunnel's estimated latency */
|
||||
uint64_t GetMeanLatency() const { return m_Latency.GetMeanLatency(); }
|
||||
/** @breif return true if this tunnel's latency fits in range [lowerbound, upperbound] */
|
||||
bool LatencyFitsRange(uint64_t lowerbound, uint64_t upperbound) const;
|
||||
|
||||
bool LatencyIsKnown() const { return m_Latency.HasSamples(); }
|
||||
protected:
|
||||
|
||||
void PrintHops (std::stringstream& s) const;
|
||||
@@ -90,6 +152,7 @@ namespace tunnel
|
||||
std::shared_ptr<TunnelPool> m_Pool; // pool, tunnel belongs to, or null
|
||||
TunnelState m_State;
|
||||
bool m_IsRecreated;
|
||||
TunnelLatency m_Latency;
|
||||
};
|
||||
|
||||
class OutboundTunnel: public Tunnel
|
||||
@@ -107,6 +170,8 @@ namespace tunnel
|
||||
|
||||
// implements TunnelBase
|
||||
void HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg);
|
||||
|
||||
bool IsInbound() const { return false; }
|
||||
|
||||
private:
|
||||
|
||||
@@ -123,7 +188,11 @@ namespace tunnel
|
||||
void HandleTunnelDataMsg (std::shared_ptr<const I2NPMessage> msg);
|
||||
virtual size_t GetNumReceivedBytes () const { return m_Endpoint.GetNumReceivedBytes (); };
|
||||
void Print (std::stringstream& s) const;
|
||||
|
||||
bool IsInbound() const { return true; }
|
||||
|
||||
// override TunnelBase
|
||||
void Cleanup () { m_Endpoint.Cleanup (); };
|
||||
|
||||
private:
|
||||
|
||||
TunnelEndpoint m_Endpoint;
|
||||
|
||||
@@ -37,6 +37,7 @@ namespace tunnel
|
||||
m_TunnelID (tunnelID), m_NextTunnelID (nextTunnelID), m_NextIdent (nextIdent),
|
||||
m_CreationTime (i2p::util::GetSecondsSinceEpoch ()) {};
|
||||
virtual ~TunnelBase () {};
|
||||
virtual void Cleanup () {};
|
||||
|
||||
virtual void HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg) = 0;
|
||||
virtual void SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg) = 0;
|
||||
@@ -48,7 +49,7 @@ namespace tunnel
|
||||
|
||||
uint32_t GetCreationTime () const { return m_CreationTime; };
|
||||
void SetCreationTime (uint32_t t) { m_CreationTime = t; };
|
||||
|
||||
|
||||
private:
|
||||
|
||||
uint32_t m_TunnelID, m_NextTunnelID;
|
||||
|
||||
@@ -102,7 +102,7 @@ namespace tunnel
|
||||
htobe32buf (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET, replyMsgID);
|
||||
RAND_bytes (clearText + BUILD_REQUEST_RECORD_PADDING_OFFSET, BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE - BUILD_REQUEST_RECORD_PADDING_OFFSET);
|
||||
i2p::crypto::ElGamalEncryption elGamalEncryption (ident->GetEncryptionPublicKey ());
|
||||
elGamalEncryption.Encrypt (clearText, BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE, record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET);
|
||||
elGamalEncryption.Encrypt (clearText, record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET);
|
||||
memcpy (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)ident->GetIdentHash (), 16);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "I2NPProtocol.h"
|
||||
#include "Transports.h"
|
||||
#include "RouterContext.h"
|
||||
#include "Timestamp.h"
|
||||
#include "TunnelEndpoint.h"
|
||||
|
||||
namespace i2p
|
||||
@@ -115,9 +116,10 @@ namespace tunnel
|
||||
if (!isFollowOnFragment) // create new incomlete message
|
||||
{
|
||||
m.nextFragmentNum = 1;
|
||||
m.receiveTime = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
auto ret = m_IncompleteMessages.insert (std::pair<uint32_t, TunnelMessageBlockEx>(msgID, m));
|
||||
if (ret.second)
|
||||
HandleOutOfSequenceFragment (msgID, ret.first->second);
|
||||
HandleOutOfSequenceFragments (msgID, ret.first->second);
|
||||
else
|
||||
LogPrint (eLogError, "TunnelMessage: Incomplete message ", msgID, " already exists");
|
||||
}
|
||||
@@ -168,7 +170,7 @@ namespace tunnel
|
||||
else
|
||||
{
|
||||
msg.nextFragmentNum++;
|
||||
HandleOutOfSequenceFragment (msgID, msg);
|
||||
HandleOutOfSequenceFragments (msgID, msg);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -192,40 +194,48 @@ namespace tunnel
|
||||
|
||||
void TunnelEndpoint::AddOutOfSequenceFragment (uint32_t msgID, uint8_t fragmentNum, bool isLastFragment, std::shared_ptr<I2NPMessage> data)
|
||||
{
|
||||
auto it = m_OutOfSequenceFragments.find (msgID);
|
||||
if (it == m_OutOfSequenceFragments.end ())
|
||||
m_OutOfSequenceFragments.insert (std::pair<uint32_t, Fragment> (msgID, {fragmentNum, isLastFragment, data}));
|
||||
if (!m_OutOfSequenceFragments.insert ({{msgID, fragmentNum}, {isLastFragment, data, i2p::util::GetMillisecondsSinceEpoch () }}).second)
|
||||
LogPrint (eLogInfo, "TunnelMessage: duplicate out-of-sequence fragment ", fragmentNum, " of message ", msgID);
|
||||
}
|
||||
|
||||
void TunnelEndpoint::HandleOutOfSequenceFragment (uint32_t msgID, TunnelMessageBlockEx& msg)
|
||||
void TunnelEndpoint::HandleOutOfSequenceFragments (uint32_t msgID, TunnelMessageBlockEx& msg)
|
||||
{
|
||||
auto it = m_OutOfSequenceFragments.find (msgID);
|
||||
if (it != m_OutOfSequenceFragments.end ())
|
||||
while (ConcatNextOutOfSequenceFragment (msgID, msg))
|
||||
{
|
||||
if (it->second.fragmentNum == msg.nextFragmentNum)
|
||||
if (!msg.nextFragmentNum) // message complete
|
||||
{
|
||||
LogPrint (eLogWarning, "TunnelMessage: Out-of-sequence fragment ", (int)it->second.fragmentNum, " of message ", msgID, " found");
|
||||
size_t size = it->second.data->GetLength ();
|
||||
if (msg.data->len + size > msg.data->maxLen)
|
||||
{
|
||||
LogPrint (eLogWarning, "TunnelMessage: Tunnel endpoint I2NP message size ", msg.data->maxLen, " is not enough");
|
||||
auto newMsg = NewI2NPMessage ();
|
||||
*newMsg = *(msg.data);
|
||||
msg.data = newMsg;
|
||||
}
|
||||
if (msg.data->Concat (it->second.data->GetBuffer (), size) < size) // concatenate out-of-sync fragment
|
||||
LogPrint (eLogError, "Tunnel endpoint I2NP buffer overflow ", msg.data->maxLen);
|
||||
if (it->second.isLastFragment)
|
||||
{
|
||||
// message complete
|
||||
HandleNextMessage (msg);
|
||||
m_IncompleteMessages.erase (msgID);
|
||||
}
|
||||
else
|
||||
msg.nextFragmentNum++;
|
||||
m_OutOfSequenceFragments.erase (it);
|
||||
}
|
||||
HandleNextMessage (msg);
|
||||
m_IncompleteMessages.erase (msgID);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool TunnelEndpoint::ConcatNextOutOfSequenceFragment (uint32_t msgID, TunnelMessageBlockEx& msg)
|
||||
{
|
||||
auto it = m_OutOfSequenceFragments.find ({msgID, msg.nextFragmentNum});
|
||||
if (it != m_OutOfSequenceFragments.end ())
|
||||
{
|
||||
LogPrint (eLogDebug, "TunnelMessage: Out-of-sequence fragment ", (int)msg.nextFragmentNum, " of message ", msgID, " found");
|
||||
size_t size = it->second.data->GetLength ();
|
||||
if (msg.data->len + size > msg.data->maxLen)
|
||||
{
|
||||
LogPrint (eLogWarning, "TunnelMessage: Tunnel endpoint I2NP message size ", msg.data->maxLen, " is not enough");
|
||||
auto newMsg = NewI2NPMessage ();
|
||||
*newMsg = *(msg.data);
|
||||
msg.data = newMsg;
|
||||
}
|
||||
if (msg.data->Concat (it->second.data->GetBuffer (), size) < size) // concatenate out-of-sync fragment
|
||||
LogPrint (eLogError, "TunnelMessage: Tunnel endpoint I2NP buffer overflow ", msg.data->maxLen);
|
||||
if (it->second.isLastFragment)
|
||||
// message complete
|
||||
msg.nextFragmentNum = 0;
|
||||
else
|
||||
msg.nextFragmentNum++;
|
||||
m_OutOfSequenceFragments.erase (it);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void TunnelEndpoint::HandleNextMessage (const TunnelMessageBlock& msg)
|
||||
@@ -262,6 +272,27 @@ namespace tunnel
|
||||
default:
|
||||
LogPrint (eLogError, "TunnelMessage: Unknown delivery type ", (int)msg.deliveryType);
|
||||
};
|
||||
}
|
||||
|
||||
void TunnelEndpoint::Cleanup ()
|
||||
{
|
||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
// out-of-sequence fragments
|
||||
for (auto it = m_OutOfSequenceFragments.begin (); it != m_OutOfSequenceFragments.end ();)
|
||||
{
|
||||
if (ts > it->second.receiveTime + i2p::I2NP_MESSAGE_EXPIRATION_TIMEOUT)
|
||||
it = m_OutOfSequenceFragments.erase (it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
// incomplete messages
|
||||
for (auto it = m_IncompleteMessages.begin (); it != m_IncompleteMessages.end ();)
|
||||
{
|
||||
if (ts > it->second.receiveTime + i2p::I2NP_MESSAGE_EXPIRATION_TIMEOUT)
|
||||
it = m_IncompleteMessages.erase (it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,14 +15,15 @@ namespace tunnel
|
||||
{
|
||||
struct TunnelMessageBlockEx: public TunnelMessageBlock
|
||||
{
|
||||
uint64_t receiveTime; // milliseconds since epoch
|
||||
uint8_t nextFragmentNum;
|
||||
};
|
||||
|
||||
struct Fragment
|
||||
{
|
||||
uint8_t fragmentNum;
|
||||
bool isLastFragment;
|
||||
std::shared_ptr<I2NPMessage> data;
|
||||
uint64_t receiveTime; // milliseconds since epoch
|
||||
};
|
||||
|
||||
public:
|
||||
@@ -30,7 +31,8 @@ namespace tunnel
|
||||
TunnelEndpoint (bool isInbound): m_IsInbound (isInbound), m_NumReceivedBytes (0) {};
|
||||
~TunnelEndpoint ();
|
||||
size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; };
|
||||
|
||||
void Cleanup ();
|
||||
|
||||
void HandleDecryptedTunnelDataMsg (std::shared_ptr<I2NPMessage> msg);
|
||||
|
||||
private:
|
||||
@@ -39,12 +41,13 @@ namespace tunnel
|
||||
void HandleNextMessage (const TunnelMessageBlock& msg);
|
||||
|
||||
void AddOutOfSequenceFragment (uint32_t msgID, uint8_t fragmentNum, bool isLastFragment, std::shared_ptr<I2NPMessage> data);
|
||||
void HandleOutOfSequenceFragment (uint32_t msgID, TunnelMessageBlockEx& msg);
|
||||
|
||||
bool ConcatNextOutOfSequenceFragment (uint32_t msgID, TunnelMessageBlockEx& msg); // true if something added
|
||||
void HandleOutOfSequenceFragments (uint32_t msgID, TunnelMessageBlockEx& msg);
|
||||
|
||||
private:
|
||||
|
||||
std::map<uint32_t, TunnelMessageBlockEx> m_IncompleteMessages;
|
||||
std::map<uint32_t, Fragment> m_OutOfSequenceFragments;
|
||||
std::map<std::pair<uint32_t, uint8_t>, Fragment> m_OutOfSequenceFragments; // (msgID, fragment#)->fragment
|
||||
bool m_IsInbound;
|
||||
size_t m_NumReceivedBytes;
|
||||
};
|
||||
|
||||
100
TunnelPool.cpp
100
TunnelPool.cpp
@@ -7,12 +7,18 @@
|
||||
#include "Garlic.h"
|
||||
#include "Transports.h"
|
||||
#include "Log.h"
|
||||
#include "Tunnel.h"
|
||||
#include "TunnelPool.h"
|
||||
#include "Destination.h"
|
||||
#ifdef WITH_EVENTS
|
||||
#include "Event.h"
|
||||
#endif
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace tunnel
|
||||
{
|
||||
|
||||
TunnelPool::TunnelPool (int numInboundHops, int numOutboundHops, int numInboundTunnels, int numOutboundTunnels):
|
||||
m_NumInboundHops (numInboundHops), m_NumOutboundHops (numOutboundHops),
|
||||
m_NumInboundTunnels (numInboundTunnels), m_NumOutboundTunnels (numOutboundTunnels), m_IsActive (true),
|
||||
@@ -67,6 +73,9 @@ namespace tunnel
|
||||
{
|
||||
if (!m_IsActive) return;
|
||||
{
|
||||
#ifdef WITH_EVENTS
|
||||
EmitTunnelEvent("tunnels.created", createdTunnel);
|
||||
#endif
|
||||
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
|
||||
m_InboundTunnels.insert (createdTunnel);
|
||||
}
|
||||
@@ -77,7 +86,10 @@ namespace tunnel
|
||||
void TunnelPool::TunnelExpired (std::shared_ptr<InboundTunnel> expiredTunnel)
|
||||
{
|
||||
if (expiredTunnel)
|
||||
{
|
||||
{
|
||||
#ifdef WITH_EVENTS
|
||||
EmitTunnelEvent("tunnels.expired", expiredTunnel);
|
||||
#endif
|
||||
expiredTunnel->SetTunnelPool (nullptr);
|
||||
for (auto& it: m_Tests)
|
||||
if (it.second.second == expiredTunnel) it.second.second = nullptr;
|
||||
@@ -91,6 +103,9 @@ namespace tunnel
|
||||
{
|
||||
if (!m_IsActive) return;
|
||||
{
|
||||
#ifdef WITH_EVENTS
|
||||
EmitTunnelEvent("tunnels.created", createdTunnel);
|
||||
#endif
|
||||
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
|
||||
m_OutboundTunnels.insert (createdTunnel);
|
||||
}
|
||||
@@ -101,6 +116,9 @@ namespace tunnel
|
||||
{
|
||||
if (expiredTunnel)
|
||||
{
|
||||
#ifdef WITH_EVENTS
|
||||
EmitTunnelEvent("tunnels.expired", expiredTunnel);
|
||||
#endif
|
||||
expiredTunnel->SetTunnelPool (nullptr);
|
||||
for (auto& it: m_Tests)
|
||||
if (it.second.first == expiredTunnel) it.second.first = nullptr;
|
||||
@@ -129,13 +147,13 @@ namespace tunnel
|
||||
|
||||
std::shared_ptr<OutboundTunnel> TunnelPool::GetNextOutboundTunnel (std::shared_ptr<OutboundTunnel> excluded) const
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
|
||||
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
|
||||
return GetNextTunnel (m_OutboundTunnels, excluded);
|
||||
}
|
||||
|
||||
std::shared_ptr<InboundTunnel> TunnelPool::GetNextInboundTunnel (std::shared_ptr<InboundTunnel> excluded) const
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
|
||||
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
|
||||
return GetNextTunnel (m_InboundTunnels, excluded);
|
||||
}
|
||||
|
||||
@@ -149,11 +167,27 @@ namespace tunnel
|
||||
{
|
||||
if (it->IsEstablished () && it != excluded)
|
||||
{
|
||||
if(HasLatencyRequirement() && it->LatencyIsKnown() && !it->LatencyFitsRange(m_MinLatency, m_MaxLatency)) {
|
||||
i ++;
|
||||
continue;
|
||||
}
|
||||
tunnel = it;
|
||||
i++;
|
||||
}
|
||||
if (i > ind && tunnel) break;
|
||||
}
|
||||
if(HasLatencyRequirement() && !tunnel) {
|
||||
ind = rand () % (tunnels.size ()/2 + 1), i = 0;
|
||||
for (const auto& it: tunnels)
|
||||
{
|
||||
if (it->IsEstablished () && it != excluded)
|
||||
{
|
||||
tunnel = it;
|
||||
i++;
|
||||
}
|
||||
if (i > ind && tunnel) break;
|
||||
}
|
||||
}
|
||||
if (!tunnel && excluded && excluded->IsEstablished ()) tunnel = excluded;
|
||||
return tunnel;
|
||||
}
|
||||
@@ -181,15 +215,6 @@ namespace tunnel
|
||||
void TunnelPool::CreateTunnels ()
|
||||
{
|
||||
int num = 0;
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
|
||||
for (const auto& it : m_InboundTunnels)
|
||||
if (it->IsEstablished ()) num++;
|
||||
}
|
||||
for (int i = num; i < m_NumInboundTunnels; i++)
|
||||
CreateInboundTunnel ();
|
||||
|
||||
num = 0;
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
|
||||
for (const auto& it : m_OutboundTunnels)
|
||||
@@ -197,6 +222,18 @@ namespace tunnel
|
||||
}
|
||||
for (int i = num; i < m_NumOutboundTunnels; i++)
|
||||
CreateOutboundTunnel ();
|
||||
|
||||
num = 0;
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
|
||||
for (const auto& it : m_InboundTunnels)
|
||||
if (it->IsEstablished ()) num++;
|
||||
}
|
||||
for (int i = num; i < m_NumInboundTunnels; i++)
|
||||
CreateInboundTunnel ();
|
||||
|
||||
if (num < m_NumInboundTunnels && m_NumInboundHops <= 0 && m_LocalDestination) // zero hops IB
|
||||
m_LocalDestination->SetLeaseSetUpdated (); // update LeaseSet immediately
|
||||
}
|
||||
|
||||
void TunnelPool::TestTunnels ()
|
||||
@@ -304,7 +341,12 @@ namespace tunnel
|
||||
test.first->SetState (eTunnelStateEstablished);
|
||||
if (test.second->GetState () == eTunnelStateTestFailed)
|
||||
test.second->SetState (eTunnelStateEstablished);
|
||||
LogPrint (eLogDebug, "Tunnels: test of ", msgID, " successful. ", i2p::util::GetMillisecondsSinceEpoch () - timestamp, " milliseconds");
|
||||
uint64_t dlt = i2p::util::GetMillisecondsSinceEpoch () - timestamp;
|
||||
LogPrint (eLogDebug, "Tunnels: test of ", msgID, " successful. ", dlt, " milliseconds");
|
||||
// update latency
|
||||
uint64_t latency = dlt / 2;
|
||||
test.first->AddLatencySample(latency);
|
||||
test.second->AddLatencySample(latency);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -505,5 +547,37 @@ namespace tunnel
|
||||
std::lock_guard<std::mutex> lock(m_CustomPeerSelectorMutex);
|
||||
return m_CustomPeerSelector != nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<InboundTunnel> TunnelPool::GetLowestLatencyInboundTunnel(std::shared_ptr<InboundTunnel> exclude) const
|
||||
{
|
||||
std::shared_ptr<InboundTunnel> tun = nullptr;
|
||||
std::unique_lock<std::mutex> lock(m_InboundTunnelsMutex);
|
||||
uint64_t min = 1000000;
|
||||
for (const auto & itr : m_InboundTunnels) {
|
||||
if(!itr->LatencyIsKnown()) continue;
|
||||
auto l = itr->GetMeanLatency();
|
||||
if (l >= min) continue;
|
||||
tun = itr;
|
||||
if(tun == exclude) continue;
|
||||
min = l;
|
||||
}
|
||||
return tun;
|
||||
}
|
||||
|
||||
std::shared_ptr<OutboundTunnel> TunnelPool::GetLowestLatencyOutboundTunnel(std::shared_ptr<OutboundTunnel> exclude) const
|
||||
{
|
||||
std::shared_ptr<OutboundTunnel> tun = nullptr;
|
||||
std::unique_lock<std::mutex> lock(m_OutboundTunnelsMutex);
|
||||
uint64_t min = 1000000;
|
||||
for (const auto & itr : m_OutboundTunnels) {
|
||||
if(!itr->LatencyIsKnown()) continue;
|
||||
auto l = itr->GetMeanLatency();
|
||||
if (l >= min) continue;
|
||||
tun = itr;
|
||||
if(tun == exclude) continue;
|
||||
min = l;
|
||||
}
|
||||
return tun;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
15
TunnelPool.h
15
TunnelPool.h
@@ -69,6 +69,17 @@ namespace tunnel
|
||||
void SetCustomPeerSelector(TunnelPeerSelector selector);
|
||||
void UnsetCustomPeerSelector();
|
||||
bool HasCustomPeerSelector();
|
||||
|
||||
/** @brief make this tunnel pool yield tunnels that fit latency range [min, max] */
|
||||
void RequireLatency(uint64_t min, uint64_t max) { m_MinLatency = min; m_MaxLatency = max; }
|
||||
|
||||
/** @brief return true if this tunnel pool has a latency requirement */
|
||||
bool HasLatencyRequirement() const { return m_MinLatency > 0 && m_MaxLatency > 0; }
|
||||
|
||||
/** @brief get the lowest latency tunnel in this tunnel pool regardless of latency requirements */
|
||||
std::shared_ptr<InboundTunnel> GetLowestLatencyInboundTunnel(std::shared_ptr<InboundTunnel> exclude=nullptr) const;
|
||||
std::shared_ptr<OutboundTunnel> GetLowestLatencyOutboundTunnel(std::shared_ptr<OutboundTunnel> exclude=nullptr) const;
|
||||
|
||||
private:
|
||||
|
||||
void CreateInboundTunnel ();
|
||||
@@ -94,6 +105,10 @@ namespace tunnel
|
||||
bool m_IsActive;
|
||||
std::mutex m_CustomPeerSelectorMutex;
|
||||
TunnelPeerSelector m_CustomPeerSelector;
|
||||
|
||||
uint64_t m_MinLatency=0; // if > 0 this tunnel pool will try building tunnels with minimum latency by ms
|
||||
uint64_t m_MaxLatency=0; // if > 0 this tunnel pool will try building tunnels with maximum latency by ms
|
||||
|
||||
public:
|
||||
|
||||
// for HTTP only
|
||||
|
||||
3
UPnP.cpp
3
UPnP.cpp
@@ -66,10 +66,13 @@ namespace transport
|
||||
try
|
||||
{
|
||||
m_Service.run ();
|
||||
// Discover failed
|
||||
break; // terminate the thread
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint (eLogError, "UPnP: runtime exception: ", ex.what ());
|
||||
PortMapping ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
137
Websocket.cpp
Normal file
137
Websocket.cpp
Normal file
@@ -0,0 +1,137 @@
|
||||
#include "Websocket.h"
|
||||
#include "Log.h"
|
||||
|
||||
#include <set>
|
||||
|
||||
#include <websocketpp/config/asio_no_tls.hpp>
|
||||
#include <websocketpp/server.hpp>
|
||||
#include <boost/property_tree/ini_parser.hpp>
|
||||
#define GCC47_BOOST149 ((BOOST_VERSION == 104900) && (__GNUC__ == 4) && (__GNUC_MINOR__ >= 7))
|
||||
#if !GCC47_BOOST149
|
||||
#include <boost/property_tree/json_parser.hpp>
|
||||
#endif
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace event
|
||||
{
|
||||
|
||||
typedef websocketpp::server<websocketpp::config::asio> ServerImpl;
|
||||
typedef websocketpp::connection_hdl ServerConn;
|
||||
|
||||
class WebsocketServerImpl : public EventListener
|
||||
{
|
||||
private:
|
||||
typedef ServerImpl::message_ptr MessagePtr;
|
||||
public:
|
||||
|
||||
WebsocketServerImpl(const std::string & addr, int port) : m_run(false), m_thread(nullptr)
|
||||
{
|
||||
m_server.init_asio();
|
||||
m_server.set_open_handler(std::bind(&WebsocketServerImpl::ConnOpened, this, std::placeholders::_1));
|
||||
m_server.set_close_handler(std::bind(&WebsocketServerImpl::ConnClosed, this, std::placeholders::_1));
|
||||
m_server.set_message_handler(std::bind(&WebsocketServerImpl::OnConnMessage, this, std::placeholders::_1, std::placeholders::_2));
|
||||
|
||||
m_server.listen(boost::asio::ip::address::from_string(addr), port);
|
||||
}
|
||||
|
||||
~WebsocketServerImpl()
|
||||
{
|
||||
}
|
||||
|
||||
void Start() {
|
||||
m_run = true;
|
||||
m_server.start_accept();
|
||||
m_thread = new std::thread([&] () {
|
||||
while(m_run) {
|
||||
try {
|
||||
m_server.run();
|
||||
} catch (std::exception & e ) {
|
||||
LogPrint(eLogError, "Websocket server: ", e.what());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Stop() {
|
||||
m_run = false;
|
||||
m_server.stop();
|
||||
if(m_thread) {
|
||||
m_thread->join();
|
||||
delete m_thread;
|
||||
}
|
||||
m_thread = nullptr;
|
||||
}
|
||||
|
||||
void ConnOpened(ServerConn c)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_connsMutex);
|
||||
m_conns.insert(c);
|
||||
}
|
||||
|
||||
void ConnClosed(ServerConn c)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_connsMutex);
|
||||
m_conns.erase(c);
|
||||
}
|
||||
|
||||
void OnConnMessage(ServerConn conn, ServerImpl::message_ptr msg)
|
||||
{
|
||||
(void) conn;
|
||||
(void) msg;
|
||||
}
|
||||
|
||||
void HandleEvent(const EventType & ev)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_connsMutex);
|
||||
LogPrint(eLogDebug, "websocket event");
|
||||
boost::property_tree::ptree event;
|
||||
for (const auto & item : ev) {
|
||||
event.put(item.first, item.second);
|
||||
}
|
||||
std::ostringstream ss;
|
||||
write_json(ss, event);
|
||||
std::string s = ss.str();
|
||||
|
||||
ConnList::iterator it;
|
||||
for (it = m_conns.begin(); it != m_conns.end(); ++it) {
|
||||
ServerImpl::connection_ptr con = m_server.get_con_from_hdl(*it);
|
||||
con->send(s);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
typedef std::set<ServerConn, std::owner_less<ServerConn> > ConnList;
|
||||
bool m_run;
|
||||
std::thread * m_thread;
|
||||
std::mutex m_connsMutex;
|
||||
ConnList m_conns;
|
||||
ServerImpl m_server;
|
||||
};
|
||||
|
||||
|
||||
WebsocketServer::WebsocketServer(const std::string & addr, int port) : m_impl(new WebsocketServerImpl(addr, port)) {}
|
||||
WebsocketServer::~WebsocketServer()
|
||||
{
|
||||
delete m_impl;
|
||||
}
|
||||
|
||||
|
||||
void WebsocketServer::Start()
|
||||
{
|
||||
m_impl->Start();
|
||||
}
|
||||
|
||||
void WebsocketServer::Stop()
|
||||
{
|
||||
m_impl->Stop();
|
||||
}
|
||||
|
||||
EventListener * WebsocketServer::ToListener()
|
||||
{
|
||||
return m_impl;
|
||||
}
|
||||
}
|
||||
}
|
||||
28
Websocket.h
Normal file
28
Websocket.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#ifndef WEBSOCKET_H__
|
||||
#define WEBSOCKET_H__
|
||||
#include "Event.h"
|
||||
namespace i2p
|
||||
{
|
||||
namespace event
|
||||
{
|
||||
|
||||
class WebsocketServerImpl;
|
||||
|
||||
class WebsocketServer
|
||||
{
|
||||
public:
|
||||
WebsocketServer(const std::string & addr, int port);
|
||||
~WebsocketServer();
|
||||
|
||||
void Start();
|
||||
void Stop();
|
||||
|
||||
EventListener * ToListener();
|
||||
|
||||
private:
|
||||
WebsocketServerImpl * m_impl;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -14,7 +14,7 @@ ShowInstDetails show
|
||||
!define URL "https://i2p.io"
|
||||
|
||||
# MUI Symbol Definitions
|
||||
!define MUI_ICON "ictoopie.ico"
|
||||
!define MUI_ICON "mask.ico"
|
||||
#!define MUI_WELCOMEFINISHPAGE_BITMAP "../share/pixmaps/nsis-wizard.bmp"
|
||||
!define MUI_HEADERIMAGE
|
||||
!define MUI_HEADERIMAGE_RIGHT
|
||||
|
||||
@@ -52,7 +52,7 @@ END
|
||||
|
||||
// Icon with lowest ID value placed first to ensure application icon
|
||||
// remains consistent on all systems.
|
||||
MAINICON ICON "ictoopie.ico"
|
||||
MAINICON ICON "mask.ico"
|
||||
//MAINICON ICON "anke.ico"
|
||||
|
||||
#endif // English (United States) resources
|
||||
|
||||
@@ -1,32 +1,41 @@
|
||||
#define I2Pd_AppName "i2pd"
|
||||
#define I2Pd_ver "2.10.0"
|
||||
#define I2Pd_ver "2.11.0"
|
||||
#define I2Pd_Publisher "PurpleI2P"
|
||||
|
||||
[Setup]
|
||||
AppName={#I2Pd_AppName}
|
||||
AppVersion={#I2Pd_ver}
|
||||
AppPublisher={#I2Pd_Publisher}
|
||||
DefaultDirName={pf}\I2Pd
|
||||
DefaultGroupName=I2Pd
|
||||
UninstallDisplayIcon={app}\I2Pd.exe
|
||||
OutputDir=.
|
||||
LicenseFile=../LICENSE
|
||||
OutputBaseFilename=setup_{#I2Pd_AppName}_v{#I2Pd_ver}
|
||||
SetupIconFile=mask.ico
|
||||
InternalCompressLevel=ultra64
|
||||
Compression=lzma/ultra64
|
||||
SolidCompression=true
|
||||
ArchitecturesInstallIn64BitMode=x64
|
||||
AppVerName={#I2Pd_AppName}
|
||||
ExtraDiskSpaceRequired=15
|
||||
AppID={{621A23E0-3CF4-4BD6-97BC-4835EA5206A2}
|
||||
AppPublisherURL=http://i2pd.website/
|
||||
AppSupportURL=https://github.com/PurpleI2P/i2pd/issues
|
||||
AppUpdatesURL=https://github.com/PurpleI2P/i2pd/releases
|
||||
|
||||
[Files]
|
||||
Source: "..\i2pd_x86.exe"; DestDir: "{app}"; DestName: "i2pd.exe"; Flags: ignoreversion; Check: not IsWin64
|
||||
Source: "..\i2pd_x64.exe"; DestDir: "{app}"; DestName: "i2pd.exe"; Flags: ignoreversion; Check: IsWin64
|
||||
Source: "..\README.md"; DestDir: "{app}"; DestName: "Readme.txt"; Flags: onlyifdoesntexist
|
||||
Source: "..\docs\i2pd.conf"; DestDir: "{userappdata}\i2pd"; Flags: onlyifdoesntexist
|
||||
Source: "..\docs\subscriptions.txt"; DestDir: "{userappdata}\i2pd"; Flags: onlyifdoesntexist
|
||||
Source: "..\docs\tunnels.conf"; DestDir: "{userappdata}\i2pd"; Flags: onlyifdoesntexist
|
||||
Source: "..\contrib\*"; DestDir: "{userappdata}\i2pd"; Flags: onlyifdoesntexist recursesubdirs createallsubdirs
|
||||
Source: ..\i2pd_x86.exe; DestDir: {app}; DestName: i2pd.exe; Flags: ignoreversion; Check: not IsWin64
|
||||
Source: ..\i2pd_x64.exe; DestDir: {app}; DestName: i2pd.exe; Flags: ignoreversion; Check: IsWin64
|
||||
Source: ..\README.md; DestDir: {app}; DestName: Readme.txt; Flags: onlyifdoesntexist
|
||||
Source: ..\docs\i2pd.conf; DestDir: {userappdata}\i2pd; Flags: onlyifdoesntexist
|
||||
Source: ..\docs\subscriptions.txt; DestDir: {userappdata}\i2pd; Flags: onlyifdoesntexist
|
||||
Source: ..\docs\tunnels.conf; DestDir: {userappdata}\i2pd; Flags: onlyifdoesntexist
|
||||
Source: ..\contrib\certificates\*; DestDir: {userappdata}\i2pd\certificates; Flags: onlyifdoesntexist recursesubdirs createallsubdirs
|
||||
|
||||
[Icons]
|
||||
Name: "{group}\I2Pd"; Filename: "{app}\i2pd.exe"
|
||||
Name: "{group}\Readme"; Filename: "{app}\Readme.txt"
|
||||
Name: {group}\I2Pd; Filename: {app}\i2pd.exe
|
||||
Name: {group}\Readme; Filename: {app}\Readme.txt
|
||||
|
||||
[UninstallDelete]
|
||||
Type: filesandordirs; Name: {app}\*
|
||||
Type: filesandordirs; Name: {app}
|
||||
|
||||
BIN
Win32/mask.bmp
Normal file
BIN
Win32/mask.bmp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
BIN
Win32/mask.ico
Normal file
BIN
Win32/mask.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 153 KiB |
@@ -2,7 +2,7 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.purplei2p.i2pd"
|
||||
android:versionCode="1"
|
||||
android:versionName="2.10.0">
|
||||
android:versionName="2.11.0">
|
||||
<uses-sdk android:minSdkVersion="9" android:targetSdkVersion="24"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
|
||||
@@ -58,6 +58,8 @@ LOCAL_SRC_FILES := DaemonAndroid.cpp i2pd_android.cpp \
|
||||
../../TunnelGateway.cpp \
|
||||
../../TunnelPool.cpp \
|
||||
../../Timestamp.cpp \
|
||||
../../Event.cpp \
|
||||
../../BloomFilter.cpp \
|
||||
../../util.cpp \
|
||||
../../i2pd.cpp ../../UPnP.cpp
|
||||
|
||||
|
||||
3
api.cpp
3
api.cpp
@@ -47,7 +47,7 @@ namespace api
|
||||
i2p::log::Logger().SendTo (logStream);
|
||||
else
|
||||
i2p::log::Logger().SendTo (i2p::fs::DataDirPath (i2p::fs::GetAppName () + ".log"));
|
||||
i2p::log::Logger().Ready();
|
||||
i2p::log::Logger().Start ();
|
||||
LogPrint(eLogInfo, "API: starting NetDB");
|
||||
i2p::data::netdb.Start();
|
||||
LogPrint(eLogInfo, "API: starting Transports");
|
||||
@@ -65,6 +65,7 @@ namespace api
|
||||
i2p::transport::transports.Stop();
|
||||
LogPrint(eLogInfo, "API: stopping NetDB");
|
||||
i2p::data::netdb.Stop();
|
||||
i2p::log::Logger().Stop ();
|
||||
}
|
||||
|
||||
void RunPeerTest ()
|
||||
|
||||
@@ -18,12 +18,15 @@ option(WITH_GUI "Include GUI (currently MS Windows only)" ON)
|
||||
option(WITH_MESHNET "Build for cjdns test network" OFF)
|
||||
option(WITH_ADDRSANITIZER "Build with address sanitizer unix only" OFF)
|
||||
option(WITH_THREADSANITIZER "Build with thread sanitizer unix only" OFF)
|
||||
option(WITH_I2LUA "Build for i2lua" OFF)
|
||||
option(WITH_WEBSOCKETS "Build with websocket ui" OFF)
|
||||
|
||||
# paths
|
||||
set ( CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules" )
|
||||
set ( CMAKE_SOURCE_DIR ".." )
|
||||
|
||||
set (LIBI2PD_SRC
|
||||
"${CMAKE_SOURCE_DIR}/BloomFilter.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/Config.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/Crypto.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/Garlic.cpp"
|
||||
@@ -58,12 +61,22 @@ set (LIBI2PD_SRC
|
||||
"${CMAKE_SOURCE_DIR}/Signature.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/Timestamp.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/api.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/Event.cpp"
|
||||
)
|
||||
|
||||
if (WITH_WEBSOCKETS)
|
||||
add_definitions(-DWITH_EVENTS)
|
||||
find_package(websocketpp REQUIRED)
|
||||
endif ()
|
||||
|
||||
if (CMAKE_SYSTEM_NAME STREQUAL "Windows" OR MSYS)
|
||||
list (APPEND LIBI2PD_SRC "${CMAKE_SOURCE_DIR}/I2PEndian.cpp")
|
||||
endif ()
|
||||
|
||||
if (WITH_I2LUA)
|
||||
add_definitions(-DI2LUA)
|
||||
endif()
|
||||
|
||||
add_library(libi2pd ${LIBI2PD_SRC})
|
||||
set_target_properties(libi2pd PROPERTIES PREFIX "")
|
||||
install(TARGETS libi2pd
|
||||
@@ -87,6 +100,9 @@ set (CLIENT_SRC
|
||||
"${CMAKE_SOURCE_DIR}/I2CP.cpp"
|
||||
)
|
||||
|
||||
if(WITH_WEBSOCKETS)
|
||||
list (APPEND CLIENT_SRC "${CMAKE_SOURCE_DIR}/Websocket.cpp")
|
||||
endif ()
|
||||
add_library(i2pdclient ${CLIENT_SRC})
|
||||
|
||||
set (DAEMON_SRC
|
||||
@@ -361,6 +377,8 @@ message(STATUS " PCH : ${WITH_PCH}")
|
||||
message(STATUS " MESHNET : ${WITH_MESHNET}")
|
||||
message(STATUS " ADDRSANITIZER : ${WITH_ADDRSANITIZER}")
|
||||
message(STATUS " THEADSANITIZER : ${WITH_THREADSANITIZER}")
|
||||
message(STATUS " I2LUA : ${WITH_I2LUA}")
|
||||
message(STATUS " WEBSOCKETS : ${WITH_WEBSOCKETS}")
|
||||
message(STATUS "---------------------------------------")
|
||||
|
||||
#Handle paths nicely
|
||||
@@ -507,7 +525,7 @@ if((WIN32 OR MSYS) AND NOT UNIX)
|
||||
# There is a bug in NSI that does not handle full unix paths properly. Make
|
||||
# sure there is at least one set of four (4) backlasshes.
|
||||
set(CPACK_NSIS_DEFINES "RequestExecutionLevel user")
|
||||
set(CPACK_PACKAGE_ICON "${CMAKE_CURRENT_SOURCE_DIR}/../Win32\\\\ictoopie.bmp")
|
||||
set(CPACK_PACKAGE_ICON "${CMAKE_CURRENT_SOURCE_DIR}/../Win32\\\\mask.bmp")
|
||||
set(CPACK_NSIS_INSTALLED_ICON_NAME "bin/i2pd.exe")
|
||||
SET(CPACK_NSIS_DISPLAY_NAME "${CPACK_PACKAGE_DESCRIPTION_SUMMARY}")
|
||||
set(CPACK_NSIS_HELP_LINK "https:\\\\\\\\github.com\\\\PurpleI2P\\\\i2pd\\\\issues")
|
||||
|
||||
2
contrib/debian/README
Normal file
2
contrib/debian/README
Normal file
@@ -0,0 +1,2 @@
|
||||
This forder contain systemd unit files.
|
||||
To use systemd daemon control, place files from this directory to debian folder.
|
||||
25
contrib/debian/i2pd.service
Normal file
25
contrib/debian/i2pd.service
Normal file
@@ -0,0 +1,25 @@
|
||||
[Unit]
|
||||
Description=I2P Router written in C++
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
User=i2pd
|
||||
Group=i2pd
|
||||
Type=forking
|
||||
ExecStart=/usr/sbin/i2pd --conf=/etc/i2pd/i2pd.conf --pidfile=/var/run/i2pd/i2pd.pid --logfile=/var/log/i2pd/i2pd.log --daemon --service
|
||||
ExecReload=/bin/kill -HUP $MAINPID
|
||||
PIDFile=/var/run/i2pd/i2pd.pid
|
||||
### Uncomment, if auto restart needed
|
||||
#Restart=on-failure
|
||||
|
||||
### Use SIGINT for gracefull stop daemon.
|
||||
# i2pd stops accepting new tunnels and waits ~10 min while old ones do not die.
|
||||
KillSignal=SIGINT
|
||||
TimeoutStopSec=10m
|
||||
|
||||
# If you have problems with hunging i2pd, you can try enable this
|
||||
#LimitNOFILE=4096
|
||||
PrivateDevices=yes
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
2
contrib/debian/i2pd.tmpfile
Normal file
2
contrib/debian/i2pd.tmpfile
Normal file
@@ -0,0 +1,2 @@
|
||||
d /var/run/i2pd 0755 i2pd i2pd - -
|
||||
d /var/log/i2pd 0755 i2pd i2pd - -
|
||||
16
contrib/rpm/i2pd.service
Normal file
16
contrib/rpm/i2pd.service
Normal file
@@ -0,0 +1,16 @@
|
||||
[Unit]
|
||||
Description=I2P router
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
User=i2pd
|
||||
Group=i2pd
|
||||
Type=simple
|
||||
ExecStart=/usr/bin/i2pd --service
|
||||
PIDFile=/var/lib/i2pd/i2pd.pid
|
||||
Restart=always
|
||||
PrivateTmp=true
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
129
contrib/rpm/i2pd.spec
Normal file
129
contrib/rpm/i2pd.spec
Normal file
@@ -0,0 +1,129 @@
|
||||
Name: i2pd
|
||||
Version: 2.10.0
|
||||
Release: 3%{?dist}
|
||||
Summary: I2P router written in C++
|
||||
|
||||
License: BSD
|
||||
URL: https://github.com/PurpleI2P/i2pd
|
||||
Source0: https://github.com/PurpleI2P/i2pd/archive/%{version}/%name-%version.tar.gz
|
||||
|
||||
%if 0%{?rhel} == 7
|
||||
BuildRequires: cmake3
|
||||
%else
|
||||
BuildRequires: cmake
|
||||
%endif
|
||||
|
||||
BuildRequires: chrpath
|
||||
BuildRequires: gcc-c++
|
||||
BuildRequires: zlib-devel
|
||||
BuildRequires: boost-devel
|
||||
BuildRequires: openssl-devel
|
||||
BuildRequires: miniupnpc-devel
|
||||
BuildRequires: systemd-units
|
||||
|
||||
%description
|
||||
C++ implementation of I2P.
|
||||
|
||||
|
||||
%package systemd
|
||||
Summary: Files to run I2P router under systemd
|
||||
Requires: i2pd
|
||||
Requires: systemd
|
||||
Requires(pre): %{_sbindir}/useradd %{_sbindir}/groupadd
|
||||
Obsoletes: %{name}-daemon
|
||||
|
||||
|
||||
%description systemd
|
||||
C++ implementation of I2P.
|
||||
|
||||
This package contains systemd unit file to run i2pd as a system service
|
||||
using dedicated user's permissions.
|
||||
|
||||
|
||||
%prep
|
||||
%setup -q
|
||||
|
||||
|
||||
%build
|
||||
cd build
|
||||
%if 0%{?rhel} == 7
|
||||
%cmake3 \
|
||||
-DWITH_LIBRARY=OFF \
|
||||
-DWITH_UPNP=ON \
|
||||
-DWITH_HARDENING=ON \
|
||||
-DBUILD_SHARED_LIBS:BOOL=OFF
|
||||
%else
|
||||
%cmake \
|
||||
-DWITH_LIBRARY=OFF \
|
||||
-DWITH_UPNP=ON \
|
||||
-DWITH_HARDENING=ON \
|
||||
-DBUILD_SHARED_LIBS:BOOL=OFF
|
||||
%endif
|
||||
|
||||
make %{?_smp_mflags}
|
||||
|
||||
|
||||
%install
|
||||
cd build
|
||||
chrpath -d i2pd
|
||||
install -D -m 755 i2pd %{buildroot}%{_bindir}/i2pd
|
||||
install -D -m 644 %{_builddir}/%{name}-%{version}/contrib/rpm/i2pd.service %{buildroot}/%{_unitdir}/i2pd.service
|
||||
install -d -m 700 %{buildroot}/%{_sharedstatedir}/i2pd
|
||||
|
||||
|
||||
%pre systemd
|
||||
getent group i2pd >/dev/null || %{_sbindir}/groupadd -r i2pd
|
||||
getent passwd i2pd >/dev/null || \
|
||||
%{_sbindir}/useradd -r -g i2pd -s %{_sbindir}/nologin \
|
||||
-d %{_sharedstatedir}/i2pd -c 'I2P Service' i2pd
|
||||
|
||||
|
||||
%post systemd
|
||||
%systemd_post i2pd.service
|
||||
|
||||
|
||||
%preun systemd
|
||||
%systemd_preun i2pd.service
|
||||
|
||||
|
||||
%postun systemd
|
||||
%systemd_postun_with_restart i2pd.service
|
||||
|
||||
|
||||
%files
|
||||
%doc LICENSE README.md
|
||||
%_bindir/i2pd
|
||||
|
||||
|
||||
%files systemd
|
||||
/%_unitdir/i2pd.service
|
||||
%dir %attr(0700,i2pd,i2pd) %_sharedstatedir/i2pd
|
||||
|
||||
|
||||
%changelog
|
||||
* Tue Oct 20 2016 Anatolii Vorona <vorona.tolik@gmail.com> - 2.10.0-3
|
||||
- add support C7
|
||||
- move rpm-related files to contrib folder
|
||||
|
||||
* Sun Oct 16 2016 Oleg Girko <ol@infoserver.lv> - 2.10.0-1
|
||||
- update to 2.10.0
|
||||
|
||||
* Sun Aug 14 2016 Oleg Girko <ol@infoserver.lv> - 2.9.0-1
|
||||
- update to 2.9.0
|
||||
|
||||
* Sun Aug 07 2016 Oleg Girko <ol@infoserver.lv> - 2.8.0-2
|
||||
- rename daemon subpackage to systemd
|
||||
|
||||
* Sat Aug 06 2016 Oleg Girko <ol@infoserver.lv> - 2.8.0-1
|
||||
- update to 2.8.0
|
||||
- remove wrong rpath from i2pd binary
|
||||
- add daemon subpackage with systemd unit file
|
||||
|
||||
* Sat May 21 2016 Oleg Girko <ol@infoserver.lv> - 2.7.0-1
|
||||
- update to 2.7.0
|
||||
|
||||
* Tue Apr 05 2016 Oleg Girko <ol@infoserver.lv> - 2.6.0-1
|
||||
- update to 2.6.0
|
||||
|
||||
* Tue Jan 26 2016 Yaroslav Sidlovsky <zawertun@gmail.com> - 2.3.0-1
|
||||
- initial package for version 2.3.0
|
||||
9
debian/.gitignore
vendored
Normal file
9
debian/.gitignore
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
files
|
||||
i2pd-dbg.substvars
|
||||
i2pd-dbg/
|
||||
i2pd.postinst.debhelper
|
||||
i2pd.postrm.debhelper
|
||||
i2pd.prerm.debhelper
|
||||
i2pd.substvars
|
||||
i2pd/
|
||||
|
||||
18
debian/changelog
vendored
18
debian/changelog
vendored
@@ -1,3 +1,21 @@
|
||||
i2pd (2.11.0-1) unstable; urgency=low
|
||||
|
||||
* updated to version 2.11.0/0.9.28
|
||||
|
||||
-- orignal <orignal@i2pmail.org> Sun, 18 Dec 2016 21:01:30 +0000
|
||||
|
||||
i2pd (2.10.2-1) unstable; urgency=low
|
||||
|
||||
* updated to version 2.10.2
|
||||
|
||||
-- orignal <orignal@i2pmail.org> Sun, 4 Dec 2016 19:38:30 +0000
|
||||
|
||||
i2pd (2.10.1-1) unstable; urgency=low
|
||||
|
||||
* updated to version 2.10.1
|
||||
|
||||
-- orignal <orignal@i2pmail.org> Mon, 7 Nov 2016 14:18:30 +0000
|
||||
|
||||
i2pd (2.10.0-1) unstable; urgency=low
|
||||
|
||||
* updated to version 2.10.0/0.9.27
|
||||
|
||||
3
debian/i2pd.default
vendored
3
debian/i2pd.default
vendored
@@ -6,3 +6,6 @@ I2PD_ENABLED="yes"
|
||||
# Additional options that are passed to the Daemon.
|
||||
# see possible switches in /usr/share/doc/i2pd/configuration.md.gz
|
||||
DAEMON_OPTS=""
|
||||
|
||||
# If you have problems with hunging i2pd, you can try enable this
|
||||
#ulimit -n 4096
|
||||
|
||||
8
debian/i2pd.init
vendored
8
debian/i2pd.init
vendored
@@ -15,10 +15,10 @@ DESC=i2pd # Introduce a short description here
|
||||
NAME=i2pd # Introduce the short server's name here
|
||||
DAEMON=/usr/sbin/$NAME # Introduce the server's location here
|
||||
DAEMON_OPTS="" # Arguments to run the daemon with
|
||||
PIDFILE=/var/run/$NAME.pid
|
||||
PIDFILE=/var/run/$NAME/$NAME.pid
|
||||
I2PCONF=/etc/$NAME/i2pd.conf
|
||||
TUNCONF=/etc/$NAME/tunnels.conf
|
||||
LOGFILE=/var/log/$NAME.log
|
||||
LOGFILE=/var/log/$NAME/$NAME.log
|
||||
USER="i2pd"
|
||||
|
||||
# Exit if the package is not installed
|
||||
@@ -41,9 +41,11 @@ do_start()
|
||||
return 2
|
||||
fi
|
||||
|
||||
test -e /var/run/i2pd || install -m 755 -o i2pd -g i2pd -d /var/run/i2pd
|
||||
touch "$PIDFILE"
|
||||
chown -f $USER:adm "$PIDFILE"
|
||||
|
||||
test -e /var/log/i2pd || install -m 755 -o i2pd -g i2pd -d /var/log/i2pd
|
||||
touch "$LOGFILE"
|
||||
chown -f $USER:adm "$LOGFILE"
|
||||
|
||||
@@ -99,7 +101,7 @@ case "$1" in
|
||||
status)
|
||||
status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
|
||||
;;
|
||||
reload)
|
||||
reload|force-reload)
|
||||
log_daemon_msg "Reloading $DESC" "$NAME"
|
||||
do_reload
|
||||
log_end_msg $?
|
||||
|
||||
7
debian/i2pd.openrc
vendored
7
debian/i2pd.openrc
vendored
@@ -1,7 +1,7 @@
|
||||
#!/sbin/openrc-run
|
||||
|
||||
pidfile="/var/run/i2pd.pid"
|
||||
logfile="/var/log/i2pd.log"
|
||||
pidfile="/var/run/i2pd/i2pd.pid"
|
||||
logfile="/var/log/i2pd/i2pd.log"
|
||||
mainconf="/etc/i2pd/i2pd.conf"
|
||||
tunconf="/etc/i2pd/tunnels.conf"
|
||||
|
||||
@@ -32,9 +32,6 @@ start_pre() {
|
||||
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
|
||||
|
||||
2
debian/i2pd.upstart
vendored
2
debian/i2pd.upstart
vendored
@@ -4,6 +4,6 @@ start on runlevel [2345]
|
||||
stop on runlevel [016] or unmounting-filesystem
|
||||
|
||||
# these can be overridden in /etc/init/i2pd.override
|
||||
env LOGFILE="/var/log/i2pd.log"
|
||||
env LOGFILE="/var/log/i2pd/i2pd.log"
|
||||
|
||||
exec /usr/sbin/i2pd --daemon --service --log=file --logfile=$LOGFILE
|
||||
|
||||
2
debian/logrotate
vendored
2
debian/logrotate
vendored
@@ -1,4 +1,4 @@
|
||||
/var/log/i2pd.log {
|
||||
/var/log/i2pd/i2pd.log {
|
||||
rotate 6
|
||||
daily
|
||||
missingok
|
||||
|
||||
21
debian/patches/01-tune-build-opts.patch
vendored
21
debian/patches/01-tune-build-opts.patch
vendored
@@ -1,17 +1,18 @@
|
||||
diff --git a/Makefile b/Makefile
|
||||
index 7d04ba0..33ee184 100644
|
||||
index bdadfe0..2f71eec 100644
|
||||
|
||||
--- a/Makefile
|
||||
+++ b/Makefile
|
||||
@@ -9,10 +9,10 @@ DEPS := obj/make.dep
|
||||
|
||||
include filelist.mk
|
||||
|
||||
-USE_AESNI := yes
|
||||
+USE_AESNI := no
|
||||
USE_STATIC := no
|
||||
USE_MESHNET := no
|
||||
-USE_UPNP := no
|
||||
+USE_UPNP := yes
|
||||
|
||||
ifeq ($(UNAME),Darwin)
|
||||
DAEMON_SRC += DaemonLinux.cpp
|
||||
-USE_AESNI := yes
|
||||
+USE_AESNI := no
|
||||
-USE_AVX := yes
|
||||
+USE_AVX := no
|
||||
USE_STATIC := no
|
||||
USE_MESHNET := no
|
||||
USE_UPNP := no
|
||||
|
||||
ifeq ($(WEBSOCKETS),1)
|
||||
|
||||
4
debian/postinst
vendored
4
debian/postinst
vendored
@@ -1,7 +1,7 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
LOGFILE='/var/log/i2pd.log'
|
||||
LOGFILE='/var/log/i2pd/i2pd.log'
|
||||
I2PDHOME='/var/lib/i2pd'
|
||||
I2PDUSER='i2pd'
|
||||
|
||||
@@ -17,6 +17,8 @@ case "$1" in
|
||||
adduser --system --quiet --group --home $I2PDHOME $I2PDUSER
|
||||
fi
|
||||
|
||||
mkdir -p -m0750 /var/log/i2pd
|
||||
chown -f ${I2PDUSER}:adm /var/log/i2pd
|
||||
touch $LOGFILE
|
||||
chmod 640 $LOGFILE
|
||||
chown -f ${I2PDUSER}:adm $LOGFILE
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user