diff --git a/Makefile.linux b/Makefile.linux index 02f099d2..af23f955 100644 --- a/Makefile.linux +++ b/Makefile.linux @@ -1,5 +1,5 @@ # set defaults instead redefine -CXXFLAGS ?= -g -Wall -Wextra -Wno-unused-parameter -pedantic +CXXFLAGS ?= -g -Wall -Wextra -Wno-unused-parameter -pedantic -Wno-misleading-indentation INCFLAGS ?= ## NOTE: The NEEDED_CXXFLAGS are here so that custom CXXFLAGS can be specified at build time diff --git a/SAM.cpp b/SAM.cpp index 929b144d..d9bc7bb2 100644 --- a/SAM.cpp +++ b/SAM.cpp @@ -252,6 +252,7 @@ namespace client Terminate (); } } + else { LogPrint (eLogWarning, "SAM: incomplete message ", bytes_transferred); @@ -278,13 +279,37 @@ namespace client return; } + std::shared_ptr forward = nullptr; + if (style == SAM_VALUE_DATAGRAM && params.find(SAM_VALUE_HOST) != params.end() && params.find(SAM_VALUE_PORT) != params.end()) + { + // udp forward selected + boost::system::error_code e; + // TODO: support hostnames in udp forward + auto addr = boost::asio::ip::address::from_string(params[SAM_VALUE_HOST], e); + if (e) + { + // not an ip address + SendI2PError("Invalid IP Address in HOST"); + return; + } + + auto port = std::stoi(params[SAM_VALUE_PORT]); + if (port == -1) + { + SendI2PError("Invalid port"); + return; + } + forward = std::make_shared(addr, port); + } + // create destination - m_Session = m_Owner.CreateSession (id, destination == SAM_VALUE_TRANSIENT ? "" : destination, ¶ms); + m_Session = m_Owner.CreateSession (id, destination == SAM_VALUE_TRANSIENT ? "" : destination, ¶ms); if (m_Session) { m_SocketType = eSAMSocketTypeSession; if (style == SAM_VALUE_DATAGRAM) { + m_Session->UDPEndpoint = forward; auto dest = m_Session->localDestination->CreateDatagramDestination (); dest->SetReceiver (std::bind (&SAMSocket::HandleI2PDatagramReceive, shared_from_this (), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5)); @@ -480,18 +505,29 @@ namespace client shared_from_this (), std::placeholders::_1, ident)); } else - { - LogPrint (eLogError, "SAM: naming failed, unknown address ", name); + { + LogPrint (eLogError, "SAM: naming failed, unknown address ", name); #ifdef _MSC_VER - size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str()); + size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str()); #else - size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str()); + size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str()); #endif - SendMessageReply (m_Buffer, len, false); + SendMessageReply (m_Buffer, len, false); } - } + } - void SAMSocket::HandleNamingLookupLeaseSetRequestComplete (std::shared_ptr leaseSet, i2p::data::IdentHash ident) + 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()); +#else + size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_STATUS_I2P_ERROR, msg.c_str()); +#endif + SendMessageReply (m_Buffer, len, true); + } + + void SAMSocket::HandleNamingLookupLeaseSetRequestComplete (std::shared_ptr leaseSet, i2p::data::IdentHash ident) { if (leaseSet) { @@ -692,23 +728,46 @@ namespace client { LogPrint (eLogDebug, "SAM: datagram received ", len); auto base64 = from.ToBase64 (); + auto ep = m_Session->UDPEndpoint; + if (ep) + { + // udp forward enabled + size_t bsz = base64.size(); + size_t sz = 4 + bsz + 1 + len; + // build datagram body + uint8_t * data = new uint8_t[sz]; + data[0] = '3'; + data[1] = '.'; + data[2] = '0'; + data[3] = ' '; + memcpy(data+4, base64.c_str(), bsz); + data[4+bsz] = '\n'; + memcpy(data+4+bsz+1, buf, sz); + // send to remote endpoint + m_Owner.SendTo(data, sz, ep); + delete [] buf; + } + else + { #ifdef _MSC_VER - size_t l = sprintf_s ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_DATAGRAM_RECEIVED, base64.c_str (), (long unsigned int)len); + size_t l = sprintf_s ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_DATAGRAM_RECEIVED, base64.c_str (), (long unsigned int)len); #else - size_t l = snprintf ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_DATAGRAM_RECEIVED, base64.c_str (), (long unsigned int)len); + size_t l = snprintf ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_DATAGRAM_RECEIVED, base64.c_str (), (long unsigned int)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): - localDestination (dest) + localDestination (dest), + UDPEndpoint(nullptr) { } @@ -873,6 +932,14 @@ namespace client return nullptr; } + void SAMBridge::SendTo(const uint8_t * buf, size_t len, std::shared_ptr remote) + { + if(remote) + { + m_DatagramSocket.send_to(boost::asio::buffer(buf, len), *remote); + } + } + void SAMBridge::ReceiveDatagram () { m_DatagramSocket.async_receive_from ( diff --git a/SAM.h b/SAM.h index db08c5a0..c5aef611 100644 --- a/SAM.h +++ b/SAM.h @@ -29,6 +29,7 @@ namespace client const char SAM_SESSION_CREATE_DUPLICATED_ID[] = "SESSION STATUS RESULT=DUPLICATED_ID\n"; const char SAM_SESSION_CREATE_DUPLICATED_DEST[] = "SESSION STATUS RESULT=DUPLICATED_DEST\n"; const char SAM_SESSION_STATUS_INVALID_KEY[] = "SESSION STATUS RESULT=INVALID_KEY\n"; + const char SAM_SESSION_STATUS_I2P_ERROR[] = "SESSION STATUS RESULT=I2P_ERROR MESSAGE=%s\n"; const char SAM_STREAM_CONNECT[] = "STREAM CONNECT"; const char SAM_STREAM_STATUS_OK[] = "STREAM STATUS RESULT=OK\n"; const char SAM_STREAM_STATUS_INVALID_ID[] = "STREAM STATUS RESULT=INVALID_ID\n"; @@ -58,7 +59,9 @@ namespace client const char SAM_VALUE_DATAGRAM[] = "DATAGRAM"; const char SAM_VALUE_RAW[] = "RAW"; const char SAM_VALUE_TRUE[] = "true"; - const char SAM_VALUE_FALSE[] = "false"; + const char SAM_VALUE_FALSE[] = "false"; + const char SAM_VALUE_HOST[] = "HOST"; + const char SAM_VALUE_PORT[] = "PORT"; enum SAMSocketType { @@ -106,6 +109,7 @@ namespace client void ProcessStreamAccept (char * buf, size_t len); void ProcessDestGenerate (); void ProcessNamingLookup (char * buf, size_t len); + void SendI2PError(const std::string & msg); size_t ProcessDatagramSend (char * buf, size_t len, const char * data); // from SAM 1.0 void ExtractParams (char * buf, std::map& params); @@ -135,6 +139,7 @@ namespace client { std::shared_ptr localDestination; std::list > m_Sockets; + std::shared_ptr UDPEndpoint; std::mutex m_SocketsMutex; /** safely add a socket to this session */ @@ -181,6 +186,9 @@ namespace client void CloseSession (const std::string& id); std::shared_ptr FindSession (const std::string& id) const; + /** send raw data to remote endpoint from our UDP Socket */ + void SendTo(const uint8_t * buf, size_t len, std::shared_ptr remote); + private: void Run ();