Browse Source

Fix up I2PTunnel UDP tunnels

pull/628/head
Jeff Becker 8 years ago
parent
commit
c65dc44f20
No known key found for this signature in database
GPG Key ID: AB950234D6EA286B
  1. 31
      ClientContext.cpp
  2. 1
      ClientContext.h
  3. 78
      I2PTunnel.cpp
  4. 146
      I2PTunnel.h

31
ClientContext.cpp

@ -404,17 +404,19 @@ namespace client
} }
if (type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT) { if (type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT) {
// udp client // udp client
// TODO: ip6 and hostnames // TODO: hostnames
boost::asio::ip::udp::endpoint end(boost::asio::ip::address::from_string(address), port); boost::asio::ip::udp::endpoint end(boost::asio::ip::address::from_string(address), port);
if (!localDestination) { if (!localDestination)
{
localDestination = m_SharedLocalDestination; localDestination = m_SharedLocalDestination;
} }
auto clientTunnel = new I2PUDPClientTunnel(name, dest, end, localDestination, destinationPort); auto clientTunnel = new I2PUDPClientTunnel(name, dest, end, localDestination, destinationPort);
if(m_ClientForwards.insert(std::make_pair(end, std::unique_ptr<I2PUDPClientTunnel>(clientTunnel))).second) { if(m_ClientForwards.insert(std::make_pair(end, std::unique_ptr<I2PUDPClientTunnel>(clientTunnel))).second)
{
clientTunnel->Start(); clientTunnel->Start();
} else {
LogPrint(eLogError, "Clients: I2P Client forward for endpoint ", end, " already exists");
} }
else
LogPrint(eLogError, "Clients: I2P Client forward for endpoint ", end, " already exists");
} else { } else {
// tcp client // tcp client
@ -443,6 +445,7 @@ namespace client
bool gzip = section.second.get (I2P_SERVER_TUNNEL_GZIP, true); bool gzip = section.second.get (I2P_SERVER_TUNNEL_GZIP, true);
i2p::data::SigningKeyType sigType = section.second.get (I2P_SERVER_TUNNEL_SIGNATURE_TYPE, i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256); i2p::data::SigningKeyType sigType = section.second.get (I2P_SERVER_TUNNEL_SIGNATURE_TYPE, i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256);
uint32_t maxConns = section.second.get(i2p::stream::I2CP_PARAM_STREAMING_MAX_CONNS_PER_MIN, i2p::stream::DEFAULT_MAX_CONNS_PER_MIN); uint32_t maxConns = section.second.get(i2p::stream::I2CP_PARAM_STREAMING_MAX_CONNS_PER_MIN, i2p::stream::DEFAULT_MAX_CONNS_PER_MIN);
std::string address = section.second.get<std::string> (I2P_SERVER_TUNNEL_ADDRESS, "127.0.0.1");
// I2CP // I2CP
std::map<std::string, std::string> options; std::map<std::string, std::string> options;
@ -455,22 +458,26 @@ namespace client
localDestination = FindLocalDestination (k.GetPublic ()->GetIdentHash ()); localDestination = FindLocalDestination (k.GetPublic ()->GetIdentHash ());
if (!localDestination) if (!localDestination)
localDestination = CreateNewLocalDestination (k, true, &options); localDestination = CreateNewLocalDestination (k, true, &options);
if (type == I2P_TUNNELS_SECTION_TYPE_UDPSERVER) { if (type == I2P_TUNNELS_SECTION_TYPE_UDPSERVER)
{
// udp server tunnel // udp server tunnel
// TODO: ipv6 and hostnames // TODO: hostnames
auto localAddress = boost::asio::ip::address::from_string(address);
boost::asio::ip::udp::endpoint endpoint(boost::asio::ip::address::from_string(host), port); boost::asio::ip::udp::endpoint endpoint(boost::asio::ip::address::from_string(host), port);
I2PUDPServerTunnel * serverTunnel = new I2PUDPServerTunnel(name, localDestination, endpoint, port); I2PUDPServerTunnel * serverTunnel = new I2PUDPServerTunnel(name, localDestination, localAddress, endpoint, port);
std::lock_guard<std::mutex> lock(m_ForwardsMutex); std::lock_guard<std::mutex> lock(m_ForwardsMutex);
if(m_ServerForwards.insert( if(m_ServerForwards.insert(
std::make_pair( std::make_pair(
std::make_pair( std::make_pair(
localDestination->GetIdentHash(), port), localDestination->GetIdentHash(), port),
std::unique_ptr<I2PUDPServerTunnel>(serverTunnel))).second) { std::unique_ptr<I2PUDPServerTunnel>(serverTunnel))).second)
{
serverTunnel->Start(); serverTunnel->Start();
LogPrint(eLogInfo, "Clients: I2P Server Forward created for UDP Endpoint ", host, ":", port, " via ",localDestination->GetIdentHash().ToBase32()); LogPrint(eLogInfo, "Clients: I2P Server Forward created for UDP Endpoint ", host, ":", port, " bound on ", address, " for ",localDestination->GetIdentHash().ToBase32());
} else {
LogPrint(eLogError, "Clients: I2P Server Forward for destination/port ", m_AddressBook.ToAddress(localDestination->GetIdentHash()), "/", port, "already exists");
} }
else
LogPrint(eLogError, "Clients: I2P Server Forward for destination/port ", m_AddressBook.ToAddress(localDestination->GetIdentHash()), "/", port, "already exists");
continue; continue;
} }

1
ClientContext.h

@ -41,6 +41,7 @@ namespace client
const char I2P_SERVER_TUNNEL_ACCESS_LIST[] = "accesslist"; const char I2P_SERVER_TUNNEL_ACCESS_LIST[] = "accesslist";
const char I2P_SERVER_TUNNEL_GZIP[] = "gzip"; const char I2P_SERVER_TUNNEL_GZIP[] = "gzip";
const char I2P_SERVER_TUNNEL_WEBIRC_PASSWORD[] = "webircpassword"; const char I2P_SERVER_TUNNEL_WEBIRC_PASSWORD[] = "webircpassword";
const char I2P_SERVER_TUNNEL_ADDRESS[] = "address";
class ClientContext class ClientContext
{ {

78
I2PTunnel.cpp

@ -532,7 +532,7 @@ namespace client
{ {
std::lock_guard<std::mutex> lock(m_SessionsMutex); std::lock_guard<std::mutex> lock(m_SessionsMutex);
auto session = ObtainUDPSession(from, toPort, fromPort); auto session = ObtainUDPSession(from, toPort, fromPort);
session->IPSocket.send_to(boost::asio::buffer(buf, len), m_Endpoint); session->IPSocket.send_to(boost::asio::buffer(buf, len), m_RemoteEndpoint);
session->LastActivity = i2p::util::GetMillisecondsSinceEpoch(); session->LastActivity = i2p::util::GetMillisecondsSinceEpoch();
} }
@ -548,20 +548,25 @@ namespace client
UDPSession * I2PUDPServerTunnel::ObtainUDPSession(const i2p::data::IdentityEx& from, uint16_t localPort, uint16_t remotePort) UDPSession * I2PUDPServerTunnel::ObtainUDPSession(const i2p::data::IdentityEx& from, uint16_t localPort, uint16_t remotePort)
{ {
auto ih = from.GetIdentHash(); auto ih = from.GetIdentHash();
for ( UDPSession * s : m_Sessions ) { for ( UDPSession * s : m_Sessions )
if ( s->Identity == ih) { {
/** found existing */ if ( s->Identity == ih)
{
/** found existing session */
LogPrint(eLogDebug, "UDPServer: found session ", s->IPSocket.local_endpoint(), " ", ih.ToBase32()); LogPrint(eLogDebug, "UDPServer: found session ", s->IPSocket.local_endpoint(), " ", ih.ToBase32());
return s; return s;
} }
} }
/** create new */ /** create new udp session */
boost::asio::ip::udp::endpoint ep(boost::asio::ip::address::from_string("127.0.0.1"), 0); boost::asio::ip::udp::endpoint ep(m_LocalAddress, 0);
m_Sessions.push_back(new UDPSession(ep, m_LocalDest, m_Endpoint, ih, localPort, remotePort)); m_Sessions.push_back(new UDPSession(ep, m_LocalDest, m_RemoteEndpoint, ih, localPort, remotePort));
return m_Sessions.back(); return m_Sessions.back();
} }
UDPSession::UDPSession(boost::asio::ip::udp::endpoint localEndpoint, const std::shared_ptr<i2p::client::ClientDestination> & localDestination, boost::asio::ip::udp::endpoint endpoint, const i2p::data::IdentHash to, uint16_t ourPort, uint16_t theirPort) : UDPSession::UDPSession(boost::asio::ip::udp::endpoint localEndpoint,
const std::shared_ptr<i2p::client::ClientDestination> & localDestination,
boost::asio::ip::udp::endpoint endpoint, const i2p::data::IdentHash to,
uint16_t ourPort, uint16_t theirPort) :
m_Destination(localDestination->GetDatagramDestination()), m_Destination(localDestination->GetDatagramDestination()),
m_Service(localDestination->GetService()), m_Service(localDestination->GetService()),
IPSocket(localDestination->GetService(), localEndpoint), IPSocket(localDestination->GetService(), localEndpoint),
@ -577,18 +582,20 @@ namespace client
void UDPSession::Receive() { void UDPSession::Receive() {
LogPrint(eLogDebug, "UDPSession: Receive"); LogPrint(eLogDebug, "UDPSession: Receive");
IPSocket.async_receive_from(boost::asio::buffer(m_Buffer, I2P_UDP_MAX_MTU), FromEndpoint, std::bind(&UDPSession::HandleReceived, this, std::placeholders::_1, std::placeholders::_2)); IPSocket.async_receive_from(boost::asio::buffer(m_Buffer, I2P_UDP_MAX_MTU),
FromEndpoint, std::bind(&UDPSession::HandleReceived, this, std::placeholders::_1, std::placeholders::_2));
} }
void UDPSession::HandleReceived(const boost::system::error_code & ecode, std::size_t len) void UDPSession::HandleReceived(const boost::system::error_code & ecode, std::size_t len)
{ {
if(!ecode) { if(!ecode)
{
LogPrint(eLogDebug, "UDPSession: forward ", len, "B from ", FromEndpoint); LogPrint(eLogDebug, "UDPSession: forward ", len, "B from ", FromEndpoint);
LastActivity = i2p::util::GetMillisecondsSinceEpoch(); LastActivity = i2p::util::GetMillisecondsSinceEpoch();
uint8_t * data = new uint8_t[len]; uint8_t * data = new uint8_t[len];
memcpy(data, m_Buffer, len); memcpy(data, m_Buffer, len);
m_Service.post([&,len, data] () { m_Service.post([&,len, data] () {
m_Destination->SendDatagramTo(data, len, Identity, 0, 0); m_Destination->SendDatagramTo(data, len, Identity, 0, 0);
delete [] data; delete [] data;
}); });
@ -600,9 +607,12 @@ namespace client
I2PUDPServerTunnel::I2PUDPServerTunnel(const std::string & name, std::shared_ptr<i2p::client::ClientDestination> localDestination, boost::asio::ip::udp::endpoint forwardTo, uint16_t port) : I2PUDPServerTunnel::I2PUDPServerTunnel(const std::string & name, std::shared_ptr<i2p::client::ClientDestination> localDestination,
const boost::asio::ip::address& localAddress, boost::asio::ip::udp::endpoint forwardTo, uint16_t port) :
m_LocalAddress(localAddress),
m_Name(name),
LocalPort(port), LocalPort(port),
m_Endpoint(forwardTo) m_RemoteEndpoint(forwardTo)
{ {
m_LocalDest = localDestination; m_LocalDest = localDestination;
m_LocalDest->Start(); m_LocalDest->Start();
@ -613,9 +623,8 @@ namespace client
I2PUDPServerTunnel::~I2PUDPServerTunnel() I2PUDPServerTunnel::~I2PUDPServerTunnel()
{ {
auto dgram = m_LocalDest->GetDatagramDestination(); auto dgram = m_LocalDest->GetDatagramDestination();
if (dgram) { if (dgram) dgram->ResetReceiver();
dgram->ResetReceiver();
}
LogPrint(eLogInfo, "UDPServer: done"); LogPrint(eLogInfo, "UDPServer: done");
} }
@ -623,7 +632,11 @@ namespace client
m_LocalDest->Start(); m_LocalDest->Start();
} }
I2PUDPClientTunnel::I2PUDPClientTunnel(const std::string & name, const std::string &remoteDest, boost::asio::ip::udp::endpoint localEndpoint, std::shared_ptr<i2p::client::ClientDestination> localDestination, uint16_t remotePort) : I2PUDPClientTunnel::I2PUDPClientTunnel(const std::string & name, const std::string &remoteDest,
boost::asio::ip::udp::endpoint localEndpoint,
std::shared_ptr<i2p::client::ClientDestination> localDestination,
uint16_t remotePort) :
m_Name(name),
m_Session(nullptr), m_Session(nullptr),
m_RemoteDest(remoteDest), m_RemoteDest(remoteDest),
m_RemoteIdent(nullptr), m_RemoteIdent(nullptr),
@ -654,45 +667,52 @@ namespace client
m_RemoteIdent = new i2p::data::IdentHash; m_RemoteIdent = new i2p::data::IdentHash;
m_RemoteIdent->Fill(0); m_RemoteIdent->Fill(0);
while(!context.GetAddressBook().GetIdentHash(m_RemoteDest, *m_RemoteIdent) && !m_cancel_resolve) { while(!context.GetAddressBook().GetIdentHash(m_RemoteDest, *m_RemoteIdent) && !m_cancel_resolve)
{
LogPrint(eLogWarning, "UDP Tunnel: failed to lookup ", m_RemoteDest); LogPrint(eLogWarning, "UDP Tunnel: failed to lookup ", m_RemoteDest);
std::this_thread::sleep_for(std::chrono::seconds(1)); std::this_thread::sleep_for(std::chrono::seconds(1));
} }
if(m_cancel_resolve) { if(m_cancel_resolve)
{
LogPrint(eLogError, "UDP Tunnel: lookup of ", m_RemoteDest, " was cancelled"); LogPrint(eLogError, "UDP Tunnel: lookup of ", m_RemoteDest, " was cancelled");
return; return;
} }
LogPrint(eLogInfo, "UDP Tunnel: resolved ", m_RemoteDest, " to ", m_RemoteIdent->ToBase32()); LogPrint(eLogInfo, "UDP Tunnel: resolved ", m_RemoteDest, " to ", m_RemoteIdent->ToBase32());
// delete existing session // delete existing session
if(m_Session) delete m_Session; if(m_Session) delete m_Session;
boost::asio::ip::udp::endpoint ep(boost::asio::ip::address::from_string("127.0.0.1"), 0); boost::asio::ip::udp::endpoint ep(boost::asio::ip::address::from_string("127.0.0.1"), 0);
m_Session = new UDPSession(m_LocalEndpoint, m_LocalDest, ep, *m_RemoteIdent, LocalPort, RemotePort); m_Session = new UDPSession(m_LocalEndpoint, m_LocalDest, ep, *m_RemoteIdent, LocalPort, RemotePort);
} }
void I2PUDPClientTunnel::HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len) void I2PUDPClientTunnel::HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
{ {
if(m_RemoteIdent && from.GetIdentHash() == *m_RemoteIdent) { if(m_RemoteIdent && from.GetIdentHash() == *m_RemoteIdent)
{
// address match // address match
if(m_Session) { if(m_Session)
{
// tell session // tell session
LogPrint(eLogDebug, "UDP Client: got ", len, "B from ", from.GetIdentHash().ToBase32()); LogPrint(eLogDebug, "UDP Client: got ", len, "B from ", from.GetIdentHash().ToBase32());
m_Session->IPSocket.send_to(boost::asio::buffer(buf, len), m_Session->FromEndpoint); m_Session->IPSocket.send_to(boost::asio::buffer(buf, len), m_Session->FromEndpoint);
} else {
LogPrint(eLogWarning, "UDP Client: no session");
} }
} else { else
LogPrint(eLogWarning, "UDP Client: unwarrented traffic from ", from.GetIdentHash().ToBase32()); LogPrint(eLogWarning, "UDP Client: no session");
} }
else
LogPrint(eLogWarning, "UDP Client: unwarrented traffic from ", from.GetIdentHash().ToBase32());
} }
I2PUDPClientTunnel::~I2PUDPClientTunnel() { I2PUDPClientTunnel::~I2PUDPClientTunnel() {
auto dgram = m_LocalDest->GetDatagramDestination(); auto dgram = m_LocalDest->GetDatagramDestination();
if (dgram) { if (dgram) dgram->ResetReceiver();
dgram->ResetReceiver();
}
if (m_Session) delete m_Session; if (m_Session) delete m_Session;
m_cancel_resolve = true; m_cancel_resolve = true;
if(m_ResolveThread) {
if(m_ResolveThread)
{
m_ResolveThread->join(); m_ResolveThread->join();
delete m_ResolveThread; delete m_ResolveThread;
m_ResolveThread = nullptr; m_ResolveThread = nullptr;

146
I2PTunnel.h

@ -132,73 +132,85 @@ namespace client
}; };
/** 2 minute timeout for udp sessions */ /** 2 minute timeout for udp sessions */
const uint64_t I2P_UDP_SESSION_TIMEOUT = 1000 * 60 * 2; const uint64_t I2P_UDP_SESSION_TIMEOUT = 1000 * 60 * 2;
/** max size for i2p udp */ /** max size for i2p udp */
const size_t I2P_UDP_MAX_MTU = i2p::datagram::MAX_DATAGRAM_SIZE; const size_t I2P_UDP_MAX_MTU = i2p::datagram::MAX_DATAGRAM_SIZE;
struct UDPSession struct UDPSession
{ {
i2p::datagram::DatagramDestination * m_Destination; i2p::datagram::DatagramDestination * m_Destination;
boost::asio::io_service & m_Service; boost::asio::io_service & m_Service;
boost::asio::ip::udp::socket IPSocket; boost::asio::ip::udp::socket IPSocket;
i2p::data::IdentHash Identity; i2p::data::IdentHash Identity;
boost::asio::ip::udp::endpoint FromEndpoint; boost::asio::ip::udp::endpoint FromEndpoint;
boost::asio::ip::udp::endpoint SendEndpoint; boost::asio::ip::udp::endpoint SendEndpoint;
uint64_t LastActivity; uint64_t LastActivity;
uint16_t LocalPort; uint16_t LocalPort;
uint16_t RemotePort; uint16_t RemotePort;
uint8_t m_Buffer[I2P_UDP_MAX_MTU]; uint8_t m_Buffer[I2P_UDP_MAX_MTU];
UDPSession(boost::asio::ip::udp::endpoint localEndpoint, const std::shared_ptr<i2p::client::ClientDestination> & localDestination, boost::asio::ip::udp::endpoint remote, const i2p::data::IdentHash ident, uint16_t ourPort, uint16_t theirPort); UDPSession(boost::asio::ip::udp::endpoint localEndpoint,
const std::shared_ptr<i2p::client::ClientDestination> & localDestination,
void HandleReceived(const boost::system::error_code & ecode, std::size_t len); boost::asio::ip::udp::endpoint remote, const i2p::data::IdentHash ident,
void Receive(); uint16_t ourPort, uint16_t theirPort);
}; void HandleReceived(const boost::system::error_code & ecode, std::size_t len);
void Receive();
/** server side udp tunnel, many i2p inbound to 1 ip outbound */ };
class I2PUDPServerTunnel
{ /** server side udp tunnel, many i2p inbound to 1 ip outbound */
public: class I2PUDPServerTunnel
I2PUDPServerTunnel(const std::string & name, std::shared_ptr<i2p::client::ClientDestination> localDestination, boost::asio::ip::udp::endpoint forwardTo, uint16_t port); {
~I2PUDPServerTunnel(); public:
/** expire stale udp conversations */ I2PUDPServerTunnel(const std::string & name,
void ExpireStale(const uint64_t delta=I2P_UDP_SESSION_TIMEOUT); std::shared_ptr<i2p::client::ClientDestination> localDestination,
void Start(); const boost::asio::ip::address & localAddress,
private: boost::asio::ip::udp::endpoint forwardTo, uint16_t port);
void HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); ~I2PUDPServerTunnel();
UDPSession * ObtainUDPSession(const i2p::data::IdentityEx& from, uint16_t localPort, uint16_t remotePort); /** expire stale udp conversations */
private: void ExpireStale(const uint64_t delta=I2P_UDP_SESSION_TIMEOUT);
const uint16_t LocalPort; void Start();
boost::asio::ip::udp::endpoint m_Endpoint; const char * GetName() const { return m_Name.c_str(); }
std::mutex m_SessionsMutex; private:
std::vector<UDPSession*> m_Sessions; void HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
std::shared_ptr<i2p::client::ClientDestination> m_LocalDest; UDPSession * ObtainUDPSession(const i2p::data::IdentityEx& from, uint16_t localPort, uint16_t remotePort);
uint8_t m_Buffer[I2P_UDP_MAX_MTU]; private:
}; const std::string m_Name;
const uint16_t LocalPort;
class I2PUDPClientTunnel boost::asio::ip::address m_LocalAddress;
{ boost::asio::ip::udp::endpoint m_RemoteEndpoint;
public: std::mutex m_SessionsMutex;
I2PUDPClientTunnel(const std::string & name, const std::string &remoteDest, boost::asio::ip::udp::endpoint localEndpoint, std::shared_ptr<i2p::client::ClientDestination> localDestination, uint16_t remotePort); std::vector<UDPSession*> m_Sessions;
~I2PUDPClientTunnel(); std::shared_ptr<i2p::client::ClientDestination> m_LocalDest;
void Start(); };
private:
void HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); class I2PUDPClientTunnel
void TryResolving(); {
UDPSession * m_Session; public:
const std::string m_RemoteDest; I2PUDPClientTunnel(const std::string & name, const std::string &remoteDest,
std::shared_ptr<i2p::client::ClientDestination> m_LocalDest; boost::asio::ip::udp::endpoint localEndpoint, std::shared_ptr<i2p::client::ClientDestination> localDestination,
const boost::asio::ip::udp::endpoint m_LocalEndpoint; uint16_t remotePort);
i2p::data::IdentHash * m_RemoteIdent; ~I2PUDPClientTunnel();
std::thread * m_ResolveThread; void Start();
uint16_t LocalPort; const char * GetName() const { return m_Name.c_str(); }
uint16_t RemotePort;
bool m_cancel_resolve; private:
}; void HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
void TryResolving();
const std::string m_Name;
UDPSession * m_Session;
const std::string m_RemoteDest;
std::shared_ptr<i2p::client::ClientDestination> m_LocalDest;
const boost::asio::ip::udp::endpoint m_LocalEndpoint;
i2p::data::IdentHash * m_RemoteIdent;
std::thread * m_ResolveThread;
uint16_t LocalPort;
uint16_t RemotePort;
bool m_cancel_resolve;
};
class I2PServerTunnel: public I2PService class I2PServerTunnel: public I2PService
{ {

Loading…
Cancel
Save