HTTPServer.cpp 43 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151
  1. #include <iomanip>
  2. #include <sstream>
  3. #include <thread>
  4. #include <memory>
  5. #include <boost/asio.hpp>
  6. #include <boost/bind.hpp>
  7. #include <boost/algorithm/string.hpp>
  8. #include "Base.h"
  9. #include "FS.h"
  10. #include "Log.h"
  11. #include "Config.h"
  12. #include "Tunnel.h"
  13. #include "Transports.h"
  14. #include "NetDb.hpp"
  15. #include "HTTP.h"
  16. #include "LeaseSet.h"
  17. #include "Destination.h"
  18. #include "RouterContext.h"
  19. #include "ClientContext.h"
  20. #include "HTTPServer.h"
  21. #include "Daemon.h"
  22. #include "util.h"
  23. #ifdef WIN32_APP
  24. #include "Win32/Win32App.h"
  25. #endif
  26. // For image and info
  27. #include "version.h"
  28. namespace i2p {
  29. namespace http {
  30. const char *itoopieFavicon =
  31. "data:image/png;base64,"
  32. "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACx"
  33. "jwv8YQUAAAAJcEhZcwAALiIAAC4iAari3ZIAAAAHdElNRQfgCQsUNSZrkhi1AAAAGXRFWHRTb2Z0"
  34. "d2FyZQBwYWludC5uZXQgNC4wLjEyQwRr7AAAAoJJREFUOE9jwAUqi4Q1oEwwcDTV1+5sETaBclGB"
  35. "vb09C5QJB6kWpvFQJoOCeLC5kmjEHCgXE2SlyETLi3h6QrkM4VL+ssWSCZUgtopITLKqaOotRTEn"
  36. "cbAkLqAkGtOqLBLVAWLXyWSVFkkmRiqLxuaqiWb/VBYJMAYrwgckJY25VEUzniqKhjU2y+RtCRSP"
  37. "6lUXy/1jIBV5tlYxZUaFVMq2NInwIi9hO8fSfOEAqDZUoCwal6MulvOvyS7gi69K4j9zxZT/m0ps"
  38. "/28ptvvvquXXryIa7QYMMdTwqi0WNtVi0GIDseXl7TnUxFKfnGlxAGp0+D8j2eH/8Ub7/9e7nf7X"
  39. "+Af/B7rwt6pI0h0l0WhQADOC9DBkhSirpImHNVZKp24ukkyoshGLnN8d5fA/y13t/44Kq/8hlnL/"
  40. "z7fZ/58f6vcxSNpbVUVFhV1RLNBVTsQzVYZPSwhsCAhkiIfpNMrkbO6TLf071Sfk/5ZSi/+7q6z/"
  41. "P5ns+v9mj/P/CpuI/20y+aeNGYxZoVoYGmsF3aFMBAAZlCwftnF9ke3//bU2//fXWP8/UGv731Am"
  42. "+V+DdNblSqnUYqhSTKAiYSOqJBrVqiaa+S3UNPr/gmyH/xuKXf63hnn/B8bIP0UxHfEyyeSNQKVM"
  43. "EB1AEB2twhcTLp+gIBJUoyKasEpVJHmqskh8qryovUG/ffCHHRU2q/Tk/YuB6eGPsbExa7ZkpLu1"
  44. "oLEcVDtuUCgV1w60rQzElpRUE1EVSX0BYidHiInXF4nagNhYQW60EF+ApH1ktni0A1SIITSUgVlZ"
  45. "JHYnlIsfzJjIp9xZKswL5YKBHL+coKJoRDaUSzoozxHVrygQU4JykQADAwAT5b1NHtwZugAAAABJ"
  46. "RU5ErkJggg==";
  47. const char *cssStyles =
  48. "<style>\r\n"
  49. " body { font: 100%/1.5em sans-serif; margin: 0; padding: 1.5em; background: #FAFAFA; color: #103456; }\r\n"
  50. " a, .slide label { text-decoration: none; color: #894C84; }\r\n"
  51. " a:hover, .slide label:hover { color: #FAFAFA; background: #894C84; }\r\n"
  52. " .header { font-size: 2.5em; text-align: center; margin: 1.5em 0; color: #894C84; }\r\n"
  53. " .wrapper { margin: 0 auto; padding: 1em; max-width: 60em; }\r\n"
  54. " .left { float: left; position: absolute; }\r\n"
  55. " .right { float: left; font-size: 1em; margin-left: 13em; max-width: 46em; overflow: auto; }\r\n"
  56. " .tunnel.established { color: #56B734; }\r\n"
  57. " .tunnel.expiring { color: #D3AE3F; }\r\n"
  58. " .tunnel.failed { color: #D33F3F; }\r\n"
  59. " .tunnel.building { color: #434343; }\r\n"
  60. " caption { font-size: 1.5em; text-align: center; color: #894C84; }\r\n"
  61. " table { width: 100%; border-collapse: collapse; text-align: center; }\r\n"
  62. " .slide p, .slide [type='checkbox']{ display:none; }\r\n"
  63. " .slide [type='checkbox']:checked ~ p { display:block; margin-top: 0; padding: 0; }\r\n"
  64. " .disabled:after { color: #D33F3F; content: \"Disabled\" }\r\n"
  65. " .enabled:after { color: #56B734; content: \"Enabled\" }\r\n"
  66. "</style>\r\n";
  67. const char HTTP_PAGE_TUNNELS[] = "tunnels";
  68. const char HTTP_PAGE_TRANSIT_TUNNELS[] = "transit_tunnels";
  69. const char HTTP_PAGE_TRANSPORTS[] = "transports";
  70. const char HTTP_PAGE_LOCAL_DESTINATIONS[] = "local_destinations";
  71. const char HTTP_PAGE_LOCAL_DESTINATION[] = "local_destination";
  72. const char HTTP_PAGE_I2CP_LOCAL_DESTINATION[] = "i2cp_local_destination";
  73. const char HTTP_PAGE_SAM_SESSIONS[] = "sam_sessions";
  74. const char HTTP_PAGE_SAM_SESSION[] = "sam_session";
  75. const char HTTP_PAGE_I2P_TUNNELS[] = "i2p_tunnels";
  76. const char HTTP_PAGE_COMMANDS[] = "commands";
  77. const char HTTP_PAGE_LEASESETS[] = "leasesets";
  78. const char HTTP_COMMAND_ENABLE_TRANSIT[] = "enable_transit";
  79. const char HTTP_COMMAND_DISABLE_TRANSIT[] = "disable_transit";
  80. const char HTTP_COMMAND_SHUTDOWN_START[] = "shutdown_start";
  81. const char HTTP_COMMAND_SHUTDOWN_CANCEL[] = "shutdown_cancel";
  82. const char HTTP_COMMAND_SHUTDOWN_NOW[] = "terminate";
  83. const char HTTP_COMMAND_RUN_PEER_TEST[] = "run_peer_test";
  84. const char HTTP_COMMAND_RELOAD_CONFIG[] = "reload_config";
  85. const char HTTP_COMMAND_LOGLEVEL[] = "set_loglevel";
  86. const char HTTP_PARAM_SAM_SESSION_ID[] = "id";
  87. const char HTTP_PARAM_ADDRESS[] = "address";
  88. static std::string ConvertTime (uint64_t time);
  89. static void ShowUptime (std::stringstream& s, int seconds)
  90. {
  91. int num;
  92. if ((num = seconds / 86400) > 0) {
  93. s << num << " days, ";
  94. seconds -= num * 86400;
  95. }
  96. if ((num = seconds / 3600) > 0) {
  97. s << num << " hours, ";
  98. seconds -= num * 3600;
  99. }
  100. if ((num = seconds / 60) > 0) {
  101. s << num << " min, ";
  102. seconds -= num * 60;
  103. }
  104. s << seconds << " seconds";
  105. }
  106. static void ShowTraffic (std::stringstream& s, uint64_t bytes)
  107. {
  108. s << std::fixed << std::setprecision(2);
  109. auto numKBytes = (double) bytes / 1024;
  110. if (numKBytes < 1024)
  111. s << numKBytes << " KiB";
  112. else if (numKBytes < 1024 * 1024)
  113. s << numKBytes / 1024 << " MiB";
  114. else
  115. s << numKBytes / 1024 / 1024 << " GiB";
  116. }
  117. static void ShowTunnelDetails (std::stringstream& s, enum i2p::tunnel::TunnelState eState, bool explr, int bytes)
  118. {
  119. std::string state;
  120. switch (eState) {
  121. case i2p::tunnel::eTunnelStateBuildReplyReceived :
  122. case i2p::tunnel::eTunnelStatePending : state = "building"; break;
  123. case i2p::tunnel::eTunnelStateBuildFailed :
  124. case i2p::tunnel::eTunnelStateTestFailed :
  125. case i2p::tunnel::eTunnelStateFailed : state = "failed"; break;
  126. case i2p::tunnel::eTunnelStateExpiring : state = "expiring"; break;
  127. case i2p::tunnel::eTunnelStateEstablished : state = "established"; break;
  128. default: state = "unknown"; break;
  129. }
  130. s << "<span class=\"tunnel " << state << "\"> " << state << ((explr) ? " (exploratory)" : "") << "</span>, ";
  131. s << " " << (int) (bytes / 1024) << "&nbsp;KiB<br>\r\n";
  132. }
  133. static void SetLogLevel (const std::string& level)
  134. {
  135. if (level == "none" || level == "error" || level == "warn" || level == "info" || level == "debug")
  136. i2p::log::Logger().SetLogLevel(level);
  137. else {
  138. LogPrint(eLogError, "HTTPServer: unknown loglevel set attempted");
  139. return;
  140. }
  141. i2p::log::Logger().Reopen ();
  142. }
  143. static void ShowPageHead (std::stringstream& s)
  144. {
  145. std::string webroot;
  146. i2p::config::GetOption("http.webroot", webroot);
  147. s <<
  148. "<!DOCTYPE html>\r\n"
  149. "<html lang=\"en\">\r\n" /* TODO: Add support for locale */
  150. " <head>\r\n" /* TODO: Find something to parse html/template system. This is horrible. */
  151. #if (!defined(WIN32))
  152. " <meta charset=\"UTF-8\">\r\n"
  153. #else
  154. " <meta charset=\"windows-1251\">\r\n"
  155. #endif
  156. " <link rel=\"shortcut icon\" href=\"" << itoopieFavicon << "\">\r\n"
  157. " <title>Purple I2P " VERSION " Webconsole</title>\r\n"
  158. << cssStyles <<
  159. "</head>\r\n";
  160. s <<
  161. "<body>\r\n"
  162. "<div class=header><b>i2pd</b> webconsole</div>\r\n"
  163. "<div class=wrapper>\r\n"
  164. "<div class=left>\r\n"
  165. " <a href=\"" << webroot << "\">Main page</a><br>\r\n<br>\r\n"
  166. " <a href=\"" << webroot << "?page=" << HTTP_PAGE_COMMANDS << "\">Router commands</a><br>\r\n"
  167. " <a href=\"" << webroot << "?page=" << HTTP_PAGE_LOCAL_DESTINATIONS << "\">Local destinations</a><br>\r\n"
  168. " <a href=\"" << webroot << "?page=" << HTTP_PAGE_LEASESETS << "\">LeaseSets</a><br>\r\n"
  169. " <a href=\"" << webroot << "?page=" << HTTP_PAGE_TUNNELS << "\">Tunnels</a><br>\r\n"
  170. " <a href=\"" << webroot << "?page=" << HTTP_PAGE_TRANSIT_TUNNELS << "\">Transit tunnels</a><br>\r\n"
  171. " <a href=\"" << webroot << "?page=" << HTTP_PAGE_TRANSPORTS << "\">Transports</a><br>\r\n"
  172. " <a href=\"" << webroot << "?page=" << HTTP_PAGE_I2P_TUNNELS << "\">I2P tunnels</a><br>\r\n";
  173. if (i2p::client::context.GetSAMBridge ())
  174. s << " <a href=\"" << webroot << "?page=" << HTTP_PAGE_SAM_SESSIONS << "\">SAM sessions</a><br>\r\n";
  175. s <<
  176. "</div>\r\n"
  177. "<div class=right>";
  178. }
  179. static void ShowPageTail (std::stringstream& s)
  180. {
  181. s <<
  182. "</div></div>\r\n"
  183. "</body>\r\n"
  184. "</html>\r\n";
  185. }
  186. static void ShowError(std::stringstream& s, const std::string& string)
  187. {
  188. s << "<b>ERROR:</b>&nbsp;" << string << "<br>\r\n";
  189. }
  190. void ShowStatus (
  191. std::stringstream& s,
  192. bool includeHiddenContent,
  193. i2p::http::OutputFormatEnum outputFormat)
  194. {
  195. s << "<b>Uptime:</b> ";
  196. ShowUptime(s, i2p::context.GetUptime ());
  197. s << "<br>\r\n";
  198. s << "<b>Network status:</b> ";
  199. switch (i2p::context.GetStatus ())
  200. {
  201. case eRouterStatusOK: s << "OK"; break;
  202. case eRouterStatusTesting: s << "Testing"; break;
  203. case eRouterStatusFirewalled: s << "Firewalled"; break;
  204. case eRouterStatusError:
  205. {
  206. s << "Error";
  207. switch (i2p::context.GetError ())
  208. {
  209. case eRouterErrorClockSkew:
  210. s << "<br>Clock skew";
  211. break;
  212. default: ;
  213. }
  214. break;
  215. }
  216. default: s << "Unknown";
  217. }
  218. s << "<br>\r\n";
  219. #if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY))
  220. if (auto remains = Daemon.gracefulShutdownInterval) {
  221. s << "<b>Stopping in:</b> ";
  222. s << remains << " seconds";
  223. s << "<br>\r\n";
  224. }
  225. #endif
  226. auto family = i2p::context.GetFamily ();
  227. if (family.length () > 0)
  228. s << "<b>Family:</b> " << family << "<br>\r\n";
  229. s << "<b>Tunnel creation success rate:</b> " << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate () << "%<br>\r\n";
  230. s << "<b>Received:</b> ";
  231. ShowTraffic (s, i2p::transport::transports.GetTotalReceivedBytes ());
  232. s << " (" << (double) i2p::transport::transports.GetInBandwidth () / 1024 << " KiB/s)<br>\r\n";
  233. s << "<b>Sent:</b> ";
  234. ShowTraffic (s, i2p::transport::transports.GetTotalSentBytes ());
  235. s << " (" << (double) i2p::transport::transports.GetOutBandwidth () / 1024 << " KiB/s)<br>\r\n";
  236. s << "<b>Transit:</b> ";
  237. ShowTraffic (s, i2p::transport::transports.GetTotalTransitTransmittedBytes ());
  238. s << " (" << (double) i2p::transport::transports.GetTransitBandwidth () / 1024 << " KiB/s)<br>\r\n";
  239. s << "<b>Data path:</b> " << i2p::fs::GetDataDir() << "<br>\r\n";
  240. s << "<div class='slide'>";
  241. if((outputFormat==OutputFormatEnum::forWebConsole)||!includeHiddenContent) {
  242. s << "<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";
  243. }
  244. if(includeHiddenContent) {
  245. s << "<b>Router Ident:</b> " << i2p::context.GetRouterInfo().GetIdentHashBase64() << "<br>\r\n";
  246. s << "<b>Router Family:</b> " << i2p::context.GetRouterInfo().GetProperty("family") << "<br>\r\n";
  247. s << "<b>Router Caps:</b> " << i2p::context.GetRouterInfo().GetProperty("caps") << "<br>\r\n";
  248. s << "<b>Our external address:</b>" << "<br>\r\n" ;
  249. for (const auto& address : i2p::context.GetRouterInfo().GetAddresses())
  250. {
  251. if (address->IsNTCP2 () && !address->IsPublishedNTCP2 ())
  252. {
  253. s << "NTCP2";
  254. if (address->host.is_v6 ()) s << "v6";
  255. s << "&nbsp;&nbsp; supported <br>\r\n";
  256. continue;
  257. }
  258. switch (address->transportStyle)
  259. {
  260. case i2p::data::RouterInfo::eTransportNTCP:
  261. {
  262. s << "NTCP";
  263. if (address->IsPublishedNTCP2 ()) s << "2";
  264. if (address->host.is_v6 ()) s << "v6";
  265. s << "&nbsp;&nbsp;";
  266. break;
  267. }
  268. case i2p::data::RouterInfo::eTransportSSU:
  269. if (address->host.is_v6 ())
  270. s << "SSUv6&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
  271. else
  272. s << "SSU&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
  273. break;
  274. default:
  275. s << "Unknown&nbsp;&nbsp;";
  276. }
  277. s << address->host.to_string() << ":" << address->port << "<br>\r\n";
  278. }
  279. }
  280. s << "</p>\r\n</div>\r\n";
  281. if(outputFormat==OutputFormatEnum::forQtUi) {
  282. s << "<br>";
  283. }
  284. s << "<b>Routers:</b> " << i2p::data::netdb.GetNumRouters () << " ";
  285. s << "<b>Floodfills:</b> " << i2p::data::netdb.GetNumFloodfills () << " ";
  286. s << "<b>LeaseSets:</b> " << i2p::data::netdb.GetNumLeaseSets () << "<br>\r\n";
  287. size_t clientTunnelCount = i2p::tunnel::tunnels.CountOutboundTunnels();
  288. clientTunnelCount += i2p::tunnel::tunnels.CountInboundTunnels();
  289. size_t transitTunnelCount = i2p::tunnel::tunnels.CountTransitTunnels();
  290. s << "<b>Client Tunnels:</b> " << std::to_string(clientTunnelCount) << " ";
  291. s << "<b>Transit Tunnels:</b> " << std::to_string(transitTunnelCount) << "<br>\r\n<br>\r\n";
  292. if(outputFormat==OutputFormatEnum::forWebConsole) {
  293. s << "<table><caption>Services</caption><tr><th>Service</th><th>State</th></tr>\r\n";
  294. s << "<tr><td>" << "HTTP Proxy" << "</td><td><div class='" << ((i2p::client::context.GetHttpProxy ()) ? "enabled" : "disabled") << "'></div></td></tr>\r\n";
  295. s << "<tr><td>" << "SOCKS Proxy" << "</td><td><div class='" << ((i2p::client::context.GetSocksProxy ()) ? "enabled" : "disabled") << "'></div></td></tr>\r\n";
  296. s << "<tr><td>" << "BOB" << "</td><td><div class='" << ((i2p::client::context.GetBOBCommandChannel ()) ? "enabled" : "disabled") << "'></div></td></tr>\r\n";
  297. s << "<tr><td>" << "SAM" << "</td><td><div class='" << ((i2p::client::context.GetSAMBridge ()) ? "enabled" : "disabled") << "'></div></td></tr>\r\n";
  298. s << "<tr><td>" << "I2CP" << "</td><td><div class='" << ((i2p::client::context.GetI2CPServer ()) ? "enabled" : "disabled") << "'></div></td></tr>\r\n";
  299. bool i2pcontrol; i2p::config::GetOption("i2pcontrol.enabled", i2pcontrol);
  300. s << "<tr><td>" << "I2PControl" << "</td><td><div class='" << ((i2pcontrol) ? "enabled" : "disabled") << "'></div></td></tr>\r\n";
  301. s << "</table>\r\n";
  302. }
  303. }
  304. void ShowLocalDestinations (std::stringstream& s)
  305. {
  306. std::string webroot; i2p::config::GetOption("http.webroot", webroot);
  307. s << "<b>Local Destinations:</b><br>\r\n<br>\r\n";
  308. for (auto& it: i2p::client::context.GetDestinations ())
  309. {
  310. auto ident = it.second->GetIdentHash ();
  311. s << "<a href=\"" << webroot << "?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
  312. s << i2p::client::context.GetAddressBook ().ToAddress(ident) << "</a><br>\r\n" << std::endl;
  313. }
  314. auto i2cpServer = i2p::client::context.GetI2CPServer ();
  315. if (i2cpServer && !(i2cpServer->GetSessions ().empty ()))
  316. {
  317. s << "<br><b>I2CP Local Destinations:</b><br>\r\n<br>\r\n";
  318. for (auto& it: i2cpServer->GetSessions ())
  319. {
  320. auto dest = it.second->GetDestination ();
  321. if (dest)
  322. {
  323. auto ident = dest->GetIdentHash ();
  324. auto& name = dest->GetNickname ();
  325. s << "<a href=\"" << webroot << "?page=" << HTTP_PAGE_I2CP_LOCAL_DESTINATION << "&i2cp_id=" << it.first << "\">[ ";
  326. s << name << " ]</a> &#8660; " << i2p::client::context.GetAddressBook ().ToAddress(ident) <<"<br>\r\n" << std::endl;
  327. }
  328. }
  329. }
  330. }
  331. static void ShowLeaseSetDestination (std::stringstream& s, std::shared_ptr<const i2p::client::LeaseSetDestination> dest)
  332. {
  333. s << "<b>Base64:</b><br>\r\n<textarea readonly=\"readonly\" cols=\"64\" rows=\"11\" wrap=\"on\">";
  334. s << dest->GetIdentity ()->ToBase64 () << "</textarea><br>\r\n<br>\r\n";
  335. if (dest->IsEncryptedLeaseSet ())
  336. {
  337. i2p::data::BlindedPublicKey blinded (dest->GetIdentity ());
  338. s << "<div class='slide'><label for='slide-b33'><b>Encrypted B33 address:</b></label>\r\n<input type='checkbox' id='slide-b33'/>\r\n<p class='content'>\r\n";
  339. s << blinded.ToB33 () << ".b32.i2p<br>\r\n";
  340. s << "</p>\r\n</div>\r\n";
  341. }
  342. if(dest->GetNumRemoteLeaseSets())
  343. {
  344. 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";
  345. for(auto& it: dest->GetLeaseSets ())
  346. s << it.first.ToBase32 () << " " << (int)it.second->GetStoreType () << "<br>\r\n";
  347. s << "</p>\r\n</div>\r\n";
  348. } else
  349. s << "<b>LeaseSets:</b> <i>0</i><br>\r\n";
  350. auto pool = dest->GetTunnelPool ();
  351. if (pool)
  352. {
  353. s << "<b>Inbound tunnels:</b><br>\r\n";
  354. for (auto & it : pool->GetInboundTunnels ()) {
  355. it->Print(s);
  356. if(it->LatencyIsKnown())
  357. s << " ( " << it->GetMeanLatency() << "ms )";
  358. ShowTunnelDetails(s, it->GetState (), false, it->GetNumReceivedBytes ());
  359. }
  360. s << "<br>\r\n";
  361. s << "<b>Outbound tunnels:</b><br>\r\n";
  362. for (auto & it : pool->GetOutboundTunnels ()) {
  363. it->Print(s);
  364. if(it->LatencyIsKnown())
  365. s << " ( " << it->GetMeanLatency() << "ms )";
  366. ShowTunnelDetails(s, it->GetState (), false, it->GetNumSentBytes ());
  367. }
  368. }
  369. s << "<br>\r\n";
  370. s << "<b>Tags</b><br>Incoming: <i>" << dest->GetNumIncomingTags () << "</i><br>";
  371. if (!dest->GetSessions ().empty ()) {
  372. std::stringstream tmp_s; uint32_t out_tags = 0;
  373. for (const auto& it: dest->GetSessions ()) {
  374. tmp_s << i2p::client::context.GetAddressBook ().ToAddress(it.first) << " " << it.second->GetNumOutgoingTags () << "<br>\r\n";
  375. out_tags = out_tags + it.second->GetNumOutgoingTags ();
  376. }
  377. s << "<div class='slide'><label for='slide-tags'>Outgoing: <i>" << out_tags << "</i></label>\r\n<input type='checkbox' id='slide-tags'/>\r\n<p class='content'>\r\n" << tmp_s.str () << "</p>\r\n</div>\r\n";
  378. } else
  379. s << "Outgoing: <i>0</i><br>\r\n";
  380. s << "<br>\r\n";
  381. }
  382. void ShowLocalDestination (std::stringstream& s, const std::string& b32)
  383. {
  384. s << "<b>Local Destination:</b><br>\r\n<br>\r\n";
  385. i2p::data::IdentHash ident;
  386. ident.FromBase32 (b32);
  387. auto dest = i2p::client::context.FindLocalDestination (ident);
  388. if (dest)
  389. {
  390. ShowLeaseSetDestination (s, dest);
  391. // show streams
  392. s << "<table><caption>Streams</caption>\r\n<tr>";
  393. s << "<th>StreamID</th>";
  394. s << "<th>Destination</th>";
  395. s << "<th>Sent</th>";
  396. s << "<th>Received</th>";
  397. s << "<th>Out</th>";
  398. s << "<th>In</th>";
  399. s << "<th>Buf</th>";
  400. s << "<th>RTT</th>";
  401. s << "<th>Window</th>";
  402. s << "<th>Status</th>";
  403. s << "</tr>\r\n";
  404. for (const auto& it: dest->GetAllStreams ())
  405. {
  406. s << "<tr>";
  407. s << "<td>" << it->GetSendStreamID () << "</td>";
  408. s << "<td>" << i2p::client::context.GetAddressBook ().ToAddress(it->GetRemoteIdentity ()) << "</td>";
  409. s << "<td>" << it->GetNumSentBytes () << "</td>";
  410. s << "<td>" << it->GetNumReceivedBytes () << "</td>";
  411. s << "<td>" << it->GetSendQueueSize () << "</td>";
  412. s << "<td>" << it->GetReceiveQueueSize () << "</td>";
  413. s << "<td>" << it->GetSendBufferSize () << "</td>";
  414. s << "<td>" << it->GetRTT () << "</td>";
  415. s << "<td>" << it->GetWindowSize () << "</td>";
  416. s << "<td>" << (int)it->GetStatus () << "</td>";
  417. s << "</tr>\r\n";
  418. }
  419. s << "</table>";
  420. }
  421. }
  422. static void ShowI2CPLocalDestination (std::stringstream& s, const std::string& id)
  423. {
  424. auto i2cpServer = i2p::client::context.GetI2CPServer ();
  425. if (i2cpServer)
  426. {
  427. s << "<b>I2CP Local Destination:</b><br>\r\n<br>\r\n";
  428. auto it = i2cpServer->GetSessions ().find (std::stoi (id));
  429. if (it != i2cpServer->GetSessions ().end ())
  430. ShowLeaseSetDestination (s, it->second->GetDestination ());
  431. else
  432. ShowError(s, "I2CP session not found");
  433. }
  434. else
  435. ShowError(s, "I2CP is not enabled");
  436. }
  437. void ShowLeasesSets(std::stringstream& s)
  438. {
  439. s << "<b>LeaseSets:</b><br>\r\n<br>\r\n";
  440. int counter = 1;
  441. // for each lease set
  442. i2p::data::netdb.VisitLeaseSets(
  443. [&s, &counter](const i2p::data::IdentHash dest, std::shared_ptr<i2p::data::LeaseSet> leaseSet)
  444. {
  445. // create copy of lease set so we extract leases
  446. auto storeType = leaseSet->GetStoreType ();
  447. std::unique_ptr<i2p::data::LeaseSet> ls;
  448. if (storeType == i2p::data::NETDB_STORE_TYPE_LEASESET)
  449. ls.reset (new i2p::data::LeaseSet (leaseSet->GetBuffer(), leaseSet->GetBufferLen()));
  450. else
  451. ls.reset (new i2p::data::LeaseSet2 (storeType, leaseSet->GetBuffer(), leaseSet->GetBufferLen()));
  452. if (!ls) return;
  453. s << "<div class='leaseset";
  454. if (ls->IsExpired())
  455. s << " expired"; // additional css class for expired
  456. s << "'>\r\n";
  457. if (!ls->IsValid())
  458. s << "<div class='invalid'>!! Invalid !! </div>\r\n";
  459. s << "<div class='slide'><label for='slide" << counter << "'>" << dest.ToBase32() << "</label>\r\n";
  460. s << "<input type='checkbox' id='slide" << (counter++) << "'/>\r\n<p class='content'>\r\n";
  461. s << "<b>Store type:</b> " << (int)storeType << "<br>\r\n";
  462. s << "<b>Expires:</b> " << ConvertTime(ls->GetExpirationTime()) << "<br>\r\n";
  463. if (storeType == i2p::data::NETDB_STORE_TYPE_LEASESET || storeType == i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2)
  464. {
  465. // leases information is available
  466. auto leases = ls->GetNonExpiredLeases();
  467. s << "<b>Non Expired Leases: " << leases.size() << "</b><br>\r\n";
  468. for ( auto & l : leases )
  469. {
  470. s << "<b>Gateway:</b> " << l->tunnelGateway.ToBase64() << "<br>\r\n";
  471. s << "<b>TunnelID:</b> " << l->tunnelID << "<br>\r\n";
  472. s << "<b>EndDate:</b> " << ConvertTime(l->endDate) << "<br>\r\n";
  473. }
  474. }
  475. s << "</p>\r\n</div>\r\n</div>\r\n";
  476. }
  477. );
  478. // end for each lease set
  479. }
  480. void ShowTunnels (std::stringstream& s)
  481. {
  482. s << "<b>Tunnels:</b><br>\r\n<br>\r\n";
  483. s << "<b>Queue size:</b> " << i2p::tunnel::tunnels.GetQueueSize () << "<br>\r\n";
  484. auto ExplPool = i2p::tunnel::tunnels.GetExploratoryPool ();
  485. s << "<b>Inbound tunnels:</b><br>\r\n";
  486. for (auto & it : i2p::tunnel::tunnels.GetInboundTunnels ()) {
  487. it->Print(s);
  488. if(it->LatencyIsKnown())
  489. s << " ( " << it->GetMeanLatency() << "ms )";
  490. ShowTunnelDetails(s, it->GetState (), (it->GetTunnelPool () == ExplPool), it->GetNumReceivedBytes ());
  491. }
  492. s << "<br>\r\n";
  493. s << "<b>Outbound tunnels:</b><br>\r\n";
  494. for (auto & it : i2p::tunnel::tunnels.GetOutboundTunnels ()) {
  495. it->Print(s);
  496. if(it->LatencyIsKnown())
  497. s << " ( " << it->GetMeanLatency() << "ms )";
  498. ShowTunnelDetails(s, it->GetState (), (it->GetTunnelPool () == ExplPool), it->GetNumSentBytes ());
  499. }
  500. s << "<br>\r\n";
  501. }
  502. static void ShowCommands (std::stringstream& s, uint32_t token)
  503. {
  504. std::string webroot; i2p::config::GetOption("http.webroot", webroot);
  505. /* commands */
  506. s << "<b>Router Commands</b><br>\r\n<br>\r\n";
  507. s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_RUN_PEER_TEST << "&token=" << token << "\">Run peer test</a><br>\r\n";
  508. //s << " <a href=\"/?cmd=" << HTTP_COMMAND_RELOAD_CONFIG << "\">Reload config</a><br>\r\n";
  509. if (i2p::context.AcceptsTunnels ())
  510. s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_DISABLE_TRANSIT << "&token=" << token << "\">Decline transit tunnels</a><br>\r\n";
  511. else
  512. s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_ENABLE_TRANSIT << "&token=" << token << "\">Accept transit tunnels</a><br>\r\n";
  513. #if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY))
  514. if (Daemon.gracefulShutdownInterval)
  515. s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_CANCEL << "&token=" << token << "\">Cancel graceful shutdown</a><br>";
  516. else
  517. s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_START << "&token=" << token << "\">Start graceful shutdown</a><br>\r\n";
  518. #elif defined(WIN32_APP)
  519. if (i2p::util::DaemonWin32::Instance().isGraceful)
  520. s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_CANCEL << "&token=" << token << "\">Cancel graceful shutdown</a><br>";
  521. else
  522. s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_START << "&token=" << token << "\">Graceful shutdown</a><br>\r\n";
  523. #endif
  524. s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_NOW << "&token=" << token << "\">Force shutdown</a><br>\r\n";
  525. s << "<br>\r\n<b>Logging level</b><br>\r\n";
  526. s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_LOGLEVEL << "&level=none&token=" << token << "\">[none]</a> ";
  527. s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_LOGLEVEL << "&level=error&token=" << token << "\">[error]</a> ";
  528. s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_LOGLEVEL << "&level=warn&token=" << token << "\">[warn]</a> ";
  529. s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_LOGLEVEL << "&level=info&token=" << token << "\">[info]</a> ";
  530. s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_LOGLEVEL << "&level=debug&token=" << token << "\">[debug]</a><br>\r\n";
  531. }
  532. void ShowTransitTunnels (std::stringstream& s)
  533. {
  534. s << "<b>Transit tunnels:</b><br>\r\n<br>\r\n";
  535. for (const auto& it: i2p::tunnel::tunnels.GetTransitTunnels ())
  536. {
  537. if (std::dynamic_pointer_cast<i2p::tunnel::TransitTunnelGateway>(it))
  538. s << it->GetTunnelID () << " &#8658; ";
  539. else if (std::dynamic_pointer_cast<i2p::tunnel::TransitTunnelEndpoint>(it))
  540. s << " &#8658; " << it->GetTunnelID ();
  541. else
  542. s << " &#8658; " << it->GetTunnelID () << " &#8658; ";
  543. s << " " << it->GetNumTransmittedBytes () << "<br>\r\n";
  544. }
  545. }
  546. template<typename Sessions>
  547. static void ShowNTCPTransports (std::stringstream& s, const Sessions& sessions, const std::string name)
  548. {
  549. std::stringstream tmp_s, tmp_s6; uint16_t cnt = 0, cnt6 = 0;
  550. for (const auto& it: sessions )
  551. {
  552. if (it.second && it.second->IsEstablished () && !it.second->GetSocket ().remote_endpoint ().address ().is_v6 ())
  553. {
  554. // incoming connection doesn't have remote RI
  555. if (it.second->IsOutgoing ()) tmp_s << " &#8658; ";
  556. tmp_s << i2p::data::GetIdentHashAbbreviation (it.second->GetRemoteIdentity ()->GetIdentHash ()) << ": "
  557. << it.second->GetSocket ().remote_endpoint().address ().to_string ();
  558. if (!it.second->IsOutgoing ()) tmp_s << " &#8658; ";
  559. tmp_s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
  560. tmp_s << "<br>\r\n" << std::endl;
  561. cnt++;
  562. }
  563. if (it.second && it.second->IsEstablished () && it.second->GetSocket ().remote_endpoint ().address ().is_v6 ())
  564. {
  565. if (it.second->IsOutgoing ()) tmp_s6 << " &#8658; ";
  566. tmp_s6 << i2p::data::GetIdentHashAbbreviation (it.second->GetRemoteIdentity ()->GetIdentHash ()) << ": "
  567. << "[" << it.second->GetSocket ().remote_endpoint().address ().to_string () << "]";
  568. if (!it.second->IsOutgoing ()) tmp_s6 << " &#8658; ";
  569. tmp_s6 << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
  570. tmp_s6 << "<br>\r\n" << std::endl;
  571. cnt6++;
  572. }
  573. }
  574. if (!tmp_s.str ().empty ())
  575. {
  576. s << "<div class='slide'><label for='slide_" << boost::algorithm::to_lower_copy(name) << "'><b>" << name << "</b> ( " << cnt << " )</label>\r\n<input type='checkbox' id='slide_" << boost::algorithm::to_lower_copy(name) << "'/>\r\n<p class='content'>";
  577. s << tmp_s.str () << "</p>\r\n</div>\r\n";
  578. }
  579. if (!tmp_s6.str ().empty ())
  580. {
  581. s << "<div class='slide'><label for='slide_" << boost::algorithm::to_lower_copy(name) << "v6'><b>" << name << "v6</b> ( " << cnt6 << " )</label>\r\n<input type='checkbox' id='slide_" << boost::algorithm::to_lower_copy(name) << "v6'/>\r\n<p class='content'>";
  582. s << tmp_s6.str () << "</p>\r\n</div>\r\n";
  583. }
  584. }
  585. void ShowTransports (std::stringstream& s)
  586. {
  587. s << "<b>Transports:</b><br>\r\n<br>\r\n";
  588. auto ntcpServer = i2p::transport::transports.GetNTCPServer ();
  589. if (ntcpServer)
  590. {
  591. auto sessions = ntcpServer->GetNTCPSessions ();
  592. if (!sessions.empty ())
  593. ShowNTCPTransports (s, sessions, "NTCP");
  594. }
  595. auto ntcp2Server = i2p::transport::transports.GetNTCP2Server ();
  596. if (ntcp2Server)
  597. {
  598. auto sessions = ntcp2Server->GetNTCP2Sessions ();
  599. if (!sessions.empty ())
  600. ShowNTCPTransports (s, sessions, "NTCP2");
  601. }
  602. auto ssuServer = i2p::transport::transports.GetSSUServer ();
  603. if (ssuServer)
  604. {
  605. auto sessions = ssuServer->GetSessions ();
  606. if (!sessions.empty ())
  607. {
  608. 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'>";
  609. for (const auto& it: sessions)
  610. {
  611. auto endpoint = it.second->GetRemoteEndpoint ();
  612. if (it.second->IsOutgoing ()) s << " &#8658; ";
  613. s << endpoint.address ().to_string () << ":" << endpoint.port ();
  614. if (!it.second->IsOutgoing ()) s << " &#8658; ";
  615. s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
  616. if (it.second->GetRelayTag ())
  617. s << " [itag:" << it.second->GetRelayTag () << "]";
  618. s << "<br>\r\n" << std::endl;
  619. }
  620. s << "</p>\r\n</div>\r\n";
  621. }
  622. auto sessions6 = ssuServer->GetSessionsV6 ();
  623. if (!sessions6.empty ())
  624. {
  625. s << "<div class='slide'><label for='slide_ssuv6'><b>SSUv6</b> ( " << (int) sessions6.size() << " )</label>\r\n<input type='checkbox' id='slide_ssuv6'/>\r\n<p class='content'>";
  626. for (const auto& it: sessions6)
  627. {
  628. auto endpoint = it.second->GetRemoteEndpoint ();
  629. if (it.second->IsOutgoing ()) s << " &#8658; ";
  630. s << "[" << endpoint.address ().to_string () << "]:" << endpoint.port ();
  631. if (!it.second->IsOutgoing ()) s << " &#8658; ";
  632. s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
  633. if (it.second->GetRelayTag ())
  634. s << " [itag:" << it.second->GetRelayTag () << "]";
  635. s << "<br>\r\n" << std::endl;
  636. }
  637. s << "</p>\r\n</div>\r\n";
  638. }
  639. }
  640. }
  641. void ShowSAMSessions (std::stringstream& s)
  642. {
  643. std::string webroot; i2p::config::GetOption("http.webroot", webroot);
  644. auto sam = i2p::client::context.GetSAMBridge ();
  645. if (!sam) {
  646. ShowError(s, "SAM disabled");
  647. return;
  648. }
  649. s << "<b>SAM Sessions:</b><br>\r\n<br>\r\n";
  650. for (auto& it: sam->GetSessions ())
  651. {
  652. auto& name = it.second->localDestination->GetNickname ();
  653. s << "<a href=\"" << webroot << "?page=" << HTTP_PAGE_SAM_SESSION << "&sam_id=" << it.first << "\">";
  654. s << name << " (" << it.first << ")</a><br>\r\n" << std::endl;
  655. }
  656. }
  657. static void ShowSAMSession (std::stringstream& s, const std::string& id)
  658. {
  659. std::string webroot; i2p::config::GetOption("http.webroot", webroot);
  660. s << "<b>SAM Session:</b><br>\r\n<br>\r\n";
  661. auto sam = i2p::client::context.GetSAMBridge ();
  662. if (!sam) {
  663. ShowError(s, "SAM disabled");
  664. return;
  665. }
  666. auto session = sam->FindSession (id);
  667. if (!session) {
  668. ShowError(s, "SAM session not found");
  669. return;
  670. }
  671. auto& ident = session->localDestination->GetIdentHash();
  672. s << "<a href=\"" << webroot << "?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
  673. s << i2p::client::context.GetAddressBook ().ToAddress(ident) << "</a><br>\r\n";
  674. s << "<br>\r\n";
  675. s << "<b>Streams:</b><br>\r\n";
  676. for (const auto& it: sam->ListSockets(id))
  677. {
  678. switch (it->GetSocketType ())
  679. {
  680. case i2p::client::eSAMSocketTypeSession : s << "session"; break;
  681. case i2p::client::eSAMSocketTypeStream : s << "stream"; break;
  682. case i2p::client::eSAMSocketTypeAcceptor : s << "acceptor"; break;
  683. default: s << "unknown"; break;
  684. }
  685. s << " [" << it->GetSocket ().remote_endpoint() << "]";
  686. s << "<br>\r\n";
  687. }
  688. }
  689. void ShowI2PTunnels (std::stringstream& s)
  690. {
  691. std::string webroot; i2p::config::GetOption("http.webroot", webroot);
  692. s << "<b>Client Tunnels:</b><br>\r\n<br>\r\n";
  693. for (auto& it: i2p::client::context.GetClientTunnels ())
  694. {
  695. auto& ident = it.second->GetLocalDestination ()->GetIdentHash();
  696. s << "<a href=\"" << webroot << "?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
  697. s << it.second->GetName () << "</a> &#8656; ";
  698. s << i2p::client::context.GetAddressBook ().ToAddress(ident);
  699. s << "<br>\r\n"<< std::endl;
  700. }
  701. auto httpProxy = i2p::client::context.GetHttpProxy ();
  702. if (httpProxy)
  703. {
  704. auto& ident = httpProxy->GetLocalDestination ()->GetIdentHash();
  705. s << "<a href=\"" << webroot << "?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
  706. s << "HTTP Proxy" << "</a> &#8656; ";
  707. s << i2p::client::context.GetAddressBook ().ToAddress(ident);
  708. s << "<br>\r\n"<< std::endl;
  709. }
  710. auto socksProxy = i2p::client::context.GetSocksProxy ();
  711. if (socksProxy)
  712. {
  713. auto& ident = socksProxy->GetLocalDestination ()->GetIdentHash();
  714. s << "<a href=\"" << webroot << "?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
  715. s << "SOCKS Proxy" << "</a> &#8656; ";
  716. s << i2p::client::context.GetAddressBook ().ToAddress(ident);
  717. s << "<br>\r\n"<< std::endl;
  718. }
  719. auto& serverTunnels = i2p::client::context.GetServerTunnels ();
  720. if (!serverTunnels.empty ()) {
  721. s << "<br>\r\n<b>Server Tunnels:</b><br>\r\n<br>\r\n";
  722. for (auto& it: serverTunnels)
  723. {
  724. auto& ident = it.second->GetLocalDestination ()->GetIdentHash();
  725. s << "<a href=\"" << webroot << "?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
  726. s << it.second->GetName () << "</a> &#8658; ";
  727. s << i2p::client::context.GetAddressBook ().ToAddress(ident);
  728. s << ":" << it.second->GetLocalPort ();
  729. s << "</a><br>\r\n"<< std::endl;
  730. }
  731. }
  732. auto& clientForwards = i2p::client::context.GetClientForwards ();
  733. if (!clientForwards.empty ())
  734. {
  735. s << "<br>\r\n<b>Client Forwards:</b><br>\r\n<br>\r\n";
  736. for (auto& it: clientForwards)
  737. {
  738. auto& ident = it.second->GetLocalDestination ()->GetIdentHash();
  739. s << "<a href=\"" << webroot << "?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
  740. s << it.second->GetName () << "</a> &#8656; ";
  741. s << i2p::client::context.GetAddressBook ().ToAddress(ident);
  742. s << "<br>\r\n"<< std::endl;
  743. }
  744. }
  745. auto& serverForwards = i2p::client::context.GetServerForwards ();
  746. if (!serverForwards.empty ())
  747. {
  748. s << "<br>\r\n<b>Server Forwards:</b><br>\r\n<br>\r\n";
  749. for (auto& it: serverForwards)
  750. {
  751. auto& ident = it.second->GetLocalDestination ()->GetIdentHash();
  752. s << "<a href=\"" << webroot << "?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
  753. s << it.second->GetName () << "</a> &#8656; ";
  754. s << i2p::client::context.GetAddressBook ().ToAddress(ident);
  755. s << "<br>\r\n"<< std::endl;
  756. }
  757. }
  758. }
  759. std::string ConvertTime (uint64_t time)
  760. {
  761. ldiv_t divTime = ldiv(time,1000);
  762. time_t t = divTime.quot;
  763. struct tm *tm = localtime(&t);
  764. char date[128];
  765. snprintf(date, sizeof(date), "%02d/%02d/%d %02d:%02d:%02d.%03ld", tm->tm_mday, tm->tm_mon + 1, tm->tm_year + 1900, tm->tm_hour, tm->tm_min, tm->tm_sec, divTime.rem);
  766. return date;
  767. }
  768. HTTPConnection::HTTPConnection (std::string hostname, std::shared_ptr<boost::asio::ip::tcp::socket> socket):
  769. m_Socket (socket), m_BufferLen (0), expected_host(hostname)
  770. {
  771. /* cache options */
  772. i2p::config::GetOption("http.auth", needAuth);
  773. i2p::config::GetOption("http.user", user);
  774. i2p::config::GetOption("http.pass", pass);
  775. }
  776. void HTTPConnection::Receive ()
  777. {
  778. m_Socket->async_read_some (boost::asio::buffer (m_Buffer, HTTP_CONNECTION_BUFFER_SIZE),
  779. std::bind(&HTTPConnection::HandleReceive, shared_from_this (),
  780. std::placeholders::_1, std::placeholders::_2));
  781. }
  782. void HTTPConnection::HandleReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred)
  783. {
  784. if (ecode) {
  785. if (ecode != boost::asio::error::operation_aborted)
  786. Terminate (ecode);
  787. return;
  788. }
  789. m_Buffer[bytes_transferred] = '\0';
  790. m_BufferLen = bytes_transferred;
  791. RunRequest();
  792. Receive ();
  793. }
  794. void HTTPConnection::RunRequest ()
  795. {
  796. HTTPReq request;
  797. int ret = request.parse(m_Buffer);
  798. if (ret < 0) {
  799. m_Buffer[0] = '\0';
  800. m_BufferLen = 0;
  801. return; /* error */
  802. }
  803. if (ret == 0)
  804. return; /* need more data */
  805. HandleRequest (request);
  806. }
  807. void HTTPConnection::Terminate (const boost::system::error_code& ecode)
  808. {
  809. if (ecode == boost::asio::error::operation_aborted)
  810. return;
  811. boost::system::error_code ignored_ec;
  812. m_Socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec);
  813. m_Socket->close ();
  814. }
  815. bool HTTPConnection::CheckAuth (const HTTPReq & req) {
  816. /* method #1: http://user:pass@127.0.0.1:7070/ */
  817. if (req.uri.find('@') != std::string::npos) {
  818. URL url;
  819. if (url.parse(req.uri) && url.user == user && url.pass == pass)
  820. return true;
  821. }
  822. /* method #2: 'Authorization' header sent */
  823. auto provided = req.GetHeader ("Authorization");
  824. if (provided.length () > 0)
  825. {
  826. std::string expected = "Basic " + i2p::data::ToBase64Standard (user + ":" + pass);
  827. if (expected == provided) return true;
  828. }
  829. LogPrint(eLogWarning, "HTTPServer: auth failure from ", m_Socket->remote_endpoint().address ());
  830. return false;
  831. }
  832. void HTTPConnection::HandleRequest (const HTTPReq & req)
  833. {
  834. std::stringstream s;
  835. std::string content;
  836. HTTPRes res;
  837. LogPrint(eLogDebug, "HTTPServer: request: ", req.uri);
  838. if (needAuth && !CheckAuth(req)) {
  839. res.code = 401;
  840. res.add_header("WWW-Authenticate", "Basic realm=\"WebAdmin\"");
  841. SendReply(res, content);
  842. return;
  843. }
  844. bool strictheaders;
  845. i2p::config::GetOption("http.strictheaders", strictheaders);
  846. if (strictheaders)
  847. {
  848. std::string http_hostname;
  849. i2p::config::GetOption("http.hostname", http_hostname);
  850. std::string host = req.GetHeader("Host");
  851. auto idx = host.find(':');
  852. /* strip out port so it's just host */
  853. if (idx != std::string::npos && idx > 0)
  854. {
  855. host = host.substr(0, idx);
  856. }
  857. if (!(host == expected_host || host == http_hostname))
  858. {
  859. /* deny request as it's from a non whitelisted hostname */
  860. res.code = 403;
  861. content = "host mismatch";
  862. SendReply(res, content);
  863. return;
  864. }
  865. }
  866. // Html5 head start
  867. ShowPageHead (s);
  868. if (req.uri.find("page=") != std::string::npos) {
  869. HandlePage (req, res, s);
  870. } else if (req.uri.find("cmd=") != std::string::npos) {
  871. HandleCommand (req, res, s);
  872. } else {
  873. ShowStatus (s, true, i2p::http::OutputFormatEnum::forWebConsole);
  874. res.add_header("Refresh", "10");
  875. }
  876. ShowPageTail (s);
  877. res.code = 200;
  878. content = s.str ();
  879. SendReply (res, content);
  880. }
  881. std::map<uint32_t, uint32_t> HTTPConnection::m_Tokens;
  882. void HTTPConnection::HandlePage (const HTTPReq& req, HTTPRes& res, std::stringstream& s)
  883. {
  884. std::map<std::string, std::string> params;
  885. std::string page("");
  886. URL url;
  887. url.parse(req.uri);
  888. url.parse_query(params);
  889. page = params["page"];
  890. if (page == HTTP_PAGE_TRANSPORTS)
  891. ShowTransports (s);
  892. else if (page == HTTP_PAGE_TUNNELS)
  893. ShowTunnels (s);
  894. else if (page == HTTP_PAGE_COMMANDS)
  895. {
  896. uint32_t token;
  897. RAND_bytes ((uint8_t *)&token, 4);
  898. token &= 0x7FFFFFFF; // clear first bit
  899. auto ts = i2p::util::GetSecondsSinceEpoch ();
  900. for (auto it = m_Tokens.begin (); it != m_Tokens.end (); )
  901. {
  902. if (ts > it->second + TOKEN_EXPIRATION_TIMEOUT)
  903. it = m_Tokens.erase (it);
  904. else
  905. ++it;
  906. }
  907. m_Tokens[token] = ts;
  908. ShowCommands (s, token);
  909. }
  910. else if (page == HTTP_PAGE_TRANSIT_TUNNELS)
  911. ShowTransitTunnels (s);
  912. else if (page == HTTP_PAGE_LOCAL_DESTINATIONS)
  913. ShowLocalDestinations (s);
  914. else if (page == HTTP_PAGE_LOCAL_DESTINATION)
  915. ShowLocalDestination (s, params["b32"]);
  916. else if (page == HTTP_PAGE_I2CP_LOCAL_DESTINATION)
  917. ShowI2CPLocalDestination (s, params["i2cp_id"]);
  918. else if (page == HTTP_PAGE_SAM_SESSIONS)
  919. ShowSAMSessions (s);
  920. else if (page == HTTP_PAGE_SAM_SESSION)
  921. ShowSAMSession (s, params["sam_id"]);
  922. else if (page == HTTP_PAGE_I2P_TUNNELS)
  923. ShowI2PTunnels (s);
  924. else if (page == HTTP_PAGE_LEASESETS)
  925. ShowLeasesSets(s);
  926. else {
  927. res.code = 400;
  928. ShowError(s, "Unknown page: " + page);
  929. return;
  930. }
  931. }
  932. void HTTPConnection::HandleCommand (const HTTPReq& req, HTTPRes& res, std::stringstream& s)
  933. {
  934. std::map<std::string, std::string> params;
  935. URL url;
  936. url.parse(req.uri);
  937. url.parse_query(params);
  938. std::string token = params["token"];
  939. if (token.empty () || m_Tokens.find (std::stoi (token)) == m_Tokens.end ())
  940. {
  941. ShowError(s, "Invalid token");
  942. return;
  943. }
  944. std::string cmd = params["cmd"];
  945. if (cmd == HTTP_COMMAND_RUN_PEER_TEST)
  946. i2p::transport::transports.PeerTest ();
  947. else if (cmd == HTTP_COMMAND_RELOAD_CONFIG)
  948. i2p::client::context.ReloadConfig ();
  949. else if (cmd == HTTP_COMMAND_ENABLE_TRANSIT)
  950. i2p::context.SetAcceptsTunnels (true);
  951. else if (cmd == HTTP_COMMAND_DISABLE_TRANSIT)
  952. i2p::context.SetAcceptsTunnels (false);
  953. else if (cmd == HTTP_COMMAND_SHUTDOWN_START) {
  954. i2p::context.SetAcceptsTunnels (false);
  955. #if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY))
  956. Daemon.gracefulShutdownInterval = 10*60;
  957. #elif defined(WIN32_APP)
  958. i2p::win32::GracefulShutdown ();
  959. #endif
  960. } else if (cmd == HTTP_COMMAND_SHUTDOWN_CANCEL) {
  961. i2p::context.SetAcceptsTunnels (true);
  962. #if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY))
  963. Daemon.gracefulShutdownInterval = 0;
  964. #elif defined(WIN32_APP)
  965. i2p::win32::StopGracefulShutdown ();
  966. #endif
  967. } else if (cmd == HTTP_COMMAND_SHUTDOWN_NOW) {
  968. #ifndef WIN32_APP
  969. Daemon.running = false;
  970. #else
  971. i2p::win32::StopWin32App ();
  972. #endif
  973. } else if (cmd == HTTP_COMMAND_LOGLEVEL){
  974. std::string level = params["level"];
  975. SetLogLevel (level);
  976. } else {
  977. res.code = 400;
  978. ShowError(s, "Unknown command: " + cmd);
  979. return;
  980. }
  981. std::string webroot; i2p::config::GetOption("http.webroot", webroot);
  982. std::string redirect = "5; url=" + webroot + "?page=commands";
  983. s << "<b>SUCCESS</b>:&nbsp;Command accepted<br><br>\r\n";
  984. s << "<a href=\"" << webroot << "?page=commands\">Back to commands list</a><br>\r\n";
  985. s << "<p>You will be redirected in 5 seconds</b>";
  986. res.add_header("Refresh", redirect.c_str());
  987. }
  988. void HTTPConnection::SendReply (HTTPRes& reply, std::string& content)
  989. {
  990. reply.add_header("X-Frame-Options", "SAMEORIGIN");
  991. reply.add_header("Content-Type", "text/html");
  992. reply.body = content;
  993. m_SendBuffer = reply.to_string();
  994. boost::asio::async_write (*m_Socket, boost::asio::buffer(m_SendBuffer),
  995. std::bind (&HTTPConnection::Terminate, shared_from_this (), std::placeholders::_1));
  996. }
  997. HTTPServer::HTTPServer (const std::string& address, int port):
  998. m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service),
  999. m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint (boost::asio::ip::address::from_string(address), port)),
  1000. m_Hostname(address)
  1001. {
  1002. }
  1003. HTTPServer::~HTTPServer ()
  1004. {
  1005. Stop ();
  1006. }
  1007. void HTTPServer::Start ()
  1008. {
  1009. bool needAuth; i2p::config::GetOption("http.auth", needAuth);
  1010. std::string user; i2p::config::GetOption("http.user", user);
  1011. std::string pass; i2p::config::GetOption("http.pass", pass);
  1012. /* generate pass if needed */
  1013. if (needAuth && pass == "") {
  1014. uint8_t random[16];
  1015. char alnum[] = "0123456789"
  1016. "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  1017. "abcdefghijklmnopqrstuvwxyz";
  1018. pass.resize(sizeof(random));
  1019. RAND_bytes(random, sizeof(random));
  1020. for (size_t i = 0; i < sizeof(random); i++) {
  1021. pass[i] = alnum[random[i] % (sizeof(alnum) - 1)];
  1022. }
  1023. i2p::config::SetOption("http.pass", pass);
  1024. LogPrint(eLogInfo, "HTTPServer: password set to ", pass);
  1025. }
  1026. m_IsRunning = true;
  1027. m_Thread = std::unique_ptr<std::thread>(new std::thread (std::bind (&HTTPServer::Run, this)));
  1028. m_Acceptor.listen ();
  1029. Accept ();
  1030. }
  1031. void HTTPServer::Stop ()
  1032. {
  1033. m_IsRunning = false;
  1034. m_Acceptor.close();
  1035. m_Service.stop ();
  1036. if (m_Thread)
  1037. {
  1038. m_Thread->join ();
  1039. m_Thread = nullptr;
  1040. }
  1041. }
  1042. void HTTPServer::Run ()
  1043. {
  1044. while (m_IsRunning)
  1045. {
  1046. try
  1047. {
  1048. m_Service.run ();
  1049. }
  1050. catch (std::exception& ex)
  1051. {
  1052. LogPrint (eLogError, "HTTPServer: runtime exception: ", ex.what ());
  1053. }
  1054. }
  1055. }
  1056. void HTTPServer::Accept ()
  1057. {
  1058. auto newSocket = std::make_shared<boost::asio::ip::tcp::socket> (m_Service);
  1059. m_Acceptor.async_accept (*newSocket, boost::bind (&HTTPServer::HandleAccept, this,
  1060. boost::asio::placeholders::error, newSocket));
  1061. }
  1062. void HTTPServer::HandleAccept(const boost::system::error_code& ecode,
  1063. std::shared_ptr<boost::asio::ip::tcp::socket> newSocket)
  1064. {
  1065. if (ecode)
  1066. {
  1067. if(newSocket) newSocket->close();
  1068. LogPrint(eLogError, "HTTP Server: error handling accept ", ecode.message());
  1069. if(ecode != boost::asio::error::operation_aborted)
  1070. Accept();
  1071. return;
  1072. }
  1073. CreateConnection(newSocket);
  1074. Accept ();
  1075. }
  1076. void HTTPServer::CreateConnection(std::shared_ptr<boost::asio::ip::tcp::socket> newSocket)
  1077. {
  1078. auto conn = std::make_shared<HTTPConnection> (m_Hostname, newSocket);
  1079. conn->Receive ();
  1080. }
  1081. } // http
  1082. } // i2p