i2pd/daemon/HTTPServer.cpp

1069 lines
39 KiB
C++
Raw Normal View History

#include <iomanip>
#include <sstream>
#include <thread>
#include <memory>
#include <boost/asio.hpp>
2013-12-10 13:03:22 +00:00
#include <boost/bind.hpp>
2015-11-03 14:15:49 +00:00
#include "Base.h"
#include "FS.h"
2014-01-15 00:56:34 +00:00
#include "Log.h"
2016-04-27 00:00:00 +00:00
#include "Config.h"
2013-12-10 13:03:22 +00:00
#include "Tunnel.h"
#include "Transports.h"
#include "NetDb.hpp"
#include "HTTP.h"
#include "LeaseSet.h"
#include "Destination.h"
2014-09-30 17:34:29 +00:00
#include "RouterContext.h"
2014-10-16 00:52:17 +00:00
#include "ClientContext.h"
2014-09-30 17:34:29 +00:00
#include "HTTPServer.h"
#include "Daemon.h"
2016-06-14 18:37:22 +00:00
#include "util.h"
2016-07-26 16:11:52 +00:00
#ifdef WIN32_APP
#include "Win32/Win32App.h"
#endif
2013-12-10 13:03:22 +00:00
// For image and info
#include "version.h"
2016-04-27 00:00:00 +00:00
namespace i2p {
namespace http {
const char *itoopieFavicon =
2016-01-12 18:18:01 +00:00
"data:image/png;base64,"
2016-11-20 21:58:38 +00:00
"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACx"
"jwv8YQUAAAAJcEhZcwAALiIAAC4iAari3ZIAAAAHdElNRQfgCQsUNSZrkhi1AAAAGXRFWHRTb2Z0"
"d2FyZQBwYWludC5uZXQgNC4wLjEyQwRr7AAAAoJJREFUOE9jwAUqi4Q1oEwwcDTV1+5sETaBclGB"
"vb09C5QJB6kWpvFQJoOCeLC5kmjEHCgXE2SlyETLi3h6QrkM4VL+ssWSCZUgtopITLKqaOotRTEn"
"cbAkLqAkGtOqLBLVAWLXyWSVFkkmRiqLxuaqiWb/VBYJMAYrwgckJY25VEUzniqKhjU2y+RtCRSP"
"6lUXy/1jIBV5tlYxZUaFVMq2NInwIi9hO8fSfOEAqDZUoCwal6MulvOvyS7gi69K4j9zxZT/m0ps"
"/28ptvvvquXXryIa7QYMMdTwqi0WNtVi0GIDseXl7TnUxFKfnGlxAGp0+D8j2eH/8Ub7/9e7nf7X"
"+Af/B7rwt6pI0h0l0WhQADOC9DBkhSirpImHNVZKp24ukkyoshGLnN8d5fA/y13t/44Kq/8hlnL/"
"z7fZ/58f6vcxSNpbVUVFhV1RLNBVTsQzVYZPSwhsCAhkiIfpNMrkbO6TLf071Sfk/5ZSi/+7q6z/"
"P5ns+v9mj/P/CpuI/20y+aeNGYxZoVoYGmsF3aFMBAAZlCwftnF9ke3//bU2//fXWP8/UGv731Am"
"+V+DdNblSqnUYqhSTKAiYSOqJBrVqiaa+S3UNPr/gmyH/xuKXf63hnn/B8bIP0UxHfEyyeSNQKVM"
"EB1AEB2twhcTLp+gIBJUoyKasEpVJHmqskh8qryovUG/ffCHHRU2q/Tk/YuB6eGPsbExa7ZkpLu1"
"oLEcVDtuUCgV1w60rQzElpRUE1EVSX0BYidHiInXF4nagNhYQW60EF+ApH1ktni0A1SIITSUgVlZ"
"JHYnlIsfzJjIp9xZKswL5YKBHL+coKJoRDaUSzoozxHVrygQU4JykQADAwAT5b1NHtwZugAAAABJ"
"RU5ErkJggg==";
2014-07-16 16:41:40 +00:00
const char *cssStyles =
"<style>\r\n"
" body { font: 100%/1.5em sans-serif; margin: 0; padding: 1.5em; background: #FAFAFA; color: #103456; }\r\n"
" a, .slide label { text-decoration: none; color: #894C84; }\r\n"
" a:hover, .slide label:hover { color: #FAFAFA; background: #894C84; }\r\n"
" .header { font-size: 2.5em; text-align: center; margin: 1.5em 0; color: #894C84; }\r\n"
" .wrapper { margin: 0 auto; padding: 1em; max-width: 60em; }\r\n"
" .left { float: left; position: absolute; }\r\n"
" .right { float: left; font-size: 1em; margin-left: 13em; max-width: 46em; overflow: auto; }\r\n"
" .tunnel.established { color: #56B734; }\r\n"
2017-09-27 20:41:34 +00:00
" .tunnel.expiring { color: #D3AE3F; }\r\n"
" .tunnel.failed { color: #D33F3F; }\r\n"
2017-11-14 18:31:13 +00:00
" .tunnel.building { color: #434343; }\r\n"
" caption { font-size: 1.5em; text-align: center; color: #894C84; }\r\n"
" table { width: 100%; border-collapse: collapse; text-align: center; }\r\n"
2017-11-14 18:31:13 +00:00
" .slide p, .slide [type='checkbox']{ display:none; }\r\n"
" .slide [type='checkbox']:checked ~ p { display:block; margin-top: 0; padding: 0; }\r\n"
" .disabled:after { color: #D33F3F; content: \"Disabled\" }\r\n"
" .enabled:after { color: #56B734; content: \"Enabled\" }\r\n"
"</style>\r\n";
const char HTTP_PAGE_TUNNELS[] = "tunnels";
const char HTTP_PAGE_TRANSIT_TUNNELS[] = "transit_tunnels";
2016-08-09 10:54:47 +00:00
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_I2CP_LOCAL_DESTINATION[] = "i2cp_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_COMMANDS[] = "commands";
2016-07-15 17:52:55 +00:00
const char HTTP_PAGE_LEASESETS[] = "leasesets";
2016-07-12 00:00:00 +00:00
const char HTTP_COMMAND_ENABLE_TRANSIT[] = "enable_transit";
const char HTTP_COMMAND_DISABLE_TRANSIT[] = "disable_transit";
const char HTTP_COMMAND_SHUTDOWN_START[] = "shutdown_start";
const char HTTP_COMMAND_SHUTDOWN_CANCEL[] = "shutdown_cancel";
2017-09-27 20:41:34 +00:00
const char HTTP_COMMAND_SHUTDOWN_NOW[] = "terminate";
const char HTTP_COMMAND_RUN_PEER_TEST[] = "run_peer_test";
2016-08-09 10:54:47 +00:00
const char HTTP_COMMAND_RELOAD_CONFIG[] = "reload_config";
const char HTTP_COMMAND_LOGLEVEL[] = "set_loglevel";
const char HTTP_PARAM_SAM_SESSION_ID[] = "id";
2016-03-11 10:30:50 +00:00
const char HTTP_PARAM_ADDRESS[] = "address";
2013-12-10 13:03:22 +00:00
static void ShowUptime (std::stringstream& s, int seconds)
{
int num;
2013-12-10 13:03:22 +00:00
if ((num = seconds / 86400) > 0) {
s << num << " days, ";
2016-05-19 00:00:00 +00:00
seconds -= num * 86400;
2013-12-10 13:03:22 +00:00
}
if ((num = seconds / 3600) > 0) {
s << num << " hours, ";
2016-05-19 00:00:00 +00:00
seconds -= num * 3600;
2014-07-16 16:41:40 +00:00
}
if ((num = seconds / 60) > 0) {
s << num << " min, ";
2016-05-19 00:00:00 +00:00
seconds -= num * 60;
}
s << seconds << " seconds";
}
2017-04-27 20:11:37 +00:00
static void ShowTraffic (std::stringstream& s, uint64_t bytes)
{
s << std::fixed << std::setprecision(2);
auto numKBytes = (double) bytes / 1024;
if (numKBytes < 1024)
s << numKBytes << " KiB";
else if (numKBytes < 1024 * 1024)
s << numKBytes / 1024 << " MiB";
else
s << numKBytes / 1024 / 1024 << " GiB";
}
static void ShowTunnelDetails (std::stringstream& s, enum i2p::tunnel::TunnelState eState, bool explr, int bytes)
2014-01-15 00:56:34 +00:00
{
std::string state;
switch (eState) {
case i2p::tunnel::eTunnelStateBuildReplyReceived :
2017-09-27 20:41:34 +00:00
case i2p::tunnel::eTunnelStatePending : state = "building"; break;
case i2p::tunnel::eTunnelStateBuildFailed :
case i2p::tunnel::eTunnelStateTestFailed :
2017-09-27 20:41:34 +00:00
case i2p::tunnel::eTunnelStateFailed : state = "failed"; break;
case i2p::tunnel::eTunnelStateExpiring : state = "expiring"; break;
case i2p::tunnel::eTunnelStateEstablished : state = "established"; break;
default: state = "unknown"; break;
2014-07-16 16:41:40 +00:00
}
s << "<span class=\"tunnel " << state << "\"> " << state << ((explr) ? " (exploratory)" : "") << "</span>, ";
s << " " << (int) (bytes / 1024) << "&nbsp;KiB<br>\r\n";
2014-07-16 16:41:40 +00:00
}
static void SetLogLevel (const std::string& level)
{
if (level == "none" || level == "error" || level == "warn" || level == "info" || level == "debug")
i2p::log::Logger().SetLogLevel(level);
else {
LogPrint(eLogError, "HTTPServer: unknown loglevel set attempted");
return;
}
i2p::log::Logger().Reopen ();
}
static void ShowPageHead (std::stringstream& s)
2014-03-27 19:42:23 +00:00
{
s <<
"<!DOCTYPE html>\r\n"
"<html lang=\"en\">\r\n" /* TODO: Add support for locale */
2016-06-27 21:00:54 +00:00
" <head>\r\n" /* TODO: Find something to parse html/template system. This is horrible. */
#if (!defined(WIN32))
" <meta charset=\"UTF-8\">\r\n"
#else
" <meta charset=\"windows-1251\">\r\n"
#endif
2016-06-14 08:25:51 +00:00
" <link rel=\"shortcut icon\" href=\"" << itoopieFavicon << "\">\r\n"
" <title>Purple I2P " VERSION " Webconsole</title>\r\n"
<< cssStyles <<
"</head>\r\n";
s <<
"<body>\r\n"
"<div class=header><b>i2pd</b> webconsole</div>\r\n"
"<div class=wrapper>\r\n"
"<div class=left>\r\n"
2016-06-14 08:25:51 +00:00
" <a href=\"/\">Main page</a><br>\r\n<br>\r\n"
" <a href=\"/?page=" << HTTP_PAGE_COMMANDS << "\">Router commands</a><br>\r\n"
" <a href=\"/?page=" << HTTP_PAGE_LOCAL_DESTINATIONS << "\">Local destinations</a><br>\r\n"
2016-08-09 10:54:47 +00:00
" <a href=\"/?page=" << HTTP_PAGE_LEASESETS << "\">LeaseSets</a><br>\r\n"
2016-06-14 08:25:51 +00:00
" <a href=\"/?page=" << HTTP_PAGE_TUNNELS << "\">Tunnels</a><br>\r\n"
" <a href=\"/?page=" << HTTP_PAGE_TRANSIT_TUNNELS << "\">Transit tunnels</a><br>\r\n"
" <a href=\"/?page=" << HTTP_PAGE_TRANSPORTS << "\">Transports</a><br>\r\n"
2016-08-09 10:54:47 +00:00
" <a href=\"/?page=" << HTTP_PAGE_I2P_TUNNELS << "\">I2P tunnels</a><br>\r\n";
if (i2p::client::context.GetSAMBridge ())
s << " <a href=\"/?page=" << HTTP_PAGE_SAM_SESSIONS << "\">SAM sessions</a><br>\r\n";
s <<
"</div>\r\n"
"<div class=right>";
2014-03-27 19:42:23 +00:00
}
static void ShowPageTail (std::stringstream& s)
2013-12-10 13:03:22 +00:00
{
s <<
"</div></div>\r\n"
"</body>\r\n"
"</html>\r\n";
2013-12-10 13:03:22 +00:00
}
static void ShowError(std::stringstream& s, const std::string& string)
2013-12-10 13:03:22 +00:00
{
s << "<b>ERROR:</b>&nbsp;" << string << "<br>\r\n";
2014-07-16 16:41:40 +00:00
}
2013-12-10 13:03:22 +00:00
void ShowStatus (std::stringstream& s, bool includeHiddenContent)
2013-12-10 13:03:22 +00:00
{
s << "<b>Uptime:</b> ";
ShowUptime(s, i2p::context.GetUptime ());
s << "<br>\r\n";
s << "<b>Network status:</b> ";
2015-02-26 18:44:18 +00:00
switch (i2p::context.GetStatus ())
{
case eRouterStatusOK: s << "OK"; break;
case eRouterStatusTesting: s << "Testing"; break;
case eRouterStatusFirewalled: s << "Firewalled"; break;
case eRouterStatusError:
{
s << "Error";
2016-09-20 01:37:04 +00:00
switch (i2p::context.GetError ())
{
case eRouterErrorClockSkew:
s << "<br>Clock skew";
2016-09-20 01:37:04 +00:00
break;
default: ;
}
2016-09-20 01:37:04 +00:00
break;
}
2015-02-26 18:44:18 +00:00
default: s << "Unknown";
}
2016-01-17 16:10:56 +00:00
s << "<br>\r\n";
#if (!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID))
2016-10-31 10:27:27 +00:00
if (auto remains = Daemon.gracefulShutdownInterval) {
s << "<b>Stopping in:</b> ";
s << remains << " seconds";
s << "<br>\r\n";
}
#endif
auto family = i2p::context.GetFamily ();
if (family.length () > 0)
s << "<b>Family:</b> " << family << "<br>\r\n";
2016-01-17 16:10:56 +00:00
s << "<b>Tunnel creation success rate:</b> " << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate () << "%<br>\r\n";
2016-03-26 14:32:19 +00:00
s << "<b>Received:</b> ";
2017-04-27 20:11:37 +00:00
ShowTraffic (s, i2p::transport::transports.GetTotalReceivedBytes ());
2016-03-26 14:32:19 +00:00
s << " (" << (double) i2p::transport::transports.GetInBandwidth () / 1024 << " KiB/s)<br>\r\n";
s << "<b>Sent:</b> ";
2017-04-27 20:11:37 +00:00
ShowTraffic (s, i2p::transport::transports.GetTotalSentBytes ());
2016-03-26 14:32:19 +00:00
s << " (" << (double) i2p::transport::transports.GetOutBandwidth () / 1024 << " KiB/s)<br>\r\n";
2017-04-27 20:11:37 +00:00
s << "<b>Transit:</b> ";
2017-05-02 18:20:00 +00:00
ShowTraffic (s, i2p::transport::transports.GetTotalTransitTransmittedBytes ());
s << " (" << (double) i2p::transport::transports.GetTransitBandwidth () / 1024 << " KiB/s)<br>\r\n";
s << "<b>Data path:</b> " << i2p::fs::GetDataDir() << "<br>\r\n";
s << "<div class='slide'><label for='slide-info'>Hidden content. Press on text to see.</label>\r\n<input type='checkbox' id='slide-info'/>\r\n<p class='content'>\r\n";
if(includeHiddenContent) {
s << "<b>Router Ident:</b> " << i2p::context.GetRouterInfo().GetIdentHashBase64() << "<br>\r\n";
s << "<b>Router Family:</b> " << i2p::context.GetRouterInfo().GetProperty("family") << "<br>\r\n";
s << "<b>Router Caps:</b> " << i2p::context.GetRouterInfo().GetProperty("caps") << "<br>\r\n";
s << "<b>Our external address:</b>" << "<br>\r\n" ;
for (const auto& address : i2p::context.GetRouterInfo().GetAddresses())
{
switch (address->transportStyle)
{
case i2p::data::RouterInfo::eTransportNTCP:
if (address->host.is_v6 ())
s << "NTCP6&nbsp;&nbsp;";
else
s << "NTCP&nbsp;&nbsp;";
break;
case i2p::data::RouterInfo::eTransportSSU:
if (address->host.is_v6 ())
s << "SSU6&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
else
s << "SSU&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
break;
default:
s << "Unknown&nbsp;&nbsp;";
}
s << address->host.to_string() << ":" << address->port << "<br>\r\n";
}
}
s << "</p>\r\n</div>\r\n";
s << "<b>Routers:</b> " << i2p::data::netdb.GetNumRouters () << " ";
2016-01-12 03:05:10 +00:00
s << "<b>Floodfills:</b> " << i2p::data::netdb.GetNumFloodfills () << " ";
2016-01-17 16:10:56 +00:00
s << "<b>LeaseSets:</b> " << i2p::data::netdb.GetNumLeaseSets () << "<br>\r\n";
size_t clientTunnelCount = i2p::tunnel::tunnels.CountOutboundTunnels();
clientTunnelCount += i2p::tunnel::tunnels.CountInboundTunnels();
size_t transitTunnelCount = i2p::tunnel::tunnels.CountTransitTunnels();
2016-08-09 10:54:47 +00:00
s << "<b>Client Tunnels:</b> " << std::to_string(clientTunnelCount) << " ";
2017-11-14 18:31:13 +00:00
s << "<b>Transit Tunnels:</b> " << std::to_string(transitTunnelCount) << "<br>\r\n<br>\r\n";
s << "<table><caption>Services</caption><tr><th>Service</th><th>State</th></tr>\r\n";
2017-11-26 09:30:18 +00:00
s << "<tr><td>" << "HTTP Proxy" << "</td><td><div class='" << ((i2p::client::context.GetHttpProxy ()) ? "enabled" : "disabled") << "'></div></td></tr>\r\n";
s << "<tr><td>" << "SOCKS Proxy" << "</td><td><div class='" << ((i2p::client::context.GetSocksProxy ()) ? "enabled" : "disabled") << "'></div></td></tr>\r\n";
s << "<tr><td>" << "BOB" << "</td><td><div class='" << ((i2p::client::context.GetBOBCommandChannel ()) ? "enabled" : "disabled") << "'></div></td></tr>\r\n";
s << "<tr><td>" << "SAM" << "</td><td><div class='" << ((i2p::client::context.GetSAMBridge ()) ? "enabled" : "disabled") << "'></div></td></tr>\r\n";
s << "<tr><td>" << "I2CP" << "</td><td><div class='" << ((i2p::client::context.GetI2CPServer ()) ? "enabled" : "disabled") << "'></div></td></tr>\r\n";
2017-11-14 18:31:13 +00:00
bool i2pcontrol; i2p::config::GetOption("i2pcontrol.enabled", i2pcontrol);
2017-11-26 09:30:18 +00:00
s << "<tr><td>" << "I2PControl" << "</td><td><div class='" << ((i2pcontrol) ? "enabled" : "disabled") << "'></div></td></tr>\r\n";
2017-11-14 18:31:13 +00:00
s << "</table>\r\n";
2014-09-28 20:12:25 +00:00
}
void ShowLocalDestinations (std::stringstream& s)
2014-09-28 23:15:04 +00:00
{
2016-01-17 16:10:56 +00:00
s << "<b>Local Destinations:</b><br>\r\n<br>\r\n";
2014-10-16 00:52:17 +00:00
for (auto& it: i2p::client::context.GetDestinations ())
2014-09-28 23:15:04 +00:00
{
auto ident = it.second->GetIdentHash ();
s << "<a href=\"/?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
2016-01-17 16:10:56 +00:00
s << i2p::client::context.GetAddressBook ().ToAddress(ident) << "</a><br>\r\n" << std::endl;
2014-09-28 23:15:04 +00:00
}
auto i2cpServer = i2p::client::context.GetI2CPServer ();
2017-11-14 18:31:13 +00:00
if (i2cpServer && !(i2cpServer->GetSessions ().empty ()))
{
s << "<br><b>I2CP Local Destinations:</b><br>\r\n<br>\r\n";
for (auto& it: i2cpServer->GetSessions ())
{
auto dest = it.second->GetDestination ();
if (dest)
{
auto ident = dest->GetIdentHash ();
2017-12-13 12:17:45 +00:00
auto& name = dest->GetNickname ();
s << "<a href=\"/?page=" << HTTP_PAGE_I2CP_LOCAL_DESTINATION << "&i2cp_id=" << it.first << "\">[ ";
s << name << " ]</a> &#8660; " << i2p::client::context.GetAddressBook ().ToAddress(ident) <<"<br>\r\n" << std::endl;
}
}
}
}
static void ShowLeaseSetDestination (std::stringstream& s, std::shared_ptr<const i2p::client::LeaseSetDestination> dest)
{
s << "<b>Base64:</b><br>\r\n<textarea readonly=\"readonly\" cols=\"64\" rows=\"11\" wrap=\"on\">";
s << dest->GetIdentity ()->ToBase64 () << "</textarea><br>\r\n<br>\r\n";
if(dest->GetNumRemoteLeaseSets())
{
s << "<div class='slide'><label for='slide-lease'><b>LeaseSets:</b> <i>" << dest->GetNumRemoteLeaseSets () << "</i></label>\r\n<input type='checkbox' id='slide-lease'/>\r\n<p class='content'>\r\n";
for(auto& it: dest->GetLeaseSets ())
s << it.second->GetIdentHash ().ToBase32 () << "<br>\r\n";
s << "</p>\r\n</div>\r\n";
2017-11-14 18:31:13 +00:00
} else
s << "<b>LeaseSets:</b> <i>0</i><br>\r\n";
auto pool = dest->GetTunnelPool ();
if (pool)
{
s << "<b>Inbound tunnels:</b><br>\r\n";
for (auto & it : pool->GetInboundTunnels ()) {
it->Print(s);
if(it->LatencyIsKnown())
s << " ( " << it->GetMeanLatency() << "ms )";
ShowTunnelDetails(s, it->GetState (), false, it->GetNumReceivedBytes ());
}
s << "<br>\r\n";
s << "<b>Outbound tunnels:</b><br>\r\n";
for (auto & it : pool->GetOutboundTunnels ()) {
it->Print(s);
if(it->LatencyIsKnown())
s << " ( " << it->GetMeanLatency() << "ms )";
ShowTunnelDetails(s, it->GetState (), false, it->GetNumSentBytes ());
}
}
s << "<br>\r\n";
s << "<b>Tags</b><br>Incoming: <i>" << dest->GetNumIncomingTags () << "</i><br>";
if (!dest->GetSessions ().empty ()) {
s << "<div class='slide'><label for='slide-tags'>Outgoing:</label>\r\n<input type='checkbox' id='slide-tags'/>\r\n<p class='content'>\r\n";
for (const auto& it: dest->GetSessions ())
s << i2p::client::context.GetAddressBook ().ToAddress(it.first) << " " << it.second->GetNumOutgoingTags () << "<br>\r\n";
s << "</p>\r\n</div>\r\n";
} else
s << "Outgoing: <i>0</i><br>\r\n";
s << "<br>\r\n";
2016-01-13 01:31:25 +00:00
}
2014-09-30 02:18:32 +00:00
void ShowLocalDestination (std::stringstream& s, const std::string& b32)
2014-09-30 02:18:32 +00:00
{
2016-01-17 16:10:56 +00:00
s << "<b>Local Destination:</b><br>\r\n<br>\r\n";
2014-09-30 02:18:32 +00:00
i2p::data::IdentHash ident;
ident.FromBase32 (b32);
auto dest = i2p::client::context.FindLocalDestination (ident);
2014-09-30 02:18:32 +00:00
if (dest)
{
ShowLeaseSetDestination (s, dest);
// show streams
s << "<table><caption>Streams</caption>\r\n<tr>";
2016-01-17 16:10:56 +00:00
s << "<th>StreamID</th>";
s << "<th>Destination</th>";
s << "<th>Sent</th>";
s << "<th>Received</th>";
s << "<th>Out</th>";
s << "<th>In</th>";
s << "<th>Buf</th>";
s << "<th>RTT</th>";
s << "<th>Window</th>";
s << "<th>Status</th>";
s << "</tr>\r\n";
2016-01-17 16:10:56 +00:00
2016-08-05 18:23:54 +00:00
for (const auto& it: dest->GetAllStreams ())
2016-08-09 10:54:47 +00:00
{
2016-01-17 16:10:56 +00:00
s << "<tr>";
s << "<td>" << it->GetSendStreamID () << "</td>";
s << "<td>" << i2p::client::context.GetAddressBook ().ToAddress(it->GetRemoteIdentity ()) << "</td>";
s << "<td>" << it->GetNumSentBytes () << "</td>";
s << "<td>" << it->GetNumReceivedBytes () << "</td>";
s << "<td>" << it->GetSendQueueSize () << "</td>";
s << "<td>" << it->GetReceiveQueueSize () << "</td>";
s << "<td>" << it->GetSendBufferSize () << "</td>";
s << "<td>" << it->GetRTT () << "</td>";
s << "<td>" << it->GetWindowSize () << "</td>";
s << "<td>" << (int)it->GetStatus () << "</td>";
s << "</tr>\r\n";
}
2016-09-03 22:04:54 +00:00
s << "</table>";
2016-08-09 10:54:47 +00:00
}
2016-01-13 01:31:25 +00:00
}
static void ShowI2CPLocalDestination (std::stringstream& s, const std::string& id)
{
auto i2cpServer = i2p::client::context.GetI2CPServer ();
if (i2cpServer)
{
s << "<b>I2CP Local Destination:</b><br>\r\n<br>\r\n";
auto it = i2cpServer->GetSessions ().find (std::stoi (id));
if (it != i2cpServer->GetSessions ().end ())
ShowLeaseSetDestination (s, it->second->GetDestination ());
else
ShowError(s, "I2CP session not found");
}
else
ShowError(s, "I2CP is not enabled");
}
void ShowLeasesSets(std::stringstream& s)
2016-07-15 17:52:55 +00:00
{
2017-11-14 18:31:13 +00:00
s << "<b>LeaseSets:</b><br>\r\n<br>\r\n";
2016-09-19 17:22:15 +00:00
int counter = 1;
2016-07-15 17:52:55 +00:00
// for each lease set
i2p::data::netdb.VisitLeaseSets(
[&s, &counter](const i2p::data::IdentHash dest, std::shared_ptr<i2p::data::LeaseSet> leaseSet)
2016-07-15 17:52:55 +00:00
{
// create copy of lease set so we extract leases
i2p::data::LeaseSet ls(leaseSet->GetBuffer(), leaseSet->GetBufferLen());
s << "<div class='leaseset";
if (ls.IsExpired())
s << " expired"; // additional css class for expired
2016-09-19 17:22:15 +00:00
s << "'>\r\n";
2016-07-15 17:52:55 +00:00
if (!ls.IsValid())
2016-09-19 17:22:15 +00:00
s << "<div class='invalid'>!! Invalid !! </div>\r\n";
s << "<div class='slide'><label for='slide" << counter << "'>" << dest.ToBase32() << "</label>\r\n";
s << "<input type='checkbox' id='slide" << (counter++) << "'/>\r\n<p class='content'>\r\n";
s << "<b>Expires:</b> " << ls.GetExpirationTime() << "<br>\r\n";
2016-07-15 17:52:55 +00:00
auto leases = ls.GetNonExpiredLeases();
2016-09-19 17:22:15 +00:00
s << "<b>Non Expired Leases: " << leases.size() << "</b><br>\r\n";
2016-07-15 17:52:55 +00:00
for ( auto & l : leases )
{
2016-09-19 17:22:15 +00:00
s << "<b>Gateway:</b> " << l->tunnelGateway.ToBase64() << "<br>\r\n";
s << "<b>TunnelID:</b> " << l->tunnelID << "<br>\r\n";
s << "<b>EndDate:</b> " << l->endDate << "<br>\r\n";
2016-07-15 17:52:55 +00:00
}
2016-09-19 17:22:15 +00:00
s << "</p>\r\n</div>\r\n</div>\r\n";
2016-07-15 17:52:55 +00:00
}
);
// end for each lease set
}
2016-08-09 10:54:47 +00:00
void ShowTunnels (std::stringstream& s)
2016-01-13 01:31:25 +00:00
{
2017-11-14 18:31:13 +00:00
s << "<b>Tunnels:</b><br>\r\n<br>\r\n";
s << "<b>Queue size:</b> " << i2p::tunnel::tunnels.GetQueueSize () << "<br>\r\n";
2016-01-13 01:31:25 +00:00
auto ExplPool = i2p::tunnel::tunnels.GetExploratoryPool ();
s << "<b>Inbound tunnels:</b><br>\r\n";
for (auto & it : i2p::tunnel::tunnels.GetInboundTunnels ()) {
it->Print(s);
2016-11-15 19:42:18 +00:00
if(it->LatencyIsKnown())
s << " ( " << it->GetMeanLatency() << "ms )";
ShowTunnelDetails(s, it->GetState (), (it->GetTunnelPool () == ExplPool), it->GetNumReceivedBytes ());
2016-01-13 01:31:25 +00:00
}
s << "<br>\r\n";
s << "<b>Outbound tunnels:</b><br>\r\n";
for (auto & it : i2p::tunnel::tunnels.GetOutboundTunnels ()) {
it->Print(s);
2016-11-15 19:42:18 +00:00
if(it->LatencyIsKnown())
s << " ( " << it->GetMeanLatency() << "ms )";
ShowTunnelDetails(s, it->GetState (), (it->GetTunnelPool () == ExplPool), it->GetNumSentBytes ());
2016-01-13 01:31:25 +00:00
}
s << "<br>\r\n";
2016-08-09 10:54:47 +00:00
}
static void ShowCommands (std::stringstream& s, uint32_t token)
{
/* commands */
2017-11-14 18:31:13 +00:00
s << "<b>Router Commands</b><br>\r\n<br>\r\n";
2016-12-15 18:10:12 +00:00
s << " <a href=\"/?cmd=" << HTTP_COMMAND_RUN_PEER_TEST << "&token=" << token << "\">Run peer test</a><br>\r\n";
2016-06-14 08:25:51 +00:00
//s << " <a href=\"/?cmd=" << HTTP_COMMAND_RELOAD_CONFIG << "\">Reload config</a><br>\r\n";
if (i2p::context.AcceptsTunnels ())
2016-12-15 18:10:12 +00:00
s << " <a href=\"/?cmd=" << HTTP_COMMAND_DISABLE_TRANSIT << "&token=" << token << "\">Decline transit tunnels</a><br>\r\n";
2016-08-09 10:54:47 +00:00
else
2016-12-15 18:10:12 +00:00
s << " <a href=\"/?cmd=" << HTTP_COMMAND_ENABLE_TRANSIT << "&token=" << token << "\">Accept transit tunnels</a><br>\r\n";
2016-07-09 20:54:11 +00:00
#if (!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID))
if (Daemon.gracefulShutdownInterval)
2016-12-15 18:10:12 +00:00
s << " <a href=\"/?cmd=" << HTTP_COMMAND_SHUTDOWN_CANCEL << "&token=" << token << "\">Cancel graceful shutdown</a><br>";
else
2016-12-15 18:10:12 +00:00
s << " <a href=\"/?cmd=" << HTTP_COMMAND_SHUTDOWN_START << "&token=" << token << "\">Start graceful shutdown</a><br>\r\n";
#elif defined(WIN32_APP)
if (i2p::util::DaemonWin32::Instance().isGraceful)
s << " <a href=\"/?cmd=" << HTTP_COMMAND_SHUTDOWN_CANCEL << "&token=" << token << "\">Cancel graceful shutdown</a><br>";
else
s << " <a href=\"/?cmd=" << HTTP_COMMAND_SHUTDOWN_START << "&token=" << token << "\">Graceful shutdown</a><br>\r\n";
2016-07-26 16:11:52 +00:00
#endif
2016-12-15 18:10:12 +00:00
s << " <a href=\"/?cmd=" << HTTP_COMMAND_SHUTDOWN_NOW << "&token=" << token << "\">Force shutdown</a><br>\r\n";
2018-01-06 03:48:51 +00:00
s << "<br>\r\n<b>Logging level</b><br>\r\n";
s << " <a href=\"/?cmd=" << HTTP_COMMAND_LOGLEVEL << "&level=none&token=" << token << "\">[none]</a> ";
s << " <a href=\"/?cmd=" << HTTP_COMMAND_LOGLEVEL << "&level=error&token=" << token << "\">[error]</a> ";
s << " <a href=\"/?cmd=" << HTTP_COMMAND_LOGLEVEL << "&level=warn&token=" << token << "\">[warn]</a> ";
s << " <a href=\"/?cmd=" << HTTP_COMMAND_LOGLEVEL << "&level=info&token=" << token << "\">[info]</a> ";
s << " <a href=\"/?cmd=" << HTTP_COMMAND_LOGLEVEL << "&level=debug&token=" << token << "\">[debug]</a><br>\r\n";
}
void ShowTransitTunnels (std::stringstream& s)
2016-01-13 01:31:25 +00:00
{
2016-01-17 16:10:56 +00:00
s << "<b>Transit tunnels:</b><br>\r\n<br>\r\n";
2016-08-05 18:23:54 +00:00
for (const auto& it: i2p::tunnel::tunnels.GetTransitTunnels ())
2016-01-13 01:31:25 +00:00
{
2016-03-02 01:48:56 +00:00
if (std::dynamic_pointer_cast<i2p::tunnel::TransitTunnelGateway>(it))
2016-08-09 10:54:47 +00:00
s << it->GetTunnelID () << " &#8658; ";
2016-03-02 01:48:56 +00:00
else if (std::dynamic_pointer_cast<i2p::tunnel::TransitTunnelEndpoint>(it))
2016-08-09 10:54:47 +00:00
s << " &#8658; " << it->GetTunnelID ();
2016-01-13 01:31:25 +00:00
else
2016-08-09 10:54:47 +00:00
s << " &#8658; " << it->GetTunnelID () << " &#8658; ";
2016-03-02 01:48:56 +00:00
s << " " << it->GetNumTransmittedBytes () << "<br>\r\n";
2016-01-13 01:31:25 +00:00
}
}
void ShowTransports (std::stringstream& s)
2016-01-13 01:31:25 +00:00
{
2016-01-17 16:10:56 +00:00
s << "<b>Transports:</b><br>\r\n<br>\r\n";
auto ntcpServer = i2p::transport::transports.GetNTCPServer ();
2016-01-13 01:31:25 +00:00
if (ntcpServer)
2016-08-09 10:54:47 +00:00
{
2017-01-01 13:58:21 +00:00
auto sessions = ntcpServer->GetNTCPSessions ();
if (!sessions.empty ())
2016-01-13 01:31:25 +00:00
{
std::stringstream tmp_s, tmp_s6; uint16_t cnt = 0, cnt6 = 0;
for (const auto& it: sessions )
2016-01-13 01:31:25 +00:00
{
if (it.second && it.second->IsEstablished () && !it.second->GetSocket ().remote_endpoint ().address ().is_v6 ())
{
// incoming connection doesn't have remote RI
if (it.second->IsOutgoing ()) tmp_s << " &#8658; ";
tmp_s << i2p::data::GetIdentHashAbbreviation (it.second->GetRemoteIdentity ()->GetIdentHash ()) << ": "
<< it.second->GetSocket ().remote_endpoint().address ().to_string ();
if (!it.second->IsOutgoing ()) tmp_s << " &#8658; ";
tmp_s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
tmp_s << "<br>\r\n" << std::endl;
cnt++;
}
if (it.second && it.second->IsEstablished () && it.second->GetSocket ().remote_endpoint ().address ().is_v6 ())
{
if (it.second->IsOutgoing ()) tmp_s6 << " &#8658; ";
tmp_s6 << i2p::data::GetIdentHashAbbreviation (it.second->GetRemoteIdentity ()->GetIdentHash ()) << ": "
<< "[" << it.second->GetSocket ().remote_endpoint().address ().to_string () << "]";
if (!it.second->IsOutgoing ()) tmp_s6 << " &#8658; ";
tmp_s6 << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
tmp_s6 << "<br>\r\n" << std::endl;
cnt6++;
}
}
if (!tmp_s.str ().empty ())
{
s << "<div class='slide'><label for='slide_ntcp'><b>NTCP</b> ( " << cnt << " )</label>\r\n<input type='checkbox' id='slide_ntcp'/>\r\n<p class='content'>";
2017-11-14 18:31:13 +00:00
s << tmp_s.str () << "</p>\r\n</div>\r\n";
}
if (!tmp_s6.str ().empty ())
{
s << "<div class='slide'><label for='slide_ntcp6'><b>NTCP6</b> ( " << cnt6 << " )</label>\r\n<input type='checkbox' id='slide_ntcp6'/>\r\n<p class='content'>";
2017-11-14 18:31:13 +00:00
s << tmp_s6.str () << "</p>\r\n</div>\r\n";
2016-01-13 01:31:25 +00:00
}
}
2016-08-09 10:54:47 +00:00
}
2016-01-13 01:31:25 +00:00
auto ssuServer = i2p::transport::transports.GetSSUServer ();
if (ssuServer)
{
2017-01-01 13:58:21 +00:00
auto sessions = ssuServer->GetSessions ();
if (!sessions.empty ())
2016-01-13 01:31:25 +00:00
{
s << "<div class='slide'><label for='slide_ssu'><b>SSU</b> ( " << (int) sessions.size() << " )</label>\r\n<input type='checkbox' id='slide_ssu'/>\r\n<p class='content'>";
for (const auto& it: sessions)
{
auto endpoint = it.second->GetRemoteEndpoint ();
if (it.second->IsOutgoing ()) s << " &#8658; ";
s << endpoint.address ().to_string () << ":" << endpoint.port ();
if (!it.second->IsOutgoing ()) s << " &#8658; ";
s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
if (it.second->GetRelayTag ())
s << " [itag:" << it.second->GetRelayTag () << "]";
s << "<br>\r\n" << std::endl;
}
2017-11-14 18:31:13 +00:00
s << "</p>\r\n</div>\r\n";
2016-01-13 01:31:25 +00:00
}
auto sessions6 = ssuServer->GetSessionsV6 ();
if (!sessions6.empty ())
2016-01-13 01:31:25 +00:00
{
s << "<div class='slide'><label for='slide_ssu6'><b>SSU6</b> ( " << (int) sessions6.size() << " )</label>\r\n<input type='checkbox' id='slide_ssu6'/>\r\n<p class='content'>";
for (const auto& it: sessions6)
{
auto endpoint = it.second->GetRemoteEndpoint ();
if (it.second->IsOutgoing ()) s << " &#8658; ";
s << "[" << endpoint.address ().to_string () << "]:" << endpoint.port ();
if (!it.second->IsOutgoing ()) s << " &#8658; ";
s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
if (it.second->GetRelayTag ())
s << " [itag:" << it.second->GetRelayTag () << "]";
s << "<br>\r\n" << std::endl;
}
2017-11-14 18:31:13 +00:00
s << "</p>\r\n</div>\r\n";
2016-01-13 01:31:25 +00:00
}
}
}
2016-08-09 10:54:47 +00:00
void ShowSAMSessions (std::stringstream& s)
{
auto sam = i2p::client::context.GetSAMBridge ();
if (!sam) {
ShowError(s, "SAM disabled");
return;
}
s << "<b>SAM Sessions:</b><br>\r\n<br>\r\n";
for (auto& it: sam->GetSessions ())
{
2017-10-05 02:29:07 +00:00
auto& name = it.second->localDestination->GetNickname ();
2016-06-14 08:25:51 +00:00
s << "<a href=\"/?page=" << HTTP_PAGE_SAM_SESSION << "&sam_id=" << it.first << "\">";
2017-10-05 02:29:07 +00:00
s << name << " (" << it.first << ")</a><br>\r\n" << std::endl;
2016-08-09 10:54:47 +00:00
}
}
static void ShowSAMSession (std::stringstream& s, const std::string& id)
{
2016-01-17 16:10:56 +00:00
s << "<b>SAM Session:</b><br>\r\n<br>\r\n";
auto sam = i2p::client::context.GetSAMBridge ();
if (!sam) {
ShowError(s, "SAM disabled");
return;
}
auto session = sam->FindSession (id);
if (!session) {
ShowError(s, "SAM session not found");
return;
}
auto& ident = session->localDestination->GetIdentHash();
2016-06-14 08:25:51 +00:00
s << "<a href=\"/?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
s << i2p::client::context.GetAddressBook ().ToAddress(ident) << "</a><br>\r\n";
s << "<br>\r\n";
s << "<b>Streams:</b><br>\r\n";
2016-08-05 18:23:54 +00:00
for (const 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"; break;
}
s << " [" << it->GetSocket ().remote_endpoint() << "]";
s << "<br>\r\n";
2016-08-09 10:54:47 +00:00
}
}
2014-09-30 17:34:29 +00:00
void ShowI2PTunnels (std::stringstream& s)
2016-01-14 01:21:53 +00:00
{
2016-01-17 16:10:56 +00:00
s << "<b>Client Tunnels:</b><br>\r\n<br>\r\n";
2016-01-14 01:21:53 +00:00
for (auto& it: i2p::client::context.GetClientTunnels ())
{
auto& ident = it.second->GetLocalDestination ()->GetIdentHash();
s << "<a href=\"/?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
2016-08-09 10:54:47 +00:00
s << it.second->GetName () << "</a> &#8656; ";
2016-01-14 01:21:53 +00:00
s << i2p::client::context.GetAddressBook ().ToAddress(ident);
2016-04-06 20:33:23 +00:00
s << "<br>\r\n"<< std::endl;
2016-08-09 10:54:47 +00:00
}
2016-10-31 19:42:50 +00:00
auto httpProxy = i2p::client::context.GetHttpProxy ();
if (httpProxy)
{
auto& ident = httpProxy->GetLocalDestination ()->GetIdentHash();
s << "<a href=\"/?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
2016-10-31 19:42:50 +00:00
s << "HTTP Proxy" << "</a> &#8656; ";
s << i2p::client::context.GetAddressBook ().ToAddress(ident);
s << "<br>\r\n"<< std::endl;
}
2016-12-28 03:45:51 +00:00
auto socksProxy = i2p::client::context.GetSocksProxy ();
if (socksProxy)
{
auto& ident = socksProxy->GetLocalDestination ()->GetIdentHash();
s << "<a href=\"/?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
2016-12-28 03:45:51 +00:00
s << "SOCKS Proxy" << "</a> &#8656; ";
s << i2p::client::context.GetAddressBook ().ToAddress(ident);
s << "<br>\r\n"<< std::endl;
2017-08-24 23:37:01 +00:00
}
auto& serverTunnels = i2p::client::context.GetServerTunnels ();
if (!serverTunnels.empty ()) {
s << "<br>\r\n<b>Server Tunnels:</b><br>\r\n<br>\r\n";
for (auto& it: serverTunnels)
{
auto& ident = it.second->GetLocalDestination ()->GetIdentHash();
s << "<a href=\"/?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
2017-08-24 23:37:01 +00:00
s << it.second->GetName () << "</a> &#8658; ";
s << i2p::client::context.GetAddressBook ().ToAddress(ident);
s << ":" << it.second->GetLocalPort ();
s << "</a><br>\r\n"<< std::endl;
}
2016-08-09 10:54:47 +00:00
}
2016-09-12 16:05:01 +00:00
auto& clientForwards = i2p::client::context.GetClientForwards ();
if (!clientForwards.empty ())
{
s << "<br>\r\n<b>Client Forwards:</b><br>\r\n<br>\r\n";
for (auto& it: clientForwards)
{
auto& ident = it.second->GetLocalDestination ()->GetIdentHash();
s << "<a href=\"/?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
2016-09-12 16:05:01 +00:00
s << it.second->GetName () << "</a> &#8656; ";
s << i2p::client::context.GetAddressBook ().ToAddress(ident);
s << "<br>\r\n"<< std::endl;
}
}
auto& serverForwards = i2p::client::context.GetServerForwards ();
if (!serverForwards.empty ())
{
s << "<br>\r\n<b>Server Forwards:</b><br>\r\n<br>\r\n";
for (auto& it: serverForwards)
{
auto& ident = it.second->GetLocalDestination ()->GetIdentHash();
s << "<a href=\"/?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
2016-09-12 16:05:01 +00:00
s << it.second->GetName () << "</a> &#8656; ";
s << i2p::client::context.GetAddressBook ().ToAddress(ident);
s << "<br>\r\n"<< std::endl;
}
}
2016-08-09 10:54:47 +00:00
}
2014-09-30 17:34:29 +00:00
2016-04-27 00:00:00 +00:00
HTTPConnection::HTTPConnection (std::shared_ptr<boost::asio::ip::tcp::socket> socket):
m_Socket (socket), m_Timer (socket->get_io_service ()), m_BufferLen (0)
2016-01-13 01:31:25 +00:00
{
2016-04-27 00:00:00 +00:00
/* cache options */
i2p::config::GetOption("http.auth", needAuth);
i2p::config::GetOption("http.user", user);
i2p::config::GetOption("http.pass", pass);
2016-06-01 00:00:00 +00:00
}
2016-01-13 01:31:25 +00:00
void HTTPConnection::Receive ()
2015-11-03 14:15:49 +00:00
{
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));
2015-11-03 14:15:49 +00:00
}
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);
2014-10-03 18:22:32 +00:00
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 */
2014-07-16 16:41:40 +00:00
}
if (ret == 0)
return; /* need more data */
2016-04-27 00:00:00 +00:00
HandleRequest (request);
}
void HTTPConnection::Terminate (const boost::system::error_code& ecode)
2014-03-27 19:42:23 +00:00
{
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 ();
2014-03-27 19:42:23 +00:00
}
2016-04-27 00:00:00 +00:00
bool HTTPConnection::CheckAuth (const HTTPReq & req) {
/* method #1: http://user:pass@127.0.0.1:7070/ */
if (req.uri.find('@') != std::string::npos) {
URL url;
if (url.parse(req.uri) && url.user == user && url.pass == pass)
return true;
2014-03-27 17:24:23 +00:00
}
2016-04-27 00:00:00 +00:00
/* method #2: 'Authorization' header sent */
2017-02-05 03:39:54 +00:00
auto provided = req.GetHeader ("Authorization");
if (provided.length () > 0)
{
2017-01-01 13:52:36 +00:00
bool result = false;
2016-04-27 00:00:00 +00:00
std::string expected = user + ":" + pass;
2017-01-01 13:54:11 +00:00
size_t b64_sz = i2p::data::Base64EncodingBufferSize(expected.length()) + 1;
char * b64_creds = new char[b64_sz];
2016-04-27 00:00:00 +00:00
std::size_t len = 0;
2017-01-01 13:54:11 +00:00
len = i2p::data::ByteStreamToBase64((unsigned char *)expected.c_str(), expected.length(), b64_creds, b64_sz);
2016-12-09 14:27:19 +00:00
/* if we decoded properly then check credentials */
if(len) {
b64_creds[len] = '\0';
expected = "Basic ";
expected += b64_creds;
2017-01-01 13:52:36 +00:00
result = expected == provided;
2016-12-09 14:27:19 +00:00
}
2017-01-01 13:52:36 +00:00
delete [] b64_creds;
return result;
2014-07-16 16:41:40 +00:00
}
2016-04-27 00:00:00 +00:00
LogPrint(eLogWarning, "HTTPServer: auth failure from ", m_Socket->remote_endpoint().address ());
return false;
2014-03-27 17:24:23 +00:00
}
void HTTPConnection::HandleRequest (const HTTPReq & req)
2014-03-27 17:24:23 +00:00
{
std::stringstream s;
std::string content;
HTTPRes res;
2016-04-27 00:00:00 +00:00
2016-04-27 00:00:00 +00:00
LogPrint(eLogDebug, "HTTPServer: request: ", req.uri);
2016-04-27 00:00:00 +00:00
if (needAuth && !CheckAuth(req)) {
res.code = 401;
res.add_header("WWW-Authenticate", "Basic realm=\"WebAdmin\"");
2016-04-27 00:00:00 +00:00
SendReply(res, content);
return;
}
// Html5 head start
ShowPageHead (s);
if (req.uri.find("page=") != std::string::npos) {
HandlePage (req, res, s);
} else if (req.uri.find("cmd=") != std::string::npos) {
HandleCommand (req, res, s);
} else {
ShowStatus (s, true);
2016-06-24 23:07:47 +00:00
res.add_header("Refresh", "10");
}
ShowPageTail (s);
res.code = 200;
content = s.str ();
SendReply (res, content);
2014-03-26 19:45:08 +00:00
}
2014-07-16 16:41:40 +00:00
2016-12-15 18:10:12 +00:00
std::map<uint32_t, uint32_t> HTTPConnection::m_Tokens;
void HTTPConnection::HandlePage (const HTTPReq& req, HTTPRes& res, std::stringstream& s)
{
std::map<std::string, std::string> params;
std::string page("");
URL url;
url.parse(req.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_COMMANDS)
2016-12-15 18:10:12 +00:00
{
uint32_t token;
RAND_bytes ((uint8_t *)&token, 4);
2016-12-23 01:34:06 +00:00
token &= 0x7FFFFFFF; // clear first bit
2016-12-15 18:10:12 +00:00
auto ts = i2p::util::GetSecondsSinceEpoch ();
for (auto it = m_Tokens.begin (); it != m_Tokens.end (); )
{
if (ts > it->second + TOKEN_EXPIRATION_TIMEOUT)
it = m_Tokens.erase (it);
else
++it;
}
m_Tokens[token] = ts;
2016-12-15 18:10:12 +00:00
ShowCommands (s, token);
}
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_I2CP_LOCAL_DESTINATION)
ShowI2CPLocalDestination (s, params["i2cp_id"]);
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);
2016-07-15 17:52:55 +00:00
else if (page == HTTP_PAGE_LEASESETS)
ShowLeasesSets(s);
else {
res.code = 400;
ShowError(s, "Unknown page: " + page);
return;
}
}
void HTTPConnection::HandleCommand (const HTTPReq& req, HTTPRes& res, std::stringstream& s)
{
std::map<std::string, std::string> params;
URL url;
url.parse(req.uri);
url.parse_query(params);
2016-12-15 18:10:12 +00:00
std::string token = params["token"];
2016-12-15 18:36:52 +00:00
if (token.empty () || m_Tokens.find (std::stoi (token)) == m_Tokens.end ())
2016-12-15 18:10:12 +00:00
{
ShowError(s, "Invalid token");
return;
}
std::string cmd = params["cmd"];
if (cmd == HTTP_COMMAND_RUN_PEER_TEST)
i2p::transport::transports.PeerTest ();
else if (cmd == HTTP_COMMAND_RELOAD_CONFIG)
i2p::client::context.ReloadConfig ();
2016-07-12 00:00:00 +00:00
else if (cmd == HTTP_COMMAND_ENABLE_TRANSIT)
i2p::context.SetAcceptsTunnels (true);
2016-07-12 00:00:00 +00:00
else if (cmd == HTTP_COMMAND_DISABLE_TRANSIT)
i2p::context.SetAcceptsTunnels (false);
else if (cmd == HTTP_COMMAND_SHUTDOWN_START) {
i2p::context.SetAcceptsTunnels (false);
2016-07-09 20:54:11 +00:00
#if (!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID))
2016-10-31 10:27:27 +00:00
Daemon.gracefulShutdownInterval = 10*60;
#elif defined(WIN32_APP)
2016-08-09 10:54:47 +00:00
i2p::win32::GracefulShutdown ();
#endif
} else if (cmd == HTTP_COMMAND_SHUTDOWN_CANCEL) {
i2p::context.SetAcceptsTunnels (true);
2016-07-09 20:54:11 +00:00
#if (!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID))
2016-10-31 10:27:27 +00:00
Daemon.gracefulShutdownInterval = 0;
#elif defined(WIN32_APP)
i2p::win32::StopGracefulShutdown ();
#endif
} else if (cmd == HTTP_COMMAND_SHUTDOWN_NOW) {
#ifndef WIN32_APP
Daemon.running = false;
#else
i2p::win32::StopWin32App ();
#endif
} else if (cmd == HTTP_COMMAND_LOGLEVEL){
std::string level = params["level"];
SetLogLevel (level);
} else {
res.code = 400;
ShowError(s, "Unknown command: " + cmd);
return;
}
s << "<b>SUCCESS</b>:&nbsp;Command accepted<br><br>\r\n";
s << "<a href=\"/?page=commands\">Back to commands list</a><br>\r\n";
s << "<p>You will be redirected in 5 seconds</b>";
res.add_header("Refresh", "5; url=/?page=commands");
2016-08-09 10:54:47 +00:00
}
void HTTPConnection::SendReply (HTTPRes& reply, std::string& content)
2016-04-14 00:00:00 +00:00
{
reply.add_header("X-Frame-Options", "SAMEORIGIN");
reply.add_header("Content-Type", "text/html");
reply.body = content;
m_SendBuffer = reply.to_string();
boost::asio::async_write (*m_Socket, boost::asio::buffer(m_SendBuffer),
std::bind (&HTTPConnection::Terminate, shared_from_this (), std::placeholders::_1));
2016-04-14 00:00:00 +00:00
}
2015-11-30 14:44:32 +00:00
HTTPServer::HTTPServer (const std::string& address, int port):
m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service),
2015-11-30 14:44:32 +00:00
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint (boost::asio::ip::address::from_string(address), port))
2013-12-10 13:03:22 +00:00
{
}
HTTPServer::~HTTPServer ()
{
Stop ();
}
void HTTPServer::Start ()
{
2017-09-27 20:41:34 +00:00
bool needAuth; i2p::config::GetOption("http.auth", needAuth);
2016-04-27 00:00:00 +00:00
std::string user; i2p::config::GetOption("http.user", user);
std::string pass; i2p::config::GetOption("http.pass", pass);
/* generate pass if needed */
if (needAuth && pass == "") {
uint8_t random[16];
2016-04-27 00:00:00 +00:00
char alnum[] = "0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz";
pass.resize(sizeof(random));
RAND_bytes(random, sizeof(random));
for (size_t i = 0; i < sizeof(random); i++) {
pass[i] = alnum[random[i] % (sizeof(alnum) - 1)];
2016-04-27 00:00:00 +00:00
}
i2p::config::SetOption("http.pass", pass);
LogPrint(eLogInfo, "HTTPServer: password set to ", pass);
}
m_IsRunning = true;
2015-11-22 22:01:37 +00:00
m_Thread = std::unique_ptr<std::thread>(new std::thread (std::bind (&HTTPServer::Run, this)));
2013-12-10 13:03:22 +00:00
m_Acceptor.listen ();
Accept ();
}
void HTTPServer::Stop ()
{
m_IsRunning = false;
2013-12-10 13:03:22 +00:00
m_Acceptor.close();
m_Service.stop ();
if (m_Thread)
{
2016-04-27 00:00:00 +00:00
m_Thread->join ();
m_Thread = nullptr;
}
2013-12-10 13:03:22 +00:00
}
void HTTPServer::Run ()
{
while (m_IsRunning)
{
try
{
m_Service.run ();
}
catch (std::exception& ex)
{
LogPrint (eLogError, "HTTPServer: runtime exception: ", ex.what ());
}
}
2014-07-16 16:41:40 +00:00
}
2013-12-10 13:03:22 +00:00
void HTTPServer::Accept ()
{
2015-11-22 22:01:37 +00:00
auto newSocket = std::make_shared<boost::asio::ip::tcp::socket> (m_Service);
m_Acceptor.async_accept (*newSocket, boost::bind (&HTTPServer::HandleAccept, this,
boost::asio::placeholders::error, newSocket));
2013-12-10 13:03:22 +00:00
}
void HTTPServer::HandleAccept(const boost::system::error_code& ecode,
2015-11-22 22:01:37 +00:00
std::shared_ptr<boost::asio::ip::tcp::socket> newSocket)
2013-12-10 13:03:22 +00:00
{
2016-04-27 00:00:00 +00:00
if (ecode)
2016-09-04 20:42:48 +00:00
{
if(newSocket) newSocket->close();
LogPrint(eLogError, "HTTP Server: error handling accept ", ecode.message());
if(ecode != boost::asio::error::operation_aborted)
Accept();
2016-04-27 00:00:00 +00:00
return;
2016-09-04 20:42:48 +00:00
}
2016-04-27 00:00:00 +00:00
CreateConnection(newSocket);
Accept ();
2014-07-16 16:41:40 +00:00
}
2015-11-22 22:01:37 +00:00
void HTTPServer::CreateConnection(std::shared_ptr<boost::asio::ip::tcp::socket> newSocket)
{
2015-11-22 22:01:37 +00:00
auto conn = std::make_shared<HTTPConnection> (newSocket);
2015-04-04 19:44:29 +00:00
conn->Receive ();
}
2016-04-27 00:00:00 +00:00
} // http
} // i2p