i2pd/libi2pd_client/SAM.cpp

1399 lines
42 KiB
C++
Raw Normal View History

/*
* 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
*/
2014-09-24 18:59:03 +00:00
#include <string.h>
2014-09-30 15:08:38 +00:00
#include <stdio.h>
2014-10-27 21:53:01 +00:00
#ifdef _MSC_VER
#include <stdlib.h>
#endif
2015-11-03 14:15:49 +00:00
#include "Base.h"
2014-09-25 17:58:09 +00:00
#include "Identity.h"
2014-09-24 16:01:26 +00:00
#include "Log.h"
#include "Destination.h"
2014-10-16 00:52:17 +00:00
#include "ClientContext.h"
2016-11-04 01:31:21 +00:00
#include "util.h"
2014-09-24 16:01:26 +00:00
#include "SAM.h"
namespace i2p
{
namespace client
2014-09-24 16:01:26 +00:00
{
2018-04-24 13:45:16 +00:00
SAMSocket::SAMSocket (SAMBridge& owner):
m_Owner (owner), m_Socket(owner.GetService()), m_Timer (m_Owner.GetService ()),
m_BufferOffset (0),
2018-01-15 13:19:57 +00:00
m_SocketType (eSAMSocketTypeUnknown), m_IsSilent (false),
m_IsAccepting (false), m_Stream (nullptr)
2014-09-24 18:59:03 +00:00
{
}
SAMSocket::~SAMSocket ()
{
2018-04-24 15:50:51 +00:00
m_Stream = nullptr;
}
2018-01-15 13:19:57 +00:00
2017-02-01 19:06:32 +00:00
void SAMSocket::Terminate (const char* reason)
2014-11-23 02:56:59 +00:00
{
2018-01-15 13:19:57 +00:00
if(m_Stream)
{
2018-04-24 13:45:16 +00:00
m_Stream->AsyncClose ();
2018-04-24 15:42:37 +00:00
m_Stream = nullptr;
2018-01-15 13:19:57 +00:00
}
auto Session = m_Owner.FindSession(m_ID);
2014-09-26 19:40:57 +00:00
switch (m_SocketType)
2014-09-25 17:58:09 +00:00
{
2014-09-26 19:40:57 +00:00
case eSAMSocketTypeSession:
m_Owner.CloseSession (m_ID);
break;
case eSAMSocketTypeStream:
{
break;
}
case eSAMSocketTypeAcceptor:
case eSAMSocketTypeForward:
2014-09-26 19:40:57 +00:00
{
2018-01-15 13:19:57 +00:00
if (Session)
2017-01-31 16:16:55 +00:00
{
2018-01-15 13:19:57 +00:00
if (m_IsAccepting && Session->localDestination)
Session->localDestination->StopAcceptingStreams ();
2016-12-23 00:38:17 +00:00
}
2014-09-26 19:40:57 +00:00
break;
}
default: ;
2014-09-25 17:58:09 +00:00
}
2014-11-23 02:56:59 +00:00
m_SocketType = eSAMSocketTypeTerminated;
if (m_Socket.is_open ())
{
2018-04-24 15:16:15 +00:00
boost::system::error_code ec;
m_Socket.shutdown (boost::asio::ip::tcp::socket::shutdown_both, ec);
m_Socket.close ();
}
2018-04-24 15:42:37 +00:00
m_Owner.RemoveSocket(shared_from_this());
2014-09-24 18:59:03 +00:00
}
void SAMSocket::ReceiveHandshake ()
{
2018-04-24 13:45:16 +00:00
m_Socket.async_read_some (boost::asio::buffer(m_Buffer, SAM_SOCKET_BUFFER_SIZE),
std::bind(&SAMSocket::HandleHandshakeReceived, shared_from_this (),
std::placeholders::_1, std::placeholders::_2));
2014-09-24 18:59:03 +00:00
}
2018-02-25 13:49:39 +00:00
static bool SAMVersionAcceptable(const std::string & ver)
2018-02-25 13:47:39 +00:00
{
return ver == "3.0" || ver == "3.1";
}
static bool SAMVersionTooLow(const std::string & ver)
{
return ver.size() && ver[0] < '3';
}
2018-02-25 13:49:39 +00:00
static bool SAMVersionTooHigh(const std::string & ver)
2018-02-25 13:47:39 +00:00
{
return ver.size() && ver > "3.1";
}
2014-09-24 18:59:03 +00:00
void SAMSocket::HandleHandshakeReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
if (ecode)
2018-04-24 13:45:16 +00:00
{
2015-12-18 06:50:12 +00:00
LogPrint (eLogError, "SAM: handshake read error: ", ecode.message ());
2014-09-24 18:59:03 +00:00
if (ecode != boost::asio::error::operation_aborted)
2017-02-01 19:06:32 +00:00
Terminate ("SAM: handshake read error");
2014-09-24 18:59:03 +00:00
}
else
2017-01-31 16:16:55 +00:00
{
2014-09-24 18:59:03 +00:00
m_Buffer[bytes_transferred] = 0;
2015-06-15 21:55:21 +00:00
char * eol = (char *)memchr (m_Buffer, '\n', bytes_transferred);
if (eol)
*eol = 0;
2015-12-18 06:50:12 +00:00
LogPrint (eLogDebug, "SAM: handshake ", m_Buffer);
2014-12-16 20:54:02 +00:00
char * separator = strchr (m_Buffer, ' ');
if (separator)
2014-09-24 18:59:03 +00:00
{
2017-01-31 16:16:55 +00:00
separator = strchr (separator + 1, ' ');
if (separator)
2014-12-16 20:54:02 +00:00
*separator = 0;
}
if (!strcmp (m_Buffer, SAM_HANDSHAKE))
{
2018-02-25 13:47:39 +00:00
std::string maxver("3.1");
std::string minver("3.0");
2014-12-16 20:54:02 +00:00
// try to find MIN and MAX, 3.0 if not found
if (separator)
{
separator++;
std::map<std::string, std::string> params;
2015-03-27 01:23:59 +00:00
ExtractParams (separator, params);
2018-02-25 13:47:39 +00:00
auto it = params.find (SAM_PARAM_MAX);
if (it != params.end ())
maxver = it->second;
it = params.find(SAM_PARAM_MIN);
if (it != params.end ())
minver = it->second;
}
// version negotiation
std::string version;
if (SAMVersionAcceptable(maxver))
{
version = maxver;
}
else if (SAMVersionAcceptable(minver))
{
version = minver;
}
2018-02-25 13:49:39 +00:00
else if (SAMVersionTooLow(minver) && SAMVersionTooHigh(maxver))
2018-02-25 13:47:39 +00:00
{
version = "3.0";
2014-12-16 20:54:02 +00:00
}
2018-02-25 13:47:39 +00:00
if (SAMVersionAcceptable(version))
2014-12-16 20:54:02 +00:00
{
#ifdef _MSC_VER
size_t l = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_HANDSHAKE_REPLY, version.c_str ());
2017-01-31 16:16:55 +00:00
#else
2014-12-16 20:54:02 +00:00
size_t l = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_HANDSHAKE_REPLY, version.c_str ());
#endif
2018-04-24 13:45:16 +00:00
boost::asio::async_write (m_Socket, boost::asio::buffer (m_Buffer, l), boost::asio::transfer_all (),
std::bind(&SAMSocket::HandleHandshakeReplySent, shared_from_this (),
2014-12-17 00:04:13 +00:00
std::placeholders::_1, std::placeholders::_2));
2017-01-31 16:16:55 +00:00
}
2014-12-16 20:54:02 +00:00
else
2018-02-25 13:47:39 +00:00
SendMessageReply (SAM_HANDSHAKE_NOVERSION, strlen (SAM_HANDSHAKE_NOVERSION), true);
2014-09-24 18:59:03 +00:00
}
else
{
2015-12-18 06:50:12 +00:00
LogPrint (eLogError, "SAM: handshake mismatch");
2017-02-01 19:06:32 +00:00
Terminate ("SAM: handshake mismatch");
2014-09-24 18:59:03 +00:00
}
}
}
2018-04-24 13:45:16 +00:00
bool SAMSocket::IsSession(const std::string & id) const
{
return id == m_ID;
}
2014-09-24 18:59:03 +00:00
void SAMSocket::HandleHandshakeReplySent (const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
if (ecode)
2018-04-24 13:45:16 +00:00
{
2015-12-18 06:50:12 +00:00
LogPrint (eLogError, "SAM: handshake reply send error: ", ecode.message ());
2014-09-24 18:59:03 +00:00
if (ecode != boost::asio::error::operation_aborted)
2017-02-01 19:06:32 +00:00
Terminate ("SAM: handshake reply send error");
2014-09-24 18:59:03 +00:00
}
2018-04-24 13:45:16 +00:00
else
2014-09-25 17:22:25 +00:00
{
2018-04-24 13:45:16 +00:00
m_Socket.async_read_some (boost::asio::buffer(m_Buffer, SAM_SOCKET_BUFFER_SIZE),
2017-01-31 16:16:55 +00:00
std::bind(&SAMSocket::HandleMessage, shared_from_this (),
std::placeholders::_1, std::placeholders::_2));
}
2014-09-25 17:22:25 +00:00
}
void SAMSocket::SendMessageReply (const char * msg, size_t len, bool close)
{
2017-02-01 19:06:32 +00:00
LogPrint (eLogDebug, "SAMSocket::SendMessageReply, close=",close?"true":"false", " reason: ", msg);
if (!m_IsSilent || m_SocketType == eSAMSocketTypeForward)
2018-04-24 13:45:16 +00:00
boost::asio::async_write (m_Socket, boost::asio::buffer (msg, len), boost::asio::transfer_all (),
2017-01-31 16:16:55 +00:00
std::bind(&SAMSocket::HandleMessageReplySent, shared_from_this (),
2014-11-23 22:00:45 +00:00
std::placeholders::_1, std::placeholders::_2, close));
2014-09-29 18:18:06 +00:00
else
{
if (close)
2017-02-01 19:06:32 +00:00
Terminate ("SAMSocket::SendMessageReply(close=true)");
2014-09-29 18:18:06 +00:00
else
2017-01-31 16:16:55 +00:00
Receive ();
}
2014-09-25 17:22:25 +00:00
}
void SAMSocket::HandleMessageReplySent (const boost::system::error_code& ecode, std::size_t bytes_transferred, bool close)
{
if (ecode)
2018-04-25 15:25:49 +00:00
{
2015-12-18 06:50:12 +00:00
LogPrint (eLogError, "SAM: reply send error: ", ecode.message ());
2014-09-25 17:22:25 +00:00
if (ecode != boost::asio::error::operation_aborted)
2017-02-01 19:06:32 +00:00
Terminate ("SAM: reply send error");
2014-09-25 17:22:25 +00:00
}
else
{
if (close)
2017-02-01 19:06:32 +00:00
Terminate ("SAMSocket::HandleMessageReplySent(close=true)");
2014-09-25 17:22:25 +00:00
else
2017-01-31 16:16:55 +00:00
Receive ();
}
2014-09-24 18:59:03 +00:00
}
2014-09-25 17:22:25 +00:00
void SAMSocket::HandleMessage (const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
if (ecode)
2018-04-25 15:25:49 +00:00
{
2015-12-18 06:50:12 +00:00
LogPrint (eLogError, "SAM: read error: ", ecode.message ());
2014-09-25 17:22:25 +00:00
if (ecode != boost::asio::error::operation_aborted)
2017-02-01 19:06:32 +00:00
Terminate ("SAM: read error");
2014-09-25 17:22:25 +00:00
}
2015-11-03 14:15:49 +00:00
else if (m_SocketType == eSAMSocketTypeStream)
HandleReceived (ecode, bytes_transferred);
2014-09-25 17:22:25 +00:00
else
{
2015-03-27 18:02:27 +00:00
bytes_transferred += m_BufferOffset;
m_BufferOffset = 0;
2014-09-25 17:22:25 +00:00
m_Buffer[bytes_transferred] = 0;
2015-03-27 19:22:56 +00:00
char * eol = (char *)memchr (m_Buffer, '\n', bytes_transferred);
2014-09-25 17:22:25 +00:00
if (eol)
{
2019-08-02 17:48:39 +00:00
if (eol > m_Buffer && eol[-1] == '\r') eol--;
2014-09-25 17:22:25 +00:00
*eol = 0;
2014-09-28 13:05:37 +00:00
char * separator = strchr (m_Buffer, ' ');
if (separator)
2014-09-30 15:08:38 +00:00
{
2017-01-31 16:16:55 +00:00
separator = strchr (separator + 1, ' ');
if (separator)
2014-09-30 15:08:38 +00:00
*separator = 0;
else
separator = eol;
2014-09-28 13:05:37 +00:00
if (!strcmp (m_Buffer, SAM_SESSION_CREATE))
ProcessSessionCreate (separator + 1, bytes_transferred - (separator - m_Buffer) - 1);
else if (!strcmp (m_Buffer, SAM_STREAM_CONNECT))
2018-01-06 03:48:51 +00:00
ProcessStreamConnect (separator + 1, bytes_transferred - (separator - m_Buffer) - 1, bytes_transferred - (eol - m_Buffer) - 1);
2014-09-28 13:05:37 +00:00
else if (!strcmp (m_Buffer, SAM_STREAM_ACCEPT))
ProcessStreamAccept (separator + 1, bytes_transferred - (separator - m_Buffer) - 1);
else if (!strcmp (m_Buffer, SAM_STREAM_FORWARD))
ProcessStreamForward (separator + 1, bytes_transferred - (separator - m_Buffer) - 1);
2014-09-30 15:08:38 +00:00
else if (!strcmp (m_Buffer, SAM_DEST_GENERATE))
ProcessDestGenerate (separator + 1, bytes_transferred - (separator - m_Buffer) - 1);
2014-10-02 20:55:01 +00:00
else if (!strcmp (m_Buffer, SAM_NAMING_LOOKUP))
ProcessNamingLookup (separator + 1, bytes_transferred - (separator - m_Buffer) - 1);
2019-07-10 17:30:31 +00:00
else if (!strcmp (m_Buffer, SAM_DATAGRAM_SEND) || !strcmp (m_Buffer, SAM_RAW_SEND))
2015-03-27 18:02:27 +00:00
{
2015-03-27 20:31:53 +00:00
size_t len = bytes_transferred - (separator - m_Buffer) - 1;
size_t processed = ProcessDatagramSend (separator + 1, len, eol + 1);
if (processed < len)
2015-03-27 18:02:27 +00:00
{
2015-03-27 20:31:53 +00:00
m_BufferOffset = len - processed;
2015-03-27 18:36:54 +00:00
if (processed > 0)
2015-03-27 20:31:53 +00:00
memmove (m_Buffer, separator + 1 + processed, m_BufferOffset);
2015-03-27 19:29:46 +00:00
else
{
// restore string back
*separator = ' ';
*eol = '\n';
}
2017-01-31 16:16:55 +00:00
}
2015-03-27 18:02:27 +00:00
// since it's SAM v1 reply is not expected
Receive ();
}
2017-01-31 16:16:55 +00:00
else
{
2015-12-18 06:50:12 +00:00
LogPrint (eLogError, "SAM: unexpected message ", m_Buffer);
2017-02-01 19:06:32 +00:00
Terminate ("SAM: unexpected message");
2014-09-28 13:05:37 +00:00
}
2014-09-30 15:08:38 +00:00
}
2014-09-28 13:05:37 +00:00
else
2014-09-30 15:08:38 +00:00
{
2015-12-18 06:50:12 +00:00
LogPrint (eLogError, "SAM: malformed message ", m_Buffer);
2017-02-01 19:06:32 +00:00
Terminate ("malformed message");
2014-09-25 17:22:25 +00:00
}
}
2017-01-30 01:38:18 +00:00
2014-09-25 17:22:25 +00:00
else
2017-01-31 16:16:55 +00:00
{
2015-12-18 06:50:12 +00:00
LogPrint (eLogWarning, "SAM: incomplete message ", bytes_transferred);
2015-03-27 18:58:26 +00:00
m_BufferOffset = bytes_transferred;
// try to receive remaining message
Receive ();
2014-09-25 17:22:25 +00:00
}
}
}
2018-02-16 11:28:22 +00:00
static bool IsAcceptableSessionName(const std::string & str)
{
auto itr = str.begin();
while(itr != str.end())
{
char ch = *itr;
++itr;
if (ch == '<' || ch == '>' || ch == '"' || ch == '\'' || ch == '/')
return false;
}
return true;
}
2014-09-25 17:22:25 +00:00
void SAMSocket::ProcessSessionCreate (char * buf, size_t len)
{
2015-12-18 06:50:12 +00:00
LogPrint (eLogDebug, "SAM: session create: ", buf);
2014-09-25 17:22:25 +00:00
std::map<std::string, std::string> params;
2015-03-27 01:23:59 +00:00
ExtractParams (buf, params);
2017-01-31 16:16:55 +00:00
std::string& style = params[SAM_PARAM_STYLE];
2014-09-25 17:22:25 +00:00
std::string& id = params[SAM_PARAM_ID];
std::string& destination = params[SAM_PARAM_DESTINATION];
2018-02-16 11:28:22 +00:00
if(!IsAcceptableSessionName(id))
{
// invalid session id
SendMessageReply (SAM_SESSION_CREATE_INVALID_ID, strlen(SAM_SESSION_CREATE_INVALID_ID), true);
return;
}
2017-01-31 16:16:55 +00:00
m_ID = id;
if (m_Owner.FindSession (id))
{
// session exists
SendMessageReply (SAM_SESSION_CREATE_DUPLICATED_ID, strlen(SAM_SESSION_CREATE_DUPLICATED_ID), true);
return;
}
2014-12-16 21:23:42 +00:00
SAMSessionType type = eSAMSessionTypeUnknown;
if (style == SAM_VALUE_STREAM) type = eSAMSessionTypeStream;
else if (style == SAM_VALUE_DATAGRAM) type = eSAMSessionTypeDatagram;
else if (style == SAM_VALUE_RAW) type = eSAMSessionTypeRaw;
if (type == eSAMSessionTypeUnknown)
{
// unknown style
SendI2PError("Unknown STYLE");
return;
}
2017-01-30 01:38:18 +00:00
std::shared_ptr<boost::asio::ip::udp::endpoint> forward = nullptr;
if ((type == eSAMSessionTypeDatagram || type == eSAMSessionTypeRaw) &&
params.find(SAM_PARAM_HOST) != params.end() && params.find(SAM_PARAM_PORT) != params.end())
2017-01-30 01:38:18 +00:00
{
// udp forward selected
boost::system::error_code e;
// TODO: support hostnames in udp forward
auto addr = boost::asio::ip::address::from_string(params[SAM_PARAM_HOST], e);
2017-01-30 01:38:18 +00:00
if (e)
{
// not an ip address
SendI2PError("Invalid IP Address in HOST");
return;
}
auto port = std::stoi(params[SAM_PARAM_PORT]);
2017-01-30 01:38:18 +00:00
if (port == -1)
{
SendI2PError("Invalid port");
return;
}
forward = std::make_shared<boost::asio::ip::udp::endpoint>(addr, port);
}
2019-02-25 08:57:18 +00:00
//ensure we actually received a destination
if (destination.empty())
{
SendMessageReply (SAM_SESSION_STATUS_INVALID_KEY, strlen(SAM_SESSION_STATUS_INVALID_KEY), true);
return;
}
2019-03-04 12:35:48 +00:00
if (destination != SAM_VALUE_TRANSIENT)
{
2019-03-04 12:35:48 +00:00
//ensure it's a base64 string
i2p::data::PrivateKeys keys;
if (!keys.FromBase64(destination))
{
SendMessageReply(SAM_SESSION_STATUS_INVALID_KEY, strlen(SAM_SESSION_STATUS_INVALID_KEY), true);
return;
}
}
2017-01-30 01:38:18 +00:00
2017-01-31 16:16:55 +00:00
// create destination
auto session = m_Owner.CreateSession (id, type, destination == SAM_VALUE_TRANSIENT ? "" : destination, &params);
2018-01-15 13:19:57 +00:00
if (session)
2014-09-25 17:22:25 +00:00
{
2014-09-25 17:58:09 +00:00
m_SocketType = eSAMSocketTypeSession;
if (type == eSAMSessionTypeDatagram || type == eSAMSessionTypeRaw)
{
2018-01-15 13:19:57 +00:00
session->UDPEndpoint = forward;
auto dest = session->localDestination->CreateDatagramDestination ();
if (type == eSAMSessionTypeDatagram)
dest->SetReceiver (std::bind (&SAMSocket::HandleI2PDatagramReceive, shared_from_this (),
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5));
else // raw
dest->SetRawReceiver (std::bind (&SAMSocket::HandleI2PRawDatagramReceive, shared_from_this (),
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
}
2018-01-15 13:19:57 +00:00
if (session->localDestination->IsReady ())
SendSessionCreateReplyOk ();
else
{
m_Timer.expires_from_now (boost::posix_time::seconds(SAM_SESSION_READINESS_CHECK_INTERVAL));
2014-11-23 22:00:45 +00:00
m_Timer.async_wait (std::bind (&SAMSocket::HandleSessionReadinessCheckTimer,
2017-01-31 16:16:55 +00:00
shared_from_this (), std::placeholders::_1));
}
2014-09-25 17:22:25 +00:00
}
else
SendMessageReply (SAM_SESSION_CREATE_DUPLICATED_DEST, strlen(SAM_SESSION_CREATE_DUPLICATED_DEST), true);
2014-09-25 17:22:25 +00:00
}
void SAMSocket::HandleSessionReadinessCheckTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
2018-01-15 13:19:57 +00:00
auto session = m_Owner.FindSession(m_ID);
if(session)
{
2018-01-15 13:19:57 +00:00
if (session->localDestination->IsReady ())
SendSessionCreateReplyOk ();
else
{
m_Timer.expires_from_now (boost::posix_time::seconds(SAM_SESSION_READINESS_CHECK_INTERVAL));
m_Timer.async_wait (std::bind (&SAMSocket::HandleSessionReadinessCheckTimer,
shared_from_this (), std::placeholders::_1));
}
2017-01-31 16:16:55 +00:00
}
}
}
void SAMSocket::SendSessionCreateReplyOk ()
{
2018-01-15 13:19:57 +00:00
auto session = m_Owner.FindSession(m_ID);
if (session)
{
uint8_t buf[1024];
char priv[1024];
size_t l = session->localDestination->GetPrivateKeys ().ToBuffer (buf, 1024);
size_t l1 = i2p::data::ByteStreamToBase64 (buf, l, priv, 1024);
priv[l1] = 0;
2014-10-27 21:53:01 +00:00
#ifdef _MSC_VER
2018-01-15 13:19:57 +00:00
size_t l2 = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_CREATE_REPLY_OK, priv);
2017-01-31 16:16:55 +00:00
#else
2018-01-15 13:19:57 +00:00
size_t l2 = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_CREATE_REPLY_OK, priv);
2014-10-27 21:53:01 +00:00
#endif
2018-01-15 13:19:57 +00:00
SendMessageReply (m_Buffer, l2, false);
}
}
2017-03-29 17:59:48 +00:00
void SAMSocket::ProcessStreamConnect (char * buf, size_t len, size_t rem)
2014-09-25 17:22:25 +00:00
{
2015-12-18 06:50:12 +00:00
LogPrint (eLogDebug, "SAM: stream connect: ", buf);
if ( m_SocketType != eSAMSocketTypeUnknown)
{
SendI2PError ("Socket already in use");
return;
}
2014-09-25 17:58:09 +00:00
std::map<std::string, std::string> params;
2015-03-27 01:23:59 +00:00
ExtractParams (buf, params);
2014-09-25 17:58:09 +00:00
std::string& id = params[SAM_PARAM_ID];
std::string& destination = params[SAM_PARAM_DESTINATION];
2014-09-29 18:18:06 +00:00
std::string& silent = params[SAM_PARAM_SILENT];
2017-01-31 16:16:55 +00:00
if (silent == SAM_VALUE_TRUE) m_IsSilent = true;
2014-09-25 17:58:09 +00:00
m_ID = id;
2018-01-15 13:19:57 +00:00
auto session = m_Owner.FindSession (id);
if (session)
2014-09-25 17:58:09 +00:00
{
2017-03-29 17:59:48 +00:00
if (rem > 0) // handle follow on data
2018-01-06 03:48:51 +00:00
{
2017-03-29 17:59:48 +00:00
memmove (m_Buffer, buf + len + 1, rem); // buf is a pointer to m_Buffer's content
2018-01-06 03:48:51 +00:00
m_BufferOffset = rem;
2017-03-29 17:59:48 +00:00
}
2018-01-06 03:48:51 +00:00
else
2017-03-29 17:59:48 +00:00
m_BufferOffset = 0;
2015-11-03 14:15:49 +00:00
auto dest = std::make_shared<i2p::data::IdentityEx> ();
2017-03-29 17:59:48 +00:00
size_t l = dest->FromBase64(destination);
if (l > 0)
2014-09-25 17:58:09 +00:00
{
2019-03-27 19:19:10 +00:00
context.GetAddressBook().InsertFullAddress(dest);
2018-01-15 13:19:57 +00:00
auto leaseSet = session->localDestination->FindLeaseSet(dest->GetIdentHash());
if (leaseSet)
Connect(leaseSet, session);
else
{
2018-01-15 13:19:57 +00:00
session->localDestination->RequestDestination(dest->GetIdentHash(),
std::bind(&SAMSocket::HandleConnectLeaseSetRequestComplete,
shared_from_this(), std::placeholders::_1));
}
2014-09-25 17:58:09 +00:00
}
else
2019-03-29 13:29:28 +00:00
SendMessageReply (SAM_STREAM_STATUS_INVALID_KEY, strlen(SAM_STREAM_STATUS_INVALID_KEY), true);
}
else
2017-01-31 16:16:55 +00:00
SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true);
}
void SAMSocket::Connect (std::shared_ptr<const i2p::data::LeaseSet> remote, std::shared_ptr<SAMSession> session)
{
if (!session) session = m_Owner.FindSession(m_ID);
if (session)
2018-01-15 13:19:57 +00:00
{
m_SocketType = eSAMSocketTypeStream;
m_Stream = session->localDestination->CreateStream (remote);
if (m_Stream)
{
m_Stream->Send ((uint8_t *)m_Buffer, m_BufferOffset); // connect and send
m_BufferOffset = 0;
I2PReceive ();
SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false);
}
else
SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true);
2018-01-15 13:19:57 +00:00
}
else
SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true);
}
void SAMSocket::HandleConnectLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet)
{
if (leaseSet)
2015-01-27 16:27:58 +00:00
Connect (leaseSet);
else
{
2015-12-18 06:50:12 +00:00
LogPrint (eLogError, "SAM: destination to connect not found");
SendMessageReply (SAM_STREAM_STATUS_CANT_REACH_PEER, strlen(SAM_STREAM_STATUS_CANT_REACH_PEER), true);
2014-09-25 17:58:09 +00:00
}
2014-09-26 19:40:57 +00:00
}
void SAMSocket::ProcessStreamAccept (char * buf, size_t len)
{
2015-12-18 06:50:12 +00:00
LogPrint (eLogDebug, "SAM: stream accept: ", buf);
if ( m_SocketType != eSAMSocketTypeUnknown)
{
SendI2PError ("Socket already in use");
return;
}
2014-09-26 19:40:57 +00:00
std::map<std::string, std::string> params;
2015-03-27 01:23:59 +00:00
ExtractParams (buf, params);
2014-09-26 19:40:57 +00:00
std::string& id = params[SAM_PARAM_ID];
2014-09-29 18:18:06 +00:00
std::string& silent = params[SAM_PARAM_SILENT];
2017-01-31 16:16:55 +00:00
if (silent == SAM_VALUE_TRUE) m_IsSilent = true;
2014-09-26 19:40:57 +00:00
m_ID = id;
2018-01-15 13:19:57 +00:00
auto session = m_Owner.FindSession (id);
if (session)
2017-01-31 16:16:55 +00:00
{
m_SocketType = eSAMSocketTypeAcceptor;
2018-01-15 13:19:57 +00:00
if (!session->localDestination->IsAcceptingStreams ())
{
m_IsAccepting = true;
2018-01-15 13:19:57 +00:00
session->localDestination->AcceptOnce (std::bind (&SAMSocket::HandleI2PAccept, shared_from_this (), std::placeholders::_1));
}
2016-12-18 17:28:32 +00:00
SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false);
2017-01-31 16:16:55 +00:00
}
2014-09-26 19:40:57 +00:00
else
2014-09-28 13:05:37 +00:00
SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true);
2014-09-25 17:22:25 +00:00
}
void SAMSocket::ProcessStreamForward (char * buf, size_t len)
{
LogPrint (eLogDebug, "SAM: stream forward: ", buf);
std::map<std::string, std::string> params;
ExtractParams (buf, params);
std::string& id = params[SAM_PARAM_ID];
auto session = m_Owner.FindSession (id);
if (!session)
{
SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true);
return;
}
if (session->localDestination->IsAcceptingStreams ())
{
SendI2PError ("Already accepting");
return;
}
auto it = params.find (SAM_PARAM_PORT);
if (it == params.end ())
{
SendI2PError ("PORT is missing");
return;
}
auto port = std::stoi (it->second);
if (port <= 0 || port >= 0xFFFF)
{
SendI2PError ("Invalid PORT");
return;
}
boost::system::error_code ec;
auto ep = m_Socket.remote_endpoint (ec);
if (ec)
{
SendI2PError ("Socket error");
return;
}
ep.port (port);
m_SocketType = eSAMSocketTypeForward;
m_ID = id;
m_IsAccepting = true;
2020-10-26 15:19:37 +00:00
std::string& silent = params[SAM_PARAM_SILENT];
if (silent == SAM_VALUE_TRUE) m_IsSilent = true;
session->localDestination->AcceptStreams (std::bind (&SAMSocket::HandleI2PForward,
shared_from_this (), std::placeholders::_1, ep));
SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false);
}
2015-03-27 18:02:27 +00:00
size_t SAMSocket::ProcessDatagramSend (char * buf, size_t len, const char * data)
2015-03-27 01:23:59 +00:00
{
2015-12-18 06:50:12 +00:00
LogPrint (eLogDebug, "SAM: datagram send: ", buf, " ", len);
2015-03-27 01:23:59 +00:00
std::map<std::string, std::string> params;
ExtractParams (buf, params);
size_t size = std::stoi(params[SAM_PARAM_SIZE]), offset = data - buf;
2015-03-27 19:50:24 +00:00
if (offset + size <= len)
2017-01-31 16:16:55 +00:00
{
2018-01-15 13:19:57 +00:00
auto session = m_Owner.FindSession(m_ID);
if (session)
2017-01-31 16:16:55 +00:00
{
2018-01-15 13:19:57 +00:00
auto d = session->localDestination->GetDatagramDestination ();
2015-03-27 01:23:59 +00:00
if (d)
{
i2p::data::IdentityEx dest;
dest.FromBase64 (params[SAM_PARAM_DESTINATION]);
if (session->Type == eSAMSessionTypeDatagram)
d->SendDatagramTo ((const uint8_t *)data, size, dest.GetIdentHash ());
else // raw
d->SendRawDatagramTo ((const uint8_t *)data, size, dest.GetIdentHash ());
2015-03-27 01:23:59 +00:00
}
else
2015-12-18 06:50:12 +00:00
LogPrint (eLogError, "SAM: missing datagram destination");
2015-03-27 01:23:59 +00:00
}
else
2015-12-18 06:50:12 +00:00
LogPrint (eLogError, "SAM: session is not created from DATAGRAM SEND");
2017-01-31 16:16:55 +00:00
}
2015-03-27 01:23:59 +00:00
else
2015-03-27 18:36:54 +00:00
{
2015-12-18 06:50:12 +00:00
LogPrint (eLogWarning, "SAM: sent datagram size ", size, " exceeds buffer ", len - offset);
2015-03-27 18:36:54 +00:00
return 0; // try to receive more
2017-01-31 16:16:55 +00:00
}
2015-03-27 18:02:27 +00:00
return offset + size;
2017-01-31 16:16:55 +00:00
}
void SAMSocket::ProcessDestGenerate (char * buf, size_t len)
2014-09-30 15:08:38 +00:00
{
2015-12-18 06:50:12 +00:00
LogPrint (eLogDebug, "SAM: dest generate");
std::map<std::string, std::string> params;
ExtractParams (buf, params);
// extract signature type
i2p::data::SigningKeyType signatureType = i2p::data::SIGNING_KEY_TYPE_DSA_SHA1;
2017-11-28 18:24:07 +00:00
i2p::data::CryptoKeyType cryptoType = i2p::data::CRYPTO_KEY_TYPE_ELGAMAL;
auto it = params.find (SAM_PARAM_SIGNATURE_TYPE);
if (it != params.end ())
2019-01-23 15:52:17 +00:00
{
if (!m_Owner.ResolveSignatureType (it->second, signatureType))
LogPrint (eLogWarning, "SAM: ", SAM_PARAM_SIGNATURE_TYPE, " is invalid ", it->second);
2019-01-23 15:52:17 +00:00
}
2017-11-28 18:24:07 +00:00
it = params.find (SAM_PARAM_CRYPTO_TYPE);
if (it != params.end ())
2019-01-23 15:52:17 +00:00
{
try
{
2019-01-23 15:52:17 +00:00
cryptoType = std::stoi(it->second);
}
catch (const std::exception& ex)
2019-01-23 15:52:17 +00:00
{
LogPrint (eLogWarning, "SAM: ", SAM_PARAM_CRYPTO_TYPE, "error: ", ex.what ());
}
}
2017-11-28 18:24:07 +00:00
auto keys = i2p::data::PrivateKeys::CreateRandomKeys (signatureType, cryptoType);
2014-10-27 21:53:01 +00:00
#ifdef _MSC_VER
size_t l = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_DEST_REPLY,
keys.GetPublic ()->ToBase64 ().c_str (), keys.ToBase64 ().c_str ());
2017-01-31 16:16:55 +00:00
#else
size_t l = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_DEST_REPLY,
2018-04-25 15:25:49 +00:00
keys.GetPublic ()->ToBase64 ().c_str (), keys.ToBase64 ().c_str ());
2014-10-27 21:53:01 +00:00
#endif
SendMessageReply (m_Buffer, l, false);
2014-09-30 15:08:38 +00:00
}
2014-10-02 20:55:01 +00:00
void SAMSocket::ProcessNamingLookup (char * buf, size_t len)
{
2015-12-18 06:50:12 +00:00
LogPrint (eLogDebug, "SAM: naming lookup: ", buf);
2014-10-02 20:55:01 +00:00
std::map<std::string, std::string> params;
2015-03-27 01:23:59 +00:00
ExtractParams (buf, params);
2014-10-02 20:55:01 +00:00
std::string& name = params[SAM_PARAM_NAME];
2015-11-03 14:15:49 +00:00
std::shared_ptr<const i2p::data::IdentityEx> identity;
2019-03-28 16:19:19 +00:00
std::shared_ptr<const Address> addr;
2018-01-15 13:19:57 +00:00
auto session = m_Owner.FindSession(m_ID);
auto dest = session == nullptr ? context.GetSharedLocalDestination() : session->localDestination;
2014-10-03 19:08:41 +00:00
if (name == "ME")
2020-12-19 01:48:08 +00:00
SendNamingLookupReply (name, dest->GetIdentity ());
2019-03-27 19:19:10 +00:00
else if ((identity = context.GetAddressBook ().GetFullAddress (name)) != nullptr)
2020-12-19 01:48:08 +00:00
SendNamingLookupReply (name, identity);
2019-03-29 15:59:59 +00:00
else if ((addr = context.GetAddressBook ().GetAddress (name)))
{
2019-03-29 15:59:59 +00:00
if (addr->IsIdentHash ())
{
auto leaseSet = dest->FindLeaseSet (addr->identHash);
if (leaseSet)
2020-12-19 01:48:08 +00:00
SendNamingLookupReply (name, leaseSet->GetIdentity ());
2019-03-29 15:59:59 +00:00
else
dest->RequestDestination (addr->identHash,
std::bind (&SAMSocket::HandleNamingLookupLeaseSetRequestComplete,
shared_from_this (), std::placeholders::_1, name));
2019-03-29 15:59:59 +00:00
}
else
2019-03-29 15:59:59 +00:00
dest->RequestDestinationWithEncryptedLeaseSet (addr->blindedPublicKey,
std::bind (&SAMSocket::HandleNamingLookupLeaseSetRequestComplete,
shared_from_this (), std::placeholders::_1, name));
}
2017-01-31 16:16:55 +00:00
else
2017-01-31 16:20:16 +00:00
{
LogPrint (eLogError, "SAM: naming failed, unknown address ", name);
2014-10-27 21:53:01 +00:00
#ifdef _MSC_VER
2017-01-31 16:20:16 +00:00
size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str());
2017-01-31 16:16:55 +00:00
#else
2017-01-31 16:20:16 +00:00
size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str());
2014-10-27 21:53:01 +00:00
#endif
2017-01-31 16:20:16 +00:00
SendMessageReply (m_Buffer, len, false);
2014-10-02 20:55:01 +00:00
}
2017-01-30 01:38:18 +00:00
}
2014-10-02 20:55:01 +00:00
2017-01-30 01:38:18 +00:00
void SAMSocket::SendI2PError(const std::string & msg)
{
LogPrint (eLogError, "SAM: i2p error ", msg);
#ifdef _MSC_VER
size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_STATUS_I2P_ERROR, msg.c_str());
2017-01-31 16:16:55 +00:00
#else
2017-01-30 01:38:18 +00:00
size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_STATUS_I2P_ERROR, msg.c_str());
#endif
SendMessageReply (m_Buffer, len, true);
}
2019-03-29 15:59:59 +00:00
void SAMSocket::HandleNamingLookupLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet, std::string name)
2014-11-26 21:51:36 +00:00
{
if (leaseSet)
2017-01-31 16:16:55 +00:00
{
2019-03-27 19:19:10 +00:00
context.GetAddressBook ().InsertFullAddress (leaseSet->GetIdentity ());
2020-12-19 01:48:08 +00:00
SendNamingLookupReply (name, leaseSet->GetIdentity ());
2017-01-31 16:16:55 +00:00
}
else
{
2019-03-29 15:59:59 +00:00
LogPrint (eLogError, "SAM: naming lookup failed. LeaseSet for ", name, " not found");
#ifdef _MSC_VER
2019-03-29 15:59:59 +00:00
size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str());
2017-01-31 16:16:55 +00:00
#else
2019-03-29 15:59:59 +00:00
size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str());
#endif
SendMessageReply (m_Buffer, len, false);
}
2017-01-31 16:16:55 +00:00
}
2020-12-19 01:48:08 +00:00
void SAMSocket::SendNamingLookupReply (const std::string& name, std::shared_ptr<const i2p::data::IdentityEx> identity)
2014-10-03 19:08:41 +00:00
{
2015-11-03 14:15:49 +00:00
auto base64 = identity->ToBase64 ();
2014-10-27 21:53:01 +00:00
#ifdef _MSC_VER
2020-12-19 01:48:08 +00:00
size_t l = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY, name.c_str (), base64.c_str ());
2017-01-31 16:16:55 +00:00
#else
2020-12-19 01:48:08 +00:00
size_t l = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY, name.c_str (), base64.c_str ());
2014-10-27 21:53:01 +00:00
#endif
2014-12-01 19:50:10 +00:00
SendMessageReply (m_Buffer, l, false);
2014-10-03 19:08:41 +00:00
}
2015-03-27 01:23:59 +00:00
void SAMSocket::ExtractParams (char * buf, std::map<std::string, std::string>& params)
2014-09-25 17:22:25 +00:00
{
2017-01-31 16:16:55 +00:00
char * separator;
2014-10-03 13:43:18 +00:00
do
2014-09-25 17:22:25 +00:00
{
2014-10-03 13:43:18 +00:00
separator = strchr (buf, ' ');
if (separator) *separator = 0;
2014-09-25 17:22:25 +00:00
char * value = strchr (buf, '=');
if (value)
{
*value = 0;
value++;
params[buf] = value;
2017-01-31 16:16:55 +00:00
}
2014-09-28 13:05:37 +00:00
buf = separator + 1;
2014-09-25 17:22:25 +00:00
}
2014-10-03 13:43:18 +00:00
while (separator);
2017-01-31 16:16:55 +00:00
}
2014-09-25 17:22:25 +00:00
2014-09-24 18:59:03 +00:00
void SAMSocket::Receive ()
{
2018-04-24 13:45:16 +00:00
m_Socket.async_read_some (boost::asio::buffer(m_Buffer + m_BufferOffset, SAM_SOCKET_BUFFER_SIZE - m_BufferOffset),
std::bind((m_SocketType == eSAMSocketTypeStream) ? &SAMSocket::HandleReceived : &SAMSocket::HandleMessage,
shared_from_this (), std::placeholders::_1, std::placeholders::_2));
2014-09-24 18:59:03 +00:00
}
void SAMSocket::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
if (ecode)
2018-01-15 13:19:57 +00:00
{
2015-12-18 06:50:12 +00:00
LogPrint (eLogError, "SAM: read error: ", ecode.message ());
2014-09-24 18:59:03 +00:00
if (ecode != boost::asio::error::operation_aborted)
2017-02-01 19:06:32 +00:00
Terminate ("read error");
2014-09-24 18:59:03 +00:00
}
else
{
if (m_Stream)
2017-01-31 16:16:55 +00:00
{
2017-03-29 17:59:48 +00:00
bytes_transferred += m_BufferOffset;
m_BufferOffset = 0;
2015-04-09 22:40:23 +00:00
m_Stream->AsyncSend ((uint8_t *)m_Buffer, bytes_transferred,
2018-04-24 13:45:16 +00:00
std::bind(&SAMSocket::HandleStreamSend, shared_from_this(), std::placeholders::_1));
}
else
{
Terminate("No Stream Remaining");
2017-01-31 16:16:55 +00:00
}
2014-09-24 18:59:03 +00:00
}
}
2014-09-26 19:40:57 +00:00
void SAMSocket::I2PReceive ()
2014-09-24 18:59:03 +00:00
{
if (m_Stream)
2016-10-28 15:33:11 +00:00
{
if (m_Stream->GetStatus () == i2p::stream::eStreamStatusNew ||
m_Stream->GetStatus () == i2p::stream::eStreamStatusOpen) // regular
2017-01-31 16:16:55 +00:00
{
2016-10-28 15:33:11 +00:00
m_Stream->AsyncReceive (boost::asio::buffer (m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE),
2018-01-15 13:19:57 +00:00
std::bind (&SAMSocket::HandleI2PReceive, shared_from_this(),
2016-10-28 15:33:11 +00:00
std::placeholders::_1, std::placeholders::_2),
2018-01-15 13:19:57 +00:00
SAM_SOCKET_CONNECTION_MAX_IDLE);
2016-10-28 15:33:11 +00:00
}
else // closed by peer
{
2018-01-15 13:19:57 +00:00
uint8_t * buff = new uint8_t[SAM_SOCKET_BUFFER_SIZE];
2016-10-28 15:33:11 +00:00
// get remaning data
2018-01-15 13:19:57 +00:00
auto len = m_Stream->ReadSome (buff, SAM_SOCKET_BUFFER_SIZE);
2016-10-28 15:33:11 +00:00
if (len > 0) // still some data
{
2018-01-15 13:19:57 +00:00
WriteI2PDataImmediate(buff, len);
2016-10-28 15:33:11 +00:00
}
else // no more data
2018-04-24 18:02:48 +00:00
{
delete [] buff;
2017-02-01 19:06:32 +00:00
Terminate ("no more data");
2018-04-24 18:02:48 +00:00
}
2018-01-06 03:48:51 +00:00
}
2016-10-28 15:33:11 +00:00
}
2017-01-31 16:16:55 +00:00
}
2014-09-24 18:59:03 +00:00
2018-01-15 13:19:57 +00:00
void SAMSocket::WriteI2PDataImmediate(uint8_t * buff, size_t sz)
{
2018-04-24 13:45:16 +00:00
boost::asio::async_write (
m_Socket,
boost::asio::buffer (buff, sz),
boost::asio::transfer_all(),
std::bind (&SAMSocket::HandleWriteI2PDataImmediate, shared_from_this (), std::placeholders::_1, buff)); // postpone termination
2018-01-15 13:19:57 +00:00
}
void SAMSocket::HandleWriteI2PDataImmediate(const boost::system::error_code & ec, uint8_t * buff)
{
delete [] buff;
}
2018-01-15 13:19:57 +00:00
void SAMSocket::WriteI2PData(size_t sz)
{
boost::asio::async_write (
m_Socket,
boost::asio::buffer (m_StreamBuffer, sz),
boost::asio::transfer_all(),
2018-04-24 15:16:15 +00:00
std::bind(&SAMSocket::HandleWriteI2PData, shared_from_this(), std::placeholders::_1, std::placeholders::_2));
2018-01-15 13:19:57 +00:00
}
2014-09-26 19:40:57 +00:00
void SAMSocket::HandleI2PReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred)
2014-09-24 18:59:03 +00:00
{
if (ecode)
{
2015-12-18 06:50:12 +00:00
LogPrint (eLogError, "SAM: stream read error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
2016-10-28 15:33:11 +00:00
{
if (bytes_transferred > 0)
2018-01-15 13:19:57 +00:00
{
WriteI2PData(bytes_transferred);
}
2017-01-31 16:16:55 +00:00
else
{
2016-12-18 14:40:52 +00:00
auto s = shared_from_this ();
2017-02-01 19:06:32 +00:00
m_Owner.GetService ().post ([s] { s->Terminate ("stream read error"); });
2016-12-18 14:40:52 +00:00
}
2016-10-28 15:33:11 +00:00
}
2017-01-31 16:16:55 +00:00
else
{
2016-12-18 14:40:52 +00:00
auto s = shared_from_this ();
2017-02-01 19:06:32 +00:00
m_Owner.GetService ().post ([s] { s->Terminate ("stream read error (op aborted)"); });
2018-01-06 03:48:51 +00:00
}
2014-09-24 18:59:03 +00:00
}
else
{
2018-01-15 13:19:57 +00:00
if (m_SocketType != eSAMSocketTypeTerminated)
{
if (bytes_transferred > 0)
{
WriteI2PData(bytes_transferred);
}
else
I2PReceive();
2018-01-15 13:19:57 +00:00
}
2014-09-24 18:59:03 +00:00
}
}
2018-01-15 13:19:57 +00:00
void SAMSocket::HandleWriteI2PData (const boost::system::error_code& ecode, size_t bytes_transferred)
2014-09-24 18:59:03 +00:00
{
if (ecode)
{
2015-12-18 06:50:12 +00:00
LogPrint (eLogError, "SAM: socket write error: ", ecode.message ());
2014-09-24 18:59:03 +00:00
if (ecode != boost::asio::error::operation_aborted)
2017-02-01 19:06:32 +00:00
Terminate ("socket write error at HandleWriteI2PData");
2014-09-24 18:59:03 +00:00
}
else
2018-01-15 13:19:57 +00:00
{
2014-09-26 19:40:57 +00:00
I2PReceive ();
2018-01-15 13:19:57 +00:00
}
2014-09-24 18:59:03 +00:00
}
2014-11-23 16:33:58 +00:00
void SAMSocket::HandleI2PAccept (std::shared_ptr<i2p::stream::Stream> stream)
2014-09-26 19:40:57 +00:00
{
2014-09-29 18:18:06 +00:00
if (stream)
{
2015-12-18 06:50:12 +00:00
LogPrint (eLogDebug, "SAM: incoming I2P connection for session ", m_ID);
2016-12-24 13:53:35 +00:00
m_SocketType = eSAMSocketTypeStream;
m_IsAccepting = false;
2014-09-29 18:18:06 +00:00
m_Stream = stream;
2019-03-27 19:19:10 +00:00
context.GetAddressBook ().InsertFullAddress (stream->GetRemoteIdentity ());
2016-12-23 00:38:17 +00:00
auto session = m_Owner.FindSession (m_ID);
2016-12-24 13:53:35 +00:00
if (session)
2017-01-31 16:16:55 +00:00
{
2016-12-24 13:53:35 +00:00
// find more pending acceptors
2018-04-24 13:56:24 +00:00
for (auto & it: m_Owner.ListSockets (m_ID))
2016-12-24 13:53:35 +00:00
if (it->m_SocketType == eSAMSocketTypeAcceptor)
{
it->m_IsAccepting = true;
2016-12-24 14:55:59 +00:00
session->localDestination->AcceptOnce (std::bind (&SAMSocket::HandleI2PAccept, it, std::placeholders::_1));
2016-12-24 13:53:35 +00:00
break;
}
}
2014-09-29 18:18:06 +00:00
if (!m_IsSilent)
{
// get remote peer address
auto ident_ptr = stream->GetRemoteIdentity();
2016-02-05 17:39:17 +00:00
const size_t ident_len = ident_ptr->GetFullLen();
uint8_t* ident = new uint8_t[ident_len];
2017-01-31 16:16:55 +00:00
// send remote peer address as base64
2016-02-05 17:39:17 +00:00
const size_t l = ident_ptr->ToBuffer (ident, ident_len);
const size_t l1 = i2p::data::ByteStreamToBase64 (ident, l, (char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE);
delete[] ident;
2015-02-01 02:49:54 +00:00
m_StreamBuffer[l1] = '\n';
HandleI2PReceive (boost::system::error_code (), l1 +1); // we send identity like it has been received from stream
2017-01-31 16:16:55 +00:00
}
2015-02-01 02:49:54 +00:00
else
I2PReceive ();
2014-09-29 18:18:06 +00:00
}
2015-02-01 14:34:32 +00:00
else
2015-12-18 06:50:12 +00:00
LogPrint (eLogWarning, "SAM: I2P acceptor has been reset");
2017-01-31 16:16:55 +00:00
}
2014-09-26 19:40:57 +00:00
void SAMSocket::HandleI2PForward (std::shared_ptr<i2p::stream::Stream> stream,
boost::asio::ip::tcp::endpoint ep)
{
if (stream)
{
LogPrint (eLogDebug, "SAM: incoming forward I2P connection for session ", m_ID);
auto newSocket = std::make_shared<SAMSocket>(m_Owner);
newSocket->SetSocketType (eSAMSocketTypeStream);
auto s = shared_from_this ();
newSocket->GetSocket ().async_connect (ep,
[s, newSocket, stream](const boost::system::error_code& ecode)
{
if (!ecode)
{
s->m_Owner.AddSocket (newSocket);
newSocket->Receive ();
newSocket->m_Stream = stream;
2020-10-26 15:19:37 +00:00
newSocket->m_ID = s->m_ID;
if (!s->m_IsSilent)
{
// get remote peer address
auto dest = stream->GetRemoteIdentity()->ToBase64 ();
memcpy (newSocket->m_StreamBuffer, dest.c_str (), dest.length ());
newSocket->m_StreamBuffer[dest.length ()] = '\n';
newSocket->HandleI2PReceive (boost::system::error_code (),dest.length () + 1); // we send identity like it has been received from stream
}
else
newSocket->I2PReceive ();
}
else
stream->AsyncClose ();
});
}
else
LogPrint (eLogWarning, "SAM: I2P forward acceptor has been reset");
}
void SAMSocket::HandleI2PDatagramReceive (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
2014-10-31 20:44:44 +00:00
{
2015-12-18 06:50:12 +00:00
LogPrint (eLogDebug, "SAM: datagram received ", len);
auto base64 = from.ToBase64 ();
2018-01-15 13:19:57 +00:00
auto session = m_Owner.FindSession(m_ID);
if(session)
2017-01-30 01:38:18 +00:00
{
2018-01-15 13:19:57 +00:00
auto ep = session->UDPEndpoint;
if (ep)
{
// udp forward enabled
size_t bsz = base64.size();
size_t sz = bsz + 1 + len;
// build datagram body
uint8_t * data = new uint8_t[sz];
// Destination
memcpy(data, base64.c_str(), bsz);
// linefeed
data[bsz] = '\n';
// Payload
memcpy(data+bsz+1, buf, len);
// send to remote endpoint
m_Owner.SendTo(data, sz, ep);
delete [] data;
}
else
{
2014-10-31 20:44:44 +00:00
#ifdef _MSC_VER
2018-01-15 13:19:57 +00:00
size_t l = sprintf_s ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_DATAGRAM_RECEIVED, base64.c_str (), (long unsigned int)len);
2017-01-31 16:16:55 +00:00
#else
2018-01-15 13:19:57 +00:00
size_t l = snprintf ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_DATAGRAM_RECEIVED, base64.c_str (), (long unsigned int)len);
2014-10-31 20:44:44 +00:00
#endif
2018-01-15 13:19:57 +00:00
if (len < SAM_SOCKET_BUFFER_SIZE - l)
{
memcpy (m_StreamBuffer + l, buf, len);
WriteI2PData(len + l);
}
else
LogPrint (eLogWarning, "SAM: received datagram size ", len," exceeds buffer");
2017-01-30 01:38:18 +00:00
}
2014-10-31 20:44:44 +00:00
}
}
void SAMSocket::HandleI2PRawDatagramReceive (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
{
LogPrint (eLogDebug, "SAM: raw datagram received ", len);
auto session = m_Owner.FindSession(m_ID);
if(session)
{
auto ep = session->UDPEndpoint;
if (ep)
// udp forward enabled
m_Owner.SendTo(buf, len, ep);
else
{
#ifdef _MSC_VER
size_t l = sprintf_s ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_RAW_RECEIVED, (long unsigned int)len);
#else
size_t l = snprintf ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_RAW_RECEIVED, (long unsigned int)len);
#endif
if (len < SAM_SOCKET_BUFFER_SIZE - l)
{
memcpy (m_StreamBuffer + l, buf, len);
WriteI2PData(len + l);
}
else
LogPrint (eLogWarning, "SAM: received raw datagram size ", len," exceeds buffer");
}
}
}
2018-04-24 13:45:16 +00:00
void SAMSocket::HandleStreamSend(const boost::system::error_code & ec)
{
m_Owner.GetService ().post (std::bind( !ec ? &SAMSocket::Receive : &SAMSocket::TerminateClose, shared_from_this()));
}
SAMSession::SAMSession (SAMBridge & parent, const std::string & id, SAMSessionType type, std::shared_ptr<ClientDestination> dest):
2018-04-24 13:45:16 +00:00
m_Bridge(parent),
2017-01-30 01:38:18 +00:00
localDestination (dest),
2018-04-24 13:45:16 +00:00
UDPEndpoint(nullptr),
Name(id), Type (type)
{
}
2016-12-23 00:38:17 +00:00
SAMSession::~SAMSession ()
{
2016-12-23 00:38:17 +00:00
i2p::client::context.DeleteLocalDestination (localDestination);
}
void SAMSession::CloseStreams ()
{
2018-04-24 13:45:16 +00:00
for(const auto & itr : m_Bridge.ListSockets(Name))
2016-12-23 00:38:17 +00:00
{
2018-04-24 13:45:16 +00:00
itr->Terminate(nullptr);
2016-12-23 00:38:17 +00:00
}
}
2020-02-04 20:31:04 +00:00
SAMBridge::SAMBridge (const std::string& address, int port, bool singleThread):
RunnableService ("SAM"), m_IsSingleThread (singleThread),
m_Acceptor (GetIOService (), boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), port)),
m_DatagramEndpoint (boost::asio::ip::address::from_string(address), port-1), m_DatagramSocket (GetIOService (), m_DatagramEndpoint),
2019-01-23 15:52:17 +00:00
m_SignatureTypes
{
{"DSA_SHA1", i2p::data::SIGNING_KEY_TYPE_DSA_SHA1},
{"ECDSA_SHA256_P256", i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256},
{"ECDSA_SHA384_P384", i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA384_P384},
{"ECDSA_SHA512_P521", i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA512_P521},
2019-01-23 15:52:17 +00:00
{"EdDSA_SHA512_Ed25519", i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519},
{"GOST_GOSTR3411256_GOSTR3410CRYPTOPROA", i2p::data::SIGNING_KEY_TYPE_GOSTR3410_CRYPTO_PRO_A_GOSTR3411_256},
{"GOST_GOSTR3411512_GOSTR3410TC26A512", i2p::data::SIGNING_KEY_TYPE_GOSTR3410_TC26_A_512_GOSTR3411_512},
{"RedDSA_SHA512_Ed25519", i2p::data::SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519},
2019-01-23 15:52:17 +00:00
}
2014-09-24 16:01:26 +00:00
{
}
SAMBridge::~SAMBridge ()
{
if (IsRunning ())
2014-12-18 00:02:16 +00:00
Stop ();
2017-01-31 16:16:55 +00:00
}
2014-09-24 16:01:26 +00:00
void SAMBridge::Start ()
{
Accept ();
ReceiveDatagram ();
StartIOService ();
2014-09-24 16:01:26 +00:00
}
void SAMBridge::Stop ()
{
try
{
m_Acceptor.cancel ();
}
catch (const std::exception& ex)
{
LogPrint (eLogError, "SAM: runtime exception: ", ex.what ());
}
2020-02-06 15:53:45 +00:00
{
std::unique_lock<std::mutex> l(m_SessionsMutex);
for (auto& it: m_Sessions)
it.second->CloseStreams ();
m_Sessions.clear ();
}
StopIOService ();
2014-09-24 16:01:26 +00:00
}
void SAMBridge::Accept ()
{
2018-04-24 13:45:16 +00:00
auto newSocket = std::make_shared<SAMSocket>(*this);
m_Acceptor.async_accept (newSocket->GetSocket(), std::bind (&SAMBridge::HandleAccept, this,
2014-11-23 22:00:45 +00:00
std::placeholders::_1, newSocket));
2014-09-24 16:01:26 +00:00
}
void SAMBridge::AddSocket(std::shared_ptr<SAMSocket> socket)
{
std::unique_lock<std::mutex> lock(m_OpenSocketsMutex);
m_OpenSockets.push_back(socket);
}
2018-04-24 15:42:37 +00:00
void SAMBridge::RemoveSocket(const std::shared_ptr<SAMSocket> & socket)
2018-04-24 13:45:16 +00:00
{
2018-04-24 15:42:37 +00:00
std::unique_lock<std::mutex> lock(m_OpenSocketsMutex);
m_OpenSockets.remove_if([socket](const std::shared_ptr<SAMSocket> & item) -> bool { return item == socket; });
2018-04-24 13:45:16 +00:00
}
2014-11-22 21:35:58 +00:00
void SAMBridge::HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<SAMSocket> socket)
2014-09-24 16:01:26 +00:00
{
if (!ecode)
{
2015-02-22 22:04:42 +00:00
boost::system::error_code ec;
auto ep = socket->GetSocket ().remote_endpoint (ec);
if (!ec)
2017-01-31 16:16:55 +00:00
{
2015-12-18 06:50:12 +00:00
LogPrint (eLogDebug, "SAM: new connection from ", ep);
AddSocket (socket);
2015-02-22 22:04:42 +00:00
socket->ReceiveHandshake ();
}
else
2015-12-18 06:50:12 +00:00
LogPrint (eLogError, "SAM: incoming connection error ", ec.message ());
2014-09-24 16:01:26 +00:00
}
else
2015-12-18 06:50:12 +00:00
LogPrint (eLogError, "SAM: accept error: ", ecode.message ());
2014-09-24 16:01:26 +00:00
if (ecode != boost::asio::error::operation_aborted)
Accept ();
}
2014-09-24 20:39:31 +00:00
std::shared_ptr<SAMSession> SAMBridge::CreateSession (const std::string& id, SAMSessionType type,
const std::string& destination, const std::map<std::string, std::string> * params)
2014-09-24 20:39:31 +00:00
{
2017-01-31 16:16:55 +00:00
std::shared_ptr<ClientDestination> localDestination = nullptr;
2014-09-25 17:22:25 +00:00
if (destination != "")
2014-09-24 20:39:31 +00:00
{
i2p::data::PrivateKeys keys;
2017-03-25 20:53:20 +00:00
if (!keys.FromBase64 (destination)) return nullptr;
localDestination = m_IsSingleThread ?
2020-02-04 20:31:04 +00:00
i2p::client::context.CreateNewLocalDestination (GetIOService (), keys, true, params) :
i2p::client::context.CreateNewLocalDestination (keys, true, params);
2014-09-24 20:39:31 +00:00
}
else // transient
2014-12-16 21:23:42 +00:00
{
// extract signature type
i2p::data::SigningKeyType signatureType = i2p::data::SIGNING_KEY_TYPE_DSA_SHA1;
2018-01-06 03:48:51 +00:00
i2p::data::CryptoKeyType cryptoType = i2p::data::CRYPTO_KEY_TYPE_ELGAMAL;
2014-12-16 21:23:42 +00:00
if (params)
{
auto it = params->find (SAM_PARAM_SIGNATURE_TYPE);
if (it != params->end ())
{
2019-01-23 15:52:17 +00:00
if (!ResolveSignatureType (it->second, signatureType))
LogPrint (eLogWarning, "SAM: ", SAM_PARAM_SIGNATURE_TYPE, " is invalid ", it->second);
}
2017-11-14 20:05:07 +00:00
it = params->find (SAM_PARAM_CRYPTO_TYPE);
if (it != params->end ())
{
try
{
cryptoType = std::stoi(it->second);
}
catch (const std::exception& ex)
{
LogPrint (eLogWarning, "SAM: ", SAM_PARAM_CRYPTO_TYPE, "error: ", ex.what ());
}
}
2014-12-16 21:23:42 +00:00
}
localDestination = m_IsSingleThread ?
2020-02-04 20:31:04 +00:00
i2p::client::context.CreateNewLocalDestination (GetIOService (), true, signatureType, cryptoType, params) :
i2p::client::context.CreateNewLocalDestination (true, signatureType, cryptoType, params);
2014-12-16 21:23:42 +00:00
}
2014-09-24 20:39:31 +00:00
if (localDestination)
{
2017-07-06 20:12:06 +00:00
localDestination->Acquire ();
auto session = std::make_shared<SAMSession>(*this, id, type, localDestination);
2014-10-06 01:59:05 +00:00
std::unique_lock<std::mutex> l(m_SessionsMutex);
2016-04-03 02:16:49 +00:00
auto ret = m_Sessions.insert (std::make_pair(id, session));
2016-12-23 00:38:17 +00:00
if (!ret.second)
2015-12-18 06:50:12 +00:00
LogPrint (eLogWarning, "SAM: Session ", id, " already exists");
2014-12-18 00:02:16 +00:00
return ret.first->second;
2014-09-24 20:39:31 +00:00
}
2014-09-25 17:22:25 +00:00
return nullptr;
2014-09-24 20:39:31 +00:00
}
void SAMBridge::CloseSession (const std::string& id)
{
2016-04-03 02:16:49 +00:00
std::shared_ptr<SAMSession> session;
2014-09-24 20:39:31 +00:00
{
2016-04-03 02:16:49 +00:00
std::unique_lock<std::mutex> l(m_SessionsMutex);
auto it = m_Sessions.find (id);
if (it != m_Sessions.end ())
2017-01-31 16:16:55 +00:00
{
2016-04-03 02:16:49 +00:00
session = it->second;
m_Sessions.erase (it);
2017-01-31 16:16:55 +00:00
}
}
2016-04-03 02:16:49 +00:00
if (session)
2017-01-31 16:16:55 +00:00
{
2017-07-06 20:12:06 +00:00
session->localDestination->Release ();
2015-02-01 14:34:32 +00:00
session->localDestination->StopAcceptingStreams ();
2014-12-18 00:02:16 +00:00
session->CloseStreams ();
2020-02-17 20:14:35 +00:00
if (m_IsSingleThread)
{
auto timer = std::make_shared<boost::asio::deadline_timer>(GetService ());
timer->expires_from_now (boost::posix_time::seconds(5)); // postpone destination clean for 5 seconds
timer->async_wait ([timer, session](const boost::system::error_code& ecode)
2020-02-17 20:14:35 +00:00
{
// session's destructor is called here
});
}
2014-09-24 20:39:31 +00:00
}
}
2014-09-25 17:22:25 +00:00
2016-04-03 02:16:49 +00:00
std::shared_ptr<SAMSession> SAMBridge::FindSession (const std::string& id) const
2014-09-25 17:22:25 +00:00
{
2014-10-06 01:59:05 +00:00
std::unique_lock<std::mutex> l(m_SessionsMutex);
2014-09-25 17:22:25 +00:00
auto it = m_Sessions.find (id);
if (it != m_Sessions.end ())
2014-12-18 00:02:16 +00:00
return it->second;
2014-09-25 17:22:25 +00:00
return nullptr;
}
2018-04-24 13:45:16 +00:00
std::list<std::shared_ptr<SAMSocket> > SAMBridge::ListSockets(const std::string & id) const
{
std::list<std::shared_ptr<SAMSocket > > list;
{
2018-04-24 15:42:37 +00:00
std::unique_lock<std::mutex> l(m_OpenSocketsMutex);
2018-04-24 13:45:16 +00:00
for (const auto & itr : m_OpenSockets)
if (itr->IsSession(id))
list.push_back(itr);
}
return list;
}
2017-01-30 01:38:18 +00:00
void SAMBridge::SendTo(const uint8_t * buf, size_t len, std::shared_ptr<boost::asio::ip::udp::endpoint> remote)
{
if(remote)
{
m_DatagramSocket.send_to(boost::asio::buffer(buf, len), *remote);
}
}
void SAMBridge::ReceiveDatagram ()
{
m_DatagramSocket.async_receive_from (
2017-01-31 16:16:55 +00:00
boost::asio::buffer (m_DatagramReceiveBuffer, i2p::datagram::MAX_DATAGRAM_SIZE),
m_SenderEndpoint,
2017-01-31 16:16:55 +00:00
std::bind (&SAMBridge::HandleReceivedDatagram, this, std::placeholders::_1, std::placeholders::_2));
}
void SAMBridge::HandleReceivedDatagram (const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
if (!ecode)
{
2014-10-24 01:14:17 +00:00
m_DatagramReceiveBuffer[bytes_transferred] = 0;
char * eol = strchr ((char *)m_DatagramReceiveBuffer, '\n');
2018-04-24 13:45:16 +00:00
if(eol)
2014-10-24 01:14:17 +00:00
{
2018-04-24 13:45:16 +00:00
*eol = 0; eol++;
size_t payloadLen = bytes_transferred - ((uint8_t *)eol - m_DatagramReceiveBuffer);
LogPrint (eLogDebug, "SAM: datagram received ", m_DatagramReceiveBuffer," size=", payloadLen);
char * sessionID = strchr ((char *)m_DatagramReceiveBuffer, ' ');
if (sessionID)
2014-10-24 01:14:17 +00:00
{
2018-04-24 13:45:16 +00:00
sessionID++;
char * destination = strchr (sessionID, ' ');
if (destination)
2017-01-31 16:16:55 +00:00
{
2018-04-24 13:45:16 +00:00
*destination = 0; destination++;
auto session = FindSession (sessionID);
if (session)
{
i2p::data::IdentityEx dest;
dest.FromBase64 (destination);
if (session->Type == eSAMSessionTypeDatagram)
session->localDestination->GetDatagramDestination ()->
SendDatagramTo ((uint8_t *)eol, payloadLen, dest.GetIdentHash ());
else // raw
session->localDestination->GetDatagramDestination ()->
SendRawDatagramTo ((uint8_t *)eol, payloadLen, dest.GetIdentHash ());
2018-04-24 13:45:16 +00:00
}
else
LogPrint (eLogError, "SAM: Session ", sessionID, " not found");
2017-01-31 16:16:55 +00:00
}
2014-10-24 01:14:17 +00:00
else
2018-04-24 13:45:16 +00:00
LogPrint (eLogError, "SAM: Missing destination key");
2014-10-24 01:14:17 +00:00
}
else
2018-04-24 13:45:16 +00:00
LogPrint (eLogError, "SAM: Missing sessionID");
2014-10-24 01:14:17 +00:00
}
else
2018-04-24 13:45:16 +00:00
LogPrint(eLogError, "SAM: invalid datagram");
ReceiveDatagram ();
}
else
2015-12-18 06:50:12 +00:00
LogPrint (eLogError, "SAM: datagram receive error: ", ecode.message ());
}
2019-01-23 15:52:17 +00:00
bool SAMBridge::ResolveSignatureType (const std::string& name, i2p::data::SigningKeyType& type) const
{
try
{
2019-01-23 15:52:17 +00:00
type = std::stoi (name);
}
catch (const std::invalid_argument& ex)
{
// name is not numeric, resolving
auto it = m_SignatureTypes.find (name);
if (it != m_SignatureTypes.end ())
type = it->second;
else
return false;
}
catch (const std::exception& ex)
2019-01-23 15:52:17 +00:00
{
return false;
}
// name has been resolved
return true;
2019-01-23 15:52:17 +00:00
}
2014-09-24 16:01:26 +00:00
}
}