#include #include #include #include #include #include #include #include "Base.h" #include "FS.h" #include "Log.h" #include "Tunnel.h" #include "TransitTunnel.h" #include "Transports.h" #include "NetDb.h" #include "HTTP.h" #include "LeaseSet.h" #include "Destination.h" #include "RouterContext.h" #include "ClientContext.h" #include "HTTPServer.h" // For image and info #include "version.h" namespace i2p { namespace http { const char *itoopieImage = "\"ICToopie"; const char *itoopieFavicon = "data:image/png;base64," "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv" "8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAYdEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My4wOGVynO" "EAAAIzSURBVDhPjZNdSFNhGMf3nm3n7OzMs+8JtfJGzdlgoPtoWBrkqc1OsLTMKEY3eZOQbbS6aBVYO" "oO8CKSLXEulQtZNahAM9Cq6lS533UUaeDEEKcN/79x7kbQT/eDhfPB7/u/7Poej08JqtXoEQbhoMpmG" "ZFn2stf/h8nEZ4aHue1SiWBlhSCV4n41NBifBINBjina8DyfzOUIVlcJtrYINjcJ3rw1oFAg4HnjHaZ" "p4/Ppv8zPH0G5XKZNPZibO4lKpYJ8vgOqqv+uKMq/d9Hfz/0sFr3w+/3IZt2YnbWhszOAxUUv0mkCs9" "ncyNT6hEL6dYBgY4Ngd5eger+zU7sODHA/mpubzUytj9FofLa0VGv4s9bWCCTJUGSaNvSzXT3stuHDM" "rc3xEqF4N2CERciURyyHfgqSZKPqfuxUMyC+OKcL4YHyl28nDFAPdqDZMcQ7tPnSfURUt0jMBgMH1nL" "fkRRDPvcLds3otfhbRTwasaE8b6He43VSrT3QW3tBT3iPdbyN3T7Ibsor988H8OxtiaMx2sB1aBbCRW" "R1hbQhbqYXh+6QkaJn8DZyzF09x6HeiaOTC6NK9cSsFqkb3aH3cLU+tCAx9l8FoXPBUy9n8LgyCCmS9" "MYez0Gm9P2iWna0GOcDp8KY2JhAsnbSQS6Ahh9OgrlklINeM40bWhAkBd4SLIEh8cBURLhOeiBIArVA" "U4yTRvJItk5PRehQVFaYfpbt9PBtTmdziaXyyUzjaHT/QZBQuKHAA0UxAAAAABJRU5ErkJggg=="; const char *cssStyles = "\r\n"; const char HTTP_PAGE_TUNNELS[] = "tunnels"; const char HTTP_PAGE_TRANSIT_TUNNELS[] = "transit_tunnels"; const char HTTP_PAGE_TRANSPORTS[] = "transports"; const char HTTP_PAGE_LOCAL_DESTINATIONS[] = "local_destinations"; const char HTTP_PAGE_LOCAL_DESTINATION[] = "local_destination"; const char HTTP_PAGE_SAM_SESSIONS[] = "sam_sessions"; const char HTTP_PAGE_SAM_SESSION[] = "sam_session"; const char HTTP_PAGE_I2P_TUNNELS[] = "i2p_tunnels"; const char HTTP_PAGE_JUMPSERVICES[] = "jumpservices"; const char HTTP_COMMAND_START_ACCEPTING_TUNNELS[] = "start_accepting_tunnels"; const char HTTP_COMMAND_STOP_ACCEPTING_TUNNELS[] = "stop_accepting_tunnels"; const char HTTP_COMMAND_RUN_PEER_TEST[] = "run_peer_test"; const char HTTP_PARAM_BASE32_ADDRESS[] = "b32"; const char HTTP_PARAM_SAM_SESSION_ID[] = "id"; const char HTTP_PARAM_ADDRESS[] = "address"; std::map jumpservices = { { "inr.i2p", "http://joajgazyztfssty4w2on5oaqksz6tqoxbduy553y34mf4byv6gpq.b32.i2p/search/?q=" }, { "stats.i2p", "http://7tbay5p4kzeekxvyvbf6v7eauazemsnnl2aoyqhg5jzpr5eke7tq.b32.i2p/cgi-bin/jump.cgi?a=" }, }; void ShowUptime (std::stringstream& s, int seconds) { int num; if ((num = seconds / 86400) > 0) { s << num << " days, "; seconds -= num; } if ((num = seconds / 3600) > 0) { s << num << " hours, "; seconds -= num; } if ((num = seconds / 60) > 0) { s << num << " min, "; seconds -= num; } s << seconds << " seconds"; } void ShowStatus (std::stringstream& s) { s << "Uptime: "; ShowUptime(s, i2p::context.GetUptime ()); s << "
\r\n"; s << "Status: "; switch (i2p::context.GetStatus ()) { case eRouterStatusOK: s << "OK"; break; case eRouterStatusTesting: s << "Testing"; break; case eRouterStatusFirewalled: s << "Firewalled"; break; default: s << "Unknown"; } s << "
\r\n"; s << "Tunnel creation success rate: " << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate () << "%
\r\n"; s << "Received: "; s << std::fixed << std::setprecision(2); auto numKBytesReceived = (double) i2p::transport::transports.GetTotalReceivedBytes () / 1024; if (numKBytesReceived < 1024) s << numKBytesReceived << " KiB"; else if (numKBytesReceived < 1024 * 1024) s << numKBytesReceived / 1024 << " MiB"; else s << numKBytesReceived / 1024 / 1024 << " GiB"; s << " (" << (double) i2p::transport::transports.GetInBandwidth () / 1024 << " KiB/s)
\r\n"; s << "Sent: "; auto numKBytesSent = (double) i2p::transport::transports.GetTotalSentBytes () / 1024; if (numKBytesSent < 1024) s << numKBytesSent << " KiB"; else if (numKBytesSent < 1024 * 1024) s << numKBytesSent / 1024 << " MiB"; else s << numKBytesSent / 1024 / 1024 << " GiB"; s << " (" << (double) i2p::transport::transports.GetOutBandwidth () / 1024 << " KiB/s)
\r\n"; s << "Data path: " << i2p::fs::GetDataDir() << "
\r\n
\r\n"; s << "Our external address:" << "
\r\n" ; for (auto address : i2p::context.GetRouterInfo().GetAddresses()) { switch (address->transportStyle) { case i2p::data::RouterInfo::eTransportNTCP: if (address->host.is_v6 ()) s << "NTCP6  "; else s << "NTCP  "; break; case i2p::data::RouterInfo::eTransportSSU: if (address->host.is_v6 ()) s << "SSU6     "; else s << "SSU     "; break; default: s << "Unknown  "; } s << address->host.to_string() << ":" << address->port << "
\r\n"; } s << "
\r\nRouters: " << i2p::data::netdb.GetNumRouters () << " "; s << "Floodfills: " << i2p::data::netdb.GetNumFloodfills () << " "; s << "LeaseSets: " << i2p::data::netdb.GetNumLeaseSets () << "
\r\n"; size_t clientTunnelCount = i2p::tunnel::tunnels.CountOutboundTunnels(); clientTunnelCount += i2p::tunnel::tunnels.CountInboundTunnels(); size_t transitTunnelCount = i2p::tunnel::tunnels.CountTransitTunnels(); s << "Client Tunnels: " << std::to_string(clientTunnelCount) << " "; s << "Transit Tunnels: " << std::to_string(transitTunnelCount) << "
\r\n"; } void ShowJumpServices (std::stringstream& s, const std::string& address) { s << "
"; s << ""; s << ""; s << ""; s << "

