mirror of
https://github.com/PurpleI2P/i2pd.git
synced 2025-01-14 04:37:58 +00:00
commit
6e3aef0b9b
6
Makefile
6
Makefile
@ -54,15 +54,15 @@ ifneq (, $(findstring darwin, $(SYS)))
|
||||
else
|
||||
include Makefile.osx
|
||||
endif
|
||||
else ifneq (, $(findstring mingw, $(SYS))$(findstring windows-gnu, $(SYS))$(findstring cygwin, $(SYS)))
|
||||
DAEMON_SRC += Win32/DaemonWin32.cpp Win32/Win32App.cpp Win32/Win32Service.cpp Win32/Win32NetState.cpp
|
||||
include Makefile.mingw
|
||||
else ifneq (, $(findstring linux, $(SYS))$(findstring gnu, $(SYS)))
|
||||
DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp
|
||||
include Makefile.linux
|
||||
else ifneq (, $(findstring freebsd, $(SYS))$(findstring openbsd, $(SYS)))
|
||||
DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp
|
||||
include Makefile.bsd
|
||||
else ifneq (, $(findstring mingw, $(SYS))$(findstring windows-gnu, $(SYS))$(findstring cygwin, $(SYS)))
|
||||
DAEMON_SRC += Win32/DaemonWin32.cpp Win32/Win32App.cpp Win32/Win32Service.cpp Win32/Win32NetState.cpp
|
||||
include Makefile.mingw
|
||||
else # not supported
|
||||
$(error Not supported platform)
|
||||
endif
|
||||
|
@ -298,15 +298,14 @@ namespace util
|
||||
|
||||
bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2);
|
||||
bool ssu2; i2p::config::GetOption("ssu2.enabled", ssu2);
|
||||
bool ssu; i2p::config::GetOption("ssu", ssu);
|
||||
bool checkInReserved; i2p::config::GetOption("reservedrange", checkInReserved);
|
||||
LogPrint(eLogInfo, "Daemon: Starting Transports");
|
||||
if(!ssu) LogPrint(eLogInfo, "Daemon: SSU disabled");
|
||||
if(!ssu2) LogPrint(eLogInfo, "Daemon: SSU2 disabled");
|
||||
if(!ntcp2) LogPrint(eLogInfo, "Daemon: NTCP2 disabled");
|
||||
|
||||
i2p::transport::transports.SetCheckReserved(checkInReserved);
|
||||
i2p::transport::transports.Start(ntcp2, ssu, ssu2);
|
||||
if (i2p::transport::transports.IsBoundSSU() || i2p::transport::transports.IsBoundSSU2() || i2p::transport::transports.IsBoundNTCP2())
|
||||
i2p::transport::transports.Start(ntcp2, ssu2);
|
||||
if (i2p::transport::transports.IsBoundSSU2() || i2p::transport::transports.IsBoundNTCP2())
|
||||
LogPrint(eLogInfo, "Daemon: Transports started");
|
||||
else
|
||||
{
|
||||
|
@ -311,12 +311,9 @@ namespace http {
|
||||
s << "<tr>\r\n<td>";
|
||||
switch (address->transportStyle)
|
||||
{
|
||||
case i2p::data::RouterInfo::eTransportNTCP:
|
||||
case i2p::data::RouterInfo::eTransportNTCP2:
|
||||
s << "NTCP2";
|
||||
break;
|
||||
case i2p::data::RouterInfo::eTransportSSU:
|
||||
s << "SSU";
|
||||
break;
|
||||
case i2p::data::RouterInfo::eTransportSSU2:
|
||||
s << "SSU2";
|
||||
break;
|
||||
@ -843,46 +840,6 @@ namespace http {
|
||||
if (!sessions.empty ())
|
||||
ShowTransportSessions (s, sessions, "NTCP2");
|
||||
}
|
||||
auto ssuServer = i2p::transport::transports.GetSSUServer ();
|
||||
if (ssuServer)
|
||||
{
|
||||
auto sessions = ssuServer->GetSessions ();
|
||||
if (!sessions.empty ())
|
||||
{
|
||||
s << "<div class='slide'><label for='slide_ssu'><b>SSU</b> ( " << (int) sessions.size() << " )</label>\r\n<input type=\"checkbox\" id=\"slide_ssu\" />\r\n<div class=\"slidecontent list\">";
|
||||
for (const auto& it: sessions)
|
||||
{
|
||||
s << "<div class=\"listitem\">\r\n";
|
||||
auto endpoint = it.second->GetRemoteEndpoint ();
|
||||
if (it.second->IsOutgoing ()) s << " ⇒ ";
|
||||
s << endpoint.address ().to_string () << ":" << endpoint.port ();
|
||||
if (!it.second->IsOutgoing ()) s << " ⇒ ";
|
||||
s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
|
||||
if (it.second->GetRelayTag ())
|
||||
s << " [itag:" << it.second->GetRelayTag () << "]";
|
||||
s << "</div>\r\n" << std::endl;
|
||||
}
|
||||
s << "</div>\r\n</div>\r\n";
|
||||
}
|
||||
auto sessions6 = ssuServer->GetSessionsV6 ();
|
||||
if (!sessions6.empty ())
|
||||
{
|
||||
s << "<div class='slide'><label for='slide_ssuv6'><b>SSUv6</b> ( " << (int) sessions6.size() << " )</label>\r\n<input type=\"checkbox\" id=\"slide_ssuv6\" />\r\n<div class=\"slidecontent list\">";
|
||||
for (const auto& it: sessions6)
|
||||
{
|
||||
s << "<div class=\"listitem\">\r\n";
|
||||
auto endpoint = it.second->GetRemoteEndpoint ();
|
||||
if (it.second->IsOutgoing ()) s << " ⇒ ";
|
||||
s << "[" << endpoint.address ().to_string () << "]:" << endpoint.port ();
|
||||
if (!it.second->IsOutgoing ()) s << " ⇒ ";
|
||||
s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
|
||||
if (it.second->GetRelayTag ())
|
||||
s << " [itag:" << it.second->GetRelayTag () << "]";
|
||||
s << "</div>\r\n" << std::endl;
|
||||
}
|
||||
s << "</div>\r\n</div>\r\n";
|
||||
}
|
||||
}
|
||||
auto ssu2Server = i2p::transport::transports.GetSSU2Server ();
|
||||
if (ssu2Server)
|
||||
{
|
||||
|
@ -248,10 +248,10 @@ namespace transport
|
||||
{
|
||||
switch (address->transportStyle)
|
||||
{
|
||||
case i2p::data::RouterInfo::eTransportNTCP:
|
||||
case i2p::data::RouterInfo::eTransportNTCP2:
|
||||
return "TCP";
|
||||
break;
|
||||
case i2p::data::RouterInfo::eTransportSSU:
|
||||
case i2p::data::RouterInfo::eTransportSSU2:
|
||||
default:
|
||||
return "UDP";
|
||||
}
|
||||
|
36
i18n/I18N.cpp
Normal file
36
i18n/I18N.cpp
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) 2021-2022, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
* See full license text in LICENSE file at top of project tree
|
||||
*/
|
||||
|
||||
#include "ClientContext.h"
|
||||
#include "I18N_langs.h"
|
||||
#include "I18N.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace i18n
|
||||
{
|
||||
void SetLanguage(const std::string &lang)
|
||||
{
|
||||
const auto it = i2p::i18n::languages.find(lang);
|
||||
if (it == i2p::i18n::languages.end()) // fallback
|
||||
i2p::client::context.SetLanguage (i2p::i18n::english::GetLocale());
|
||||
else
|
||||
i2p::client::context.SetLanguage (it->second.LocaleFunc());
|
||||
}
|
||||
|
||||
std::string translate (const std::string& arg)
|
||||
{
|
||||
return i2p::client::context.GetLanguage ()->GetString (arg);
|
||||
}
|
||||
|
||||
std::string translate (const std::string& arg, const std::string& arg2, const int& n)
|
||||
{
|
||||
return i2p::client::context.GetLanguage ()->GetPlural (arg, arg2, n);
|
||||
}
|
||||
} // i18n
|
||||
} // i2p
|
70
i18n/I18N.h
70
i18n/I18N.h
@ -9,30 +9,68 @@
|
||||
#ifndef __I18N_H__
|
||||
#define __I18N_H__
|
||||
|
||||
#include "ClientContext.h"
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <utility>
|
||||
#include <functional>
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace i18n
|
||||
{
|
||||
inline void SetLanguage(const std::string &lang)
|
||||
class Locale
|
||||
{
|
||||
const auto it = i2p::i18n::languages.find(lang);
|
||||
if (it == i2p::i18n::languages.end()) // fallback
|
||||
i2p::client::context.SetLanguage (i2p::i18n::english::GetLocale());
|
||||
else
|
||||
i2p::client::context.SetLanguage (it->second.LocaleFunc());
|
||||
}
|
||||
public:
|
||||
Locale (
|
||||
const std::string& language,
|
||||
const std::map<std::string, std::string>& strings,
|
||||
const std::map<std::string, std::vector<std::string>>& plurals,
|
||||
std::function<int(int)> formula
|
||||
): m_Language (language), m_Strings (strings), m_Plurals (plurals), m_Formula (formula) { };
|
||||
|
||||
inline std::string translate (const std::string& arg)
|
||||
{
|
||||
return i2p::client::context.GetLanguage ()->GetString (arg);
|
||||
}
|
||||
// Get activated language name for webconsole
|
||||
std::string GetLanguage() const
|
||||
{
|
||||
return m_Language;
|
||||
}
|
||||
|
||||
inline std::string translate (const std::string& arg, const std::string& arg2, const int& n)
|
||||
{
|
||||
return i2p::client::context.GetLanguage ()->GetPlural (arg, arg2, n);
|
||||
}
|
||||
std::string GetString (const std::string& arg) const
|
||||
{
|
||||
const auto it = m_Strings.find(arg);
|
||||
if (it == m_Strings.end())
|
||||
{
|
||||
return arg;
|
||||
}
|
||||
else
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
|
||||
std::string GetPlural (const std::string& arg, const std::string& arg2, const int& n) const
|
||||
{
|
||||
const auto it = m_Plurals.find(arg2);
|
||||
if (it == m_Plurals.end()) // not found, fallback to english
|
||||
{
|
||||
return n == 1 ? arg : arg2;
|
||||
}
|
||||
else
|
||||
{
|
||||
int form = m_Formula(n);
|
||||
return it->second[form];
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const std::string m_Language;
|
||||
const std::map<std::string, std::string> m_Strings;
|
||||
const std::map<std::string, std::vector<std::string>> m_Plurals;
|
||||
std::function<int(int)> m_Formula;
|
||||
};
|
||||
|
||||
void SetLanguage(const std::string &lang);
|
||||
std::string translate (const std::string& arg);
|
||||
std::string translate (const std::string& arg, const std::string& arg2, const int& n);
|
||||
} // i18n
|
||||
} // i2p
|
||||
|
||||
|
@ -9,60 +9,12 @@
|
||||
#ifndef __I18N_LANGS_H__
|
||||
#define __I18N_LANGS_H__
|
||||
|
||||
#include "I18N.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace i18n
|
||||
{
|
||||
class Locale
|
||||
{
|
||||
public:
|
||||
Locale (
|
||||
const std::string& language,
|
||||
const std::map<std::string, std::string>& strings,
|
||||
const std::map<std::string, std::vector<std::string>>& plurals,
|
||||
std::function<int(int)> formula
|
||||
): m_Language (language), m_Strings (strings), m_Plurals (plurals), m_Formula (formula) { };
|
||||
|
||||
// Get activated language name for webconsole
|
||||
std::string GetLanguage() const
|
||||
{
|
||||
return m_Language;
|
||||
}
|
||||
|
||||
std::string GetString (const std::string& arg) const
|
||||
{
|
||||
const auto it = m_Strings.find(arg);
|
||||
if (it == m_Strings.end())
|
||||
{
|
||||
return arg;
|
||||
}
|
||||
else
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
|
||||
std::string GetPlural (const std::string& arg, const std::string& arg2, const int& n) const
|
||||
{
|
||||
const auto it = m_Plurals.find(arg2);
|
||||
if (it == m_Plurals.end()) // not found, fallback to english
|
||||
{
|
||||
return n == 1 ? arg : arg2;
|
||||
}
|
||||
else
|
||||
{
|
||||
int form = m_Formula(n);
|
||||
return it->second[form];
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const std::string m_Language;
|
||||
const std::map<std::string, std::string> m_Strings;
|
||||
const std::map<std::string, std::vector<std::string>> m_Plurals;
|
||||
std::function<int(int)> m_Formula;
|
||||
};
|
||||
|
||||
struct langData
|
||||
{
|
||||
std::string LocaleName; // localized name
|
||||
|
@ -1,77 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2020, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
* See full license text in LICENSE file at top of project tree
|
||||
*/
|
||||
|
||||
#include "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);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2020, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
* See full license text in LICENSE file at top of project tree
|
||||
*/
|
||||
|
||||
#ifndef 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
|
@ -64,7 +64,7 @@ namespace config {
|
||||
("bandwidth", value<std::string>()->default_value(""), "Transit traffic bandwidth limit: integer in KBps or letters: L (32), O (256), P (2048), X (>9000)")
|
||||
("share", value<int>()->default_value(100), "Limit of transit traffic from max bandwidth in percents. (default: 100)")
|
||||
("ntcp", bool_switch()->default_value(false), "Ignored. Always false")
|
||||
("ssu", bool_switch()->default_value(false), "Enable SSU transport (default: disabled)")
|
||||
("ssu", bool_switch()->default_value(false), "Ignored. Always false")
|
||||
("ntcpproxy", value<std::string>()->default_value(""), "Ignored")
|
||||
#ifdef _WIN32
|
||||
("svcctl", value<std::string>()->default_value(""), "Ignored")
|
||||
|
@ -239,55 +239,6 @@ namespace crypto
|
||||
|
||||
static BIGNUM * (* g_ElggTable)[255] = nullptr;
|
||||
|
||||
// DH
|
||||
|
||||
DHKeys::DHKeys ()
|
||||
{
|
||||
m_DH = DH_new ();
|
||||
DH_set0_pqg (m_DH, BN_dup (elgp), NULL, BN_dup (elgg));
|
||||
DH_set0_key (m_DH, NULL, NULL);
|
||||
}
|
||||
|
||||
DHKeys::~DHKeys ()
|
||||
{
|
||||
DH_free (m_DH);
|
||||
}
|
||||
|
||||
void DHKeys::GenerateKeys ()
|
||||
{
|
||||
BIGNUM * priv_key = NULL, * pub_key = NULL;
|
||||
#if !defined(__x86_64__) // use short exponent for non x64
|
||||
priv_key = BN_new ();
|
||||
BN_rand (priv_key, ELGAMAL_SHORT_EXPONENT_NUM_BITS, 0, 1);
|
||||
#endif
|
||||
if (g_ElggTable)
|
||||
{
|
||||
#if defined(__x86_64__)
|
||||
priv_key = BN_new ();
|
||||
BN_rand (priv_key, ELGAMAL_FULL_EXPONENT_NUM_BITS, 0, 1);
|
||||
#endif
|
||||
auto ctx = BN_CTX_new ();
|
||||
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);
|
||||
DH_get0_key (m_DH, (const BIGNUM **)&pub_key, (const BIGNUM **)&priv_key);
|
||||
}
|
||||
|
||||
bn2buf (pub_key, m_PublicKey, 256);
|
||||
}
|
||||
|
||||
void DHKeys::Agree (const uint8_t * pub, uint8_t * shared)
|
||||
{
|
||||
BIGNUM * pk = BN_bin2bn (pub, 256, NULL);
|
||||
DH_compute_key (shared, pk, m_DH);
|
||||
BN_free (pk);
|
||||
}
|
||||
|
||||
// x25519
|
||||
X25519Keys::X25519Keys ()
|
||||
{
|
||||
@ -601,77 +552,6 @@ namespace crypto
|
||||
BN_CTX_free (ctx);
|
||||
}
|
||||
|
||||
// HMAC
|
||||
const uint64_t IPAD = 0x3636363636363636;
|
||||
const uint64_t OPAD = 0x5C5C5C5C5C5C5C5C;
|
||||
|
||||
|
||||
static const uint64_t ipads[] = { IPAD, IPAD, IPAD, IPAD };
|
||||
static const uint64_t opads[] = { OPAD, OPAD, OPAD, OPAD };
|
||||
|
||||
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(__x86_64__) || defined(__i386__)) && defined(__AVX__) // not all X86 targets supports AVX (like old Pentium, see #1600)
|
||||
if(i2p::cpu::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
|
||||
#endif
|
||||
{
|
||||
// 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;
|
||||
// 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);
|
||||
}
|
||||
|
||||
// concatenate with msg
|
||||
memcpy (buf + 8, msg, len);
|
||||
// calculate first hash
|
||||
MD5((uint8_t *)buf, len + 64, (uint8_t *)(hash + 8)); // 16 bytes
|
||||
|
||||
// calculate digest
|
||||
MD5((uint8_t *)hash, 96, digest);
|
||||
}
|
||||
|
||||
// AES
|
||||
#ifdef __AES__
|
||||
#define KeyExpansion256(round0,round1) \
|
||||
|
@ -62,24 +62,6 @@ namespace crypto
|
||||
// RSA
|
||||
const BIGNUM * GetRSAE ();
|
||||
|
||||
// DH
|
||||
class DHKeys
|
||||
{
|
||||
public:
|
||||
|
||||
DHKeys ();
|
||||
~DHKeys ();
|
||||
|
||||
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];
|
||||
};
|
||||
|
||||
// x25519
|
||||
class X25519Keys
|
||||
{
|
||||
@ -121,10 +103,6 @@ namespace crypto
|
||||
bool ECIESDecrypt (const EC_GROUP * curve, const BIGNUM * key, const uint8_t * encrypted, uint8_t * data); // 514 bytes encrypted, 222 data
|
||||
void GenerateECIESKeyPair (const EC_GROUP * curve, BIGNUM *& priv, EC_POINT *& pub);
|
||||
|
||||
// HMAC
|
||||
typedef i2p::data::Tag<32> MACKey;
|
||||
void HMACMD5Digest (uint8_t * msg, size_t len, const MACKey& key, uint8_t * digest);
|
||||
|
||||
// AES
|
||||
struct ChipherBlock
|
||||
{
|
||||
|
@ -309,7 +309,7 @@ namespace transport
|
||||
|
||||
KDF3Bob ();
|
||||
if (i2p::crypto::AEADChaCha20Poly1305 (m_SessionConfirmedBuffer + 48, m3p2Len - 16, GetH (), 32, GetK (), nonce, m3p2Buf, m3p2Len - 16, false)) // decrypt
|
||||
// caclulate new h again for KDF data
|
||||
// calculate new h again for KDF data
|
||||
MixHash (m_SessionConfirmedBuffer + 48, m3p2Len); // h = SHA256(h || ciphertext)
|
||||
else
|
||||
{
|
||||
|
@ -628,8 +628,8 @@ namespace data
|
||||
(it.second->IsFloodfill () && totalFloodfills - deletedFloodfillsCount < NETDB_MIN_FLOODFILLS)))
|
||||
it.second->SetUnreachable (false);
|
||||
// find & mark expired routers
|
||||
if (!it.second->IsReachable () && (it.second->GetCompatibleTransports (true) & (RouterInfo::eSSUV4 | RouterInfo::eSSU2V4)))
|
||||
// non-reachable router, but reachable by ipv4 SSU or SSU2 means introducers
|
||||
if (!it.second->IsReachable () && (it.second->GetCompatibleTransports (true) & RouterInfo::eSSU2V4))
|
||||
// non-reachable router, but reachable by ipv4 SSU2 means introducers
|
||||
{
|
||||
if (ts > it.second->GetTimestamp () + NETDB_INTRODUCEE_EXPIRATION_TIMEOUT*1000LL)
|
||||
// RouterInfo expires after 1 hour if uses introducer
|
||||
@ -649,7 +649,8 @@ namespace data
|
||||
} // m_RouterInfos iteration
|
||||
|
||||
m_RouterInfoBuffersPool.CleanUpMt ();
|
||||
|
||||
m_RouterInfoAddressesPool.CleanUpMt ();
|
||||
|
||||
if (updatedCount > 0)
|
||||
LogPrint (eLogInfo, "NetDb: Saved ", updatedCount, " new/updated routers");
|
||||
if (deletedCount > 0)
|
||||
@ -1209,16 +1210,6 @@ namespace data
|
||||
});
|
||||
}
|
||||
|
||||
std::shared_ptr<const RouterInfo> NetDb::GetRandomPeerTestRouter (bool v4, const std::set<IdentHash>& excluded) const
|
||||
{
|
||||
return GetRandomRouter (
|
||||
[v4, &excluded](std::shared_ptr<const RouterInfo> router)->bool
|
||||
{
|
||||
return !router->IsHidden () && router->IsECIES () &&
|
||||
router->IsPeerTesting (v4) && !excluded.count (router->GetIdentHash ());
|
||||
});
|
||||
}
|
||||
|
||||
std::shared_ptr<const RouterInfo> NetDb::GetRandomSSU2PeerTestRouter (bool v4, const std::set<IdentHash>& excluded) const
|
||||
{
|
||||
return GetRandomRouter (
|
||||
@ -1229,25 +1220,6 @@ namespace data
|
||||
});
|
||||
}
|
||||
|
||||
std::shared_ptr<const RouterInfo> NetDb::GetRandomSSUV6Router () const
|
||||
{
|
||||
return GetRandomRouter (
|
||||
[](std::shared_ptr<const RouterInfo> router)->bool
|
||||
{
|
||||
return !router->IsHidden () && router->IsECIES () && router->IsSSUV6 ();
|
||||
});
|
||||
}
|
||||
|
||||
std::shared_ptr<const RouterInfo> NetDb::GetRandomIntroducer (bool v4, const std::set<IdentHash>& excluded) const
|
||||
{
|
||||
return GetRandomRouter (
|
||||
[v4, &excluded](std::shared_ptr<const RouterInfo> router)->bool
|
||||
{
|
||||
return !router->IsHidden () && router->IsECIES () && !router->IsFloodfill () && // floodfills don't send relay tag
|
||||
router->IsIntroducer (v4) && !excluded.count (router->GetIdentHash ());
|
||||
});
|
||||
}
|
||||
|
||||
std::shared_ptr<const RouterInfo> NetDb::GetRandomSSU2Introducer (bool v4, const std::set<IdentHash>& excluded) const
|
||||
{
|
||||
return GetRandomRouter (
|
||||
|
@ -89,10 +89,7 @@ namespace data
|
||||
std::shared_ptr<const RouterInfo> GetRandomRouter () const;
|
||||
std::shared_ptr<const RouterInfo> GetRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith, bool reverse) const;
|
||||
std::shared_ptr<const RouterInfo> GetHighBandwidthRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith, bool reverse) const;
|
||||
std::shared_ptr<const RouterInfo> GetRandomPeerTestRouter (bool v4, const std::set<IdentHash>& excluded) const;
|
||||
std::shared_ptr<const RouterInfo> GetRandomSSU2PeerTestRouter (bool v4, const std::set<IdentHash>& excluded) const;
|
||||
std::shared_ptr<const RouterInfo> GetRandomSSUV6Router () const; // TODO: change to v6 peer test later
|
||||
std::shared_ptr<const RouterInfo> GetRandomIntroducer (bool v4, const std::set<IdentHash>& excluded) const;
|
||||
std::shared_ptr<const RouterInfo> GetRandomSSU2Introducer (bool v4, const std::set<IdentHash>& excluded) 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,
|
||||
@ -126,6 +123,7 @@ namespace data
|
||||
void ClearRouterInfos () { m_RouterInfos.clear (); };
|
||||
std::shared_ptr<RouterInfo::Buffer> NewRouterInfoBuffer () { return m_RouterInfoBuffersPool.AcquireSharedMt (); };
|
||||
void PopulateRouterInfoBuffer (std::shared_ptr<RouterInfo> r);
|
||||
std::shared_ptr<RouterInfo::Address> NewRouterInfoAddress () { return m_RouterInfoAddressesPool.AcquireSharedMt (); };
|
||||
std::shared_ptr<Lease> NewLease (const Lease& lease) { return m_LeasesPool.AcquireSharedMt (lease); };
|
||||
|
||||
uint32_t GetPublishReplyToken () const { return m_PublishReplyToken; };
|
||||
@ -183,6 +181,7 @@ namespace data
|
||||
uint32_t m_PublishReplyToken = 0;
|
||||
|
||||
i2p::util::MemoryPoolMt<RouterInfo::Buffer> m_RouterInfoBuffersPool;
|
||||
i2p::util::MemoryPoolMt<RouterInfo::Address> m_RouterInfoAddressesPool;
|
||||
i2p::util::MemoryPoolMt<Lease> m_LeasesPool;
|
||||
};
|
||||
|
||||
|
@ -63,7 +63,6 @@ namespace i2p
|
||||
if (!port) port = SelectRandomPort ();
|
||||
bool ipv4; i2p::config::GetOption("ipv4", ipv4);
|
||||
bool ipv6; i2p::config::GetOption("ipv6", ipv6);
|
||||
bool ssu; i2p::config::GetOption("ssu", ssu);
|
||||
bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2);
|
||||
bool ssu2; i2p::config::GetOption("ssu2.enabled", ssu2);
|
||||
bool ygg; i2p::config::GetOption("meshnets.yggdrasil", ygg);
|
||||
@ -109,17 +108,12 @@ namespace i2p
|
||||
routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv);
|
||||
}
|
||||
}
|
||||
if (ssu)
|
||||
{
|
||||
routerInfo.AddSSUAddress (host.c_str(), port, nullptr);
|
||||
caps |= i2p::data::RouterInfo::eReachable; // R
|
||||
}
|
||||
if (ssu2)
|
||||
{
|
||||
if (ssu2Published)
|
||||
{
|
||||
uint16_t ssu2Port; i2p::config::GetOption ("ssu2.port", ssu2Port);
|
||||
if (!ssu2Port) ssu2Port = ssu ? (port + 1) : port;
|
||||
if (!ssu2Port) ssu2Port = port;
|
||||
routerInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, boost::asio::ip::address_v4::from_string (host), ssu2Port);
|
||||
}
|
||||
else
|
||||
@ -158,17 +152,12 @@ namespace i2p
|
||||
addressCaps |= i2p::data::RouterInfo::AddressCaps::eV6;
|
||||
}
|
||||
}
|
||||
if (ssu)
|
||||
{
|
||||
routerInfo.AddSSUAddress (host.c_str(), port, nullptr);
|
||||
caps |= i2p::data::RouterInfo::eReachable; // R
|
||||
}
|
||||
if (ssu2)
|
||||
{
|
||||
if (ssu2Published)
|
||||
{
|
||||
uint16_t ssu2Port; i2p::config::GetOption ("ssu2.port", ssu2Port);
|
||||
if (!ssu2Port) ssu2Port = ssu ? (port + 1) : port;
|
||||
if (!ssu2Port) ssu2Port = port;
|
||||
routerInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, boost::asio::ip::address_v6::from_string (host), ssu2Port);
|
||||
}
|
||||
else
|
||||
@ -236,13 +225,6 @@ namespace i2p
|
||||
fk.write ((char *)m_SSU2Keys.get (), sizeof (SSU2PrivateKeys));
|
||||
}
|
||||
|
||||
bool RouterContext::IsSSU2Only () const
|
||||
{
|
||||
auto transports = m_RouterInfo.GetCompatibleTransports (false);
|
||||
return (transports & (i2p::data::RouterInfo::eSSU2V4 | i2p::data::RouterInfo::eSSU2V6)) &&
|
||||
!(transports & (i2p::data::RouterInfo::eSSUV4 | i2p::data::RouterInfo::eSSUV6));
|
||||
}
|
||||
|
||||
void RouterContext::SetStatus (RouterStatus status)
|
||||
{
|
||||
if (status != m_Status)
|
||||
@ -262,13 +244,7 @@ namespace i2p
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RouterContext::SetStatusSSU2 (RouterStatus status)
|
||||
{
|
||||
if (IsSSU2Only ())
|
||||
SetStatus (status);
|
||||
}
|
||||
|
||||
|
||||
void RouterContext::SetStatusV6 (RouterStatus status)
|
||||
{
|
||||
if (status != m_StatusV6)
|
||||
@ -288,19 +264,13 @@ namespace i2p
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RouterContext::SetStatusV6SSU2 (RouterStatus status)
|
||||
{
|
||||
if (IsSSU2Only ())
|
||||
SetStatusV6 (status);
|
||||
}
|
||||
|
||||
|
||||
void RouterContext::UpdatePort (int port)
|
||||
{
|
||||
bool updated = false;
|
||||
for (auto& address : m_RouterInfo.GetAddresses ())
|
||||
{
|
||||
if (address->port != port && (address->transportStyle == i2p::data::RouterInfo::eTransportSSU || IsSSU2Only ()))
|
||||
if (address->port != port && address->transportStyle == i2p::data::RouterInfo::eTransportSSU2)
|
||||
{
|
||||
address->port = port;
|
||||
updated = true;
|
||||
@ -476,8 +446,6 @@ namespace i2p
|
||||
mtu = maxMTU;
|
||||
LogPrint(eLogWarning, "Router: MTU dropped to upper limit of ", maxMTU, " bytes");
|
||||
}
|
||||
if (mtu && !address->IsSSU2 ()) // SSU1
|
||||
mtu = (mtu >> 4) << 4; // round to multiple of 16
|
||||
address->ssu->mtu = mtu;
|
||||
updated = true;
|
||||
}
|
||||
@ -488,23 +456,8 @@ namespace i2p
|
||||
UpdateRouterInfo ();
|
||||
}
|
||||
|
||||
bool RouterContext::AddIntroducer (const i2p::data::RouterInfo::Introducer& introducer)
|
||||
{
|
||||
bool ret = m_RouterInfo.AddIntroducer (introducer);
|
||||
if (ret)
|
||||
UpdateRouterInfo ();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void RouterContext::RemoveIntroducer (const boost::asio::ip::udp::endpoint& e)
|
||||
{
|
||||
if (m_RouterInfo.RemoveIntroducer (e))
|
||||
UpdateRouterInfo ();
|
||||
}
|
||||
|
||||
bool RouterContext::AddSSU2Introducer (const i2p::data::RouterInfo::Introducer& introducer, bool v4)
|
||||
{
|
||||
if (!IsSSU2Only ()) return false;
|
||||
bool ret = m_RouterInfo.AddSSU2Introducer (introducer, v4);
|
||||
if (ret)
|
||||
UpdateRouterInfo ();
|
||||
@ -513,7 +466,6 @@ namespace i2p
|
||||
|
||||
void RouterContext::RemoveSSU2Introducer (const i2p::data::IdentHash& h, bool v4)
|
||||
{
|
||||
if (!IsSSU2Only ()) return;
|
||||
if (m_RouterInfo.RemoveSSU2Introducer (h, v4))
|
||||
UpdateRouterInfo ();
|
||||
}
|
||||
@ -631,50 +583,6 @@ namespace i2p
|
||||
return m_RouterInfo.GetCaps () & i2p::data::RouterInfo::eUnreachable;
|
||||
}
|
||||
|
||||
void RouterContext::RemoveNTCPAddress (bool v4only)
|
||||
{
|
||||
bool updated = false;
|
||||
auto& addresses = m_RouterInfo.GetAddresses ();
|
||||
for (auto it = addresses.begin (); it != addresses.end ();)
|
||||
{
|
||||
if ((*it)->transportStyle == i2p::data::RouterInfo::eTransportNTCP && !(*it)->IsNTCP2 () &&
|
||||
(!v4only || (*it)->host.is_v4 ()))
|
||||
{
|
||||
it = addresses.erase (it);
|
||||
updated = true;
|
||||
if (v4only) break; // otherwise might be more than one address
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
if (updated)
|
||||
m_RouterInfo.UpdateSupportedTransports ();
|
||||
}
|
||||
|
||||
void RouterContext::RemoveSSUAddress ()
|
||||
{
|
||||
bool updated = false;
|
||||
auto& addresses = m_RouterInfo.GetAddresses ();
|
||||
for (auto it = addresses.begin (); it != addresses.end ();)
|
||||
{
|
||||
if ((*it)->transportStyle == i2p::data::RouterInfo::eTransportSSU)
|
||||
{
|
||||
it = addresses.erase (it);
|
||||
updated = true;
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
if (updated)
|
||||
m_RouterInfo.UpdateSupportedTransports ();
|
||||
}
|
||||
|
||||
void RouterContext::SetUnreachableSSU2 (bool v4, bool v6)
|
||||
{
|
||||
if (IsSSU2Only ())
|
||||
SetUnreachable (v4, v6);
|
||||
}
|
||||
|
||||
void RouterContext::SetUnreachable (bool v4, bool v6)
|
||||
{
|
||||
if (v4 || (v6 && !SupportsV4 ()))
|
||||
@ -691,8 +599,7 @@ namespace i2p
|
||||
// delete previous introducers
|
||||
auto& addresses = m_RouterInfo.GetAddresses ();
|
||||
for (auto& addr : addresses)
|
||||
if (addr->ssu && (!addr->IsSSU2 () || IsSSU2Only ()) &&
|
||||
((v4 && addr->IsV4 ()) || (v6 && addr->IsV6 ())))
|
||||
if (addr->ssu && ((v4 && addr->IsV4 ()) || (v6 && addr->IsV6 ())))
|
||||
{
|
||||
addr->published = false;
|
||||
addr->caps &= ~i2p::data::RouterInfo::eSSUIntroducer; // can't be introducer
|
||||
@ -722,19 +629,15 @@ namespace i2p
|
||||
}
|
||||
uint16_t port = 0;
|
||||
// delete previous introducers
|
||||
bool isSSU2Published = IsSSU2Only (); // TODO
|
||||
if (isSSU2Published)
|
||||
i2p::config::GetOption ("ssu2.published", isSSU2Published);
|
||||
bool isSSU2Published; i2p::config::GetOption ("ssu2.published", isSSU2Published);
|
||||
auto& addresses = m_RouterInfo.GetAddresses ();
|
||||
for (auto& addr : addresses)
|
||||
if (addr->ssu && (!addr->IsSSU2 () || isSSU2Published) &&
|
||||
((v4 && addr->IsV4 ()) || (v6 && addr->IsV6 ())))
|
||||
if (addr->ssu && isSSU2Published && ((v4 && addr->IsV4 ()) || (v6 && addr->IsV6 ())))
|
||||
{
|
||||
addr->published = true;
|
||||
addr->caps |= i2p::data::RouterInfo::eSSUIntroducer;
|
||||
addr->ssu->introducers.clear ();
|
||||
if (addr->port && (!addr->IsSSU2 () || IsSSU2Only ()))
|
||||
port = addr->port;
|
||||
if (addr->port) port = addr->port;
|
||||
}
|
||||
// publish NTCP2
|
||||
bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2);
|
||||
@ -758,7 +661,7 @@ namespace i2p
|
||||
if (supportsV6)
|
||||
{
|
||||
// insert v6 addresses if necessary
|
||||
bool foundSSU = false, foundNTCP2 = false, foundSSU2 = false;
|
||||
bool foundNTCP2 = false, foundSSU2 = false;
|
||||
uint16_t port = 0;
|
||||
auto& addresses = m_RouterInfo.GetAddresses ();
|
||||
for (auto& addr: addresses)
|
||||
@ -767,10 +670,7 @@ namespace i2p
|
||||
{
|
||||
switch (addr->transportStyle)
|
||||
{
|
||||
case i2p::data::RouterInfo::eTransportSSU:
|
||||
foundSSU = true;
|
||||
break;
|
||||
case i2p::data::RouterInfo::eTransportNTCP:
|
||||
case i2p::data::RouterInfo::eTransportNTCP2:
|
||||
foundNTCP2 = true;
|
||||
break;
|
||||
case i2p::data::RouterInfo::eTransportSSU2:
|
||||
@ -786,13 +686,6 @@ namespace i2p
|
||||
i2p::config::GetOption("port", port);
|
||||
if (!port) port = SelectRandomPort ();
|
||||
}
|
||||
// SSU
|
||||
bool ssu; i2p::config::GetOption("ssu", ssu);
|
||||
if (!foundSSU && ssu)
|
||||
{
|
||||
std::string host = "::1"; // TODO: read host
|
||||
m_RouterInfo.AddSSUAddress (host.c_str (), port, nullptr);
|
||||
}
|
||||
// NTCP2
|
||||
if (!foundNTCP2)
|
||||
{
|
||||
@ -825,7 +718,7 @@ namespace i2p
|
||||
if (ssu2Published)
|
||||
{
|
||||
uint16_t ssu2Port; i2p::config::GetOption ("ssu2.port", ssu2Port);
|
||||
if (!ssu2Port) ssu2Port = ssu ? (port + 1) : port;
|
||||
if (!ssu2Port) ssu2Port = port;
|
||||
m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, boost::asio::ip::address::from_string ("::1"), ssu2Port);
|
||||
}
|
||||
else
|
||||
@ -847,7 +740,7 @@ namespace i2p
|
||||
// update
|
||||
if (supportsV4)
|
||||
{
|
||||
bool foundSSU = false, foundNTCP2 = false, foundSSU2 = false;
|
||||
bool foundNTCP2 = false, foundSSU2 = false;
|
||||
std::string host = "127.0.0.1";
|
||||
uint16_t port = 0;
|
||||
auto& addresses = m_RouterInfo.GetAddresses ();
|
||||
@ -857,10 +750,7 @@ namespace i2p
|
||||
{
|
||||
switch (addr->transportStyle)
|
||||
{
|
||||
case i2p::data::RouterInfo::eTransportSSU:
|
||||
foundSSU = true;
|
||||
break;
|
||||
case i2p::data::RouterInfo::eTransportNTCP:
|
||||
case i2p::data::RouterInfo::eTransportNTCP2:
|
||||
foundNTCP2 = true;
|
||||
break;
|
||||
case i2p::data::RouterInfo::eTransportSSU2:
|
||||
@ -876,11 +766,6 @@ namespace i2p
|
||||
i2p::config::GetOption("port", port);
|
||||
if (!port) port = SelectRandomPort ();
|
||||
}
|
||||
// SSU
|
||||
bool ssu; i2p::config::GetOption("ssu", ssu);
|
||||
if (!foundSSU && ssu)
|
||||
m_RouterInfo.AddSSUAddress (host.c_str (), port, nullptr);
|
||||
|
||||
// NTCP2
|
||||
if (!foundNTCP2)
|
||||
{
|
||||
@ -908,7 +793,7 @@ namespace i2p
|
||||
if (ssu2Published)
|
||||
{
|
||||
uint16_t ssu2Port; i2p::config::GetOption ("ssu2.port", ssu2Port);
|
||||
if (!ssu2Port) ssu2Port = ssu ? (port + 1) : port;
|
||||
if (!ssu2Port) ssu2Port = port;
|
||||
m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, boost::asio::ip::address::from_string ("127.0.0.1"), ssu2Port);
|
||||
}
|
||||
else
|
||||
@ -956,32 +841,9 @@ namespace i2p
|
||||
for (auto& addr: addresses)
|
||||
{
|
||||
if (addr->ssu && ((v4 && addr->IsV4 ()) || (!v4 && addr->IsV6 ())))
|
||||
{
|
||||
if (!addr->IsSSU2 ()) // SSU1
|
||||
{
|
||||
// round to multiple of 16
|
||||
if (v4)
|
||||
{
|
||||
if (mtu > 1484) mtu = 1484;
|
||||
else
|
||||
{
|
||||
mtu -= 12;
|
||||
mtu = (mtu >> 4) << 4;
|
||||
mtu += 12;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mtu > 1488) mtu = 1488;
|
||||
else
|
||||
mtu = (mtu >> 4) << 4;
|
||||
}
|
||||
}
|
||||
if (mtu)
|
||||
{
|
||||
addr->ssu->mtu = mtu;
|
||||
LogPrint (eLogDebug, "Router: MTU for ", v4 ? "ipv4" : "ipv6", " address ", addr->host.to_string(), " is set to ", mtu);
|
||||
}
|
||||
{
|
||||
addr->ssu->mtu = mtu;
|
||||
LogPrint (eLogDebug, "Router: MTU for ", v4 ? "ipv4" : "ipv6", " address ", addr->host.to_string(), " is set to ", mtu);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -102,12 +102,10 @@ namespace garlic
|
||||
uint64_t GetTransitBandwidthLimit () const { return (m_BandwidthLimit*m_ShareRatio)/100LL; };
|
||||
RouterStatus GetStatus () const { return m_Status; };
|
||||
void SetStatus (RouterStatus status);
|
||||
void SetStatusSSU2 (RouterStatus status);
|
||||
RouterError GetError () const { return m_Error; };
|
||||
void SetError (RouterError error) { m_Error = error; };
|
||||
RouterStatus GetStatusV6 () const { return m_StatusV6; };
|
||||
void SetStatusV6 (RouterStatus status);
|
||||
void SetStatusV6SSU2 (RouterStatus status);
|
||||
RouterError GetErrorV6 () const { return m_ErrorV6; };
|
||||
void SetErrorV6 (RouterError error) { m_ErrorV6 = error; };
|
||||
int GetNetID () const { return m_NetID; };
|
||||
@ -121,16 +119,11 @@ namespace garlic
|
||||
void UpdateNTCP2Address (bool enable);
|
||||
void PublishSSU2Address (int port, bool publish, bool v4, bool v6);
|
||||
void UpdateSSU2Address (bool enable);
|
||||
void RemoveNTCPAddress (bool v4only = true); // delete NTCP address for older routers. TODO: remove later
|
||||
void RemoveSSUAddress (); // delete SSU address for older routers
|
||||
bool AddIntroducer (const i2p::data::RouterInfo::Introducer& introducer);
|
||||
void RemoveIntroducer (const boost::asio::ip::udp::endpoint& e);
|
||||
bool AddSSU2Introducer (const i2p::data::RouterInfo::Introducer& introducer, bool v4);
|
||||
void RemoveSSU2Introducer (const i2p::data::IdentHash& h, bool v4);
|
||||
void ClearSSU2Introducers (bool v4);
|
||||
bool IsUnreachable () const;
|
||||
void SetUnreachable (bool v4, bool v6);
|
||||
void SetUnreachableSSU2 (bool v4, bool v6);
|
||||
void SetReachable (bool v4, bool v6);
|
||||
bool IsFloodfill () const { return m_IsFloodfill; };
|
||||
void SetFloodfill (bool floodfill);
|
||||
|
@ -206,26 +206,24 @@ namespace data
|
||||
s.read ((char *)&m_Timestamp, sizeof (m_Timestamp));
|
||||
m_Timestamp = be64toh (m_Timestamp);
|
||||
// read addresses
|
||||
auto addresses = boost::make_shared<Addresses>();
|
||||
if (!m_NewAddresses) m_NewAddresses = boost::make_shared<Addresses>();
|
||||
uint8_t numAddresses;
|
||||
s.read ((char *)&numAddresses, sizeof (numAddresses));
|
||||
addresses->reserve (numAddresses);
|
||||
for (int i = 0; i < numAddresses; i++)
|
||||
{
|
||||
uint8_t supportedTransports = 0;
|
||||
auto address = std::make_shared<Address> ();
|
||||
auto address = netdb.NewRouterInfoAddress ();
|
||||
uint8_t cost; // ignore
|
||||
s.read ((char *)&cost, sizeof (cost));
|
||||
s.read ((char *)&address->date, sizeof (address->date));
|
||||
bool isHost = false, isIntroKey = false, isStaticKey = false, isV2 = false;
|
||||
Tag<32> iV2; // for 'i' field in SSU, TODO: remove later
|
||||
bool isHost = false, isStaticKey = false, isV2 = false;
|
||||
char transportStyle[6];
|
||||
ReadString (transportStyle, 6, s);
|
||||
if (!strncmp (transportStyle, "NTCP", 4)) // NTCP or NTCP2
|
||||
address->transportStyle = eTransportNTCP;
|
||||
address->transportStyle = eTransportNTCP2;
|
||||
else if (!strncmp (transportStyle, "SSU", 3)) // SSU or SSU2
|
||||
{
|
||||
address->transportStyle = (transportStyle[3] == '2') ? eTransportSSU2 : eTransportSSU;
|
||||
address->transportStyle = eTransportSSU2;
|
||||
address->ssu.reset (new SSUExt ());
|
||||
address->ssu->mtu = 0;
|
||||
}
|
||||
@ -283,13 +281,6 @@ namespace data
|
||||
else
|
||||
LogPrint (eLogWarning, "RouterInfo: Unexpected field 'mtu' for NTCP2");
|
||||
}
|
||||
else if (!strcmp (key, "key"))
|
||||
{
|
||||
if (address->ssu)
|
||||
isIntroKey = (Base64ToByteStream (value, strlen (value), address->i, 32) == 32);
|
||||
else
|
||||
LogPrint (eLogWarning, "RouterInfo: Unexpected field 'key' for NTCP2");
|
||||
}
|
||||
else if (!strcmp (key, "caps"))
|
||||
address->caps = ExtractAddressCaps (value);
|
||||
else if (!strcmp (key, "s")) // ntcp2 or ssu2 static key
|
||||
@ -306,8 +297,6 @@ namespace data
|
||||
}
|
||||
else if (address->IsSSU2 ())
|
||||
Base64ToByteStream (value, strlen (value), address->i, 32);
|
||||
else
|
||||
Base64ToByteStream (value, strlen (value), iV2, 32);
|
||||
}
|
||||
else if (!strcmp (key, "v"))
|
||||
{
|
||||
@ -340,21 +329,9 @@ namespace data
|
||||
}
|
||||
Introducer& introducer = address->ssu->introducers.at (index);
|
||||
if (!strcmp (key, "ihost"))
|
||||
{
|
||||
boost::system::error_code ecode;
|
||||
introducer.iHost = boost::asio::ip::address::from_string (value, ecode);
|
||||
}
|
||||
introducer.isH = false; // SSU1
|
||||
else if (!strcmp (key, "iport"))
|
||||
{
|
||||
try
|
||||
{
|
||||
introducer.iPort = boost::lexical_cast<int>(value);
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint (eLogWarning, "RouterInfo: 'iport' exception ", ex.what ());
|
||||
}
|
||||
}
|
||||
introducer.isH = false; // SSU1
|
||||
else if (!strcmp (key, "itag"))
|
||||
{
|
||||
try
|
||||
@ -366,8 +343,11 @@ namespace data
|
||||
LogPrint (eLogWarning, "RouterInfo: 'itag' exception ", ex.what ());
|
||||
}
|
||||
}
|
||||
else if (!strcmp (key, "ikey") || !strcmp (key, "ih"))
|
||||
Base64ToByteStream (value, strlen (value), introducer.iKey, 32);
|
||||
else if (!strcmp (key, "ih"))
|
||||
{
|
||||
Base64ToByteStream (value, strlen (value), introducer.iH, 32);
|
||||
introducer.isH = true;
|
||||
}
|
||||
else if (!strcmp (key, "iexp"))
|
||||
{
|
||||
try
|
||||
@ -382,7 +362,7 @@ namespace data
|
||||
}
|
||||
if (!s) return;
|
||||
}
|
||||
if (address->transportStyle == eTransportNTCP)
|
||||
if (address->transportStyle == eTransportNTCP2)
|
||||
{
|
||||
if (isStaticKey)
|
||||
{
|
||||
@ -406,49 +386,7 @@ namespace data
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (address->transportStyle == eTransportSSU)
|
||||
{
|
||||
if (isIntroKey)
|
||||
{
|
||||
if (isHost)
|
||||
supportedTransports |= address->host.is_v4 () ? eSSUV4 : eSSUV6;
|
||||
else if (address->caps & AddressCaps::eV6)
|
||||
{
|
||||
supportedTransports |= eSSUV6;
|
||||
if (address->caps & AddressCaps::eV4) supportedTransports |= eSSUV4; // in additional to v6
|
||||
}
|
||||
else
|
||||
supportedTransports |= eSSUV4; // in case if host or 6 caps is not preasented, we assume 4
|
||||
if (address->ssu && !address->ssu->introducers.empty ())
|
||||
{
|
||||
// exclude invalid introducers
|
||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
int numValid = 0;
|
||||
for (auto& it: address->ssu->introducers)
|
||||
{
|
||||
if (!it.iExp) it.iExp = m_Timestamp/1000 + NETDB_INTRODUCEE_EXPIRATION_TIMEOUT;
|
||||
if (ts <= it.iExp && it.iPort > 0 &&
|
||||
((it.iHost.is_v4 () && address->IsV4 ()) || (it.iHost.is_v6 () && address->IsV6 ())))
|
||||
numValid++;
|
||||
else
|
||||
{
|
||||
it.iPort = 0;
|
||||
if (isV2) numValid++;
|
||||
}
|
||||
}
|
||||
if (numValid)
|
||||
m_ReachableTransports |= supportedTransports;
|
||||
else
|
||||
address->ssu->introducers.resize (0);
|
||||
}
|
||||
else if (isHost && address->port)
|
||||
{
|
||||
address->published = true;
|
||||
m_ReachableTransports |= supportedTransports;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (address->transportStyle == eTransportSSU2 || (isV2 && address->transportStyle == eTransportSSU))
|
||||
else if (address->transportStyle == eTransportSSU2 && isV2)
|
||||
{
|
||||
if (address->IsV4 ()) supportedTransports |= eSSU2V4;
|
||||
if (address->IsV6 ()) supportedTransports |= eSSU2V6;
|
||||
@ -456,62 +394,42 @@ namespace data
|
||||
{
|
||||
if (address->host.is_v4 ()) m_ReachableTransports |= eSSU2V4;
|
||||
if (address->host.is_v6 ()) m_ReachableTransports |= eSSU2V6;
|
||||
address->published = true;
|
||||
}
|
||||
if (address->transportStyle == eTransportSSU2)
|
||||
if (address->ssu && !address->ssu->introducers.empty ())
|
||||
{
|
||||
if (address->port) address->published = true;
|
||||
if (address->ssu && !address->ssu->introducers.empty ())
|
||||
{
|
||||
// exclude invalid introducers
|
||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
int numValid = 0;
|
||||
for (auto& it: address->ssu->introducers)
|
||||
{
|
||||
if (it.iTag && ts <= it.iExp)
|
||||
numValid++;
|
||||
else
|
||||
it.iTag = 0;
|
||||
}
|
||||
if (numValid)
|
||||
m_ReachableTransports |= supportedTransports;
|
||||
else
|
||||
address->ssu->introducers.resize (0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// create additional SSU2 address. TODO: remove later
|
||||
auto ssu2addr = std::make_shared<Address> ();
|
||||
ssu2addr->transportStyle = eTransportSSU2;
|
||||
ssu2addr->host = address->host; ssu2addr->port = address->port;
|
||||
ssu2addr->s = address->s; ssu2addr->i = iV2;
|
||||
ssu2addr->date = address->date; ssu2addr->caps = address->caps;
|
||||
ssu2addr->published = address->published;
|
||||
ssu2addr->ssu.reset (new SSUExt ()); ssu2addr->ssu->mtu = address->ssu->mtu;
|
||||
// exclude invalid introducers
|
||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
if (!address->ssu->introducers.empty ())
|
||||
int numValid = 0;
|
||||
for (auto& it: address->ssu->introducers)
|
||||
{
|
||||
for (const auto& introducer: address->ssu->introducers)
|
||||
if (!introducer.iPort && introducer.iHost.is_unspecified () && ts < introducer.iExp) // SSU2
|
||||
ssu2addr->ssu->introducers.push_back (introducer);
|
||||
if (!ssu2addr->ssu->introducers.empty ())
|
||||
m_ReachableTransports |= supportedTransports;
|
||||
if (it.iTag && ts < it.iExp && it.isH)
|
||||
numValid++;
|
||||
else
|
||||
it.iTag = 0;
|
||||
}
|
||||
addresses->push_back(ssu2addr);
|
||||
if (numValid)
|
||||
m_ReachableTransports |= supportedTransports;
|
||||
else
|
||||
address->ssu->introducers.resize (0);
|
||||
}
|
||||
}
|
||||
if (supportedTransports)
|
||||
{
|
||||
if (!(m_SupportedTransports & supportedTransports)) // avoid duplicates
|
||||
addresses->push_back(address);
|
||||
m_NewAddresses->push_back(address);
|
||||
m_SupportedTransports |= supportedTransports;
|
||||
}
|
||||
}
|
||||
// update addresses
|
||||
auto prev = m_Addresses;
|
||||
#if (BOOST_VERSION >= 105300)
|
||||
boost::atomic_store (&m_Addresses, addresses);
|
||||
boost::atomic_store (&m_Addresses, m_NewAddresses);
|
||||
#else
|
||||
m_Addresses = addresses; // race condition
|
||||
m_Addresses = m_NewAddresses; // race condition
|
||||
#endif
|
||||
if (prev) prev->clear ();
|
||||
m_NewAddresses = prev;
|
||||
// read peers
|
||||
uint8_t numPeers;
|
||||
s.read ((char *)&numPeers, sizeof (numPeers)); if (!s) return;
|
||||
@ -635,10 +553,10 @@ namespace data
|
||||
case CAPS_FLAG_V6:
|
||||
caps |= AddressCaps::eV6;
|
||||
break;
|
||||
case CAPS_FLAG_SSU_TESTING:
|
||||
case CAPS_FLAG_SSU2_TESTING:
|
||||
caps |= AddressCaps::eSSUTesting;
|
||||
break;
|
||||
case CAPS_FLAG_SSU_INTRODUCER:
|
||||
case CAPS_FLAG_SSU2_INTRODUCER:
|
||||
caps |= AddressCaps::eSSUIntroducer;
|
||||
break;
|
||||
default: ;
|
||||
@ -701,36 +619,13 @@ namespace data
|
||||
return l+1;
|
||||
}
|
||||
|
||||
|
||||
void RouterInfo::AddSSUAddress (const char * host, int port, const uint8_t * key, int mtu)
|
||||
{
|
||||
auto addr = std::make_shared<Address>();
|
||||
addr->host = boost::asio::ip::address::from_string (host);
|
||||
addr->port = port;
|
||||
addr->transportStyle = eTransportSSU;
|
||||
addr->published = true;
|
||||
addr->caps = i2p::data::RouterInfo::eSSUTesting | i2p::data::RouterInfo::eSSUIntroducer; // BC;
|
||||
addr->date = 0;
|
||||
addr->ssu.reset (new SSUExt ());
|
||||
addr->ssu->mtu = mtu;
|
||||
if (key)
|
||||
memcpy (addr->i, key, 32);
|
||||
else
|
||||
RAND_bytes (addr->i, 32);
|
||||
for (const auto& it: *m_Addresses) // don't insert same address twice
|
||||
if (*it == *addr) return;
|
||||
m_SupportedTransports |= addr->host.is_v6 () ? eSSUV6 : eSSUV4;
|
||||
m_ReachableTransports |= addr->host.is_v6 () ? eSSUV6 : eSSUV4;
|
||||
m_Addresses->push_back(std::move(addr));
|
||||
}
|
||||
|
||||
void RouterInfo::AddNTCP2Address (const uint8_t * staticKey, const uint8_t * iv,
|
||||
const boost::asio::ip::address& host, int port, uint8_t caps)
|
||||
{
|
||||
auto addr = std::make_shared<Address>();
|
||||
addr->host = host;
|
||||
addr->port = port;
|
||||
addr->transportStyle = eTransportNTCP;
|
||||
addr->transportStyle = eTransportNTCP2;
|
||||
addr->caps = caps;
|
||||
addr->date = 0;
|
||||
if (port) addr->published = true;
|
||||
@ -792,51 +687,6 @@ namespace data
|
||||
m_Addresses->push_back(std::move(addr));
|
||||
}
|
||||
|
||||
bool RouterInfo::AddIntroducer (const Introducer& introducer)
|
||||
{
|
||||
for (auto& addr : *m_Addresses)
|
||||
{
|
||||
if (addr->transportStyle == eTransportSSU &&
|
||||
((addr->IsV4 () && introducer.iHost.is_v4 ()) || (addr->IsV6 () && introducer.iHost.is_v6 ())))
|
||||
{
|
||||
for (auto& intro: addr->ssu->introducers)
|
||||
if (intro.iTag == introducer.iTag) return false; // already presented
|
||||
addr->ssu->introducers.push_back (introducer);
|
||||
m_ReachableTransports |= (addr->IsV4 () ? eSSUV4 : eSSUV6);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RouterInfo::RemoveIntroducer (const boost::asio::ip::udp::endpoint& e)
|
||||
{
|
||||
for (auto& addr: *m_Addresses)
|
||||
{
|
||||
if (addr->transportStyle == eTransportSSU &&
|
||||
((addr->IsV4 () && e.address ().is_v4 ()) || (addr->IsV6 () && e.address ().is_v6 ())))
|
||||
{
|
||||
for (auto it = addr->ssu->introducers.begin (); it != addr->ssu->introducers.end (); ++it)
|
||||
if (boost::asio::ip::udp::endpoint (it->iHost, it->iPort) == e)
|
||||
{
|
||||
addr->ssu->introducers.erase (it);
|
||||
if (addr->ssu->introducers.empty ())
|
||||
m_ReachableTransports &= ~(addr->IsV4 () ? eSSUV4 : eSSUV6);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RouterInfo::IsSSU (bool v4only) const
|
||||
{
|
||||
if (v4only)
|
||||
return m_SupportedTransports & eSSUV4;
|
||||
else
|
||||
return m_SupportedTransports & (eSSUV4 | eSSUV6);
|
||||
}
|
||||
|
||||
bool RouterInfo::IsNTCP2 (bool v4only) const
|
||||
{
|
||||
if (v4only)
|
||||
@ -943,24 +793,6 @@ namespace data
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetSSUAddress (bool v4only) const
|
||||
{
|
||||
return GetAddress (
|
||||
[v4only](std::shared_ptr<const RouterInfo::Address> address)->bool
|
||||
{
|
||||
return (address->transportStyle == eTransportSSU) && (!v4only || address->IsV4 ());
|
||||
});
|
||||
}
|
||||
|
||||
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetSSUV6Address () const
|
||||
{
|
||||
return GetAddress (
|
||||
[](std::shared_ptr<const RouterInfo::Address> address)->bool
|
||||
{
|
||||
return (address->transportStyle == eTransportSSU) && address->IsV6();
|
||||
});
|
||||
}
|
||||
|
||||
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetSSU2V4Address () const
|
||||
{
|
||||
return GetAddress (
|
||||
@ -1075,21 +907,10 @@ namespace data
|
||||
bool RouterInfo::IsEligibleFloodfill () const
|
||||
{
|
||||
// floodfill must be reachable by ipv4, >= 0.9.38 and not DSA
|
||||
return IsReachableBy (eNTCP2V4 | eSSUV4) && m_Version >= NETDB_MIN_FLOODFILL_VERSION &&
|
||||
return IsReachableBy (eNTCP2V4 | eSSU2V4) && m_Version >= NETDB_MIN_FLOODFILL_VERSION &&
|
||||
GetIdentity ()->GetSigningKeyType () != SIGNING_KEY_TYPE_DSA_SHA1;
|
||||
}
|
||||
|
||||
bool RouterInfo::IsPeerTesting (bool v4) const
|
||||
{
|
||||
if (!(m_SupportedTransports & (v4 ? eSSUV4 : eSSUV6))) return false;
|
||||
return (bool)GetAddress (
|
||||
[v4](std::shared_ptr<const RouterInfo::Address> address)->bool
|
||||
{
|
||||
return (address->transportStyle == eTransportSSU) && address->IsPeerTesting () &&
|
||||
((v4 && address->IsV4 ()) || (!v4 && address->IsV6 ())) && address->IsReachableSSU ();
|
||||
});
|
||||
}
|
||||
|
||||
bool RouterInfo::IsSSU2PeerTesting (bool v4) const
|
||||
{
|
||||
if (!(m_SupportedTransports & (v4 ? eSSU2V4 : eSSU2V6))) return false;
|
||||
@ -1101,17 +922,6 @@ namespace data
|
||||
});
|
||||
}
|
||||
|
||||
bool RouterInfo::IsIntroducer (bool v4) const
|
||||
{
|
||||
if (!(m_SupportedTransports & (v4 ? eSSUV4 : eSSUV6))) return false;
|
||||
return (bool)GetAddress (
|
||||
[v4](std::shared_ptr<const RouterInfo::Address> address)->bool
|
||||
{
|
||||
return (address->transportStyle == eTransportSSU) && address->IsIntroducer () &&
|
||||
((v4 && address->IsV4 ()) || (!v4 && address->IsV6 ())) && !address->host.is_unspecified ();
|
||||
});
|
||||
}
|
||||
|
||||
bool RouterInfo::IsSSU2Introducer (bool v4) const
|
||||
{
|
||||
if (!(m_SupportedTransports & (v4 ? eSSU2V4 : eSSU2V6))) return false;
|
||||
@ -1127,8 +937,7 @@ namespace data
|
||||
{
|
||||
for (auto& addr: *m_Addresses)
|
||||
{
|
||||
// TODO: implement SSU
|
||||
if (!addr->published && (addr->transportStyle == eTransportNTCP || addr->transportStyle == eTransportSSU2))
|
||||
if (!addr->published && (addr->transportStyle == eTransportNTCP2 || addr->transportStyle == eTransportSSU2))
|
||||
{
|
||||
addr->caps &= ~(eV4 | eV6);
|
||||
addr->caps |= transports;
|
||||
@ -1145,19 +954,13 @@ namespace data
|
||||
uint8_t transports = 0;
|
||||
switch (addr->transportStyle)
|
||||
{
|
||||
case eTransportNTCP:
|
||||
case eTransportNTCP2:
|
||||
if (addr->IsV4 ()) transports |= eNTCP2V4;
|
||||
if (addr->IsV6 ())
|
||||
transports |= (i2p::util::net::IsYggdrasilAddress (addr->host) ? eNTCP2V6Mesh : eNTCP2V6);
|
||||
if (addr->IsPublishedNTCP2 ())
|
||||
m_ReachableTransports |= transports;
|
||||
break;
|
||||
case eTransportSSU:
|
||||
if (addr->IsV4 ()) transports |= eSSUV4;
|
||||
if (addr->IsV6 ()) transports |= eSSUV6;
|
||||
if (addr->IsReachableSSU ())
|
||||
m_ReachableTransports |= transports;
|
||||
break;
|
||||
case eTransportSSU2:
|
||||
if (addr->IsV4 ()) transports |= eSSU2V4;
|
||||
if (addr->IsV6 ()) transports |= eSSU2V6;
|
||||
@ -1257,17 +1060,17 @@ namespace data
|
||||
const Address& address = *addr_ptr;
|
||||
// calculate cost
|
||||
uint8_t cost = 0x7f;
|
||||
if (address.transportStyle == eTransportNTCP)
|
||||
if (address.transportStyle == eTransportNTCP2)
|
||||
cost = address.published ? COST_NTCP2_PUBLISHED : COST_NTCP2_NON_PUBLISHED;
|
||||
else if (address.transportStyle == eTransportSSU)
|
||||
cost = address.published ? COST_SSU_DIRECT : COST_SSU_THROUGH_INTRODUCERS;
|
||||
else if (address.transportStyle == eTransportSSU2)
|
||||
cost = address.published ? COST_SSU2_DIRECT : COST_SSU2_NON_PUBLISHED;
|
||||
else
|
||||
continue; // skip unknown address
|
||||
s.write ((const char *)&cost, sizeof (cost));
|
||||
s.write ((const char *)&address.date, sizeof (address.date));
|
||||
std::stringstream properties;
|
||||
bool isPublished = false;
|
||||
if (address.transportStyle == eTransportNTCP)
|
||||
if (address.transportStyle == eTransportNTCP2)
|
||||
{
|
||||
if (address.IsNTCP2 ())
|
||||
{
|
||||
@ -1289,43 +1092,6 @@ namespace data
|
||||
else
|
||||
continue; // don't write NTCP address
|
||||
}
|
||||
else if (address.transportStyle == eTransportSSU)
|
||||
{
|
||||
WriteString ("SSU", s);
|
||||
// caps
|
||||
WriteString ("caps", properties);
|
||||
properties << '=';
|
||||
std::string caps;
|
||||
if (address.IsPeerTesting ()) caps += CAPS_FLAG_SSU_TESTING;
|
||||
if (address.host.is_v4 ())
|
||||
{
|
||||
if (address.published)
|
||||
{
|
||||
isPublished = true;
|
||||
if (address.IsIntroducer ()) caps += CAPS_FLAG_SSU_INTRODUCER;
|
||||
}
|
||||
else
|
||||
caps += CAPS_FLAG_V4;
|
||||
}
|
||||
else if (address.host.is_v6 ())
|
||||
{
|
||||
if (address.published)
|
||||
{
|
||||
isPublished = true;
|
||||
if (address.IsIntroducer ()) caps += CAPS_FLAG_SSU_INTRODUCER;
|
||||
}
|
||||
else
|
||||
caps += CAPS_FLAG_V6;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (address.IsV4 ()) caps += CAPS_FLAG_V4;
|
||||
if (address.IsV6 ()) caps += CAPS_FLAG_V6;
|
||||
if (caps.empty ()) caps += CAPS_FLAG_V4;
|
||||
}
|
||||
WriteString (caps, properties);
|
||||
properties << ';';
|
||||
}
|
||||
else if (address.transportStyle == eTransportSSU2)
|
||||
{
|
||||
WriteString ("SSU2", s);
|
||||
@ -1334,8 +1100,8 @@ namespace data
|
||||
if (address.published)
|
||||
{
|
||||
isPublished = true;
|
||||
if (address.IsPeerTesting ()) caps += CAPS_FLAG_SSU_TESTING;
|
||||
if (address.IsIntroducer ()) caps += CAPS_FLAG_SSU_INTRODUCER;
|
||||
if (address.IsPeerTesting ()) caps += CAPS_FLAG_SSU2_TESTING;
|
||||
if (address.IsIntroducer ()) caps += CAPS_FLAG_SSU2_INTRODUCER;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1368,7 +1134,7 @@ namespace data
|
||||
size_t len = address.IsSSU2 () ? 32 : 16;
|
||||
WriteString (address.i.ToBase64 (len), properties); properties << ';';
|
||||
}
|
||||
if (address.transportStyle == eTransportSSU || address.IsSSU2 ())
|
||||
if (address.transportStyle == eTransportSSU2)
|
||||
{
|
||||
// write introducers if any
|
||||
if (address.ssu && !address.ssu->introducers.empty())
|
||||
@ -1385,45 +1151,18 @@ namespace data
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (address.transportStyle == eTransportSSU)
|
||||
{
|
||||
i = 0;
|
||||
for (const auto& introducer: address.ssu->introducers)
|
||||
{
|
||||
WriteString ("ihost" + boost::lexical_cast<std::string>(i), properties);
|
||||
properties << '=';
|
||||
WriteString (introducer.iHost.to_string (), properties);
|
||||
properties << ';';
|
||||
i++;
|
||||
}
|
||||
}
|
||||
i = 0;
|
||||
for (const auto& introducer: address.ssu->introducers)
|
||||
{
|
||||
if (address.IsSSU2 ())
|
||||
WriteString ("ih" + boost::lexical_cast<std::string>(i), properties);
|
||||
else
|
||||
WriteString ("ikey" + boost::lexical_cast<std::string>(i), properties);
|
||||
WriteString ("ih" + boost::lexical_cast<std::string>(i), properties);
|
||||
properties << '=';
|
||||
char value[64];
|
||||
size_t l = ByteStreamToBase64 (introducer.iKey, 32, value, 64);
|
||||
size_t l = ByteStreamToBase64 (introducer.iH, 32, value, 64);
|
||||
value[l] = 0;
|
||||
WriteString (value, properties);
|
||||
properties << ';';
|
||||
i++;
|
||||
}
|
||||
if (address.transportStyle == eTransportSSU)
|
||||
{
|
||||
i = 0;
|
||||
for (const auto& introducer: address.ssu->introducers)
|
||||
{
|
||||
WriteString ("iport" + boost::lexical_cast<std::string>(i), properties);
|
||||
properties << '=';
|
||||
WriteString (boost::lexical_cast<std::string>(introducer.iPort), properties);
|
||||
properties << ';';
|
||||
i++;
|
||||
}
|
||||
}
|
||||
i = 0;
|
||||
for (const auto& introducer: address.ssu->introducers)
|
||||
{
|
||||
@ -1435,18 +1174,8 @@ namespace data
|
||||
}
|
||||
}
|
||||
}
|
||||
if (address.transportStyle == eTransportSSU)
|
||||
{
|
||||
// write intro key
|
||||
WriteString ("key", properties);
|
||||
properties << '=';
|
||||
char value[64];
|
||||
size_t l = ByteStreamToBase64 (address.i, 32, value, 64);
|
||||
value[l] = 0;
|
||||
WriteString (value, properties);
|
||||
properties << ';';
|
||||
}
|
||||
if (address.transportStyle == eTransportSSU || address.IsSSU2 ())
|
||||
|
||||
if (address.transportStyle == eTransportSSU2)
|
||||
{
|
||||
// write mtu
|
||||
if (address.ssu && address.ssu->mtu)
|
||||
@ -1457,7 +1186,7 @@ namespace data
|
||||
properties << ';';
|
||||
}
|
||||
}
|
||||
if ((isPublished || (address.ssu && !address.IsSSU2 ())) && address.port)
|
||||
if (isPublished && address.port)
|
||||
{
|
||||
WriteString ("port", properties);
|
||||
properties << '=';
|
||||
@ -1549,7 +1278,7 @@ namespace data
|
||||
if (addr->IsSSU2 () && ((v4 && addr->IsV4 ()) || (!v4 && addr->IsV6 ())))
|
||||
{
|
||||
for (auto it = addr->ssu->introducers.begin (); it != addr->ssu->introducers.end (); ++it)
|
||||
if (h == it->iKey)
|
||||
if (h == it->iH)
|
||||
{
|
||||
addr->ssu->introducers.erase (it);
|
||||
if (addr->ssu->introducers.empty ())
|
||||
|
@ -47,14 +47,12 @@ namespace data
|
||||
|
||||
const char CAPS_FLAG_V4 = '4';
|
||||
const char CAPS_FLAG_V6 = '6';
|
||||
const char CAPS_FLAG_SSU_TESTING = 'B';
|
||||
const char CAPS_FLAG_SSU_INTRODUCER = 'C';
|
||||
const char CAPS_FLAG_SSU2_TESTING = 'B';
|
||||
const char CAPS_FLAG_SSU2_INTRODUCER = 'C';
|
||||
|
||||
const uint8_t COST_NTCP2_PUBLISHED = 3;
|
||||
const uint8_t COST_NTCP2_NON_PUBLISHED = 14;
|
||||
const uint8_t COST_SSU2_DIRECT = 8;
|
||||
const uint8_t COST_SSU_DIRECT = 9;
|
||||
const uint8_t COST_SSU_THROUGH_INTRODUCERS = 11;
|
||||
const uint8_t COST_SSU2_NON_PUBLISHED = 15;
|
||||
|
||||
const size_t MAX_RI_BUFFER_SIZE = 3072; // if RouterInfo exceeds 3K we consider it as malformed, might extend later
|
||||
@ -66,11 +64,9 @@ namespace data
|
||||
{
|
||||
eNTCP2V4 = 0x01,
|
||||
eNTCP2V6 = 0x02,
|
||||
eSSUV4 = 0x04,
|
||||
eSSUV6 = 0x08,
|
||||
eSSU2V4 = 0x04,
|
||||
eSSU2V6 = 0x08,
|
||||
eNTCP2V6Mesh = 0x10,
|
||||
eSSU2V4 = 0x20,
|
||||
eSSU2V6 = 0x40,
|
||||
eAllTransports = 0xFF
|
||||
};
|
||||
typedef uint8_t CompatibleTransports;
|
||||
@ -96,20 +92,17 @@ namespace data
|
||||
enum TransportStyle
|
||||
{
|
||||
eTransportUnknown = 0,
|
||||
eTransportNTCP,
|
||||
eTransportSSU,
|
||||
eTransportNTCP2,
|
||||
eTransportSSU2
|
||||
};
|
||||
|
||||
typedef Tag<32> IntroKey; // should be castable to MacKey and AESKey
|
||||
struct Introducer
|
||||
{
|
||||
Introducer (): iPort (0), iExp (0) {};
|
||||
boost::asio::ip::address iHost;
|
||||
int iPort;
|
||||
IntroKey iKey; // or ih for SSU2
|
||||
Introducer (): iTag (0), iExp (0), isH (false) {};
|
||||
IdentHash iH;
|
||||
uint32_t iTag;
|
||||
uint32_t iExp;
|
||||
bool isH; // TODO: remove later
|
||||
};
|
||||
|
||||
struct SSUExt
|
||||
@ -146,7 +139,7 @@ namespace data
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
bool IsNTCP2 () const { return transportStyle == eTransportNTCP; };
|
||||
bool IsNTCP2 () const { return transportStyle == eTransportNTCP2; };
|
||||
bool IsSSU2 () const { return transportStyle == eTransportSSU2; };
|
||||
bool IsPublishedNTCP2 () const { return IsNTCP2 () && published; };
|
||||
bool IsReachableSSU () const { return (bool)ssu && (published || UsesIntroducer ()); };
|
||||
@ -188,34 +181,27 @@ namespace data
|
||||
std::shared_ptr<const Address> GetSSU2AddressWithStaticKey (const uint8_t * key, bool isV6) const;
|
||||
std::shared_ptr<const Address> GetPublishedNTCP2V4Address () const;
|
||||
std::shared_ptr<const Address> GetPublishedNTCP2V6Address () const;
|
||||
std::shared_ptr<const Address> GetSSUAddress (bool v4only = true) const;
|
||||
std::shared_ptr<const Address> GetSSUV6Address () const;
|
||||
std::shared_ptr<const Address> GetYggdrasilAddress () const;
|
||||
std::shared_ptr<const Address> GetSSU2V4Address () const;
|
||||
std::shared_ptr<const Address> GetSSU2V6Address () const;
|
||||
std::shared_ptr<const Address> GetSSU2Address (bool v4) const;
|
||||
|
||||
void AddSSUAddress (const char * host, int port, const uint8_t * key, int mtu = 0);
|
||||
void AddNTCP2Address (const uint8_t * staticKey, const uint8_t * iv,
|
||||
const boost::asio::ip::address& host = boost::asio::ip::address(), int port = 0, uint8_t caps = 0);
|
||||
void AddSSU2Address (const uint8_t * staticKey, const uint8_t * introKey, uint8_t caps = 0); // non published
|
||||
void AddSSU2Address (const uint8_t * staticKey, const uint8_t * introKey,
|
||||
const boost::asio::ip::address& host, int port); // published
|
||||
bool AddIntroducer (const Introducer& introducer);
|
||||
bool RemoveIntroducer (const boost::asio::ip::udp::endpoint& e);
|
||||
void SetUnreachableAddressesTransportCaps (uint8_t transports); // bitmask of AddressCaps
|
||||
void UpdateSupportedTransports ();
|
||||
bool IsFloodfill () const { return m_Caps & Caps::eFloodfill; };
|
||||
bool IsReachable () const { return m_Caps & Caps::eReachable; };
|
||||
bool IsECIES () const { return m_RouterIdentity->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD; };
|
||||
bool IsSSU (bool v4only = true) const;
|
||||
bool IsSSUV6 () const { return m_SupportedTransports & eSSUV6; };
|
||||
bool IsNTCP2 (bool v4only = true) const;
|
||||
bool IsNTCP2V6 () const { return m_SupportedTransports & eNTCP2V6; };
|
||||
bool IsSSU2V4 () const { return m_SupportedTransports & eSSU2V4; };
|
||||
bool IsSSU2V6 () const { return m_SupportedTransports & eSSU2V6; };
|
||||
bool IsV6 () const { return m_SupportedTransports & (eSSUV6 | eNTCP2V6 | eSSU2V6); };
|
||||
bool IsV4 () const { return m_SupportedTransports & (eSSUV4 | eNTCP2V4 | eSSU2V4); };
|
||||
bool IsV6 () const { return m_SupportedTransports & (eNTCP2V6 | eSSU2V6); };
|
||||
bool IsV4 () const { return m_SupportedTransports & (eNTCP2V4 | eSSU2V4); };
|
||||
bool IsMesh () const { return m_SupportedTransports & eNTCP2V6Mesh; };
|
||||
void EnableV6 ();
|
||||
void DisableV6 ();
|
||||
@ -232,9 +218,7 @@ namespace data
|
||||
bool IsHighBandwidth () const { return m_Caps & RouterInfo::eHighBandwidth; };
|
||||
bool IsExtraBandwidth () const { return m_Caps & RouterInfo::eExtraBandwidth; };
|
||||
bool IsEligibleFloodfill () const;
|
||||
bool IsPeerTesting (bool v4) const;
|
||||
bool IsSSU2PeerTesting (bool v4) const;
|
||||
bool IsIntroducer (bool v4) const;
|
||||
bool IsSSU2Introducer (bool v4) const;
|
||||
|
||||
uint8_t GetCaps () const { return m_Caps; };
|
||||
@ -298,7 +282,7 @@ namespace data
|
||||
std::shared_ptr<Buffer> m_Buffer;
|
||||
size_t m_BufferLen;
|
||||
uint64_t m_Timestamp;
|
||||
boost::shared_ptr<Addresses> m_Addresses; // TODO: use std::shared_ptr and std::atomic_store for gcc >= 4.9
|
||||
boost::shared_ptr<Addresses> m_Addresses, m_NewAddresses; // TODO: use std::shared_ptr and std::atomic_store for gcc >= 4.9
|
||||
bool m_IsUpdated, m_IsUnreachable;
|
||||
CompatibleTransports m_SupportedTransports, m_ReachableTransports;
|
||||
uint8_t m_Caps;
|
||||
@ -311,6 +295,7 @@ namespace data
|
||||
public:
|
||||
|
||||
LocalRouterInfo () = default;
|
||||
LocalRouterInfo (const std::string& fullPath): RouterInfo (fullPath) {};
|
||||
void CreateBuffer (const PrivateKeys& privateKeys);
|
||||
void UpdateCaps (uint8_t caps);
|
||||
|
||||
|
996
libi2pd/SSU.cpp
996
libi2pd/SSU.cpp
@ -1,996 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
* See full license text in LICENSE file at top of project tree
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "Log.h"
|
||||
#include "Timestamp.h"
|
||||
#include "RouterContext.h"
|
||||
#include "NetDb.hpp"
|
||||
#include "Config.h"
|
||||
#include "util.h"
|
||||
#include "SSU.h"
|
||||
|
||||
#if defined(__linux__) && !defined(_NETINET_IN_H)
|
||||
#include <linux/in6.h>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <boost/winapi/error_codes.hpp>
|
||||
#endif
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace transport
|
||||
{
|
||||
SSUServer::SSUServer (int port):
|
||||
m_IsRunning(false), m_Thread (nullptr),
|
||||
m_ReceiversThread (nullptr), m_ReceiversThreadV6 (nullptr), m_Work (m_Service),
|
||||
m_ReceiversWork (m_ReceiversService), m_ReceiversWorkV6 (m_ReceiversServiceV6),
|
||||
m_Endpoint (boost::asio::ip::udp::v4 (), port), m_EndpointV6 (boost::asio::ip::udp::v6 (), port),
|
||||
m_Socket (m_ReceiversService), m_SocketV6 (m_ReceiversServiceV6),
|
||||
m_IntroducersUpdateTimer (m_Service), m_IntroducersUpdateTimerV6 (m_Service),
|
||||
m_PeerTestsCleanupTimer (m_Service), m_TerminationTimer (m_Service), m_TerminationTimerV6 (m_Service),
|
||||
m_IsSyncClockFromPeers (true)
|
||||
{
|
||||
}
|
||||
|
||||
SSUServer::~SSUServer ()
|
||||
{
|
||||
}
|
||||
|
||||
void SSUServer::OpenSocket ()
|
||||
{
|
||||
try
|
||||
{
|
||||
m_Socket.open (boost::asio::ip::udp::v4());
|
||||
m_Socket.set_option (boost::asio::socket_base::receive_buffer_size (SSU_SOCKET_RECEIVE_BUFFER_SIZE));
|
||||
m_Socket.set_option (boost::asio::socket_base::send_buffer_size (SSU_SOCKET_SEND_BUFFER_SIZE));
|
||||
m_Socket.bind (m_Endpoint);
|
||||
LogPrint (eLogInfo, "SSU: Start listening v4 port ", m_Endpoint.port());
|
||||
}
|
||||
catch ( std::exception & ex )
|
||||
{
|
||||
LogPrint (eLogError, "SSU: Failed to bind to v4 port ", m_Endpoint.port(), ": ", ex.what());
|
||||
ThrowFatal ("Unable to start IPv4 SSU transport at port ", m_Endpoint.port(), ": ", ex.what ());
|
||||
}
|
||||
}
|
||||
|
||||
void SSUServer::OpenSocketV6 ()
|
||||
{
|
||||
try
|
||||
{
|
||||
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 (SSU_SOCKET_RECEIVE_BUFFER_SIZE));
|
||||
m_SocketV6.set_option (boost::asio::socket_base::send_buffer_size (SSU_SOCKET_SEND_BUFFER_SIZE));
|
||||
#if defined(__linux__) && !defined(_NETINET_IN_H)
|
||||
if (m_EndpointV6.address() == boost::asio::ip::address().from_string("::")) // only if not binded to address
|
||||
{
|
||||
// Set preference to use public IPv6 address -- tested on linux, not works on windows, and not tested on others
|
||||
#if (BOOST_VERSION >= 105500)
|
||||
typedef boost::asio::detail::socket_option::integer<BOOST_ASIO_OS_DEF(IPPROTO_IPV6), IPV6_ADDR_PREFERENCES> ipv6PreferAddr;
|
||||
#else
|
||||
typedef boost::asio::detail::socket_option::integer<IPPROTO_IPV6, IPV6_ADDR_PREFERENCES> ipv6PreferAddr;
|
||||
#endif
|
||||
m_SocketV6.set_option (ipv6PreferAddr(IPV6_PREFER_SRC_PUBLIC | IPV6_PREFER_SRC_HOME | IPV6_PREFER_SRC_NONCGA));
|
||||
}
|
||||
#endif
|
||||
m_SocketV6.bind (m_EndpointV6);
|
||||
LogPrint (eLogInfo, "SSU: Start listening v6 port ", m_EndpointV6.port());
|
||||
}
|
||||
catch ( std::exception & ex )
|
||||
{
|
||||
LogPrint (eLogError, "SSU: Failed to bind to v6 port ", m_EndpointV6.port(), ": ", ex.what());
|
||||
ThrowFatal ("Unable to start IPv6 SSU transport at port ", m_Endpoint.port(), ": ", ex.what ());
|
||||
}
|
||||
}
|
||||
|
||||
void SSUServer::Start ()
|
||||
{
|
||||
i2p::config::GetOption("nettime.frompeers", m_IsSyncClockFromPeers);
|
||||
m_IsRunning = true;
|
||||
m_Thread = new std::thread (std::bind (&SSUServer::Run, this));
|
||||
if (context.SupportsV4 ())
|
||||
{
|
||||
OpenSocket ();
|
||||
m_ReceiversThread = new std::thread (std::bind (&SSUServer::RunReceivers, this));
|
||||
m_ReceiversService.post (std::bind (&SSUServer::Receive, this));
|
||||
ScheduleTermination ();
|
||||
ScheduleIntroducersUpdateTimer (); // wait for 30 seconds and decide if we need introducers
|
||||
}
|
||||
if (context.SupportsV6 ())
|
||||
{
|
||||
OpenSocketV6 ();
|
||||
m_ReceiversThreadV6 = new std::thread (std::bind (&SSUServer::RunReceiversV6, this));
|
||||
m_ReceiversServiceV6.post (std::bind (&SSUServer::ReceiveV6, this));
|
||||
ScheduleTerminationV6 ();
|
||||
ScheduleIntroducersUpdateTimerV6 (); // wait for 30 seconds and decide if we need introducers
|
||||
}
|
||||
SchedulePeerTestsCleanupTimer ();
|
||||
}
|
||||
|
||||
void SSUServer::Stop ()
|
||||
{
|
||||
DeleteAllSessions ();
|
||||
m_IsRunning = false;
|
||||
m_TerminationTimer.cancel ();
|
||||
m_TerminationTimerV6.cancel ();
|
||||
m_IntroducersUpdateTimer.cancel ();
|
||||
m_IntroducersUpdateTimerV6.cancel ();
|
||||
m_Service.stop ();
|
||||
m_Socket.close ();
|
||||
m_SocketV6.close ();
|
||||
m_ReceiversService.stop ();
|
||||
m_ReceiversServiceV6.stop ();
|
||||
if (m_ReceiversThread)
|
||||
{
|
||||
m_ReceiversThread->join ();
|
||||
delete m_ReceiversThread;
|
||||
m_ReceiversThread = nullptr;
|
||||
}
|
||||
if (m_ReceiversThreadV6)
|
||||
{
|
||||
m_ReceiversThreadV6->join ();
|
||||
delete m_ReceiversThreadV6;
|
||||
m_ReceiversThreadV6 = nullptr;
|
||||
}
|
||||
if (m_Thread)
|
||||
{
|
||||
m_Thread->join ();
|
||||
delete m_Thread;
|
||||
m_Thread = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void SSUServer::Run ()
|
||||
{
|
||||
i2p::util::SetThreadName("SSU");
|
||||
|
||||
while (m_IsRunning)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_Service.run ();
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint (eLogError, "SSU: Server runtime exception: ", ex.what ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SSUServer::RunReceivers ()
|
||||
{
|
||||
i2p::util::SetThreadName("SSUv4");
|
||||
|
||||
while (m_IsRunning)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_ReceiversService.run ();
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint (eLogError, "SSU: Receivers runtime exception: ", ex.what ());
|
||||
if (m_IsRunning)
|
||||
{
|
||||
// restart socket
|
||||
m_Socket.close ();
|
||||
OpenSocket ();
|
||||
Receive ();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SSUServer::RunReceiversV6 ()
|
||||
{
|
||||
i2p::util::SetThreadName("SSUv6");
|
||||
|
||||
while (m_IsRunning)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_ReceiversServiceV6.run ();
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint (eLogError, "SSU: v6 receivers runtime exception: ", ex.what ());
|
||||
if (m_IsRunning)
|
||||
{
|
||||
m_SocketV6.close ();
|
||||
OpenSocketV6 ();
|
||||
ReceiveV6 ();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SSUServer::SetLocalAddress (const boost::asio::ip::address& localAddress)
|
||||
{
|
||||
if (localAddress.is_v6 ())
|
||||
m_EndpointV6.address (localAddress);
|
||||
else if (localAddress.is_v4 ())
|
||||
m_Endpoint.address (localAddress);
|
||||
}
|
||||
|
||||
void SSUServer::AddRelay (uint32_t tag, std::shared_ptr<SSUSession> relay)
|
||||
{
|
||||
m_Relays.emplace (tag, relay);
|
||||
}
|
||||
|
||||
void SSUServer::RemoveRelay (uint32_t tag)
|
||||
{
|
||||
m_Relays.erase (tag);
|
||||
}
|
||||
|
||||
std::shared_ptr<SSUSession> SSUServer::FindRelaySession (uint32_t tag)
|
||||
{
|
||||
auto it = m_Relays.find (tag);
|
||||
if (it != m_Relays.end ())
|
||||
{
|
||||
if (it->second->GetState () == eSessionStateEstablished)
|
||||
return it->second;
|
||||
else
|
||||
m_Relays.erase (it);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void SSUServer::Send (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& to)
|
||||
{
|
||||
boost::system::error_code ec;
|
||||
if (to.protocol () == boost::asio::ip::udp::v4())
|
||||
m_Socket.send_to (boost::asio::buffer (buf, len), to, 0, ec);
|
||||
else
|
||||
m_SocketV6.send_to (boost::asio::buffer (buf, len), to, 0, ec);
|
||||
|
||||
if (ec)
|
||||
{
|
||||
LogPrint (eLogError, "SSU: Send exception: ", ec.message (), " while trying to send data to ", to.address (), ":", to.port (), " (length: ", len, ")");
|
||||
}
|
||||
}
|
||||
|
||||
void SSUServer::Receive ()
|
||||
{
|
||||
SSUPacket * packet = m_PacketsPool.AcquireMt ();
|
||||
m_Socket.async_receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V4), packet->from,
|
||||
std::bind (&SSUServer::HandleReceivedFrom, this, std::placeholders::_1, std::placeholders::_2, packet));
|
||||
}
|
||||
|
||||
void SSUServer::ReceiveV6 ()
|
||||
{
|
||||
SSUPacket * packet = m_PacketsPool.AcquireMt ();
|
||||
m_SocketV6.async_receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V6), packet->from,
|
||||
std::bind (&SSUServer::HandleReceivedFromV6, this, std::placeholders::_1, std::placeholders::_2, packet));
|
||||
}
|
||||
|
||||
void SSUServer::HandleReceivedFrom (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet)
|
||||
{
|
||||
if (!ecode
|
||||
|| ecode == boost::asio::error::connection_refused
|
||||
|| ecode == boost::asio::error::connection_reset
|
||||
|| ecode == boost::asio::error::network_unreachable
|
||||
|| ecode == boost::asio::error::host_unreachable
|
||||
#ifdef _WIN32 // windows can throw WinAPI error, which is not handled by ASIO
|
||||
|| ecode.value() == boost::winapi::ERROR_CONNECTION_REFUSED_
|
||||
|| ecode.value() == boost::winapi::ERROR_NETWORK_UNREACHABLE_
|
||||
|| ecode.value() == boost::winapi::ERROR_HOST_UNREACHABLE_
|
||||
#endif
|
||||
)
|
||||
// just try continue reading when received ICMP response otherwise socket can crash,
|
||||
// but better to find out which host were sent it and mark that router as unreachable
|
||||
{
|
||||
packet->len = bytes_transferred;
|
||||
std::vector<SSUPacket *> packets;
|
||||
packets.push_back (packet);
|
||||
|
||||
boost::system::error_code ec;
|
||||
size_t moreBytes = m_Socket.available(ec);
|
||||
if (!ec)
|
||||
{
|
||||
while (moreBytes && packets.size () < 25)
|
||||
{
|
||||
packet = m_PacketsPool.AcquireMt ();
|
||||
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: code ", ec.value(), ": ", ec.message ());
|
||||
m_PacketsPool.ReleaseMt (packet);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_Service.post (std::bind (&SSUServer::HandleReceivedPackets, this, packets, &m_Sessions));
|
||||
Receive ();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_PacketsPool.ReleaseMt (packet);
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
LogPrint (eLogError, "SSU: Receive error: code ", ecode.value(), ": ", ecode.message ());
|
||||
m_Socket.close ();
|
||||
OpenSocket ();
|
||||
Receive ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SSUServer::HandleReceivedFromV6 (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet)
|
||||
{
|
||||
if (!ecode
|
||||
|| ecode == boost::asio::error::connection_refused
|
||||
|| ecode == boost::asio::error::connection_reset
|
||||
|| ecode == boost::asio::error::network_unreachable
|
||||
|| ecode == boost::asio::error::host_unreachable
|
||||
#ifdef _WIN32 // windows can throw WinAPI error, which is not handled by ASIO
|
||||
|| ecode.value() == boost::winapi::ERROR_CONNECTION_REFUSED_
|
||||
|| ecode.value() == boost::winapi::ERROR_NETWORK_UNREACHABLE_
|
||||
|| ecode.value() == boost::winapi::ERROR_HOST_UNREACHABLE_
|
||||
#endif
|
||||
)
|
||||
// just try continue reading when received ICMP response otherwise socket can crash,
|
||||
// but better to find out which host were sent it and mark that router as unreachable
|
||||
{
|
||||
packet->len = bytes_transferred;
|
||||
std::vector<SSUPacket *> packets;
|
||||
packets.push_back (packet);
|
||||
|
||||
boost::system::error_code ec;
|
||||
size_t moreBytes = m_SocketV6.available (ec);
|
||||
if (!ec)
|
||||
{
|
||||
while (moreBytes && packets.size () < 25)
|
||||
{
|
||||
packet = m_PacketsPool.AcquireMt ();
|
||||
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: code ", ec.value(), ": ", ec.message ());
|
||||
m_PacketsPool.ReleaseMt (packet);;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_Service.post (std::bind (&SSUServer::HandleReceivedPackets, this, packets, &m_SessionsV6));
|
||||
ReceiveV6 ();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_PacketsPool.ReleaseMt (packet);
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
LogPrint (eLogError, "SSU: v6 receive error: code ", ecode.value(), ": ", ecode.message ());
|
||||
m_SocketV6.close ();
|
||||
OpenSocketV6 ();
|
||||
ReceiveV6 ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SSUServer::HandleReceivedPackets (std::vector<SSUPacket *> packets,
|
||||
std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<SSUSession> > * sessions)
|
||||
{
|
||||
if (!m_IsRunning) return;
|
||||
std::shared_ptr<SSUSession> session;
|
||||
for (auto& packet: packets)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!session || session->GetRemoteEndpoint () != packet->from) // we received packet for other session than previous
|
||||
{
|
||||
if (session)
|
||||
{
|
||||
session->FlushData ();
|
||||
session = nullptr;
|
||||
}
|
||||
auto it = sessions->find (packet->from);
|
||||
if (it != sessions->end ())
|
||||
session = it->second;
|
||||
if (!session && packet->len > 0)
|
||||
{
|
||||
session = std::make_shared<SSUSession> (*this, packet->from);
|
||||
session->WaitForConnect ();
|
||||
(*sessions)[packet->from] = session;
|
||||
LogPrint (eLogDebug, "SSU: New session from ", packet->from.address ().to_string (), ":", packet->from.port (), " created");
|
||||
}
|
||||
}
|
||||
if (session)
|
||||
session->ProcessNextMessage (packet->buf, packet->len, packet->from);
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint (eLogError, "SSU: HandleReceivedPackets ", ex.what ());
|
||||
if (session) session->FlushData ();
|
||||
session = nullptr;
|
||||
}
|
||||
}
|
||||
m_PacketsPool.ReleaseMt (packets);
|
||||
if (session) session->FlushData ();
|
||||
}
|
||||
|
||||
std::shared_ptr<SSUSession> SSUServer::FindSession (const boost::asio::ip::udp::endpoint& e) const
|
||||
{
|
||||
auto& sessions = e.address ().is_v6 () ? m_SessionsV6 : m_Sessions;
|
||||
auto it = sessions.find (e);
|
||||
if (it != sessions.end ())
|
||||
return it->second;
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool SSUServer::CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest, bool v4only)
|
||||
{
|
||||
auto address = router->GetSSUAddress (v4only || !context.SupportsV6 ());
|
||||
if (address)
|
||||
return CreateSession (router, address, peerTest);
|
||||
else
|
||||
LogPrint (eLogWarning, "SSU: Router ", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()), " doesn't have SSU address");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SSUServer::CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router,
|
||||
std::shared_ptr<const i2p::data::RouterInfo::Address> address, bool peerTest)
|
||||
{
|
||||
if (router && address)
|
||||
{
|
||||
if (address->UsesIntroducer ())
|
||||
m_Service.post (std::bind (&SSUServer::CreateSessionThroughIntroducer, this, router, address, peerTest)); // always V4 thread
|
||||
else
|
||||
{
|
||||
if (address->host.is_unspecified () || !address->port) return false;
|
||||
boost::asio::ip::udp::endpoint remoteEndpoint (address->host, address->port);
|
||||
m_Service.post (std::bind (&SSUServer::CreateDirectSession, this, router, remoteEndpoint, peerTest));
|
||||
}
|
||||
}
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void SSUServer::CreateDirectSession (std::shared_ptr<const i2p::data::RouterInfo> router, boost::asio::ip::udp::endpoint remoteEndpoint, bool peerTest)
|
||||
{
|
||||
auto& sessions = remoteEndpoint.address ().is_v6 () ? m_SessionsV6 : m_Sessions;
|
||||
auto it = sessions.find (remoteEndpoint);
|
||||
if (it != sessions.end ())
|
||||
{
|
||||
auto session = it->second;
|
||||
if (peerTest && session->GetState () == eSessionStateEstablished)
|
||||
session->SendPeerTest ();
|
||||
}
|
||||
else
|
||||
{
|
||||
// otherwise create new session
|
||||
auto session = std::make_shared<SSUSession> (*this, remoteEndpoint, router, peerTest);
|
||||
sessions[remoteEndpoint] = session;
|
||||
|
||||
// connect
|
||||
LogPrint (eLogDebug, "SSU: Creating new session to [", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()), "] ",
|
||||
remoteEndpoint.address ().to_string (), ":", remoteEndpoint.port ());
|
||||
session->Connect ();
|
||||
}
|
||||
}
|
||||
|
||||
void SSUServer::CreateSessionThroughIntroducer (std::shared_ptr<const i2p::data::RouterInfo> router,
|
||||
std::shared_ptr<const i2p::data::RouterInfo::Address> address, bool peerTest)
|
||||
{
|
||||
if (router && address && address->UsesIntroducer ())
|
||||
{
|
||||
if (address->IsV4 () && !i2p::context.SupportsV4 ()) return;
|
||||
if (address->IsV6 () && !i2p::context.SupportsV6 ()) return;
|
||||
if (!address->host.is_unspecified () && address->port)
|
||||
{
|
||||
// we rarely come here
|
||||
auto& sessions = address->host.is_v6 () ? m_SessionsV6 : m_Sessions;
|
||||
boost::asio::ip::udp::endpoint remoteEndpoint (address->host, address->port);
|
||||
auto it = sessions.find (remoteEndpoint);
|
||||
// check if session is presented already
|
||||
if (it != sessions.end ())
|
||||
{
|
||||
auto session = it->second;
|
||||
if (peerTest && session->GetState () == eSessionStateEstablished)
|
||||
session->SendPeerTest ();
|
||||
return;
|
||||
}
|
||||
}
|
||||
// create new session
|
||||
int numIntroducers = address->ssu->introducers.size ();
|
||||
if (numIntroducers > 0)
|
||||
{
|
||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
std::shared_ptr<SSUSession> introducerSession;
|
||||
const i2p::data::RouterInfo::Introducer * introducer = nullptr;
|
||||
// we might have a session to introducer already
|
||||
auto offset = rand ();
|
||||
for (int i = 0; i < numIntroducers; i++)
|
||||
{
|
||||
auto intr = &(address->ssu->introducers[(offset + i)%numIntroducers]);
|
||||
if (!intr->iPort) continue; // skip invalid introducer
|
||||
if (intr->iExp > 0 && ts > intr->iExp) continue; // skip expired introducer
|
||||
boost::asio::ip::udp::endpoint ep (intr->iHost, intr->iPort);
|
||||
if (ep.address ().is_v4 () && address->IsV4 ()) // ipv4
|
||||
{
|
||||
if (!introducer) introducer = intr;
|
||||
auto it = m_Sessions.find (ep);
|
||||
if (it != m_Sessions.end ())
|
||||
{
|
||||
introducerSession = it->second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ep.address ().is_v6 () && address->IsV6 ()) // ipv6
|
||||
{
|
||||
if (!introducer) introducer = intr;
|
||||
auto it = m_SessionsV6.find (ep);
|
||||
if (it != m_SessionsV6.end ())
|
||||
{
|
||||
introducerSession = it->second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!introducer)
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU: Can't connect to unreachable router and no compatibe non-expired introducers presented");
|
||||
return;
|
||||
}
|
||||
|
||||
if (introducerSession) // session found
|
||||
LogPrint (eLogWarning, "SSU: Session to introducer already exists");
|
||||
else // create new
|
||||
{
|
||||
LogPrint (eLogDebug, "SSU: Creating new session to introducer ", introducer->iHost);
|
||||
boost::asio::ip::udp::endpoint introducerEndpoint (introducer->iHost, introducer->iPort);
|
||||
introducerSession = std::make_shared<SSUSession> (*this, introducerEndpoint, router);
|
||||
if (introducerEndpoint.address ().is_v4 ())
|
||||
m_Sessions[introducerEndpoint] = introducerSession;
|
||||
else if (introducerEndpoint.address ().is_v6 ())
|
||||
m_SessionsV6[introducerEndpoint] = introducerSession;
|
||||
}
|
||||
if (!address->host.is_unspecified () && address->port)
|
||||
{
|
||||
// create session
|
||||
boost::asio::ip::udp::endpoint remoteEndpoint (address->host, address->port);
|
||||
auto session = std::make_shared<SSUSession> (*this, remoteEndpoint, router, peerTest);
|
||||
if (address->host.is_v4 ())
|
||||
m_Sessions[remoteEndpoint] = session;
|
||||
else if (address->host.is_v6 ())
|
||||
m_SessionsV6[remoteEndpoint] = session;
|
||||
|
||||
// introduce
|
||||
LogPrint (eLogInfo, "SSU: Introduce new session to [", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()),
|
||||
"] through introducer ", introducer->iHost, ":", introducer->iPort);
|
||||
session->WaitForIntroduction ();
|
||||
if ((address->host.is_v4 () && i2p::context.GetStatus () == eRouterStatusFirewalled) ||
|
||||
(address->host.is_v6 () && i2p::context.GetStatusV6 () == eRouterStatusFirewalled))
|
||||
{
|
||||
uint8_t buf[1];
|
||||
Send (buf, 0, remoteEndpoint); // send HolePunch
|
||||
}
|
||||
}
|
||||
introducerSession->Introduce (*introducer, router);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "SSU: Can't connect to unreachable router and no introducers present");
|
||||
}
|
||||
}
|
||||
|
||||
void SSUServer::DeleteSession (std::shared_ptr<SSUSession> session)
|
||||
{
|
||||
if (session)
|
||||
{
|
||||
session->Close ();
|
||||
auto& ep = session->GetRemoteEndpoint ();
|
||||
if (ep.address ().is_v6 ())
|
||||
m_SessionsV6.erase (ep);
|
||||
else
|
||||
m_Sessions.erase (ep);
|
||||
}
|
||||
}
|
||||
|
||||
void SSUServer::DeleteAllSessions ()
|
||||
{
|
||||
for (auto& it: m_Sessions)
|
||||
it.second->Close ();
|
||||
m_Sessions.clear ();
|
||||
|
||||
for (auto& it: m_SessionsV6)
|
||||
it.second->Close ();
|
||||
m_SessionsV6.clear ();
|
||||
}
|
||||
|
||||
template<typename Filter>
|
||||
std::shared_ptr<SSUSession> SSUServer::GetRandomV4Session (Filter filter) // v4 only
|
||||
{
|
||||
std::vector<std::shared_ptr<SSUSession> > filteredSessions;
|
||||
for (const auto& s :m_Sessions)
|
||||
if (filter (s.second)) filteredSessions.push_back (s.second);
|
||||
if (filteredSessions.size () > 0)
|
||||
{
|
||||
auto ind = rand () % filteredSessions.size ();
|
||||
return filteredSessions[ind];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<SSUSession> SSUServer::GetRandomEstablishedV4Session (std::shared_ptr<const SSUSession> excluded) // v4 only
|
||||
{
|
||||
return GetRandomV4Session (
|
||||
[excluded](std::shared_ptr<SSUSession> session)->bool
|
||||
{
|
||||
return session->GetState () == eSessionStateEstablished && session != excluded;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
template<typename Filter>
|
||||
std::shared_ptr<SSUSession> SSUServer::GetRandomV6Session (Filter filter) // v6 only
|
||||
{
|
||||
std::vector<std::shared_ptr<SSUSession> > filteredSessions;
|
||||
for (const auto& s :m_SessionsV6)
|
||||
if (filter (s.second)) filteredSessions.push_back (s.second);
|
||||
if (filteredSessions.size () > 0)
|
||||
{
|
||||
auto ind = rand () % filteredSessions.size ();
|
||||
return filteredSessions[ind];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<SSUSession> SSUServer::GetRandomEstablishedV6Session (std::shared_ptr<const SSUSession> excluded) // v6 only
|
||||
{
|
||||
return GetRandomV6Session (
|
||||
[excluded](std::shared_ptr<SSUSession> session)->bool
|
||||
{
|
||||
return session->GetState () == eSessionStateEstablished && session != excluded;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
std::list<std::shared_ptr<SSUSession> > SSUServer::FindIntroducers (int maxNumIntroducers,
|
||||
bool v4, std::set<i2p::data::IdentHash>& excluded)
|
||||
{
|
||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
std::list<std::shared_ptr<SSUSession> > ret;
|
||||
const auto& sessions = v4 ? m_Sessions : m_SessionsV6;
|
||||
for (const auto& s : sessions)
|
||||
{
|
||||
if (s.second->GetRelayTag () && s.second->GetState () == eSessionStateEstablished &&
|
||||
ts < s.second->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_EXPIRATION)
|
||||
ret.push_back (s.second);
|
||||
else if (s.second->GetRemoteIdentity ())
|
||||
excluded.insert (s.second->GetRemoteIdentity ()->GetIdentHash ());
|
||||
}
|
||||
if ((int)ret.size () > maxNumIntroducers)
|
||||
{
|
||||
// shink ret randomly
|
||||
int sz = ret.size () - maxNumIntroducers;
|
||||
for (int i = 0; i < sz; i++)
|
||||
{
|
||||
auto ind = rand () % ret.size ();
|
||||
auto it = ret.begin ();
|
||||
std::advance (it, ind);
|
||||
ret.erase (it);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void SSUServer::RescheduleIntroducersUpdateTimer ()
|
||||
{
|
||||
m_IntroducersUpdateTimer.cancel ();
|
||||
m_IntroducersUpdateTimer.expires_from_now (boost::posix_time::seconds(SSU_KEEP_ALIVE_INTERVAL/2));
|
||||
m_IntroducersUpdateTimer.async_wait (std::bind (&SSUServer::HandleIntroducersUpdateTimer,
|
||||
this, std::placeholders::_1, true));
|
||||
}
|
||||
|
||||
void SSUServer::ScheduleIntroducersUpdateTimer ()
|
||||
{
|
||||
m_IntroducersUpdateTimer.expires_from_now (boost::posix_time::seconds(SSU_KEEP_ALIVE_INTERVAL));
|
||||
m_IntroducersUpdateTimer.async_wait (std::bind (&SSUServer::HandleIntroducersUpdateTimer,
|
||||
this, std::placeholders::_1, true));
|
||||
}
|
||||
|
||||
void SSUServer::RescheduleIntroducersUpdateTimerV6 ()
|
||||
{
|
||||
m_IntroducersUpdateTimerV6.cancel ();
|
||||
m_IntroducersUpdateTimerV6.expires_from_now (boost::posix_time::seconds(SSU_KEEP_ALIVE_INTERVAL/2));
|
||||
m_IntroducersUpdateTimerV6.async_wait (std::bind (&SSUServer::HandleIntroducersUpdateTimer,
|
||||
this, std::placeholders::_1, false));
|
||||
}
|
||||
|
||||
void SSUServer::ScheduleIntroducersUpdateTimerV6 ()
|
||||
{
|
||||
m_IntroducersUpdateTimerV6.expires_from_now (boost::posix_time::seconds(SSU_KEEP_ALIVE_INTERVAL));
|
||||
m_IntroducersUpdateTimerV6.async_wait (std::bind (&SSUServer::HandleIntroducersUpdateTimer,
|
||||
this, std::placeholders::_1, false));
|
||||
}
|
||||
|
||||
void SSUServer::HandleIntroducersUpdateTimer (const boost::system::error_code& ecode, bool v4)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
// timeout expired
|
||||
if (v4)
|
||||
{
|
||||
if (i2p::context.GetStatus () == eRouterStatusTesting)
|
||||
{
|
||||
// we still don't know if we need introducers
|
||||
ScheduleIntroducersUpdateTimer ();
|
||||
return;
|
||||
}
|
||||
if (i2p::context.GetStatus () != eRouterStatusFirewalled)
|
||||
{
|
||||
// we don't need introducers
|
||||
m_Introducers.clear ();
|
||||
return;
|
||||
}
|
||||
// we are firewalled
|
||||
if (!i2p::context.IsUnreachable ()) i2p::context.SetUnreachable (true, false); // v4
|
||||
}
|
||||
else
|
||||
{
|
||||
if (i2p::context.GetStatusV6 () == eRouterStatusTesting)
|
||||
{
|
||||
// we still don't know if we need introducers
|
||||
ScheduleIntroducersUpdateTimerV6 ();
|
||||
return;
|
||||
}
|
||||
if (i2p::context.GetStatusV6 () != eRouterStatusFirewalled)
|
||||
{
|
||||
// we don't need introducers
|
||||
m_IntroducersV6.clear ();
|
||||
return;
|
||||
}
|
||||
// we are firewalled
|
||||
auto addr = i2p::context.GetRouterInfo ().GetSSUV6Address ();
|
||||
if (addr && addr->ssu && addr->ssu->introducers.empty ())
|
||||
i2p::context.SetUnreachable (false, true); // v6
|
||||
}
|
||||
|
||||
std::list<boost::asio::ip::udp::endpoint> newList;
|
||||
size_t numIntroducers = 0;
|
||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
std::set<i2p::data::IdentHash> excluded;
|
||||
auto& introducers = v4 ? m_Introducers : m_IntroducersV6;
|
||||
for (const auto& it : introducers)
|
||||
{
|
||||
auto session = FindSession (it);
|
||||
if (session)
|
||||
{
|
||||
if (ts < session->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_EXPIRATION)
|
||||
session->SendKeepAlive ();
|
||||
if (ts < session->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_DURATION)
|
||||
{
|
||||
newList.push_back (it);
|
||||
numIntroducers++;
|
||||
if (session->GetRemoteIdentity ())
|
||||
excluded.insert (session->GetRemoteIdentity ()->GetIdentHash ());
|
||||
}
|
||||
else
|
||||
session = nullptr;
|
||||
}
|
||||
if (!session)
|
||||
i2p::context.RemoveIntroducer (it);
|
||||
}
|
||||
if (numIntroducers < SSU_MAX_NUM_INTRODUCERS)
|
||||
{
|
||||
// create new
|
||||
auto sessions = FindIntroducers (SSU_MAX_NUM_INTRODUCERS, v4, excluded); // try to find if duplicates
|
||||
if (sessions.empty () && !introducers.empty ())
|
||||
{
|
||||
// bump creation time for previous introducers if no new sessions found
|
||||
LogPrint (eLogDebug, "SSU: No new introducers found. Trying to reuse existing");
|
||||
for (const auto& it : introducers)
|
||||
{
|
||||
auto session = FindSession (it);
|
||||
if (session)
|
||||
session->SetCreationTime (session->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_DURATION);
|
||||
}
|
||||
// try again
|
||||
excluded.clear ();
|
||||
sessions = FindIntroducers (SSU_MAX_NUM_INTRODUCERS, v4, excluded);
|
||||
}
|
||||
for (const auto& it1: sessions)
|
||||
{
|
||||
const auto& ep = it1->GetRemoteEndpoint ();
|
||||
i2p::data::RouterInfo::Introducer introducer;
|
||||
introducer.iHost = ep.address ();
|
||||
introducer.iPort = ep.port ();
|
||||
introducer.iTag = it1->GetRelayTag ();
|
||||
introducer.iKey = it1->GetIntroKey ();
|
||||
introducer.iExp = it1->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_EXPIRATION;
|
||||
if (i2p::context.AddIntroducer (introducer))
|
||||
{
|
||||
newList.push_back (ep);
|
||||
if (newList.size () >= SSU_MAX_NUM_INTRODUCERS) break;
|
||||
}
|
||||
if (it1->GetRemoteIdentity ())
|
||||
excluded.insert (it1->GetRemoteIdentity ()->GetIdentHash ());
|
||||
}
|
||||
}
|
||||
introducers = newList;
|
||||
if (introducers.size () < SSU_MAX_NUM_INTRODUCERS)
|
||||
{
|
||||
for (auto i = introducers.size (); i < SSU_MAX_NUM_INTRODUCERS; i++)
|
||||
{
|
||||
auto introducer = i2p::data::netdb.GetRandomIntroducer (v4, excluded);
|
||||
if (introducer)
|
||||
{
|
||||
auto address = v4 ? introducer->GetSSUAddress (true) : introducer->GetSSUV6Address ();
|
||||
if (address && !address->host.is_unspecified () && address->port)
|
||||
{
|
||||
boost::asio::ip::udp::endpoint ep (address->host, address->port);
|
||||
if (std::find (introducers.begin (), introducers.end (), ep) == introducers.end ()) // not connected yet
|
||||
{
|
||||
CreateDirectSession (introducer, ep, false);
|
||||
excluded.insert (introducer->GetIdentHash ());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogDebug, "SSU: Can't find more introducers");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (v4)
|
||||
ScheduleIntroducersUpdateTimer ();
|
||||
else
|
||||
ScheduleIntroducersUpdateTimerV6 ();
|
||||
}
|
||||
}
|
||||
|
||||
void SSUServer::NewPeerTest (uint32_t nonce, PeerTestParticipant role, std::shared_ptr<SSUSession> session)
|
||||
{
|
||||
m_PeerTests[nonce] = { i2p::util::GetMillisecondsSinceEpoch (), role, session };
|
||||
}
|
||||
|
||||
PeerTestParticipant SSUServer::GetPeerTestParticipant (uint32_t nonce)
|
||||
{
|
||||
auto it = m_PeerTests.find (nonce);
|
||||
if (it != m_PeerTests.end ())
|
||||
return it->second.role;
|
||||
else
|
||||
return ePeerTestParticipantUnknown;
|
||||
}
|
||||
|
||||
std::shared_ptr<SSUSession> SSUServer::GetPeerTestSession (uint32_t nonce)
|
||||
{
|
||||
auto it = m_PeerTests.find (nonce);
|
||||
if (it != m_PeerTests.end ())
|
||||
return it->second.session;
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void SSUServer::UpdatePeerTest (uint32_t nonce, PeerTestParticipant role)
|
||||
{
|
||||
auto it = m_PeerTests.find (nonce);
|
||||
if (it != m_PeerTests.end ())
|
||||
it->second.role = role;
|
||||
}
|
||||
|
||||
void SSUServer::RemovePeerTest (uint32_t nonce)
|
||||
{
|
||||
m_PeerTests.erase (nonce);
|
||||
}
|
||||
|
||||
void SSUServer::SchedulePeerTestsCleanupTimer ()
|
||||
{
|
||||
m_PeerTestsCleanupTimer.expires_from_now (boost::posix_time::seconds(SSU_PEER_TEST_TIMEOUT));
|
||||
m_PeerTestsCleanupTimer.async_wait (std::bind (&SSUServer::HandlePeerTestsCleanupTimer,
|
||||
this, std::placeholders::_1));
|
||||
}
|
||||
|
||||
void SSUServer::HandlePeerTestsCleanupTimer (const boost::system::error_code& ecode)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
int numDeleted = 0;
|
||||
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
for (auto it = m_PeerTests.begin (); it != m_PeerTests.end ();)
|
||||
{
|
||||
if (ts > it->second.creationTime + SSU_PEER_TEST_TIMEOUT*1000LL)
|
||||
{
|
||||
numDeleted++;
|
||||
it = m_PeerTests.erase (it);
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
if (numDeleted > 0)
|
||||
LogPrint (eLogDebug, "SSU: ", numDeleted, " peer tests have been expired");
|
||||
// some cleaups. TODO: use separate timer
|
||||
m_FragmentsPool.CleanUp ();
|
||||
m_IncompleteMessagesPool.CleanUp ();
|
||||
m_SentMessagesPool.CleanUp ();
|
||||
|
||||
SchedulePeerTestsCleanupTimer ();
|
||||
}
|
||||
}
|
||||
|
||||
void SSUServer::ScheduleTermination ()
|
||||
{
|
||||
uint64_t timeout = SSU_TERMINATION_CHECK_TIMEOUT + (rand () % SSU_TERMINATION_CHECK_TIMEOUT)/5;
|
||||
m_TerminationTimer.expires_from_now (boost::posix_time::seconds(timeout));
|
||||
m_TerminationTimer.async_wait (std::bind (&SSUServer::HandleTerminationTimer,
|
||||
this, std::placeholders::_1));
|
||||
}
|
||||
|
||||
void SSUServer::HandleTerminationTimer (const boost::system::error_code& ecode)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
for (auto& it: m_Sessions)
|
||||
if (it.second->IsTerminationTimeoutExpired (ts))
|
||||
{
|
||||
auto session = it.second;
|
||||
if (it.first != session->GetRemoteEndpoint ())
|
||||
LogPrint (eLogWarning, "SSU: Remote endpoint ", session->GetRemoteEndpoint (), " doesn't match key ", it.first, " adjusted");
|
||||
m_Service.post ([session]
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU: No activity with ", session->GetRemoteEndpoint (), " for ", session->GetTerminationTimeout (), " seconds");
|
||||
session->Failed ();
|
||||
});
|
||||
}
|
||||
else
|
||||
it.second->CleanUp (ts);
|
||||
ScheduleTermination ();
|
||||
}
|
||||
}
|
||||
|
||||
void SSUServer::ScheduleTerminationV6 ()
|
||||
{
|
||||
uint64_t timeout = SSU_TERMINATION_CHECK_TIMEOUT + (rand () % SSU_TERMINATION_CHECK_TIMEOUT)/5;
|
||||
m_TerminationTimerV6.expires_from_now (boost::posix_time::seconds(timeout));
|
||||
m_TerminationTimerV6.async_wait (std::bind (&SSUServer::HandleTerminationTimerV6,
|
||||
this, std::placeholders::_1));
|
||||
}
|
||||
|
||||
void SSUServer::HandleTerminationTimerV6 (const boost::system::error_code& ecode)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
for (auto& it: m_SessionsV6)
|
||||
if (it.second->IsTerminationTimeoutExpired (ts))
|
||||
{
|
||||
auto session = it.second;
|
||||
if (it.first != session->GetRemoteEndpoint ())
|
||||
LogPrint (eLogWarning, "SSU: Remote endpoint ", session->GetRemoteEndpoint (), " doesn't match key ", it.first);
|
||||
m_Service.post ([session]
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU: No activity with ", session->GetRemoteEndpoint (), " for ", session->GetTerminationTimeout (), " seconds");
|
||||
session->Failed ();
|
||||
});
|
||||
}
|
||||
else
|
||||
it.second->CleanUp (ts);
|
||||
ScheduleTerminationV6 ();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
159
libi2pd/SSU.h
159
libi2pd/SSU.h
@ -1,159 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
* See full license text in LICENSE file at top of project tree
|
||||
*/
|
||||
|
||||
#ifndef SSU_H__
|
||||
#define SSU_H__
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <set>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <boost/asio.hpp>
|
||||
#include "Crypto.h"
|
||||
#include "util.h"
|
||||
#include "I2PEndian.h"
|
||||
#include "Identity.h"
|
||||
#include "RouterInfo.h"
|
||||
#include "I2NPProtocol.h"
|
||||
#include "SSUSession.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace transport
|
||||
{
|
||||
const int SSU_KEEP_ALIVE_INTERVAL = 30; // 30 seconds
|
||||
const int SSU_PEER_TEST_TIMEOUT = 60; // 60 seconds
|
||||
const int SSU_TO_INTRODUCER_SESSION_DURATION = 3600; // 1 hour
|
||||
const int SSU_TO_INTRODUCER_SESSION_EXPIRATION = 4800; // 80 minutes
|
||||
const int SSU_TERMINATION_CHECK_TIMEOUT = 30; // 30 seconds
|
||||
const size_t SSU_MAX_NUM_INTRODUCERS = 3;
|
||||
const size_t SSU_SOCKET_RECEIVE_BUFFER_SIZE = 0x1FFFF; // 128K
|
||||
const size_t SSU_SOCKET_SEND_BUFFER_SIZE = 0x1FFFF; // 128K
|
||||
|
||||
struct SSUPacket
|
||||
{
|
||||
i2p::crypto::AESAlignedBuffer<SSU_MTU_V6 + 18> buf; // max MTU + iv + size
|
||||
boost::asio::ip::udp::endpoint from;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
class SSUServer
|
||||
{
|
||||
public:
|
||||
|
||||
SSUServer (int port);
|
||||
~SSUServer ();
|
||||
void Start ();
|
||||
void Stop ();
|
||||
bool CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest = false, bool v4only = false);
|
||||
bool CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router,
|
||||
std::shared_ptr<const i2p::data::RouterInfo::Address> address, bool peerTest = false);
|
||||
void CreateDirectSession (std::shared_ptr<const i2p::data::RouterInfo> router, boost::asio::ip::udp::endpoint remoteEndpoint, bool peerTest);
|
||||
std::shared_ptr<SSUSession> FindSession (const boost::asio::ip::udp::endpoint& e) const;
|
||||
std::shared_ptr<SSUSession> GetRandomEstablishedV4Session (std::shared_ptr<const SSUSession> excluded);
|
||||
std::shared_ptr<SSUSession> GetRandomEstablishedV6Session (std::shared_ptr<const SSUSession> excluded);
|
||||
void DeleteSession (std::shared_ptr<SSUSession> session);
|
||||
void DeleteAllSessions ();
|
||||
|
||||
boost::asio::io_service& GetService () { return m_Service; };
|
||||
i2p::util::MemoryPool<Fragment>& GetFragmentsPool () { return m_FragmentsPool; };
|
||||
i2p::util::MemoryPool<IncompleteMessage>& GetIncompleteMessagesPool () { return m_IncompleteMessagesPool; };
|
||||
i2p::util::MemoryPool<SentMessage>& GetSentMessagesPool () { return m_SentMessagesPool; };
|
||||
|
||||
uint16_t GetPort () const { return m_Endpoint.port (); };
|
||||
bool IsSyncClockFromPeers () const { return m_IsSyncClockFromPeers; };
|
||||
void SetLocalAddress (const boost::asio::ip::address& localAddress);
|
||||
|
||||
void Send (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& to);
|
||||
void AddRelay (uint32_t tag, std::shared_ptr<SSUSession> relay);
|
||||
void RemoveRelay (uint32_t tag);
|
||||
std::shared_ptr<SSUSession> FindRelaySession (uint32_t tag);
|
||||
void RescheduleIntroducersUpdateTimer ();
|
||||
void RescheduleIntroducersUpdateTimerV6 ();
|
||||
|
||||
void NewPeerTest (uint32_t nonce, PeerTestParticipant role, std::shared_ptr<SSUSession> session = nullptr);
|
||||
PeerTestParticipant GetPeerTestParticipant (uint32_t nonce);
|
||||
std::shared_ptr<SSUSession> GetPeerTestSession (uint32_t nonce);
|
||||
void UpdatePeerTest (uint32_t nonce, PeerTestParticipant role);
|
||||
void RemovePeerTest (uint32_t nonce);
|
||||
|
||||
private:
|
||||
|
||||
void OpenSocket ();
|
||||
void OpenSocketV6 ();
|
||||
void Run ();
|
||||
void RunReceivers ();
|
||||
void RunReceiversV6 ();
|
||||
void Receive ();
|
||||
void ReceiveV6 ();
|
||||
void HandleReceivedFrom (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet);
|
||||
void HandleReceivedFromV6 (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet);
|
||||
void HandleReceivedPackets (std::vector<SSUPacket *> packets,
|
||||
std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<SSUSession> >* sessions);
|
||||
|
||||
void CreateSessionThroughIntroducer (std::shared_ptr<const i2p::data::RouterInfo> router,
|
||||
std::shared_ptr<const i2p::data::RouterInfo::Address> address, bool peerTest = false);
|
||||
template<typename Filter>
|
||||
std::shared_ptr<SSUSession> GetRandomV4Session (Filter filter);
|
||||
template<typename Filter>
|
||||
std::shared_ptr<SSUSession> GetRandomV6Session (Filter filter);
|
||||
|
||||
std::list<std::shared_ptr<SSUSession> > FindIntroducers (int maxNumIntroducers, bool v4, std::set<i2p::data::IdentHash>& excluded);
|
||||
void ScheduleIntroducersUpdateTimer ();
|
||||
void ScheduleIntroducersUpdateTimerV6 ();
|
||||
void HandleIntroducersUpdateTimer (const boost::system::error_code& ecode, bool v4);
|
||||
|
||||
void SchedulePeerTestsCleanupTimer ();
|
||||
void HandlePeerTestsCleanupTimer (const boost::system::error_code& ecode);
|
||||
|
||||
// timer
|
||||
void ScheduleTermination ();
|
||||
void HandleTerminationTimer (const boost::system::error_code& ecode);
|
||||
void ScheduleTerminationV6 ();
|
||||
void HandleTerminationTimerV6 (const boost::system::error_code& ecode);
|
||||
|
||||
private:
|
||||
|
||||
struct PeerTest
|
||||
{
|
||||
uint64_t creationTime;
|
||||
PeerTestParticipant role;
|
||||
std::shared_ptr<SSUSession> session; // for Bob to Alice
|
||||
};
|
||||
|
||||
volatile bool m_IsRunning;
|
||||
std::thread * m_Thread, * m_ReceiversThread, * m_ReceiversThreadV6;
|
||||
boost::asio::io_service m_Service, m_ReceiversService, m_ReceiversServiceV6;
|
||||
boost::asio::io_service::work m_Work, m_ReceiversWork, m_ReceiversWorkV6;
|
||||
boost::asio::ip::udp::endpoint m_Endpoint, m_EndpointV6;
|
||||
boost::asio::ip::udp::socket m_Socket, m_SocketV6;
|
||||
boost::asio::deadline_timer m_IntroducersUpdateTimer, m_IntroducersUpdateTimerV6,
|
||||
m_PeerTestsCleanupTimer, m_TerminationTimer, m_TerminationTimerV6;
|
||||
bool m_IsSyncClockFromPeers;
|
||||
std::list<boost::asio::ip::udp::endpoint> m_Introducers, m_IntroducersV6; // introducers we are connected to
|
||||
std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<SSUSession> > m_Sessions, m_SessionsV6;
|
||||
std::map<uint32_t, std::shared_ptr<SSUSession> > m_Relays; // we are introducer
|
||||
std::map<uint32_t, PeerTest> m_PeerTests; // nonce -> creation time in milliseconds
|
||||
|
||||
i2p::util::MemoryPool<Fragment> m_FragmentsPool;
|
||||
i2p::util::MemoryPool<IncompleteMessage> m_IncompleteMessagesPool;
|
||||
i2p::util::MemoryPool<SentMessage> m_SentMessagesPool;
|
||||
i2p::util::MemoryPoolMt<SSUPacket> m_PacketsPool;
|
||||
|
||||
public:
|
||||
// for HTTP only
|
||||
const decltype(m_Sessions)& GetSessions () const { return m_Sessions; };
|
||||
const decltype(m_SessionsV6)& GetSessionsV6 () const { return m_SessionsV6; };
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -68,9 +68,8 @@ namespace transport
|
||||
if (ssu2Port) port = ssu2Port;
|
||||
else
|
||||
{
|
||||
bool ssu; i2p::config::GetOption("ssu", ssu);
|
||||
uint16_t p; i2p::config::GetOption ("port", p);
|
||||
if (p) port = ssu ? (p + 1) : p;
|
||||
if (p) port = p;
|
||||
}
|
||||
}
|
||||
if (port)
|
||||
@ -409,7 +408,7 @@ namespace transport
|
||||
return it->second;
|
||||
it++;
|
||||
}
|
||||
// not found, try from begining
|
||||
// not found, try from beginning
|
||||
it = m_Sessions.begin ();
|
||||
while (it != m_Sessions.end () && ind)
|
||||
{
|
||||
@ -642,7 +641,7 @@ namespace transport
|
||||
// try to find existing session first
|
||||
for (auto& it: address->ssu->introducers)
|
||||
{
|
||||
auto it1 = m_SessionsByRouterHash.find (it.iKey);
|
||||
auto it1 = m_SessionsByRouterHash.find (it.iH);
|
||||
if (it1 != m_SessionsByRouterHash.end ())
|
||||
{
|
||||
it1->second->Introduce (session, it.iTag);
|
||||
@ -655,17 +654,17 @@ namespace transport
|
||||
uint32_t relayTag = 0;
|
||||
if (!address->ssu->introducers.empty ())
|
||||
{
|
||||
std::vector<int> indicies;
|
||||
for (int i = 0; i < (int)address->ssu->introducers.size (); i++) indicies.push_back(i);
|
||||
if (indicies.size () > 1)
|
||||
std::shuffle (indicies.begin(), indicies.end(), std::mt19937(std::random_device()()));
|
||||
std::vector<int> indices;
|
||||
for (int i = 0; i < (int)address->ssu->introducers.size (); i++) indices.push_back(i);
|
||||
if (indices.size () > 1)
|
||||
std::shuffle (indices.begin(), indices.end(), std::mt19937(std::random_device()()));
|
||||
|
||||
for (auto i: indicies)
|
||||
for (auto i: indices)
|
||||
{
|
||||
const auto& introducer = address->ssu->introducers[indicies[i]];
|
||||
const auto& introducer = address->ssu->introducers[indices[i]];
|
||||
if (introducer.iTag && ts < introducer.iExp)
|
||||
{
|
||||
r = i2p::data::netdb.FindRouter (introducer.iKey);
|
||||
r = i2p::data::netdb.FindRouter (introducer.iH);
|
||||
if (r && r->IsReachableFrom (i2p::context.GetRouterInfo ()))
|
||||
{
|
||||
relayTag = introducer.iTag;
|
||||
@ -714,7 +713,7 @@ namespace transport
|
||||
// introducers not found, try to request them
|
||||
for (auto& it: address->ssu->introducers)
|
||||
if (it.iTag && ts < it.iExp)
|
||||
i2p::data::netdb.RequestDestination (it.iKey);
|
||||
i2p::data::netdb.RequestDestination (it.iH);
|
||||
}
|
||||
}
|
||||
|
||||
@ -962,7 +961,8 @@ namespace transport
|
||||
{
|
||||
i2p::data::RouterInfo::Introducer introducer;
|
||||
introducer.iTag = it->GetRelayTag ();
|
||||
introducer.iKey = it->GetRemoteIdentity ()->GetIdentHash ();
|
||||
introducer.iH = it->GetRemoteIdentity ()->GetIdentHash ();
|
||||
introducer.isH = true;
|
||||
introducer.iExp = it->GetCreationTime () + SSU2_TO_INTRODUCER_SESSION_EXPIRATION;
|
||||
excluded.insert (it->GetRemoteIdentity ()->GetIdentHash ());
|
||||
if (i2p::context.AddSSU2Introducer (introducer, v4))
|
||||
@ -1068,7 +1068,7 @@ namespace transport
|
||||
// we are firewalled
|
||||
auto addr = i2p::context.GetRouterInfo ().GetSSU2V4Address ();
|
||||
if (addr && addr->ssu && addr->ssu->introducers.empty ())
|
||||
i2p::context.SetUnreachableSSU2 (true, false); // v4
|
||||
i2p::context.SetUnreachable (true, false); // v4
|
||||
|
||||
UpdateIntroducers (true);
|
||||
ScheduleIntroducersUpdateTimer ();
|
||||
@ -1091,7 +1091,7 @@ namespace transport
|
||||
// we are firewalled
|
||||
auto addr = i2p::context.GetRouterInfo ().GetSSU2V6Address ();
|
||||
if (addr && addr->ssu && addr->ssu->introducers.empty ())
|
||||
i2p::context.SetUnreachableSSU2 (false, true); // v6
|
||||
i2p::context.SetUnreachable (false, true); // v6
|
||||
|
||||
UpdateIntroducers (false);
|
||||
ScheduleIntroducersUpdateTimerV6 ();
|
||||
|
@ -94,7 +94,10 @@ namespace transport
|
||||
if (!ecode)
|
||||
{
|
||||
// timeout expired
|
||||
LogPrint (eLogWarning, "SSU2: Session with ", m_RemoteEndpoint, " was not established after ", SSU2_CONNECT_TIMEOUT, " seconds");
|
||||
if (m_State == eSSU2SessionStateIntroduced) // WaitForIntroducer
|
||||
LogPrint (eLogWarning, "SSU2: Session was not introduced after ", SSU2_CONNECT_TIMEOUT, " seconds");
|
||||
else
|
||||
LogPrint (eLogWarning, "SSU2: Session with ", m_RemoteEndpoint, " was not established after ", SSU2_CONNECT_TIMEOUT, " seconds");
|
||||
Terminate ();
|
||||
}
|
||||
}
|
||||
@ -103,7 +106,7 @@ namespace transport
|
||||
{
|
||||
// we are Alice
|
||||
if (!session || !relayTag) return false;
|
||||
// find local adddress to introduce
|
||||
// find local address to introduce
|
||||
auto localAddress = session->FindLocalAddress ();
|
||||
if (!localAddress) return false;
|
||||
// create nonce
|
||||
@ -520,6 +523,11 @@ namespace transport
|
||||
case eSSU2PeerTest:
|
||||
{
|
||||
// TODO: remove later
|
||||
if (len < 32)
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU2: PeerTest message too short ", len);
|
||||
break;
|
||||
}
|
||||
const uint8_t nonce[12] = {0};
|
||||
uint64_t headerX[2];
|
||||
i2p::crypto::ChaCha20 (buf + 16, 16, i2p::context.GetSSU2IntroKey (), nonce, (uint8_t *)headerX);
|
||||
@ -603,6 +611,11 @@ namespace transport
|
||||
void SSU2Session::ProcessSessionRequest (Header& header, uint8_t * buf, size_t len)
|
||||
{
|
||||
// we are Bob
|
||||
if (len < 80)
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU2: SessionRequest message too short ", len);
|
||||
return;
|
||||
}
|
||||
const uint8_t nonce[12] = {0};
|
||||
uint8_t headerX[48];
|
||||
i2p::crypto::ChaCha20 (buf + 16, 48, i2p::context.GetSSU2IntroKey (), nonce, headerX);
|
||||
@ -723,6 +736,11 @@ namespace transport
|
||||
if (header.h.type != eSSU2SessionCreated)
|
||||
// this situation is valid, because it might be Retry with different encryption
|
||||
return false;
|
||||
if (len < 80)
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU2: SessionCreated message too short ", len);
|
||||
return false;
|
||||
}
|
||||
const uint8_t nonce[12] = {0};
|
||||
uint8_t headerX[48];
|
||||
i2p::crypto::ChaCha20 (buf + 16, 48, kh2, nonce, headerX);
|
||||
@ -863,6 +881,12 @@ namespace transport
|
||||
LogPrint (eLogError, "SSU2: Too many fragments ", numFragments, " in SessionConfirmed");
|
||||
return false;
|
||||
}
|
||||
if (len < 32)
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU2: SessionConfirmed fragment too short ", len);
|
||||
if (m_SessionConfirmedFragment) m_SessionConfirmedFragment.reset (nullptr);
|
||||
return false;
|
||||
}
|
||||
if (!(header.h.flags[0] & 0xF0))
|
||||
{
|
||||
// first fragment
|
||||
@ -910,6 +934,12 @@ namespace transport
|
||||
len = m_SessionConfirmedFragment->payloadSize + 16;
|
||||
}
|
||||
}
|
||||
if (len < 80)
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU2: SessionConfirmed message too short ", len);
|
||||
if (m_SessionConfirmedFragment) m_SessionConfirmedFragment.reset (nullptr);
|
||||
return false;
|
||||
}
|
||||
// KDF for Session Confirmed part 1
|
||||
m_NoiseState->MixHash (header.buf, 16); // h = SHA256(h || header)
|
||||
// decrypt part1
|
||||
@ -1118,6 +1148,11 @@ namespace transport
|
||||
LogPrint (eLogWarning, "SSU2: Unexpected message type ", (int)header.h.type, " instead ", (int)eSSU2Retry);
|
||||
return false;
|
||||
}
|
||||
if (len < 48)
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU2: Retry message too short ", len);
|
||||
return false;
|
||||
}
|
||||
uint8_t nonce[12] = {0};
|
||||
uint64_t headerX[2]; // sourceConnID, token
|
||||
i2p::crypto::ChaCha20 (buf + 16, 16, m_Address->i, nonce, (uint8_t *)headerX);
|
||||
@ -1202,6 +1237,11 @@ namespace transport
|
||||
LogPrint (eLogWarning, "SSU2: Unexpected message type ", (int)header.h.type, " instead ", (int)eSSU2HolePunch);
|
||||
return false;
|
||||
}
|
||||
if (len < 48)
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU2: HolePunch message too short ", len);
|
||||
return false;
|
||||
}
|
||||
uint8_t nonce[12] = {0};
|
||||
uint64_t headerX[2]; // sourceConnID, token
|
||||
i2p::crypto::ChaCha20 (buf + 16, 16, i2p::context.GetSSU2IntroKey (), nonce, (uint8_t *)headerX);
|
||||
@ -1273,6 +1313,11 @@ namespace transport
|
||||
LogPrint (eLogWarning, "SSU2: Unexpected message type ", (int)header.h.type, " instead ", (int)eSSU2PeerTest);
|
||||
return false;
|
||||
}
|
||||
if (len < 48)
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU2: PeerTest message too short ", len);
|
||||
return false;
|
||||
}
|
||||
uint8_t nonce[12] = {0};
|
||||
uint64_t headerX[2]; // sourceConnID, token
|
||||
i2p::crypto::ChaCha20 (buf + 16, 16, i2p::context.GetSSU2IntroKey (), nonce, (uint8_t *)headerX);
|
||||
@ -1340,6 +1385,11 @@ namespace transport
|
||||
m_RemoteEndpoint = from;
|
||||
SendPathChallenge ();
|
||||
}
|
||||
if (len < 32)
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU2: Data message too short ", len);
|
||||
return;
|
||||
}
|
||||
uint8_t payload[SSU2_MAX_PACKET_SIZE];
|
||||
size_t payloadSize = len - 32;
|
||||
uint32_t packetNum = be32toh (header.h.packetNum);
|
||||
@ -2266,9 +2316,9 @@ namespace transport
|
||||
if (m_Address)
|
||||
{
|
||||
if (m_Address->IsV4 ())
|
||||
i2p::context.SetStatusSSU2 (status);
|
||||
i2p::context.SetStatus (status);
|
||||
else if (m_Address->IsV6 ())
|
||||
i2p::context.SetStatusV6SSU2 (status);
|
||||
i2p::context.SetStatusV6 (status);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -35,7 +35,7 @@ namespace transport
|
||||
const int SSU2_PEER_TEST_EXPIRATION_TIMEOUT = 60; // 60 seconds
|
||||
const size_t SSU2_MAX_PACKET_SIZE = 1500;
|
||||
const size_t SSU2_MIN_PACKET_SIZE = 1280;
|
||||
const int SSU2_HANDSHAKE_RESEND_INTERVAL = 1000; // in millseconds
|
||||
const int SSU2_HANDSHAKE_RESEND_INTERVAL = 1000; // in milliseconds
|
||||
const int SSU2_RESEND_INTERVAL = 300; // in milliseconds
|
||||
const int SSU2_MAX_NUM_RESENDS = 5;
|
||||
const int SSU2_INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT = 30; // in seconds
|
||||
|
@ -1,516 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
* See full license text in LICENSE file at top of project tree
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "Log.h"
|
||||
#include "Timestamp.h"
|
||||
#include "NetDb.hpp"
|
||||
#include "SSU.h"
|
||||
#include "SSUData.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace transport
|
||||
{
|
||||
void IncompleteMessage::AttachNextFragment (const uint8_t * fragment, size_t fragmentSize)
|
||||
{
|
||||
if (msg->len + fragmentSize > msg->maxLen)
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU: I2NP message size ", msg->maxLen, " is not enough");
|
||||
auto newMsg = NewI2NPMessage ();
|
||||
*newMsg = *msg;
|
||||
msg = newMsg;
|
||||
}
|
||||
if (msg->Concat (fragment, fragmentSize) < fragmentSize)
|
||||
LogPrint (eLogError, "SSU: I2NP buffer overflow ", msg->maxLen);
|
||||
nextFragmentNum++;
|
||||
}
|
||||
|
||||
SSUData::SSUData (SSUSession& session):
|
||||
m_Session (session), m_ResendTimer (session.GetService ()),
|
||||
m_MaxPacketSize (session.IsV6 () ? SSU_V6_MAX_PACKET_SIZE : SSU_V4_MAX_PACKET_SIZE),
|
||||
m_PacketSize (m_MaxPacketSize), m_LastMessageReceivedTime (0)
|
||||
{
|
||||
}
|
||||
|
||||
SSUData::~SSUData ()
|
||||
{
|
||||
}
|
||||
|
||||
void SSUData::Start ()
|
||||
{
|
||||
}
|
||||
|
||||
void SSUData::Stop ()
|
||||
{
|
||||
m_ResendTimer.cancel ();
|
||||
m_IncompleteMessages.clear ();
|
||||
m_SentMessages.clear ();
|
||||
m_ReceivedMessages.clear ();
|
||||
}
|
||||
|
||||
void SSUData::AdjustPacketSize (std::shared_ptr<const i2p::data::RouterInfo> remoteRouter)
|
||||
{
|
||||
if (!remoteRouter) return;
|
||||
auto ssuAddress = remoteRouter->GetSSUAddress ();
|
||||
if (ssuAddress && ssuAddress->ssu->mtu)
|
||||
{
|
||||
if (m_Session.IsV6 ())
|
||||
m_PacketSize = ssuAddress->ssu->mtu - IPV6_HEADER_SIZE - UDP_HEADER_SIZE;
|
||||
else
|
||||
m_PacketSize = ssuAddress->ssu->mtu - IPV4_HEADER_SIZE - UDP_HEADER_SIZE;
|
||||
if (m_PacketSize > 0)
|
||||
{
|
||||
// make sure packet size multiple of 16
|
||||
m_PacketSize >>= 4;
|
||||
m_PacketSize <<= 4;
|
||||
if (m_PacketSize > m_MaxPacketSize) m_PacketSize = m_MaxPacketSize;
|
||||
LogPrint (eLogDebug, "SSU: MTU=", ssuAddress->ssu->mtu, " packet size=", m_PacketSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU: Unexpected MTU ", ssuAddress->ssu->mtu);
|
||||
m_PacketSize = m_MaxPacketSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SSUData::UpdatePacketSize (const i2p::data::IdentHash& remoteIdent)
|
||||
{
|
||||
auto routerInfo = i2p::data::netdb.FindRouter (remoteIdent);
|
||||
if (routerInfo)
|
||||
AdjustPacketSize (routerInfo);
|
||||
}
|
||||
|
||||
void SSUData::ProcessSentMessageAck (uint32_t msgID)
|
||||
{
|
||||
auto it = m_SentMessages.find (msgID);
|
||||
if (it != m_SentMessages.end ())
|
||||
{
|
||||
m_SentMessages.erase (it);
|
||||
if (m_SentMessages.empty ())
|
||||
m_ResendTimer.cancel ();
|
||||
}
|
||||
}
|
||||
|
||||
void SSUData::ProcessAcks (uint8_t *& buf, uint8_t flag)
|
||||
{
|
||||
if (flag & DATA_FLAG_EXPLICIT_ACKS_INCLUDED)
|
||||
{
|
||||
// explicit ACKs
|
||||
uint8_t numAcks =*buf;
|
||||
buf++;
|
||||
for (int i = 0; i < numAcks; i++)
|
||||
ProcessSentMessageAck (bufbe32toh (buf+i*4));
|
||||
buf += numAcks*4;
|
||||
}
|
||||
if (flag & DATA_FLAG_ACK_BITFIELDS_INCLUDED)
|
||||
{
|
||||
// explicit ACK bitfields
|
||||
uint8_t numBitfields =*buf;
|
||||
buf++;
|
||||
for (int i = 0; i < numBitfields; i++)
|
||||
{
|
||||
uint32_t msgID = bufbe32toh (buf);
|
||||
buf += 4; // msgID
|
||||
auto it = m_SentMessages.find (msgID);
|
||||
// process individual Ack bitfields
|
||||
bool isNonLast = false;
|
||||
int fragment = 0;
|
||||
do
|
||||
{
|
||||
uint8_t bitfield = *buf;
|
||||
isNonLast = bitfield & 0x80;
|
||||
bitfield &= 0x7F; // clear MSB
|
||||
if (bitfield && it != m_SentMessages.end ())
|
||||
{
|
||||
int numSentFragments = it->second->fragments.size ();
|
||||
// process bits
|
||||
uint8_t mask = 0x01;
|
||||
for (int j = 0; j < 7; j++)
|
||||
{
|
||||
if (bitfield & mask)
|
||||
{
|
||||
if (fragment < numSentFragments)
|
||||
it->second->fragments[fragment] = nullptr;
|
||||
}
|
||||
fragment++;
|
||||
mask <<= 1;
|
||||
}
|
||||
}
|
||||
buf++;
|
||||
}
|
||||
while (isNonLast);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SSUData::ProcessFragments (uint8_t * buf)
|
||||
{
|
||||
uint8_t numFragments = *buf; // number of fragments
|
||||
buf++;
|
||||
for (int i = 0; i < numFragments; i++)
|
||||
{
|
||||
uint32_t msgID = bufbe32toh (buf); // message ID
|
||||
buf += 4;
|
||||
uint8_t frag[4] = {0};
|
||||
memcpy (frag + 1, buf, 3);
|
||||
buf += 3;
|
||||
uint32_t fragmentInfo = bufbe32toh (frag); // fragment info
|
||||
uint16_t fragmentSize = fragmentInfo & 0x3FFF; // bits 0 - 13
|
||||
bool isLast = fragmentInfo & 0x010000; // bit 16
|
||||
uint8_t fragmentNum = fragmentInfo >> 17; // bits 23 - 17
|
||||
if (fragmentSize >= SSU_V4_MAX_PACKET_SIZE)
|
||||
{
|
||||
LogPrint (eLogError, "SSU: Fragment size ", fragmentSize, " exceeds max SSU packet size");
|
||||
return;
|
||||
}
|
||||
|
||||
// find message with msgID
|
||||
auto it = m_IncompleteMessages.find (msgID);
|
||||
if (it == m_IncompleteMessages.end ())
|
||||
{
|
||||
// create new message
|
||||
auto msg = NewI2NPShortMessage ();
|
||||
msg->len -= I2NP_SHORT_HEADER_SIZE;
|
||||
it = m_IncompleteMessages.insert (std::make_pair (msgID,
|
||||
m_Session.GetServer ().GetIncompleteMessagesPool ().AcquireShared (std::move (msg)))).first;
|
||||
}
|
||||
auto& incompleteMessage = it->second;
|
||||
// mark fragment as received
|
||||
if (fragmentNum < 64)
|
||||
incompleteMessage->receivedFragmentsBits |= (uint64_t(0x01) << fragmentNum);
|
||||
else
|
||||
LogPrint (eLogWarning, "SSU: Fragment number ", fragmentNum, " exceeds 64");
|
||||
|
||||
// handle current fragment
|
||||
if (fragmentNum == incompleteMessage->nextFragmentNum)
|
||||
{
|
||||
// expected fragment
|
||||
incompleteMessage->AttachNextFragment (buf, fragmentSize);
|
||||
if (!isLast && !incompleteMessage->savedFragments.empty ())
|
||||
{
|
||||
// try saved fragments
|
||||
for (auto it1 = incompleteMessage->savedFragments.begin (); it1 != incompleteMessage->savedFragments.end ();)
|
||||
{
|
||||
auto& savedFragment = *it1;
|
||||
if (savedFragment->fragmentNum == incompleteMessage->nextFragmentNum)
|
||||
{
|
||||
incompleteMessage->AttachNextFragment (savedFragment->buf, savedFragment->len);
|
||||
isLast = savedFragment->isLast;
|
||||
incompleteMessage->savedFragments.erase (it1++);
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
if (isLast)
|
||||
LogPrint (eLogDebug, "SSU: Message ", msgID, " complete");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fragmentNum < incompleteMessage->nextFragmentNum)
|
||||
// duplicate fragment
|
||||
LogPrint (eLogWarning, "SSU: Duplicate fragment ", (int)fragmentNum, " of message ", msgID, ", ignored");
|
||||
else
|
||||
{
|
||||
// missing fragment
|
||||
LogPrint (eLogWarning, "SSU: Missing fragments from ", (int)incompleteMessage->nextFragmentNum, " to ", fragmentNum - 1, " of message ", msgID);
|
||||
auto savedFragment = m_Session.GetServer ().GetFragmentsPool ().AcquireShared (fragmentNum, buf, fragmentSize, isLast);
|
||||
if (incompleteMessage->savedFragments.insert (savedFragment).second)
|
||||
incompleteMessage->lastFragmentInsertTime = i2p::util::GetSecondsSinceEpoch ();
|
||||
else
|
||||
LogPrint (eLogWarning, "SSU: Fragment ", (int)fragmentNum, " of message ", msgID, " already saved");
|
||||
}
|
||||
isLast = false;
|
||||
}
|
||||
|
||||
if (isLast)
|
||||
{
|
||||
// delete incomplete message
|
||||
auto msg = incompleteMessage->msg;
|
||||
incompleteMessage->msg = nullptr;
|
||||
m_IncompleteMessages.erase (msgID);
|
||||
// process message
|
||||
SendMsgAck (msgID);
|
||||
msg->FromSSU (msgID);
|
||||
if (m_Session.GetState () == eSessionStateEstablished)
|
||||
{
|
||||
if (!m_ReceivedMessages.count (msgID))
|
||||
{
|
||||
m_LastMessageReceivedTime = i2p::util::GetSecondsSinceEpoch ();
|
||||
m_ReceivedMessages.emplace (msgID, m_LastMessageReceivedTime);
|
||||
if (!msg->IsExpired ())
|
||||
{
|
||||
m_Handler.PutNextMessage (std::move (msg));
|
||||
}
|
||||
else
|
||||
LogPrint (eLogDebug, "SSU: message expired");
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "SSU: Message ", msgID, " already received");
|
||||
}
|
||||
else
|
||||
{
|
||||
// we expect DeliveryStatus
|
||||
if (msg->GetTypeID () == eI2NPDeliveryStatus)
|
||||
{
|
||||
LogPrint (eLogDebug, "SSU: session established");
|
||||
m_Session.Established ();
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "SSU: unexpected message ", (int)msg->GetTypeID ());
|
||||
}
|
||||
}
|
||||
else
|
||||
SendFragmentAck (msgID, incompleteMessage->receivedFragmentsBits);
|
||||
buf += fragmentSize;
|
||||
}
|
||||
}
|
||||
|
||||
void SSUData::FlushReceivedMessage ()
|
||||
{
|
||||
m_Handler.Flush ();
|
||||
}
|
||||
|
||||
void SSUData::ProcessMessage (uint8_t * buf, size_t len)
|
||||
{
|
||||
//uint8_t * start = buf;
|
||||
uint8_t flag = *buf;
|
||||
buf++;
|
||||
LogPrint (eLogDebug, "SSU: Process data, flags=", (int)flag, ", len=", len);
|
||||
// process acks if presented
|
||||
if (flag & (DATA_FLAG_ACK_BITFIELDS_INCLUDED | DATA_FLAG_EXPLICIT_ACKS_INCLUDED))
|
||||
ProcessAcks (buf, flag);
|
||||
// extended data if presented
|
||||
if (flag & DATA_FLAG_EXTENDED_DATA_INCLUDED)
|
||||
{
|
||||
uint8_t extendedDataSize = *buf;
|
||||
buf++; // size
|
||||
LogPrint (eLogDebug, "SSU: extended data of ", extendedDataSize, " bytes present");
|
||||
buf += extendedDataSize;
|
||||
}
|
||||
// process data
|
||||
ProcessFragments (buf);
|
||||
}
|
||||
|
||||
void SSUData::Send (std::shared_ptr<i2p::I2NPMessage> msg)
|
||||
{
|
||||
uint32_t msgID = msg->ToSSU ();
|
||||
if (m_SentMessages.find (msgID) != m_SentMessages.end())
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU: message ", msgID, " already sent");
|
||||
return;
|
||||
}
|
||||
if (m_SentMessages.empty ()) // schedule resend at first message only
|
||||
ScheduleResend ();
|
||||
|
||||
auto ret = m_SentMessages.emplace (msgID, m_Session.GetServer ().GetSentMessagesPool ().AcquireShared ());
|
||||
auto& sentMessage = ret.first->second;
|
||||
if (ret.second)
|
||||
{
|
||||
sentMessage->nextResendTime = i2p::util::GetSecondsSinceEpoch () + RESEND_INTERVAL;
|
||||
sentMessage->numResends = 0;
|
||||
}
|
||||
auto& fragments = sentMessage->fragments;
|
||||
size_t payloadSize = m_PacketSize - sizeof (SSUHeader) - 9; // 9 = flag + #frg(1) + messageID(4) + frag info (3)
|
||||
size_t len = msg->GetLength ();
|
||||
uint8_t * msgBuf = msg->GetSSUHeader ();
|
||||
|
||||
uint32_t fragmentNum = 0;
|
||||
while (len > 0 && fragmentNum <= 127)
|
||||
{
|
||||
auto fragment = m_Session.GetServer ().GetFragmentsPool ().AcquireShared ();
|
||||
fragment->fragmentNum = fragmentNum;
|
||||
uint8_t * payload = fragment->buf + sizeof (SSUHeader);
|
||||
*payload = DATA_FLAG_WANT_REPLY; // for compatibility
|
||||
payload++;
|
||||
*payload = 1; // always 1 message fragment per message
|
||||
payload++;
|
||||
htobe32buf (payload, msgID);
|
||||
payload += 4;
|
||||
bool isLast = (len <= payloadSize) || fragmentNum == 127; // 127 fragments max
|
||||
size_t size = isLast ? len : payloadSize;
|
||||
uint32_t fragmentInfo = (fragmentNum << 17);
|
||||
if (isLast)
|
||||
fragmentInfo |= 0x010000;
|
||||
|
||||
fragmentInfo |= size;
|
||||
fragmentInfo = htobe32 (fragmentInfo);
|
||||
memcpy (payload, (uint8_t *)(&fragmentInfo) + 1, 3);
|
||||
payload += 3;
|
||||
memcpy (payload, msgBuf, size);
|
||||
|
||||
size += payload - fragment->buf;
|
||||
uint8_t rem = size & 0x0F;
|
||||
if (rem) // make sure 16 bytes boundary
|
||||
{
|
||||
auto padding = 16 - rem;
|
||||
memset (fragment->buf + size, 0, padding);
|
||||
size += padding;
|
||||
}
|
||||
fragment->len = size;
|
||||
fragments.push_back (fragment);
|
||||
|
||||
// encrypt message with session key
|
||||
uint8_t buf[SSU_V4_MAX_PACKET_SIZE + 18];
|
||||
m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, fragment->buf, size, buf);
|
||||
try
|
||||
{
|
||||
m_Session.Send (buf, size);
|
||||
}
|
||||
catch (boost::system::system_error& ec)
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU: Can't send data fragment ", ec.what ());
|
||||
}
|
||||
if (!isLast)
|
||||
{
|
||||
len -= payloadSize;
|
||||
msgBuf += payloadSize;
|
||||
}
|
||||
else
|
||||
len = 0;
|
||||
fragmentNum++;
|
||||
}
|
||||
}
|
||||
|
||||
void SSUData::SendMsgAck (uint32_t msgID)
|
||||
{
|
||||
uint8_t buf[48 + 18] = {0}; // actual length is 44 = 37 + 7 but pad it to multiple of 16
|
||||
uint8_t * payload = buf + sizeof (SSUHeader);
|
||||
*payload = DATA_FLAG_EXPLICIT_ACKS_INCLUDED; // flag
|
||||
payload++;
|
||||
*payload = 1; // number of ACKs
|
||||
payload++;
|
||||
htobe32buf (payload, msgID); // msgID
|
||||
payload += 4;
|
||||
*payload = 0; // number of fragments
|
||||
|
||||
// encrypt message with session key
|
||||
m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, 48);
|
||||
m_Session.Send (buf, 48);
|
||||
}
|
||||
|
||||
void SSUData::SendFragmentAck (uint32_t msgID, uint64_t bits)
|
||||
{
|
||||
if (!bits) return;
|
||||
uint8_t buf[64 + 18] = {0};
|
||||
uint8_t * payload = buf + sizeof (SSUHeader);
|
||||
*payload = DATA_FLAG_ACK_BITFIELDS_INCLUDED; // flag
|
||||
payload++;
|
||||
*payload = 1; // number of ACK bitfields
|
||||
payload++;
|
||||
// one ack
|
||||
*(uint32_t *)(payload) = htobe32 (msgID); // msgID
|
||||
payload += 4;
|
||||
size_t len = 0;
|
||||
while (bits)
|
||||
{
|
||||
*payload = (bits & 0x7F); // next 7 bits
|
||||
bits >>= 7;
|
||||
if (bits) *payload &= 0x80; // 0x80 means non-last
|
||||
payload++; len++;
|
||||
}
|
||||
*payload = 0; // number of fragments
|
||||
len = (len <= 4) ? 48 : 64; // 48 = 37 + 7 + 4
|
||||
// encrypt message with session key
|
||||
m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, len);
|
||||
m_Session.Send (buf, len);
|
||||
}
|
||||
|
||||
void SSUData::ScheduleResend()
|
||||
{
|
||||
m_ResendTimer.cancel ();
|
||||
m_ResendTimer.expires_from_now (boost::posix_time::seconds(RESEND_INTERVAL));
|
||||
auto s = m_Session.shared_from_this();
|
||||
m_ResendTimer.async_wait ([s](const boost::system::error_code& ecode)
|
||||
{ s->m_Data.HandleResendTimer (ecode); });
|
||||
}
|
||||
|
||||
void SSUData::HandleResendTimer (const boost::system::error_code& ecode)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
uint8_t buf[SSU_V4_MAX_PACKET_SIZE + 18];
|
||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
int numResent = 0;
|
||||
for (auto it = m_SentMessages.begin (); it != m_SentMessages.end ();)
|
||||
{
|
||||
if (ts >= it->second->nextResendTime)
|
||||
{
|
||||
if (it->second->numResends < MAX_NUM_RESENDS)
|
||||
{
|
||||
for (auto& f: it->second->fragments)
|
||||
if (f)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, f->buf, f->len, buf);
|
||||
m_Session.Send (buf, f->len); // resend
|
||||
numResent++;
|
||||
}
|
||||
catch (boost::system::system_error& ec)
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU: Can't resend message ", it->first, " data fragment: ", ec.what ());
|
||||
}
|
||||
}
|
||||
|
||||
it->second->numResends++;
|
||||
it->second->nextResendTime += it->second->numResends*RESEND_INTERVAL;
|
||||
++it;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogInfo, "SSU: message ", it->first, " has not been ACKed after ", MAX_NUM_RESENDS, " attempts, deleted");
|
||||
it = m_SentMessages.erase (it);
|
||||
}
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
if (m_SentMessages.empty ()) return; // nothing to resend
|
||||
if (numResent < MAX_OUTGOING_WINDOW_SIZE)
|
||||
ScheduleResend ();
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "SSU: resend window exceeds max size. Session terminated");
|
||||
m_Session.Close ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SSUData::CleanUp (uint64_t ts)
|
||||
{
|
||||
for (auto it = m_IncompleteMessages.begin (); it != m_IncompleteMessages.end ();)
|
||||
{
|
||||
if (ts > it->second->lastFragmentInsertTime + INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT)
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU: message ", it->first, " was not completed in ", INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT, " seconds, deleted");
|
||||
it = m_IncompleteMessages.erase (it);
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
|
||||
if (m_ReceivedMessages.size () > MAX_NUM_RECEIVED_MESSAGES || ts > m_LastMessageReceivedTime + DECAY_INTERVAL)
|
||||
// decay
|
||||
m_ReceivedMessages.clear ();
|
||||
else
|
||||
{
|
||||
// delete old received messages
|
||||
for (auto it = m_ReceivedMessages.begin (); it != m_ReceivedMessages.end ();)
|
||||
{
|
||||
if (ts > it->second + RECEIVED_MESSAGES_CLEANUP_TIMEOUT)
|
||||
it = m_ReceivedMessages.erase (it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,131 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
* See full license text in LICENSE file at top of project tree
|
||||
*/
|
||||
|
||||
#ifndef SSU_DATA_H__
|
||||
#define SSU_DATA_H__
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
#include <boost/asio.hpp>
|
||||
#include "I2NPProtocol.h"
|
||||
#include "Identity.h"
|
||||
#include "RouterInfo.h"
|
||||
#include "TransportSession.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace transport
|
||||
{
|
||||
const size_t SSU_MTU_V4 = 1484;
|
||||
const size_t SSU_MTU_V6 = 1488;
|
||||
const size_t SSU_V4_MAX_PACKET_SIZE = SSU_MTU_V4 - IPV4_HEADER_SIZE - UDP_HEADER_SIZE; // 1456
|
||||
const size_t SSU_V6_MAX_PACKET_SIZE = SSU_MTU_V6 - IPV6_HEADER_SIZE - UDP_HEADER_SIZE; // 1440
|
||||
const int RESEND_INTERVAL = 3; // in seconds
|
||||
const int MAX_NUM_RESENDS = 5;
|
||||
const int DECAY_INTERVAL = 20; // in seconds
|
||||
const int INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT = 30; // in seconds
|
||||
const int RECEIVED_MESSAGES_CLEANUP_TIMEOUT = 40; // in seconds
|
||||
const unsigned int MAX_NUM_RECEIVED_MESSAGES = 1000; // how many msgID we store for duplicates check
|
||||
const int MAX_OUTGOING_WINDOW_SIZE = 200; // how many unacked message we can store
|
||||
// data flags
|
||||
const uint8_t DATA_FLAG_EXTENDED_DATA_INCLUDED = 0x02;
|
||||
const uint8_t DATA_FLAG_WANT_REPLY = 0x04;
|
||||
const uint8_t DATA_FLAG_REQUEST_PREVIOUS_ACKS = 0x08;
|
||||
const uint8_t DATA_FLAG_EXPLICIT_CONGESTION_NOTIFICATION = 0x10;
|
||||
const uint8_t DATA_FLAG_ACK_BITFIELDS_INCLUDED = 0x40;
|
||||
const uint8_t DATA_FLAG_EXPLICIT_ACKS_INCLUDED = 0x80;
|
||||
|
||||
struct Fragment
|
||||
{
|
||||
int fragmentNum;
|
||||
size_t len;
|
||||
bool isLast;
|
||||
uint8_t buf[SSU_V4_MAX_PACKET_SIZE + 18]; // use biggest
|
||||
|
||||
Fragment () = default;
|
||||
Fragment (int n, const uint8_t * b, int l, bool last):
|
||||
fragmentNum (n), len (l), isLast (last) { memcpy (buf, b, len); };
|
||||
};
|
||||
|
||||
struct FragmentCmp
|
||||
{
|
||||
bool operator() (const std::shared_ptr<Fragment>& f1, const std::shared_ptr<Fragment>& f2) const
|
||||
{
|
||||
return f1->fragmentNum < f2->fragmentNum;
|
||||
};
|
||||
};
|
||||
|
||||
struct IncompleteMessage
|
||||
{
|
||||
std::shared_ptr<I2NPMessage> msg;
|
||||
int nextFragmentNum;
|
||||
uint32_t lastFragmentInsertTime; // in seconds
|
||||
uint64_t receivedFragmentsBits;
|
||||
std::set<std::shared_ptr<Fragment>, FragmentCmp> savedFragments;
|
||||
|
||||
IncompleteMessage (std::shared_ptr<I2NPMessage>&& m): msg (m), nextFragmentNum (0),
|
||||
lastFragmentInsertTime (0), receivedFragmentsBits (0) {};
|
||||
void AttachNextFragment (const uint8_t * fragment, size_t fragmentSize);
|
||||
};
|
||||
|
||||
struct SentMessage
|
||||
{
|
||||
std::vector<std::shared_ptr<Fragment> > fragments;
|
||||
uint32_t nextResendTime; // in seconds
|
||||
int numResends;
|
||||
};
|
||||
|
||||
class SSUSession;
|
||||
class SSUData
|
||||
{
|
||||
public:
|
||||
|
||||
SSUData (SSUSession& session);
|
||||
~SSUData ();
|
||||
|
||||
void Start ();
|
||||
void Stop ();
|
||||
void CleanUp (uint64_t ts);
|
||||
|
||||
void ProcessMessage (uint8_t * buf, size_t len);
|
||||
void FlushReceivedMessage ();
|
||||
void Send (std::shared_ptr<i2p::I2NPMessage> msg);
|
||||
|
||||
void AdjustPacketSize (std::shared_ptr<const i2p::data::RouterInfo> remoteRouter);
|
||||
void UpdatePacketSize (const i2p::data::IdentHash& remoteIdent);
|
||||
|
||||
private:
|
||||
|
||||
void SendMsgAck (uint32_t msgID);
|
||||
void SendFragmentAck (uint32_t msgID, uint64_t bits);
|
||||
void ProcessAcks (uint8_t *& buf, uint8_t flag);
|
||||
void ProcessFragments (uint8_t * buf);
|
||||
void ProcessSentMessageAck (uint32_t msgID);
|
||||
|
||||
void ScheduleResend ();
|
||||
void HandleResendTimer (const boost::system::error_code& ecode);
|
||||
|
||||
private:
|
||||
|
||||
SSUSession& m_Session;
|
||||
std::map<uint32_t, std::shared_ptr<IncompleteMessage> > m_IncompleteMessages;
|
||||
std::map<uint32_t, std::shared_ptr<SentMessage> > m_SentMessages;
|
||||
std::unordered_map<uint32_t, uint64_t> m_ReceivedMessages; // msgID -> timestamp in seconds
|
||||
boost::asio::deadline_timer m_ResendTimer;
|
||||
int m_MaxPacketSize, m_PacketSize;
|
||||
i2p::I2NPMessagesHandler m_Handler;
|
||||
uint32_t m_LastMessageReceivedTime; // in second
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
@ -1,177 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
* See full license text in LICENSE file at top of project tree
|
||||
*/
|
||||
|
||||
#ifndef SSU_SESSION_H__
|
||||
#define SSU_SESSION_H__
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <set>
|
||||
#include <memory>
|
||||
#include "Crypto.h"
|
||||
#include "I2NPProtocol.h"
|
||||
#include "TransportSession.h"
|
||||
#include "SSUData.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace transport
|
||||
{
|
||||
const uint8_t SSU_HEADER_EXTENDED_OPTIONS_INCLUDED = 0x04;
|
||||
struct SSUHeader
|
||||
{
|
||||
uint8_t mac[16];
|
||||
uint8_t iv[16];
|
||||
uint8_t flag;
|
||||
uint8_t time[4];
|
||||
|
||||
uint8_t GetPayloadType () const { return flag >> 4; };
|
||||
bool IsExtendedOptions () const { return flag & SSU_HEADER_EXTENDED_OPTIONS_INCLUDED; };
|
||||
};
|
||||
|
||||
const int SSU_CONNECT_TIMEOUT = 5; // 5 seconds
|
||||
const int SSU_TERMINATION_TIMEOUT = 330; // 5.5 minutes
|
||||
const int SSU_CLOCK_SKEW = 60; // in seconds
|
||||
const int SSU_CLOCK_THRESHOLD = 15; // in seconds, if more we should adjust
|
||||
const size_t SSU_MAX_I2NP_MESSAGE_SIZE = 32768;
|
||||
|
||||
// payload types (4 bits)
|
||||
const uint8_t PAYLOAD_TYPE_SESSION_REQUEST = 0;
|
||||
const uint8_t PAYLOAD_TYPE_SESSION_CREATED = 1;
|
||||
const uint8_t PAYLOAD_TYPE_SESSION_CONFIRMED = 2;
|
||||
const uint8_t PAYLOAD_TYPE_RELAY_REQUEST = 3;
|
||||
const uint8_t PAYLOAD_TYPE_RELAY_RESPONSE = 4;
|
||||
const uint8_t PAYLOAD_TYPE_RELAY_INTRO = 5;
|
||||
const uint8_t PAYLOAD_TYPE_DATA = 6;
|
||||
const uint8_t PAYLOAD_TYPE_PEER_TEST = 7;
|
||||
const uint8_t PAYLOAD_TYPE_SESSION_DESTROYED = 8;
|
||||
|
||||
// extended options
|
||||
const uint16_t EXTENDED_OPTIONS_FLAG_REQUEST_RELAY_TAG = 0x0001;
|
||||
|
||||
enum SessionState
|
||||
{
|
||||
eSessionStateUnknown,
|
||||
eSessionStateIntroduced,
|
||||
eSessionStateEstablished,
|
||||
eSessionStateClosed,
|
||||
eSessionStateFailed
|
||||
};
|
||||
|
||||
enum PeerTestParticipant
|
||||
{
|
||||
ePeerTestParticipantUnknown = 0,
|
||||
ePeerTestParticipantAlice1,
|
||||
ePeerTestParticipantAlice2,
|
||||
ePeerTestParticipantBob,
|
||||
ePeerTestParticipantCharlie
|
||||
};
|
||||
|
||||
class SSUServer;
|
||||
class SSUSession: public TransportSession, public std::enable_shared_from_this<SSUSession>
|
||||
{
|
||||
public:
|
||||
|
||||
SSUSession (SSUServer& server, boost::asio::ip::udp::endpoint& remoteEndpoint,
|
||||
std::shared_ptr<const i2p::data::RouterInfo> router = nullptr, bool peerTest = false);
|
||||
void ProcessNextMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint);
|
||||
~SSUSession ();
|
||||
|
||||
void Connect ();
|
||||
void WaitForConnect ();
|
||||
void Introduce (const i2p::data::RouterInfo::Introducer& introducer,
|
||||
std::shared_ptr<const i2p::data::RouterInfo> to); // Alice to Charlie
|
||||
void WaitForIntroduction ();
|
||||
void Close ();
|
||||
void Done ();
|
||||
void Failed ();
|
||||
const boost::asio::ip::udp::endpoint& GetRemoteEndpoint () { return m_RemoteEndpoint; };
|
||||
SSUServer& GetServer () { return m_Server; };
|
||||
|
||||
bool IsV6 () const { return m_RemoteEndpoint.address ().is_v6 (); };
|
||||
void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs);
|
||||
void SendPeerTest (); // Alice
|
||||
|
||||
SessionState GetState () const { return m_State; };
|
||||
size_t GetNumSentBytes () const { return m_NumSentBytes; };
|
||||
size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; };
|
||||
|
||||
void SendKeepAlive ();
|
||||
uint32_t GetRelayTag () const { return m_RelayTag; };
|
||||
const i2p::data::RouterInfo::IntroKey& GetIntroKey () const { return m_IntroKey; };
|
||||
|
||||
void FlushData ();
|
||||
void CleanUp (uint64_t ts);
|
||||
|
||||
private:
|
||||
|
||||
boost::asio::io_service& GetService ();
|
||||
void CreateAESandMacKey (const uint8_t * pubKey);
|
||||
size_t GetSSUHeaderSize (const uint8_t * buf) const;
|
||||
void PostI2NPMessages (std::vector<std::shared_ptr<I2NPMessage> > msgs);
|
||||
void ProcessMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); // call for established session
|
||||
void ProcessSessionRequest (const uint8_t * buf, size_t len);
|
||||
void SendSessionRequest ();
|
||||
void SendRelayRequest (const i2p::data::RouterInfo::Introducer& introducer, uint32_t nonce);
|
||||
void ProcessSessionCreated (uint8_t * buf, size_t len);
|
||||
void SendSessionCreated (const uint8_t * x, bool sendRelayTag = true);
|
||||
void ProcessSessionConfirmed (const uint8_t * buf, size_t len);
|
||||
void SendSessionConfirmed (const uint8_t * y, const uint8_t * ourAddress, size_t ourAddressLen);
|
||||
void ProcessRelayRequest (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& from);
|
||||
void SendRelayResponse (uint32_t nonce, const boost::asio::ip::udp::endpoint& from,
|
||||
const uint8_t * introKey, const boost::asio::ip::udp::endpoint& to);
|
||||
void SendRelayIntro (std::shared_ptr<SSUSession> session, const boost::asio::ip::udp::endpoint& from);
|
||||
void ProcessRelayResponse (const uint8_t * buf, size_t len);
|
||||
void ProcessRelayIntro (const uint8_t * buf, size_t len);
|
||||
void Established ();
|
||||
void ScheduleConnectTimer ();
|
||||
void HandleConnectTimer (const boost::system::error_code& ecode);
|
||||
void ProcessPeerTest (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint);
|
||||
void SendPeerTest (uint32_t nonce, const boost::asio::ip::address& address, uint16_t port, const uint8_t * introKey, bool toAddress = true, bool sendAddress = true);
|
||||
void ProcessData (uint8_t * buf, size_t len);
|
||||
void SendSessionDestroyed ();
|
||||
void Send (uint8_t type, const uint8_t * payload, size_t len); // with session key
|
||||
void Send (const uint8_t * buf, size_t size);
|
||||
|
||||
void FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len, const i2p::crypto::AESKey& aesKey,
|
||||
const uint8_t * iv, const i2p::crypto::MACKey& macKey, uint8_t flag = 0);
|
||||
void FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len); // with session key
|
||||
void FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * in, size_t len, uint8_t * out); // with session key
|
||||
void Decrypt (uint8_t * buf, size_t len, const i2p::crypto::AESKey& aesKey);
|
||||
void DecryptSessionKey (uint8_t * buf, size_t len);
|
||||
bool Validate (uint8_t * buf, size_t len, const i2p::crypto::MACKey& macKey);
|
||||
|
||||
void Reset ();
|
||||
|
||||
static size_t ExtractIPAddressAndPort (const uint8_t * buf, size_t len, boost::asio::ip::address& ip, uint16_t& port); // returns actual buf size
|
||||
|
||||
private:
|
||||
|
||||
friend class SSUData; // TODO: change in later
|
||||
SSUServer& m_Server;
|
||||
const boost::asio::ip::udp::endpoint m_RemoteEndpoint;
|
||||
boost::asio::deadline_timer m_ConnectTimer;
|
||||
bool m_IsPeerTest;
|
||||
SessionState m_State;
|
||||
bool m_IsSessionKey;
|
||||
uint32_t m_RelayTag; // received from peer
|
||||
uint32_t m_SentRelayTag; // sent by us
|
||||
i2p::crypto::CBCEncryption m_SessionKeyEncryption;
|
||||
i2p::crypto::CBCDecryption m_SessionKeyDecryption;
|
||||
i2p::crypto::AESKey m_SessionKey;
|
||||
i2p::crypto::MACKey m_MacKey;
|
||||
i2p::data::RouterInfo::IntroKey m_IntroKey;
|
||||
SSUData m_Data;
|
||||
bool m_IsDataReceived;
|
||||
std::unique_ptr<SignedData> m_SignedData; // we need it for SessionConfirmed only
|
||||
std::map<uint32_t, std::pair <std::shared_ptr<const i2p::data::RouterInfo>, uint64_t > > m_RelayRequests; // nonce->(Charlie, timestamp)
|
||||
std::shared_ptr<i2p::crypto::DHKeys> m_DHKeysPair; // X - for client and Y - for server
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -25,7 +25,7 @@ namespace util
|
||||
uint32_t GetHoursSinceEpoch ();
|
||||
|
||||
void GetCurrentDate (char * date); // returns date as YYYYMMDD string, 9 bytes
|
||||
void GetDateString (uint64_t timestamp, char * date); // timestap is seconds since epoch, returns date as YYYYMMDD string, 9 bytes
|
||||
void GetDateString (uint64_t timestamp, char * date); // timestamp is seconds since epoch, returns date as YYYYMMDD string, 9 bytes
|
||||
void AdjustTimeOffset (int64_t offset); // in seconds from current
|
||||
|
||||
class NTPTimeSync
|
||||
|
@ -136,7 +136,7 @@ namespace transport
|
||||
Transports::Transports ():
|
||||
m_IsOnline (true), m_IsRunning (false), m_IsNAT (true), m_CheckReserved(true), m_Thread (nullptr),
|
||||
m_Service (nullptr), m_Work (nullptr), m_PeerCleanupTimer (nullptr), m_PeerTestTimer (nullptr),
|
||||
m_SSUServer (nullptr), m_SSU2Server (nullptr), m_NTCP2Server (nullptr),
|
||||
m_SSU2Server (nullptr), m_NTCP2Server (nullptr),
|
||||
m_X25519KeysPairSupplier (15), // 15 pre-generated keys
|
||||
m_TotalSentBytes(0), m_TotalReceivedBytes(0), m_TotalTransitTransmittedBytes (0),
|
||||
m_InBandwidth (0), m_OutBandwidth (0), m_TransitBandwidth(0),
|
||||
@ -157,7 +157,7 @@ namespace transport
|
||||
}
|
||||
}
|
||||
|
||||
void Transports::Start (bool enableNTCP2, bool enableSSU, bool enableSSU2)
|
||||
void Transports::Start (bool enableNTCP2, bool enableSSU2)
|
||||
{
|
||||
if (!m_Service)
|
||||
{
|
||||
@ -205,22 +205,6 @@ namespace transport
|
||||
m_NTCP2Server = new NTCP2Server ();
|
||||
}
|
||||
|
||||
// create SSU server
|
||||
int ssuPort = 0;
|
||||
if (enableSSU)
|
||||
{
|
||||
auto& addresses = context.GetRouterInfo ().GetAddresses ();
|
||||
for (const auto& address: addresses)
|
||||
{
|
||||
if (!address) continue;
|
||||
if (address->transportStyle == RouterInfo::eTransportSSU)
|
||||
{
|
||||
ssuPort = address->port;
|
||||
m_SSUServer = new SSUServer (address->port);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// create SSU2 server
|
||||
if (enableSSU2)
|
||||
{
|
||||
@ -255,7 +239,6 @@ namespace transport
|
||||
if (!ec)
|
||||
{
|
||||
if (m_NTCP2Server) m_NTCP2Server->SetLocalAddress (addr);
|
||||
if (m_SSUServer) m_SSUServer->SetLocalAddress (addr);
|
||||
if (m_SSU2Server) m_SSU2Server->SetLocalAddress (addr);
|
||||
}
|
||||
}
|
||||
@ -282,7 +265,6 @@ namespace transport
|
||||
if (!ec)
|
||||
{
|
||||
if (m_NTCP2Server) m_NTCP2Server->SetLocalAddress (addr);
|
||||
if (m_SSUServer) m_SSUServer->SetLocalAddress (addr);
|
||||
if (m_SSU2Server) m_SSU2Server->SetLocalAddress (addr);
|
||||
}
|
||||
}
|
||||
@ -315,22 +297,7 @@ namespace transport
|
||||
// start servers
|
||||
if (m_NTCP2Server) m_NTCP2Server->Start ();
|
||||
if (m_SSU2Server) m_SSU2Server->Start ();
|
||||
if (m_SSUServer)
|
||||
{
|
||||
LogPrint (eLogInfo, "Transports: Start listening UDP port ", ssuPort);
|
||||
try
|
||||
{
|
||||
m_SSUServer->Start ();
|
||||
}
|
||||
catch (std::exception& ex )
|
||||
{
|
||||
LogPrint(eLogError, "Transports: Failed to bind to UDP port", ssuPort);
|
||||
m_SSUServer->Stop ();
|
||||
delete m_SSUServer;
|
||||
m_SSUServer = nullptr;
|
||||
}
|
||||
}
|
||||
if (m_SSUServer || m_SSU2Server) DetectExternalIP ();
|
||||
if (m_SSU2Server) DetectExternalIP ();
|
||||
|
||||
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));
|
||||
@ -347,12 +314,6 @@ namespace transport
|
||||
if (m_PeerCleanupTimer) m_PeerCleanupTimer->cancel ();
|
||||
if (m_PeerTestTimer) m_PeerTestTimer->cancel ();
|
||||
m_Peers.clear ();
|
||||
if (m_SSUServer)
|
||||
{
|
||||
m_SSUServer->Stop ();
|
||||
delete m_SSUServer;
|
||||
m_SSUServer = nullptr;
|
||||
}
|
||||
|
||||
if (m_SSU2Server)
|
||||
{
|
||||
@ -538,21 +499,6 @@ namespace transport
|
||||
}
|
||||
break;
|
||||
}
|
||||
case i2p::data::RouterInfo::eSSUV4:
|
||||
case i2p::data::RouterInfo::eSSUV6:
|
||||
{
|
||||
if (!m_SSUServer) continue;
|
||||
std::shared_ptr<const RouterInfo::Address> address = (tr == i2p::data::RouterInfo::eSSUV6) ?
|
||||
peer.router->GetSSUV6Address () : peer.router->GetSSUAddress (true);
|
||||
if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host))
|
||||
address = nullptr;
|
||||
if (address && address->IsReachableSSU ())
|
||||
{
|
||||
if (m_SSUServer->CreateSession (peer.router, address))
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case i2p::data::RouterInfo::eNTCP2V6Mesh:
|
||||
{
|
||||
if (!m_NTCP2Server) continue;
|
||||
@ -595,9 +541,7 @@ namespace transport
|
||||
i2p::data::RouterInfo::eNTCP2V4,
|
||||
i2p::data::RouterInfo::eSSU2V6,
|
||||
i2p::data::RouterInfo::eSSU2V4,
|
||||
i2p::data::RouterInfo::eNTCP2V6Mesh,
|
||||
i2p::data::RouterInfo::eSSUV6,
|
||||
i2p::data::RouterInfo::eSSUV4
|
||||
i2p::data::RouterInfo::eNTCP2V6Mesh
|
||||
},
|
||||
ssu2Priority =
|
||||
{
|
||||
@ -605,9 +549,7 @@ namespace transport
|
||||
i2p::data::RouterInfo::eSSU2V4,
|
||||
i2p::data::RouterInfo::eNTCP2V6,
|
||||
i2p::data::RouterInfo::eNTCP2V4,
|
||||
i2p::data::RouterInfo::eNTCP2V6Mesh,
|
||||
i2p::data::RouterInfo::eSSUV6,
|
||||
i2p::data::RouterInfo::eSSUV4
|
||||
i2p::data::RouterInfo::eNTCP2V6Mesh
|
||||
};
|
||||
if (!peer.router) return;
|
||||
auto compatibleTransports = context.GetRouterInfo ().GetCompatibleTransports (false) &
|
||||
@ -654,7 +596,7 @@ namespace transport
|
||||
i2p::context.SetStatus (eRouterStatusOK);
|
||||
return;
|
||||
}
|
||||
if (m_SSUServer || m_SSU2Server)
|
||||
if (m_SSU2Server)
|
||||
PeerTest ();
|
||||
else
|
||||
LogPrint (eLogWarning, "Transports: Can't detect external IP. SSU or SSU2 is not available");
|
||||
@ -662,103 +604,44 @@ namespace transport
|
||||
|
||||
void Transports::PeerTest (bool ipv4, bool ipv6)
|
||||
{
|
||||
if (RoutesRestricted() || (!m_SSUServer && !m_SSU2Server)) return;
|
||||
if (RoutesRestricted() || !m_SSU2Server || m_SSU2Server->UsesProxy ()) return;
|
||||
if (ipv4 && i2p::context.SupportsV4 ())
|
||||
{
|
||||
LogPrint (eLogInfo, "Transports: Started peer test IPv4");
|
||||
std::set<i2p::data::IdentHash> excluded;
|
||||
excluded.insert (i2p::context.GetIdentHash ()); // don't pick own router
|
||||
if (m_SSUServer)
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
bool statusChanged = false;
|
||||
for (int i = 0; i < 5; i++)
|
||||
auto router = i2p::data::netdb.GetRandomSSU2PeerTestRouter (true, excluded); // v4
|
||||
if (router)
|
||||
{
|
||||
auto router = i2p::data::netdb.GetRandomPeerTestRouter (true, excluded); // v4
|
||||
if (router)
|
||||
{
|
||||
auto addr = router->GetSSUAddress (true); // ipv4
|
||||
if (addr && !i2p::util::net::IsInReservedRange(addr->host))
|
||||
{
|
||||
if (!statusChanged)
|
||||
{
|
||||
statusChanged = true;
|
||||
i2p::context.SetStatus (eRouterStatusTesting); // first time only
|
||||
}
|
||||
m_SSUServer->CreateSession (router, addr, true); // peer test v4
|
||||
}
|
||||
excluded.insert (router->GetIdentHash ());
|
||||
}
|
||||
}
|
||||
if (!statusChanged)
|
||||
LogPrint (eLogWarning, "Transports: Can't find routers for peer test IPv4");
|
||||
}
|
||||
// SSU2
|
||||
if (m_SSU2Server && !m_SSU2Server->UsesProxy ())
|
||||
{
|
||||
excluded.clear ();
|
||||
excluded.insert (i2p::context.GetIdentHash ());
|
||||
int numTests = m_SSUServer ? 3 : 5;
|
||||
for (int i = 0; i < numTests; i++)
|
||||
{
|
||||
auto router = i2p::data::netdb.GetRandomSSU2PeerTestRouter (true, excluded); // v4
|
||||
if (router)
|
||||
{
|
||||
if (i2p::context.GetStatus () != eRouterStatusTesting)
|
||||
i2p::context.SetStatusSSU2 (eRouterStatusTesting);
|
||||
m_SSU2Server->StartPeerTest (router, true);
|
||||
excluded.insert (router->GetIdentHash ());
|
||||
}
|
||||
if (i2p::context.GetStatus () != eRouterStatusTesting)
|
||||
i2p::context.SetStatus (eRouterStatusTesting);
|
||||
m_SSU2Server->StartPeerTest (router, true);
|
||||
excluded.insert (router->GetIdentHash ());
|
||||
}
|
||||
}
|
||||
if (excluded.size () <= 1)
|
||||
LogPrint (eLogWarning, "Transports: Can't find routers for peer test IPv4");
|
||||
}
|
||||
if (ipv6 && i2p::context.SupportsV6 ())
|
||||
{
|
||||
LogPrint (eLogInfo, "Transports: Started peer test IPv6");
|
||||
std::set<i2p::data::IdentHash> excluded;
|
||||
excluded.insert (i2p::context.GetIdentHash ()); // don't pick own router
|
||||
if (m_SSUServer)
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
bool statusChanged = false;
|
||||
for (int i = 0; i < 5; i++)
|
||||
auto router = i2p::data::netdb.GetRandomSSU2PeerTestRouter (false, excluded); // v6
|
||||
if (router)
|
||||
{
|
||||
auto router = i2p::data::netdb.GetRandomPeerTestRouter (false, excluded); // v6
|
||||
if (router)
|
||||
{
|
||||
auto addr = router->GetSSUV6Address ();
|
||||
if (addr && !i2p::util::net::IsInReservedRange(addr->host))
|
||||
{
|
||||
if (!statusChanged)
|
||||
{
|
||||
statusChanged = true;
|
||||
i2p::context.SetStatusV6 (eRouterStatusTesting); // first time only
|
||||
}
|
||||
m_SSUServer->CreateSession (router, addr, true); // peer test v6
|
||||
}
|
||||
excluded.insert (router->GetIdentHash ());
|
||||
}
|
||||
}
|
||||
if (!statusChanged)
|
||||
LogPrint (eLogWarning, "Transports: Can't find routers for peer test IPv6");
|
||||
}
|
||||
|
||||
// SSU2
|
||||
if (m_SSU2Server && !m_SSU2Server->UsesProxy ())
|
||||
{
|
||||
excluded.clear ();
|
||||
excluded.insert (i2p::context.GetIdentHash ());
|
||||
int numTests = m_SSUServer ? 3 : 5;
|
||||
for (int i = 0; i < numTests; i++)
|
||||
{
|
||||
auto router = i2p::data::netdb.GetRandomSSU2PeerTestRouter (false, excluded); // v6
|
||||
if (router)
|
||||
{
|
||||
if (i2p::context.GetStatusV6 () != eRouterStatusTesting)
|
||||
i2p::context.SetStatusV6SSU2 (eRouterStatusTesting);
|
||||
m_SSU2Server->StartPeerTest (router, false);
|
||||
excluded.insert (router->GetIdentHash ());
|
||||
}
|
||||
if (i2p::context.GetStatusV6 () != eRouterStatusTesting)
|
||||
i2p::context.SetStatusV6 (eRouterStatusTesting);
|
||||
m_SSU2Server->StartPeerTest (router, false);
|
||||
excluded.insert (router->GetIdentHash ());
|
||||
}
|
||||
}
|
||||
if (excluded.size () <= 1)
|
||||
LogPrint (eLogWarning, "Transports: Can't find routers for peer test IPv6");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1075,7 +958,6 @@ namespace transport
|
||||
i2p::context.SetSupportsV4 (ipv4);
|
||||
i2p::context.SetSupportsMesh (ygg, yggaddr);
|
||||
|
||||
i2p::context.RemoveNTCPAddress (!ipv6); // TODO: remove later
|
||||
bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2);
|
||||
if (ntcp2)
|
||||
{
|
||||
@ -1108,15 +990,13 @@ namespace transport
|
||||
if (!ipv4 && !ipv6)
|
||||
i2p::context.SetStatus (eRouterStatusMesh);
|
||||
}
|
||||
bool ssu; i2p::config::GetOption("ssu", ssu);
|
||||
if (!ssu) i2p::context.RemoveSSUAddress (); // TODO: remove later
|
||||
bool ssu2; i2p::config::GetOption("ssu2.enabled", ssu2);
|
||||
if (ssu2 && i2p::config::IsDefault ("ssu2.enabled") && !ipv4 && !ipv6)
|
||||
ssu2 = false; // don't enable ssu2 for yggdrasil only router
|
||||
if (ssu2)
|
||||
{
|
||||
uint16_t ssu2port; i2p::config::GetOption("ssu2.port", ssu2port);
|
||||
if (!ssu2port && port) ssu2port = ssu ? (port + 1) : port;
|
||||
if (!ssu2port && port) ssu2port = port;
|
||||
bool published; i2p::config::GetOption("ssu2.published", published);
|
||||
if (published)
|
||||
i2p::context.PublishSSU2Address (ssu2port, true, ipv4, ipv6); // publish
|
||||
|
@ -21,7 +21,6 @@
|
||||
#include <atomic>
|
||||
#include <boost/asio.hpp>
|
||||
#include "TransportSession.h"
|
||||
#include "SSU.h"
|
||||
#include "SSU2.h"
|
||||
#include "NTCP2.h"
|
||||
#include "RouterInfo.h"
|
||||
@ -96,10 +95,9 @@ namespace transport
|
||||
Transports ();
|
||||
~Transports ();
|
||||
|
||||
void Start (bool enableNTCP2=true, bool enableSSU=true, bool enableSSU2=false);
|
||||
void Start (bool enableNTCP2=true, bool enableSSU2=true);
|
||||
void Stop ();
|
||||
|
||||
bool IsBoundSSU() const { return m_SSUServer != nullptr; }
|
||||
bool IsBoundSSU2() const { return m_SSU2Server != nullptr; }
|
||||
bool IsBoundNTCP2() const { return m_NTCP2Server != nullptr; }
|
||||
|
||||
@ -170,7 +168,6 @@ namespace transport
|
||||
boost::asio::io_service::work * m_Work;
|
||||
boost::asio::deadline_timer * m_PeerCleanupTimer, * m_PeerTestTimer;
|
||||
|
||||
SSUServer * m_SSUServer;
|
||||
SSU2Server * m_SSU2Server;
|
||||
NTCP2Server * m_NTCP2Server;
|
||||
mutable std::mutex m_PeersMutex;
|
||||
@ -196,7 +193,6 @@ namespace transport
|
||||
public:
|
||||
|
||||
// for HTTP only
|
||||
const SSUServer * GetSSUServer () const { return m_SSUServer; };
|
||||
const NTCP2Server * GetNTCP2Server () const { return m_NTCP2Server; };
|
||||
const SSU2Server * GetSSU2Server () const { return m_SSU2Server; };
|
||||
const decltype(m_Peers)& GetPeers () const { return m_Peers; };
|
||||
|
@ -76,12 +76,12 @@ namespace tunnel
|
||||
if (m_NumInboundHops > size)
|
||||
{
|
||||
m_NumInboundHops = size;
|
||||
LogPrint (eLogInfo, "Tunnels: Inbound tunnel length has beed adjusted to ", size, " for explicit peers");
|
||||
LogPrint (eLogInfo, "Tunnels: Inbound tunnel length has been adjusted to ", size, " for explicit peers");
|
||||
}
|
||||
if (m_NumOutboundHops > size)
|
||||
{
|
||||
m_NumOutboundHops = size;
|
||||
LogPrint (eLogInfo, "Tunnels: Outbound tunnel length has beed adjusted to ", size, " for explicit peers");
|
||||
LogPrint (eLogInfo, "Tunnels: Outbound tunnel length has been adjusted to ", size, " for explicit peers");
|
||||
}
|
||||
m_NumInboundTunnels = 1;
|
||||
m_NumOutboundTunnels = 1;
|
||||
|
@ -84,7 +84,7 @@ const char *inet_ntop_xp(int af, const void *src, char *dst, socklen_t size)
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
/* cannot direclty use &size because of strict aliasing rules */
|
||||
/* cannot directly use &size because of strict aliasing rules */
|
||||
return (WSAAddressToString((struct sockaddr *)&ss, sizeof(ss), NULL, dst, &s) == 0)? dst : NULL;
|
||||
}
|
||||
|
||||
|
@ -259,7 +259,7 @@ namespace client
|
||||
void I2PUDPClientTunnel::HandleRecvFromLocal (const boost::system::error_code & ec, std::size_t transferred)
|
||||
{
|
||||
if (m_cancel_resolve) {
|
||||
LogPrint (eLogDebug, "UDP Client: Ignoring incomming data: stopping");
|
||||
LogPrint (eLogDebug, "UDP Client: Ignoring incoming data: stopping");
|
||||
return;
|
||||
}
|
||||
if (ec) {
|
||||
|
@ -16,28 +16,26 @@ void BlindTest (SigningKeyType sigType)
|
||||
auto timestamp = GetSecondsSinceEpoch ();
|
||||
char date[9];
|
||||
GetDateString (timestamp, date);
|
||||
uint8_t blindedPriv[64], blindedPub[128];
|
||||
uint8_t blindedPriv[32], blindedPub[32];
|
||||
auto publicKeyLen = blindedKey.BlindPrivateKey (keys.GetSigningPrivateKey (), date, blindedPriv, blindedPub);
|
||||
uint8_t blindedPub1[128];
|
||||
uint8_t blindedPub1[32];
|
||||
blindedKey.GetBlindedKey (date, blindedPub1);
|
||||
// check if public key produced from private blinded key matches blided public key
|
||||
assert (!memcmp (blindedPub, blindedPub1, publicKeyLen));
|
||||
// try to sign and verify
|
||||
std::unique_ptr<Signer> blindedSigner (PrivateKeys::CreateSigner (sigType, blindedPriv));
|
||||
uint8_t buf[100], signature[128];
|
||||
std::unique_ptr<Signer> blindedSigner (PrivateKeys::CreateSigner (blindedKey.GetBlindedSigType (), blindedPriv));
|
||||
uint8_t buf[100], signature[64];
|
||||
memset (buf, 1, 100);
|
||||
blindedSigner->Sign (buf, 100, signature);
|
||||
std::unique_ptr<Verifier> blindedVerifier (IdentityEx::CreateVerifier (sigType));
|
||||
blindedVerifier->SetPublicKey (blindedPub1);
|
||||
std::unique_ptr<Verifier> blindedVerifier (IdentityEx::CreateVerifier (blindedKey.GetBlindedSigType ()));
|
||||
blindedVerifier->SetPublicKey (blindedPub);
|
||||
assert (blindedVerifier->Verify (buf, 100, signature));
|
||||
}
|
||||
|
||||
int main ()
|
||||
{
|
||||
// EdDSA test
|
||||
BlindTest (SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519);
|
||||
// RedDSA test
|
||||
BlindTest (SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519);
|
||||
// P256 test
|
||||
BlindTest (SIGNING_KEY_TYPE_ECDSA_SHA256_P256);
|
||||
// P384 test
|
||||
BlindTest (SIGNING_KEY_TYPE_ECDSA_SHA384_P384);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user