diff --git a/client/SAM.cpp b/client/SAM.cpp index 38b249e3..89a8e722 100644 --- a/client/SAM.cpp +++ b/client/SAM.cpp @@ -3,6 +3,7 @@ #ifdef _MSC_VER #include #endif +#include #include #include "util/base64.h" #include "Identity.h" @@ -42,6 +43,7 @@ namespace client switch (m_SocketType) { + case eSAMSocketTypeUDPForward: case eSAMSocketTypeSession: m_Owner.CloseSession (m_ID); break; @@ -282,8 +284,27 @@ namespace client if (style == SAM_VALUE_DATAGRAM) { auto dest = m_Session->localDestination->CreateDatagramDestination (); + auto portitr = params.find(SAM_PARAM_PORT); + + if ( portitr != params.end() ) { + // port parameter set, this means they want to do UDP forward + auto port = boost::lexical_cast (portitr->second); + // XXX: have default host configurable? + std::string host = "127.0.0.1"; + + auto hostitr = params.find(SAM_PARAM_HOST); + if ( hostitr != params.end() ) { + // host parameter set use that instead of loopback + host = hostitr->second; + } + // set forward addresss + m_udpForward = boost::asio::ip::udp::endpoint(boost::asio::ip::address::from_string(host), port); + // we are now a udp forward socket + m_SocketType = eSAMSocketTypeUDPForward; + } dest->SetReceiver (std::bind (&SAMSocket::HandleI2PDatagramReceive, shared_from_this (), - std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5)); + std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5)); + } if (m_Session->localDestination->IsReady ()) @@ -646,20 +667,27 @@ namespace client void SAMSocket::HandleI2PDatagramReceive (const i2p::data::IdentityEx& from, uint16_t, uint16_t, const uint8_t * buf, size_t len) { LogPrint (eLogDebug, "SAM datagram received ", len); - auto base64 = from.ToBase64 (); + if (m_SocketType == eSAMSocketTypeUDPForward) + { + // use the bridge to forward it via udp + m_Owner.ForwardUDP(m_udpForward, from, buf, len); + } else { + auto base64 = from.ToBase64 (); + // use the session socket #ifdef _MSC_VER - size_t l = sprintf_s ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_DATAGRAM_RECEIVED, base64.c_str (), len); + size_t l = sprintf_s ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_DATAGRAM_RECEIVED, base64.c_str (), len); #else - size_t l = snprintf ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_DATAGRAM_RECEIVED, base64.c_str (), len); + size_t l = snprintf ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_DATAGRAM_RECEIVED, base64.c_str (), len); #endif - if (len < SAM_SOCKET_BUFFER_SIZE - l) - { - memcpy (m_StreamBuffer + l, buf, len); - boost::asio::async_write (m_Socket, boost::asio::buffer (m_StreamBuffer, len + l), - std::bind (&SAMSocket::HandleWriteI2PData, shared_from_this (), std::placeholders::_1)); + if (len < SAM_SOCKET_BUFFER_SIZE - l) + { + memcpy (m_StreamBuffer + l, buf, len); + boost::asio::async_write (m_Socket, boost::asio::buffer (m_StreamBuffer, len + l), + std::bind (&SAMSocket::HandleWriteI2PData, shared_from_this (), std::placeholders::_1)); + } + else + LogPrint (eLogWarning, "SAM received datagram size ", len," exceeds buffer"); } - else - LogPrint (eLogWarning, "SAM received datagram size ", len," exceeds buffer"); } SAMSession::SAMSession (std::shared_ptr dest): @@ -690,7 +718,8 @@ namespace client boost::asio::ip::address::from_string(address), port) ), m_DatagramEndpoint(boost::asio::ip::address::from_string(address), port - 1), - m_DatagramSocket(m_Service, m_DatagramEndpoint) + m_DatagramSocket(m_Service, m_DatagramEndpoint), + m_Forward(nullptr) { } @@ -870,5 +899,40 @@ namespace client else LogPrint ("SAM datagram receive error: ", ecode.message ()); } + + void SAMBridge::ForwardUDP(const boost::asio::ip::udp::endpoint & to_ep, const i2p::data::IdentityEx& from, const uint8_t * buff, size_t bufflen) + { + + if (m_Forward) + { + // drop, we are already trying to send + LogPrint(eLogWarning, "Dropping a forwarded datagram"); + } else { + auto base64 = from.ToBase64(); + auto b64_size = base64.size(); + std::size_t forward_len = bufflen + sizeof(char) + b64_size; + m_Forward = new uint8_t[forward_len]; + // make datagram + memcpy(m_Forward, base64.c_str(), b64_size); + memset(m_Forward + b64_size, 10, 1); + memcpy(m_Forward + b64_size + 1, buff, bufflen); + // async sendto + m_DatagramSocket.async_send_to( + boost::asio::buffer(m_Forward, forward_len), + to_ep, + std::bind(&SAMBridge::HandleForwardedUDP, this, std::placeholders::_1, std::placeholders::_2)); + } + + } + + void SAMBridge::HandleForwardedUDP(const boost::system::error_code& ecode, std::size_t bytes_transferrted) + { + if(m_Forward) + { + delete [] m_Forward; + m_Forward = nullptr; + } + } + } } diff --git a/client/SAM.h b/client/SAM.h index 10b97191..f75d66d9 100644 --- a/client/SAM.h +++ b/client/SAM.h @@ -53,10 +53,13 @@ namespace client const char SAM_PARAM_NAME[] = "NAME"; const char SAM_PARAM_SIGNATURE_TYPE[] = "SIGNATURE_TYPE"; const char SAM_PARAM_SIZE[] = "SIZE"; + const char SAM_PARAM_FORWARD[] = "FORWARD"; + const char SAM_PARAM_HOST[] = "HOST"; + const char SAM_PARAM_PORT[] = "PORT"; const char SAM_VALUE_TRANSIENT[] = "TRANSIENT"; const char SAM_VALUE_STREAM[] = "STREAM"; const char SAM_VALUE_DATAGRAM[] = "DATAGRAM"; - const char SAM_VALUE_RAW[] = "RAW"; + const char SAM_VALUE_RAW[] = "RAW"; const char SAM_VALUE_TRUE[] = "true"; const char SAM_VALUE_FALSE[] = "false"; @@ -66,7 +69,8 @@ namespace client eSAMSocketTypeSession, eSAMSocketTypeStream, eSAMSocketTypeAcceptor, - eSAMSocketTypeTerminated + eSAMSocketTypeUDPForward, + eSAMSocketTypeTerminated, }; class SAMBridge; @@ -119,6 +123,7 @@ namespace client private: SAMBridge& m_Owner; + boost::asio::ip::udp::endpoint m_udpForward; boost::asio::ip::tcp::socket m_Socket; boost::asio::deadline_timer m_Timer; char m_Buffer[SAM_SOCKET_BUFFER_SIZE + 1]; @@ -158,6 +163,9 @@ namespace client void CloseSession (const std::string& id); SAMSession * FindSession (const std::string& id) const; + // forward a datagran to a udp endpoint + void ForwardUDP(const boost::asio::ip::udp::endpoint & to_ep, const i2p::data::IdentityEx& from, const uint8_t * buff, size_t bufflen); + private: void Run (); @@ -167,7 +175,8 @@ namespace client void ReceiveDatagram (); void HandleReceivedDatagram (const boost::system::error_code& ecode, std::size_t bytes_transferred); - + void HandleForwardedUDP(const boost::system::error_code& ecode, std::size_t bytes_transferrted); + private: bool m_IsRunning; @@ -179,7 +188,7 @@ namespace client mutable std::mutex m_SessionsMutex; std::map m_Sessions; uint8_t m_DatagramReceiveBuffer[i2p::datagram::MAX_DATAGRAM_SIZE+1]; - + uint8_t * m_Forward; // current buffer to forward or nullptr if there is none to forward now public: // for HTTP