\r\n"; s << "Jump services for " << address << "\r\n\r\n"; } void ShowLocalDestinations (std::stringstream& s) { s << "Local Destinations:
\r\n
\r\n"; for (auto& it: i2p::client::context.GetDestinations ()) { auto ident = it.second->GetIdentHash ();; s << ""; s << i2p::client::context.GetAddressBook ().ToAddress(ident) << "
\r\n" << std::endl; } } void ShowLocalDestination (std::stringstream& s, const std::string& b32) { s << "Local Destination:
\r\n
\r\n"; i2p::data::IdentHash ident; ident.FromBase32 (b32); auto dest = i2p::client::context.FindLocalDestination (ident); if (dest) { s << "Base64:
\r\n
\r\n
\r\n"; s << "LeaseSets: " << dest->GetNumRemoteLeaseSets () << "
\r\n"; auto pool = dest->GetTunnelPool (); if (pool) { s << "Tunnels:
\r\n"; for (auto it: pool->GetOutboundTunnels ()) { it->Print (s); auto state = it->GetState (); if (state == i2p::tunnel::eTunnelStateFailed) s << " " << "Failed"; else if (state == i2p::tunnel::eTunnelStateExpiring) s << " " << "Exp"; s << "
\r\n" << std::endl; } for (auto it: pool->GetInboundTunnels ()) { it->Print (s); auto state = it->GetState (); if (state == i2p::tunnel::eTunnelStateFailed) s << " " << "Failed"; else if (state == i2p::tunnel::eTunnelStateExpiring) s << " " << "Exp"; s << "
\r\n" << std::endl; } } s << "Tags
Incoming: " << dest->GetNumIncomingTags () << "
Outgoing:
" << std::endl; for (auto it: dest->GetSessions ()) { s << i2p::client::context.GetAddressBook ().ToAddress(it.first) << " "; s << it.second->GetNumOutgoingTags () << "
" << std::endl; } s << "
" << std::endl; // s << "
\r\nStreams:
\r\n"; // for (auto it: dest->GetStreamingDestination ()->GetStreams ()) // { // s << it.first << "->" << i2p::client::context.GetAddressBook ().ToAddress(it.second->GetRemoteIdentity ()) << " "; // s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]"; // s << " [out:" << it.second->GetSendQueueSize () << "][in:" << it.second->GetReceiveQueueSize () << "]"; // s << "[buf:" << it.second->GetSendBufferSize () << "]"; // s << "[RTT:" << it.second->GetRTT () << "]"; // s << "[Window:" << it.second->GetWindowSize () << "]"; // s << "[Status:" << (int)it.second->GetStatus () << "]"; // s << "
\r\n"<< std::endl; // } s << "
\r\n"; s << ""; s << ""; s << ""; s << ""; s << ""; s << ""; s << ""; s << ""; s << ""; s << ""; s << ""; for (auto it: dest->GetAllStreams ()) { s << ""; s << ""; s << ""; s << ""; s << ""; s << ""; s << ""; s << ""; s << ""; s << ""; s << ""; s << "
\r\n" << std::endl; } } } void ShowTunnels (std::stringstream& s) { s << "Tunnels:
\r\n
\r\n"; s << "Queue size: " << i2p::tunnel::tunnels.GetQueueSize () << "
\r\n"; for (auto it: i2p::tunnel::tunnels.GetOutboundTunnels ()) { it->Print (s); auto state = it->GetState (); if (state == i2p::tunnel::eTunnelStateFailed) s << " " << "Failed"; else if (state == i2p::tunnel::eTunnelStateExpiring) s << " " << "Expiring"; s << " " << (int)it->GetNumSentBytes () << "
\r\n"; s << std::endl; } for (auto it: i2p::tunnel::tunnels.GetInboundTunnels ()) { it->Print (s); auto state = it->GetState (); if (state == i2p::tunnel::eTunnelStateFailed) s << " " << "Failed"; else if (state == i2p::tunnel::eTunnelStateExpiring) s << " " << "Expiring"; s << " " << (int)it->GetNumReceivedBytes () << "
\r\n"; s << std::endl; } } void ShowTransitTunnels (std::stringstream& s) { s << "Transit tunnels:
\r\n
\r\n"; for (auto it: i2p::tunnel::tunnels.GetTransitTunnels ()) { if (std::dynamic_pointer_cast(it)) s << it->GetTunnelID () << " ⇒ "; else if (std::dynamic_pointer_cast(it)) s << " ⇒ " << it->GetTunnelID (); else s << " ⇒ " << it->GetTunnelID () << " ⇒ "; s << " " << it->GetNumTransmittedBytes () << "
\r\n"; } } void ShowTransports (std::stringstream& s) { s << "Transports:
\r\n
\r\n"; auto ntcpServer = i2p::transport::transports.GetNTCPServer (); if (ntcpServer) { s << "NTCP
\r\n"; for (auto it: ntcpServer->GetNTCPSessions ()) { if (it.second && it.second->IsEstablished ()) { // incoming connection doesn't have remote RI if (it.second->IsOutgoing ()) s << " ⇒ "; s << i2p::data::GetIdentHashAbbreviation (it.second->GetRemoteIdentity ()->GetIdentHash ()) << ": " << it.second->GetSocket ().remote_endpoint().address ().to_string (); if (!it.second->IsOutgoing ()) s << " ⇒ "; s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]"; s << "
\r\n" << std::endl; } } } auto ssuServer = i2p::transport::transports.GetSSUServer (); if (ssuServer) { s << "
\r\nSSU
\r\n"; for (auto it: ssuServer->GetSessions ()) { 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 << "
\r\n" << std::endl; } s << "
\r\nSSU6
\r\n"; for (auto it: ssuServer->GetSessionsV6 ()) { 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 () << "]"; s << "
\r\n" << std::endl; } } } void ShowSAMSessions (std::stringstream& s) { s << "SAM Sessions:
\r\n
\r\n"; auto sam = i2p::client::context.GetSAMBridge (); if (sam) { for (auto& it: sam->GetSessions ()) { s << ""; s << it.first << "
\r\n" << std::endl; } } } void ShowSAMSession (std::stringstream& s, const std::string& id) { s << "SAM Session:
\r\n
\r\n"; auto sam = i2p::client::context.GetSAMBridge (); if (sam) { auto session = sam->FindSession (id); if (session) { auto& ident = session->localDestination->GetIdentHash(); s << ""; s << i2p::client::context.GetAddressBook ().ToAddress(ident) << "
\r\n" << std::endl; s << "Streams:
\r\n"; for (auto it: session->ListSockets()) { switch (it->GetSocketType ()) { case i2p::client::eSAMSocketTypeSession: s << "session"; break; case i2p::client::eSAMSocketTypeStream: s << "stream"; break; case i2p::client::eSAMSocketTypeAcceptor: s << "acceptor"; break; default: s << "unknown"; } s << " [" << it->GetSocket ().remote_endpoint() << "]"; s << "
\r\n" << std::endl; } } } } void ShowI2PTunnels (std::stringstream& s) { s << "Client Tunnels:
\r\n
\r\n"; for (auto& it: i2p::client::context.GetClientTunnels ()) { auto& ident = it.second->GetLocalDestination ()->GetIdentHash(); s << ""; s << it.second->GetName () << " ⇐ "; s << i2p::client::context.GetAddressBook ().ToAddress(ident); s << "
\r\n"<< std::endl; } s << "
\r\nServer Tunnels:
\r\n
\r\n"; for (auto& it: i2p::client::context.GetServerTunnels ()) { auto& ident = it.second->GetLocalDestination ()->GetIdentHash(); s << ""; s << it.second->GetName () << " ⇒ "; s << i2p::client::context.GetAddressBook ().ToAddress(ident); s << ":" << it.second->GetLocalPort (); s << "
\r\n"<< std::endl; } } void HTTPConnection::Receive () { m_Socket->async_read_some (boost::asio::buffer (m_Buffer, HTTP_CONNECTION_BUFFER_SIZE), std::bind(&HTTPConnection::HandleReceive, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); } void HTTPConnection::HandleReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred) { if (ecode) { if (ecode != boost::asio::error::operation_aborted) Terminate (ecode); return; } m_Buffer[bytes_transferred] = '\0'; m_BufferLen = bytes_transferred; RunRequest(); Receive (); } void HTTPConnection::RunRequest () { HTTPReq request; int ret = request.parse(m_Buffer); if (ret < 0) { m_Buffer[0] = '\0'; m_BufferLen = 0; return; /* error */ } if (ret == 0) return; /* need more data */ HandleRequest (request.uri); } void HTTPConnection::Terminate (const boost::system::error_code& ecode) { if (ecode == boost::asio::error::operation_aborted) return; boost::system::error_code ignored_ec; m_Socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec); m_Socket->close (); } void HTTPConnection::HandleRequest (const std::string &uri) { std::stringstream s; // Html5 head start s << "\r\n" "\r\n" /* TODO: Add support for locale */ " \r\n" " \r\n" /* TODO: Find something to parse html/template system. This is horrible. */ " \r\n" " Purple I2P " VERSION " Webconsole\r\n" << cssStyles << "\r\n"; s << "\r\n" "
i2pd webconsole
\r\n" "
\r\n" "
\r\n" " Main page
\r\n
\r\n" " Local destinations
\r\n" " Tunnels
\r\n" " Transit tunnels
\r\n" " Transports
\r\n" " I2P tunnels
\r\n" " Jump services
\r\n" ; if (i2p::client::context.GetSAMBridge ()) s << " SAM sessions
\r\n"; /* commands */ s << "
\r\n"; s << " Run peer test
\r\n"; if (i2p::context.AcceptsTunnels ()) s << " Stop accepting tunnels
\r\n"; else s << " Start accepting tunnels
\r\n"; s << "
\r\n"; s << "
"; if (uri.find("page=") != std::string::npos) HandlePage (s, uri); else if (uri.find("cmd=") != std::string::npos) HandleCommand (s, uri); else ShowStatus (s); s << "
\r\n" "\r\n" "\r\n"; SendReply (s.str ()); } void HTTPConnection::HandlePage (std::stringstream& s, const std::string & uri) { std::map params; std::string page(""); URL url; url.parse(uri); url.parse_query(params); page = params["page"]; if (page == HTTP_PAGE_TRANSPORTS) ShowTransports (s); else if (page == HTTP_PAGE_TUNNELS) ShowTunnels (s); else if (page == HTTP_PAGE_JUMPSERVICES) ShowJumpServices (s, params["address"]); else if (page == HTTP_PAGE_TRANSIT_TUNNELS) ShowTransitTunnels (s); else if (page == HTTP_PAGE_LOCAL_DESTINATIONS) ShowLocalDestinations (s); else if (page == HTTP_PAGE_LOCAL_DESTINATION) ShowLocalDestination (s, params["b32"]); else if (page == HTTP_PAGE_SAM_SESSIONS) ShowSAMSessions (s); else if (page == HTTP_PAGE_SAM_SESSION) ShowSAMSession (s, params["sam_id"]); else if (page == HTTP_PAGE_I2P_TUNNELS) ShowI2PTunnels (s); else SendError("Unknown page: " + page); } void HTTPConnection::HandleCommand (std::stringstream& s, const std::string & uri) { std::map params; std::string cmd(""); URL url; url.parse(uri); url.parse_query(params); cmd = params["cmd"]; if (cmd == HTTP_COMMAND_RUN_PEER_TEST) i2p::transport::transports.PeerTest (); else if (cmd == HTTP_COMMAND_START_ACCEPTING_TUNNELS) i2p::context.SetAcceptsTunnels (true); else if (cmd == HTTP_COMMAND_STOP_ACCEPTING_TUNNELS) i2p::context.SetAcceptsTunnels (false); else { SendError("Unknown command: " + cmd); return; } s << "Command accepted"; } void HTTPConnection::SendReply (const std::string& content, int code) { std::time_t time_now = std::time(nullptr); char time_buff[128]; std::strftime(time_buff, sizeof(time_buff), "%a, %d %b %Y %H:%M:%S GMT", std::gmtime(&time_now)); HTTPRes reply; reply.code = code; reply.status = HTTPCodeToStatus(code); reply.headers.insert(std::pair("Date", time_buff)); reply.headers.insert(std::pair("Content-Type", "text/html")); reply.headers.insert(std::pair("Content-Length", std::to_string(content.size()))); std::string res = reply.to_string(); std::vector buffers; buffers.push_back(boost::asio::buffer(res)); buffers.push_back(boost::asio::buffer(content)); boost::asio::async_write (*m_Socket, buffers, std::bind (&HTTPConnection::Terminate, shared_from_this (), std::placeholders::_1)); } void HTTPConnection::SendError(const std::string& content) { std::stringstream ss; ss << "" << itoopieImage << "
\r\n" << content << ""; SendReply (ss.str(), 504); } HTTPServer::HTTPServer (const std::string& address, int port): m_Thread (nullptr), m_Work (m_Service), m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint (boost::asio::ip::address::from_string(address), port)) { } HTTPServer::~HTTPServer () { Stop (); } void HTTPServer::Start () { m_Thread = std::unique_ptr(new std::thread (std::bind (&HTTPServer::Run, this))); m_Acceptor.listen (); Accept (); } void HTTPServer::Stop () { m_Acceptor.close(); m_Service.stop (); if (m_Thread) { m_Thread->join (); m_Thread = nullptr; } } void HTTPServer::Run () { m_Service.run (); } void HTTPServer::Accept () { auto newSocket = std::make_shared (m_Service); m_Acceptor.async_accept (*newSocket, boost::bind (&HTTPServer::HandleAccept, this, boost::asio::placeholders::error, newSocket)); } void HTTPServer::HandleAccept(const boost::system::error_code& ecode, std::shared_ptr newSocket) { if (ecode) return; CreateConnection(newSocket); Accept (); } void HTTPServer::CreateConnection(std::shared_ptr newSocket) { auto conn = std::make_shared (newSocket); conn->Receive (); } } // http } // i2p
Streams
StreamIDDestinationSentReceivedOutInBufRTTWindowStatus
" << it->GetSendStreamID () << "" << i2p::client::context.GetAddressBook ().ToAddress(it->GetRemoteIdentity ()) << "" << it->GetNumSentBytes () << "" << it->GetNumReceivedBytes () << "" << it->GetSendQueueSize () << "" << it->GetReceiveQueueSize () << "" << it->GetSendBufferSize () << "" << it->GetRTT () << "" << it->GetWindowSize () << "" << (int)it->GetStatus () << "