mirror of
https://github.com/PurpleI2P/i2pd
synced 2024-11-10 08:00:38 +03:00
Merge pull request #271 from EinMByte/master
Benchmarking, webui and windows build
This commit is contained in:
commit
af66f335c9
2
.gitignore
vendored
2
.gitignore
vendored
@ -13,8 +13,10 @@ build/CMakeFiles/*
|
||||
build/tests
|
||||
build/client
|
||||
build/core
|
||||
build/benchmark
|
||||
build/i2pd
|
||||
build/i2pd-tests
|
||||
build/i2pd-benchmark
|
||||
*.cmake
|
||||
*.a
|
||||
*.o
|
||||
|
@ -9,6 +9,9 @@ option(WITH_BINARY "Build binary" ON)
|
||||
option(WITH_STATIC "Static build" OFF)
|
||||
option(WITH_UPNP "Include support for UPnP client" OFF)
|
||||
option(WITH_TESTS "Build unit tests" OFF)
|
||||
option(WITH_BENCHMARK "Build benchmarking code" OFF)
|
||||
option(WITH_OPTIMIZE "Optimization flags" OFF)
|
||||
option(I2PD_DATA_PATH "The path to the i2pd data folder")
|
||||
|
||||
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/build/cmake_modules")
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
@ -80,6 +83,9 @@ if(WITH_AESNI)
|
||||
add_definitions( "-maes -DAESNI")
|
||||
endif()
|
||||
|
||||
if(WITH_OPTIMIZE AND (NOT MSVC))
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3")
|
||||
endif()
|
||||
|
||||
# Libraries
|
||||
# TODO: once CMake 3.1+ becomes mainstream, see e.g.
|
||||
@ -93,17 +99,8 @@ endif()
|
||||
|
||||
if(WITH_STATIC)
|
||||
set(Boost_USE_STATIC_LIBS ON)
|
||||
set(Boost_USE_STATIC_RUNTIME ON)
|
||||
if(WIN32)
|
||||
# http://www.cmake.org/Wiki/CMake_FAQ#Dynamic_Replace
|
||||
# Note that you might need to rebuild Crypto++
|
||||
foreach(flag_var CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
|
||||
CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
|
||||
if(${flag_var} MATCHES "/MD")
|
||||
string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}")
|
||||
endif(${flag_var} MATCHES "/MD")
|
||||
endforeach(flag_var)
|
||||
else()
|
||||
set(Boost_USE_STATIC_RUNTIME OFF)
|
||||
if(NOT WIN32)
|
||||
set(CMAKE_FIND_LIBRARY_SUFFIXES .a)
|
||||
endif()
|
||||
|
||||
@ -120,10 +117,10 @@ elseif(NOT WIN32)
|
||||
# No need in -fPIC overhead for binary if not interested in library
|
||||
# HINT: revert c266cff CMakeLists.txt: compilation speed up
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
|
||||
else() # Not a static build
|
||||
add_definitions(-DBOOST_ALL_DYN_LINK)
|
||||
endif()
|
||||
|
||||
add_definitions(-DBOOST_ALL_DYN_LINK)
|
||||
|
||||
find_package(
|
||||
Boost COMPONENTS
|
||||
system filesystem regex program_options date_time thread chrono REQUIRED
|
||||
@ -148,6 +145,18 @@ include_directories(
|
||||
"core/"
|
||||
)
|
||||
|
||||
if(I2PD_DATA_PATH)
|
||||
set(I2PD_DATA_DIR ${I2PD_DATA_PATH})
|
||||
# Using custom path, make sure the code knows about this
|
||||
add_definitions(-DI2PD_CUSTOM_DATA_PATH="${I2PD_DATA_PATH}")
|
||||
elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows")
|
||||
set(I2PD_DATA_DIR "$ENV{APPDATA}\\i2pd")
|
||||
elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
|
||||
set(I2PD_DATA_DIR "$ENV{HOME}/Library/Application Support/i2pd")
|
||||
else()
|
||||
set(I2PD_DATA_DIR "$ENV{HOME}/.i2pd")
|
||||
endif()
|
||||
|
||||
# Show summary
|
||||
message(STATUS "---------------------------------------")
|
||||
message(STATUS "Build type : ${CMAKE_BUILD_TYPE}")
|
||||
@ -155,6 +164,7 @@ message(STATUS "Compiler vendor : ${CMAKE_CXX_COMPILER_ID}")
|
||||
message(STATUS "Compiler version : ${CMAKE_CXX_COMPILER_VERSION}")
|
||||
message(STATUS "Compiler path : ${CMAKE_CXX_COMPILER}")
|
||||
message(STATUS "Install prefix: : ${CMAKE_INSTALL_PREFIX}")
|
||||
message(STATUS "I2PD data directory: ${I2PD_DATA_DIR}")
|
||||
message(STATUS "Options:")
|
||||
message(STATUS " AESNI : ${WITH_AESNI}")
|
||||
message(STATUS " HARDENING : ${WITH_HARDENING}")
|
||||
@ -163,6 +173,8 @@ message(STATUS " BINARY : ${WITH_BINARY}")
|
||||
message(STATUS " STATIC BUILD : ${WITH_STATIC}")
|
||||
message(STATUS " UPnP : ${WITH_UPNP}")
|
||||
message(STATUS " TESTS : ${WITH_TESTS}")
|
||||
message(STATUS " BENCHMARKING : ${WITH_BENCHMARK}")
|
||||
message(STATUS " OPTIMIZATION : ${WITH_OPTIMIZE}")
|
||||
message(STATUS "---------------------------------------")
|
||||
|
||||
# Handle paths nicely
|
||||
@ -171,6 +183,13 @@ include(GNUInstallDirs)
|
||||
set(CORE_NAME "${PROJECT_NAME}-core")
|
||||
set(CLIENT_NAME "${PROJECT_NAME}-client")
|
||||
set(TESTS_NAME "${PROJECT_NAME}-tests")
|
||||
set(BENCHMARK_NAME "${PROJECT_NAME}-benchmark")
|
||||
add_subdirectory(core)
|
||||
add_subdirectory(client)
|
||||
add_subdirectory(tests)
|
||||
add_subdirectory(benchmark)
|
||||
|
||||
if(WITH_BINARY)
|
||||
file(MAKE_DIRECTORY "${I2PD_DATA_DIR}/webui")
|
||||
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/webui" DESTINATION "${I2PD_DATA_DIR}")
|
||||
endif()
|
||||
|
12
README.md
12
README.md
@ -15,13 +15,7 @@ http://download.i2p.io/purplei2p/i2pd/releases/
|
||||
Build Statuses
|
||||
---------------
|
||||
|
||||
- Linux x64 - Maintaince
|
||||
- Linux ARM - Maintaince
|
||||
- Mac OS X - Maintaince
|
||||
- Linux x64 - Maintenance
|
||||
- Linux ARM - Maintenance
|
||||
- Mac OS X - Maintenance
|
||||
- Microsoft VC13 - To be added
|
||||
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
This project is licensed under the BSD 3-clause license, which can be found in the doc directory.
|
||||
|
14
benchmark/CMakeLists.txt
Normal file
14
benchmark/CMakeLists.txt
Normal file
@ -0,0 +1,14 @@
|
||||
set(BENCHMARK_SRC
|
||||
"main.cpp"
|
||||
)
|
||||
|
||||
if(WITH_BENCHMARK)
|
||||
add_executable(${BENCHMARK_NAME} ${BENCHMARK_SRC})
|
||||
target_link_libraries(
|
||||
${BENCHMARK_NAME} ${CORE_NAME} ${DL_LIB} ${Boost_LIBRARIES} ${CRYPTO++_LIBRARIES}
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
)
|
||||
install(TARGETS
|
||||
${BENCHMARK_NAME} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
)
|
||||
endif()
|
83
benchmark/main.cpp
Normal file
83
benchmark/main.cpp
Normal file
@ -0,0 +1,83 @@
|
||||
#include "crypto/Signature.h"
|
||||
#include <cryptopp/osrng.h>
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
|
||||
typedef std::function<void(CryptoPP::RandomNumberGenerator&, uint8_t*, uint8_t*)> KeyGenerator;
|
||||
|
||||
template<class Verifier, class Signer>
|
||||
void benchmark(std::size_t count, std::size_t public_key_size, std::size_t private_key_size,
|
||||
std::size_t signature_size, KeyGenerator generator)
|
||||
{
|
||||
typedef std::chrono::time_point<std::chrono::high_resolution_clock> TimePoint;
|
||||
CryptoPP::AutoSeededRandomPool rng;
|
||||
|
||||
uint8_t private_key[private_key_size] = {};
|
||||
uint8_t public_key[public_key_size] = {};
|
||||
|
||||
generator(rng, private_key, public_key);
|
||||
|
||||
Verifier verifier(public_key);
|
||||
Signer signer(private_key);
|
||||
|
||||
uint8_t message[512] = {};
|
||||
uint8_t output[signature_size] = {};
|
||||
|
||||
std::chrono::nanoseconds sign_duration(0);
|
||||
std::chrono::nanoseconds verify_duration(0);
|
||||
|
||||
for(std::size_t i = 0; i < count; ++i) {
|
||||
rng.GenerateBlock(message, 512);
|
||||
TimePoint begin1 = std::chrono::high_resolution_clock::now();
|
||||
signer.Sign(rng, message, 512, output);
|
||||
TimePoint end1 = std::chrono::high_resolution_clock::now();
|
||||
|
||||
sign_duration += std::chrono::duration_cast<std::chrono::nanoseconds>(end1 - begin1);
|
||||
|
||||
TimePoint begin2 = std::chrono::high_resolution_clock::now();
|
||||
verifier.Verify(message, 512, output);
|
||||
TimePoint end2 = std::chrono::high_resolution_clock::now();
|
||||
|
||||
verify_duration += std::chrono::duration_cast<std::chrono::nanoseconds>(end2 - begin2);
|
||||
}
|
||||
std::cout << "Conducted " << count << " experiments." << std::endl;
|
||||
std::cout << "Total sign time: " << std::chrono::duration_cast<std::chrono::milliseconds>(sign_duration).count() << std::endl;
|
||||
std::cout << "Total verify time: " << std::chrono::duration_cast<std::chrono::milliseconds>(verify_duration).count() << std::endl;
|
||||
}
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
using namespace i2p::crypto;
|
||||
std::cout << "--------DSA---------" << std::endl;
|
||||
benchmark<DSAVerifier, DSASigner>(
|
||||
1000, DSA_PUBLIC_KEY_LENGTH,
|
||||
DSA_PRIVATE_KEY_LENGTH, DSA_SIGNATURE_LENGTH,
|
||||
&CreateDSARandomKeys
|
||||
);
|
||||
std::cout << "-----ECDSAP256------" << std::endl;
|
||||
benchmark<ECDSAP256Verifier, ECDSAP256Signer>(
|
||||
1000, ECDSAP256_KEY_LENGTH,
|
||||
ECDSAP256_KEY_LENGTH, 64,
|
||||
&CreateECDSAP256RandomKeys
|
||||
);
|
||||
std::cout << "-----ECDSAP384------" << std::endl;
|
||||
benchmark<ECDSAP384Verifier, ECDSAP384Signer>(
|
||||
1000, ECDSAP384_KEY_LENGTH,
|
||||
ECDSAP384_KEY_LENGTH, 64,
|
||||
&CreateECDSAP384RandomKeys
|
||||
);
|
||||
std::cout << "-----ECDSAP521------" << std::endl;
|
||||
benchmark<ECDSAP521Verifier, ECDSAP521Signer>(
|
||||
1000, ECDSAP521_KEY_LENGTH,
|
||||
ECDSAP521_KEY_LENGTH, 64,
|
||||
&CreateECDSAP521RandomKeys
|
||||
);
|
||||
std::cout << "-----EDDSA25519-----" << std::endl;
|
||||
benchmark<EDDSA25519Verifier, EDDSA25519Signer>(
|
||||
1000, EDDSA25519_PUBLIC_KEY_LENGTH,
|
||||
EDDSA25519_PRIVATE_KEY_LENGTH, 64,
|
||||
&CreateEDDSARandomKeys
|
||||
);
|
||||
}
|
@ -114,10 +114,12 @@ namespace client
|
||||
// I2P Control
|
||||
int i2pcontrolPort = i2p::util::config::GetArg("-i2pcontrolport", 0);
|
||||
if(i2pcontrolPort) {
|
||||
m_I2PControlService = new I2PControlService(
|
||||
m_I2PControlService = new i2pcontrol::I2PControlService(
|
||||
i2p::util::config::GetArg("-i2pcontroladdress", "127.0.0.1"),
|
||||
i2pcontrolPort,
|
||||
i2p::util::config::GetArg("-i2pcontrolpassword", I2P_CONTROL_DEFAULT_PASSWORD)
|
||||
i2p::util::config::GetArg(
|
||||
"-i2pcontrolpassword", i2pcontrol::constants::DEFAULT_PASSWORD
|
||||
)
|
||||
);
|
||||
m_I2PControlService->Start();
|
||||
LogPrint("I2PControl started");
|
||||
|
@ -72,7 +72,7 @@ namespace client
|
||||
std::map<i2p::data::IdentHash, std::unique_ptr<I2PServerTunnel> > m_ServerTunnels; // destination->tunnel
|
||||
SAMBridge * m_SamBridge;
|
||||
BOBCommandChannel * m_BOBCommandChannel;
|
||||
I2PControlService * m_I2PControlService;
|
||||
i2pcontrol::I2PControlService * m_I2PControlService;
|
||||
|
||||
public:
|
||||
// for HTTP
|
||||
|
@ -56,7 +56,19 @@ namespace i2p
|
||||
LogPrint("\n\n\n\ni2pd starting\n");
|
||||
LogPrint("Version ", VERSION);
|
||||
LogPrint("data directory: ", i2p::util::filesystem::GetDataDir().string());
|
||||
i2p::util::filesystem::ReadConfigFile(i2p::util::config::mapArgs, i2p::util::config::mapMultiArgs);
|
||||
i2p::util::filesystem::ReadConfigFile(
|
||||
i2p::util::config::mapArgs, i2p::util::config::mapMultiArgs
|
||||
);
|
||||
|
||||
if(i2p::util::config::HasArg("-install")) {
|
||||
try {
|
||||
i2p::util::filesystem::InstallFiles();
|
||||
LogPrint("Succesfully installed all files.");
|
||||
} catch(const std::runtime_error& e) {
|
||||
LogPrint(eLogError, "Failed to install: ", e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
isDaemon = i2p::util::config::GetArg("-daemon", 0);
|
||||
isLogging = i2p::util::config::GetArg("-log", 1);
|
||||
@ -104,7 +116,6 @@ namespace i2p
|
||||
else
|
||||
StartLog (""); // write to stdout
|
||||
}
|
||||
|
||||
d.httpServer = new i2p::util::HTTPServer(
|
||||
i2p::util::config::GetArg("-httpaddress", "127.0.0.1"),
|
||||
i2p::util::config::GetArg("-httpport", 7070)
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include "./Win32/Win32Service.h"
|
||||
#include "Win32Service.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -6,119 +6,80 @@
|
||||
#include <memory>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/array.hpp>
|
||||
#include "i2pcontrol/I2PControl.h"
|
||||
#include "util/HTTP.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
const size_t HTTP_CONNECTION_BUFFER_SIZE = 8192;
|
||||
const int HTTP_DESTINATION_REQUEST_TIMEOUT = 10; // in seconds
|
||||
class HTTPConnection: public std::enable_shared_from_this<HTTPConnection>
|
||||
{
|
||||
protected:
|
||||
namespace i2p {
|
||||
namespace util {
|
||||
|
||||
struct header
|
||||
{
|
||||
std::string name;
|
||||
std::string value;
|
||||
};
|
||||
const size_t HTTP_CONNECTION_BUFFER_SIZE = 8192;
|
||||
const int HTTP_DESTINATION_REQUEST_TIMEOUT = 10; // in seconds
|
||||
|
||||
struct request
|
||||
{
|
||||
std::string method;
|
||||
std::string uri;
|
||||
std::string host;
|
||||
int port;
|
||||
int http_version_major;
|
||||
int http_version_minor;
|
||||
std::vector<header> headers;
|
||||
};
|
||||
class HTTPConnection: public std::enable_shared_from_this<HTTPConnection> {
|
||||
public:
|
||||
|
||||
struct reply
|
||||
{
|
||||
std::vector<header> headers;
|
||||
std::string content;
|
||||
HTTPConnection(boost::asio::ip::tcp::socket* socket,
|
||||
std::shared_ptr<i2p::client::i2pcontrol::I2PControlSession> session);
|
||||
|
||||
std::vector<boost::asio::const_buffer> to_buffers (int status);
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
HTTPConnection (boost::asio::ip::tcp::socket * socket):
|
||||
m_Socket (socket), m_Timer (socket->get_io_service ()),
|
||||
m_BufferLen (0) {};
|
||||
~HTTPConnection() { delete m_Socket; }
|
||||
void Receive ();
|
||||
|
||||
private:
|
||||
|
||||
void Terminate ();
|
||||
void HandleReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||
void HandleWriteReply(const boost::system::error_code& ecode);
|
||||
void SendReply (const std::string& content, int status = 200);
|
||||
|
||||
void HandleRequest (const std::string& address);
|
||||
void HandleCommand (const std::string& command, std::stringstream& s);
|
||||
void ShowTransports (std::stringstream& s);
|
||||
void ShowTunnels (std::stringstream& s);
|
||||
void ShowTransitTunnels (std::stringstream& s);
|
||||
void ShowLocalDestinations (std::stringstream& s);
|
||||
void ShowLocalDestination (const std::string& b32, std::stringstream& s);
|
||||
void ShowSAMSessions (std::stringstream& s);
|
||||
void ShowSAMSession (const std::string& id, std::stringstream& s);
|
||||
void StartAcceptingTunnels (std::stringstream& s);
|
||||
void StopAcceptingTunnels (std::stringstream& s);
|
||||
void FillContent (std::stringstream& s);
|
||||
std::string ExtractAddress ();
|
||||
void ExtractParams (const std::string& str, std::map<std::string, std::string>& params);
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
boost::asio::ip::tcp::socket * m_Socket;
|
||||
boost::asio::deadline_timer m_Timer;
|
||||
char m_Buffer[HTTP_CONNECTION_BUFFER_SIZE + 1], m_StreamBuffer[HTTP_CONNECTION_BUFFER_SIZE + 1];
|
||||
size_t m_BufferLen;
|
||||
request m_Request;
|
||||
reply m_Reply;
|
||||
|
||||
protected:
|
||||
~HTTPConnection() { delete m_Socket; }
|
||||
void Receive();
|
||||
|
||||
virtual void RunRequest ();
|
||||
private:
|
||||
|
||||
public:
|
||||
void Terminate();
|
||||
void HandleReceive(const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||
void RunRequest();
|
||||
void HandleWriteReply(const boost::system::error_code& ecode);
|
||||
void SendReply();
|
||||
|
||||
static const std::string itoopieImage;
|
||||
static const std::string itoopieFavicon;
|
||||
};
|
||||
void Send404Reply();
|
||||
|
||||
class HTTPServer
|
||||
{
|
||||
public:
|
||||
/*
|
||||
* @throw std::runtime_error when the file is not accessible
|
||||
*/
|
||||
std::string GetFileContents(const std::string& filename, bool preprocess) const;
|
||||
|
||||
HTTPServer (const std::string& address, int port);
|
||||
virtual ~HTTPServer ();
|
||||
void HandleRequest();
|
||||
void HandleI2PControlRequest();
|
||||
void ExtractParams(const std::string& str, std::map<std::string, std::string>& params);
|
||||
|
||||
bool isAllowed(const std::string& address) const;
|
||||
private:
|
||||
boost::asio::ip::tcp::socket* m_Socket;
|
||||
char m_Buffer[HTTP_CONNECTION_BUFFER_SIZE + 1];
|
||||
size_t m_BufferLen;
|
||||
util::http::Request m_Request;
|
||||
util::http::Response m_Reply;
|
||||
std::shared_ptr<i2p::client::i2pcontrol::I2PControlSession> m_Session;
|
||||
};
|
||||
|
||||
void Start ();
|
||||
void Stop ();
|
||||
class HTTPServer {
|
||||
public:
|
||||
|
||||
private:
|
||||
HTTPServer(const std::string& address, int port);
|
||||
virtual ~HTTPServer();
|
||||
|
||||
void Run ();
|
||||
void Accept ();
|
||||
void HandleAccept(const boost::system::error_code& ecode);
|
||||
|
||||
private:
|
||||
void Start();
|
||||
void Stop();
|
||||
|
||||
std::thread * m_Thread;
|
||||
boost::asio::io_service m_Service;
|
||||
boost::asio::io_service::work m_Work;
|
||||
boost::asio::ip::tcp::acceptor m_Acceptor;
|
||||
boost::asio::ip::tcp::socket * m_NewSocket;
|
||||
private:
|
||||
|
||||
protected:
|
||||
virtual void CreateConnection(boost::asio::ip::tcp::socket * m_NewSocket);
|
||||
};
|
||||
void Run();
|
||||
void Accept();
|
||||
void HandleAccept(const boost::system::error_code& ecode);
|
||||
|
||||
private:
|
||||
|
||||
std::thread * m_Thread;
|
||||
boost::asio::io_service m_Service;
|
||||
boost::asio::io_service::work m_Work;
|
||||
boost::asio::ip::tcp::acceptor m_Acceptor;
|
||||
boost::asio::ip::tcp::socket * m_NewSocket;
|
||||
std::shared_ptr<i2p::client::i2pcontrol::I2PControlSession> m_Session;
|
||||
|
||||
protected:
|
||||
void CreateConnection(boost::asio::ip::tcp::socket* m_NewSocket);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include "Daemon.h"
|
||||
#include "Log.h"
|
||||
#include "util/Log.h"
|
||||
|
||||
I2PService *I2PService::s_service = NULL;
|
||||
|
||||
|
@ -3,13 +3,13 @@
|
||||
#include "Daemon.h"
|
||||
#include "Reseed.h"
|
||||
|
||||
int main( int argc, char* argv[] )
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
Daemon.init(argc, argv);
|
||||
if (Daemon.start())
|
||||
{
|
||||
while (Daemon.running)
|
||||
{
|
||||
if(!Daemon.init(argc, argv))
|
||||
return EXIT_FAILURE;
|
||||
|
||||
if(Daemon.start()) {
|
||||
while (Daemon.running) {
|
||||
//TODO Meeh: Find something better to do here.
|
||||
std::this_thread::sleep_for (std::chrono::seconds(1));
|
||||
}
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <cryptopp/filters.h>
|
||||
|
||||
#include <boost/property_tree/json_parser.hpp>
|
||||
#include "util/util.h"
|
||||
#include "util/Log.h"
|
||||
#include "util/Timestamp.h"
|
||||
#include "transport/Transports.h"
|
||||
@ -21,6 +22,65 @@
|
||||
|
||||
namespace i2p {
|
||||
namespace client {
|
||||
namespace i2pcontrol {
|
||||
|
||||
JsonObject::JsonObject(const std::string& value)
|
||||
: children(), value("\"" + value + "\"")
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
JsonObject::JsonObject(int value)
|
||||
: children(), value(std::to_string(value))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
JsonObject::JsonObject(double v)
|
||||
: children(), value()
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << std::fixed << std::setprecision(2) << v;
|
||||
value = oss.str();
|
||||
}
|
||||
|
||||
JsonObject& JsonObject::operator[](const std::string& key)
|
||||
{
|
||||
return children[key];
|
||||
}
|
||||
|
||||
std::string JsonObject::toString() const
|
||||
{
|
||||
if(children.empty())
|
||||
return value;
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << '{';
|
||||
for(auto it = children.begin(); it != children.end(); ++it) {
|
||||
if(it != children.begin())
|
||||
oss << ',';
|
||||
oss << '"' << it->first << "\":" << it->second.toString();
|
||||
}
|
||||
oss << '}';
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
JsonObject tunnelToJsonObject(i2p::tunnel::Tunnel* tunnel)
|
||||
{
|
||||
JsonObject obj;
|
||||
|
||||
std::stringstream ss;
|
||||
tunnel->GetTunnelConfig()->Print(ss); // TODO: use a JsonObject
|
||||
obj["layout"] = JsonObject(ss.str());
|
||||
|
||||
const auto state = tunnel->GetState();
|
||||
if(state == i2p::tunnel::eTunnelStateFailed)
|
||||
obj["state"] = JsonObject("failed");
|
||||
else if(state == i2p::tunnel::eTunnelStateExpiring)
|
||||
obj["state"] = JsonObject("expiring");
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
I2PControlSession::Response::Response(const std::string& version)
|
||||
: id(), version(version), error(ErrorCode::None), parameters()
|
||||
@ -90,6 +150,11 @@ void I2PControlSession::Response::setParam(const std::string& param, double valu
|
||||
parameters[param] = oss.str();
|
||||
}
|
||||
|
||||
void I2PControlSession::Response::setParam(const std::string& param, const JsonObject& value)
|
||||
{
|
||||
parameters[param] = value.toString();
|
||||
}
|
||||
|
||||
void I2PControlSession::Response::setError(ErrorCode code)
|
||||
{
|
||||
error = code;
|
||||
@ -104,28 +169,35 @@ I2PControlSession::I2PControlSession(boost::asio::io_service& ios, const std::st
|
||||
: password(pass), tokens(), tokensMutex(),
|
||||
service(ios), shutdownTimer(ios), expireTokensTimer(ios)
|
||||
{
|
||||
using namespace i2p::client::i2pcontrol::constants;
|
||||
// Method handlers
|
||||
methodHandlers[I2P_CONTROL_METHOD_AUTHENTICATE] = &I2PControlSession::handleAuthenticate;
|
||||
methodHandlers[I2P_CONTROL_METHOD_ECHO] = &I2PControlSession::handleEcho;
|
||||
methodHandlers[I2P_CONTROL_METHOD_I2PCONTROL] = &I2PControlSession::handleI2PControl;
|
||||
methodHandlers[I2P_CONTROL_METHOD_ROUTER_INFO] = &I2PControlSession::handleRouterInfo;
|
||||
methodHandlers[I2P_CONTROL_METHOD_ROUTER_MANAGER] = &I2PControlSession::handleRouterManager;
|
||||
methodHandlers[I2P_CONTROL_METHOD_NETWORK_SETTING] = &I2PControlSession::handleNetworkSetting;
|
||||
methodHandlers[METHOD_AUTHENTICATE] = &I2PControlSession::handleAuthenticate;
|
||||
methodHandlers[METHOD_ECHO] = &I2PControlSession::handleEcho;
|
||||
methodHandlers[METHOD_I2PCONTROL] = &I2PControlSession::handleI2PControl;
|
||||
methodHandlers[METHOD_ROUTER_INFO] = &I2PControlSession::handleRouterInfo;
|
||||
methodHandlers[METHOD_ROUTER_MANAGER] = &I2PControlSession::handleRouterManager;
|
||||
methodHandlers[METHOD_NETWORK_SETTING] = &I2PControlSession::handleNetworkSetting;
|
||||
// RouterInfo handlers
|
||||
routerInfoHandlers[I2P_CONTROL_ROUTER_INFO_UPTIME] = &I2PControlSession::handleUptime;
|
||||
routerInfoHandlers[I2P_CONTROL_ROUTER_INFO_VERSION] = &I2PControlSession::handleVersion;
|
||||
routerInfoHandlers[I2P_CONTROL_ROUTER_INFO_STATUS] = &I2PControlSession::handleStatus;
|
||||
routerInfoHandlers[I2P_CONTROL_ROUTER_INFO_NETDB_KNOWNPEERS]= &I2PControlSession::handleNetDbKnownPeers;
|
||||
routerInfoHandlers[I2P_CONTROL_ROUTER_INFO_NETDB_ACTIVEPEERS] = &I2PControlSession::handleNetDbActivePeers;
|
||||
routerInfoHandlers[I2P_CONTROL_ROUTER_INFO_NET_STATUS] = &I2PControlSession::handleNetStatus;
|
||||
routerInfoHandlers[I2P_CONTROL_ROUTER_INFO_TUNNELS_PARTICIPATING] = &I2PControlSession::handleTunnelsParticipating;
|
||||
routerInfoHandlers[I2P_CONTROL_ROUTER_INFO_BW_IB_1S] = &I2PControlSession::handleInBandwidth1S;
|
||||
routerInfoHandlers[I2P_CONTROL_ROUTER_INFO_BW_OB_1S] = &I2PControlSession::handleOutBandwidth1S;
|
||||
routerInfoHandlers[ROUTER_INFO_UPTIME] = &I2PControlSession::handleUptime;
|
||||
routerInfoHandlers[ROUTER_INFO_VERSION] = &I2PControlSession::handleVersion;
|
||||
routerInfoHandlers[ROUTER_INFO_STATUS] = &I2PControlSession::handleStatus;
|
||||
routerInfoHandlers[ROUTER_INFO_DATAPATH] = &I2PControlSession::handleDatapath;
|
||||
routerInfoHandlers[ROUTER_INFO_NETDB_KNOWNPEERS]= &I2PControlSession::handleNetDbKnownPeers;
|
||||
routerInfoHandlers[ROUTER_INFO_NETDB_ACTIVEPEERS] = &I2PControlSession::handleNetDbActivePeers;
|
||||
routerInfoHandlers[ROUTER_INFO_NETDB_LEASESETS] = &I2PControlSession::handleNetDbLeaseSets;
|
||||
routerInfoHandlers[ROUTER_INFO_NETDB_FLOODFILLS] = &I2PControlSession::handleNetDbFloodfills;
|
||||
routerInfoHandlers[ROUTER_INFO_NET_STATUS] = &I2PControlSession::handleNetStatus;
|
||||
routerInfoHandlers[ROUTER_INFO_TUNNELS_PARTICIPATING] = &I2PControlSession::handleTunnelsParticipating;
|
||||
routerInfoHandlers[ROUTER_INFO_TUNNELS_CREATION_SUCCESS] = &I2PControlSession::handleTunnelsCreationSuccess;
|
||||
routerInfoHandlers[ROUTER_INFO_TUNNELS_IN_LIST] = &I2PControlSession::handleTunnelsInList;
|
||||
routerInfoHandlers[ROUTER_INFO_TUNNELS_OUT_LIST] = &I2PControlSession::handleTunnelsOutList;
|
||||
routerInfoHandlers[ROUTER_INFO_BW_IB_1S] = &I2PControlSession::handleInBandwidth1S;
|
||||
routerInfoHandlers[ROUTER_INFO_BW_OB_1S] = &I2PControlSession::handleOutBandwidth1S;
|
||||
|
||||
// RouterManager handlers
|
||||
routerManagerHandlers[I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN] = &I2PControlSession::handleShutdown;
|
||||
routerManagerHandlers[I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN_GRACEFUL] = &I2PControlSession::handleShutdownGraceful;
|
||||
routerManagerHandlers[I2P_CONTROL_ROUTER_MANAGER_RESEED] = &I2PControlSession::handleReseed;
|
||||
routerManagerHandlers[ROUTER_MANAGER_SHUTDOWN] = &I2PControlSession::handleShutdown;
|
||||
routerManagerHandlers[ROUTER_MANAGER_SHUTDOWN_GRACEFUL] = &I2PControlSession::handleShutdownGraceful;
|
||||
routerManagerHandlers[ROUTER_MANAGER_RESEED] = &I2PControlSession::handleReseed;
|
||||
}
|
||||
|
||||
void I2PControlSession::start()
|
||||
@ -147,9 +219,9 @@ I2PControlSession::Response I2PControlSession::handleRequest(std::stringstream&
|
||||
|
||||
Response response;
|
||||
try {
|
||||
response.setId(pt.get<std::string>(I2P_CONTROL_PROPERTY_ID));
|
||||
response.setId(pt.get<std::string>(constants::PROPERTY_ID));
|
||||
|
||||
std::string method = pt.get<std::string>(I2P_CONTROL_PROPERTY_METHOD);
|
||||
std::string method = pt.get<std::string>(constants::PROPERTY_METHOD);
|
||||
auto it = methodHandlers.find(method);
|
||||
if(it == methodHandlers.end()) { // Not found
|
||||
LogPrint(eLogWarning, "Unknown I2PControl method ", method);
|
||||
@ -157,8 +229,8 @@ I2PControlSession::Response I2PControlSession::handleRequest(std::stringstream&
|
||||
return response;
|
||||
}
|
||||
|
||||
PropertyTree params = pt.get_child(I2P_CONTROL_PROPERTY_PARAMS);
|
||||
if(method != I2P_CONTROL_METHOD_AUTHENTICATE && !authenticate(params, response)) {
|
||||
PropertyTree params = pt.get_child(constants::PROPERTY_PARAMS);
|
||||
if(method != constants::METHOD_AUTHENTICATE && !authenticate(params, response)) {
|
||||
LogPrint(eLogWarning, "I2PControl invalid token presented");
|
||||
return response;
|
||||
}
|
||||
@ -177,14 +249,14 @@ I2PControlSession::Response I2PControlSession::handleRequest(std::stringstream&
|
||||
bool I2PControlSession::authenticate(const PropertyTree& pt, Response& response)
|
||||
{
|
||||
try {
|
||||
std::string token = pt.get<std::string>(I2P_CONTROL_PARAM_TOKEN);
|
||||
std::string token = pt.get<std::string>(constants::PARAM_TOKEN);
|
||||
|
||||
std::lock_guard<std::mutex> lock(tokensMutex);
|
||||
auto it = tokens.find(token);
|
||||
if(it == tokens.end()) {
|
||||
response.setError(ErrorCode::NonexistentToken);
|
||||
return false;
|
||||
} else if(util::GetSecondsSinceEpoch() - it->second > I2P_CONTROL_TOKEN_LIFETIME) {
|
||||
} else if(util::GetSecondsSinceEpoch() - it->second > constants::TOKEN_LIFETIME) {
|
||||
response.setError(ErrorCode::ExpiredToken);
|
||||
return false;
|
||||
}
|
||||
@ -199,12 +271,12 @@ bool I2PControlSession::authenticate(const PropertyTree& pt, Response& response)
|
||||
|
||||
std::string I2PControlSession::generateToken() const
|
||||
{
|
||||
byte random_data[I2P_CONTROL_TOKEN_SIZE] = {};
|
||||
byte random_data[constants::TOKEN_SIZE] = {};
|
||||
CryptoPP::AutoSeededRandomPool rng;
|
||||
rng.GenerateBlock(random_data, I2P_CONTROL_TOKEN_SIZE);
|
||||
rng.GenerateBlock(random_data, constants::TOKEN_SIZE);
|
||||
std::string token;
|
||||
CryptoPP::StringSource ss(
|
||||
random_data, I2P_CONTROL_TOKEN_SIZE, true,
|
||||
random_data, constants::TOKEN_SIZE, true,
|
||||
new CryptoPP::HexEncoder(new CryptoPP::StringSink(token))
|
||||
);
|
||||
return token;
|
||||
@ -212,8 +284,8 @@ std::string I2PControlSession::generateToken() const
|
||||
|
||||
void I2PControlSession::handleAuthenticate(const PropertyTree& pt, Response& response)
|
||||
{
|
||||
const int api = pt.get<int>(I2P_CONTROL_PARAM_API);
|
||||
const std::string given_pass = pt.get<std::string>(I2P_CONTROL_PARAM_PASSWORD);
|
||||
const int api = pt.get<int>(constants::PARAM_API);
|
||||
const std::string given_pass = pt.get<std::string>(constants::PARAM_PASSWORD);
|
||||
LogPrint(eLogDebug, "I2PControl Authenticate API = ", api, " Password = ", given_pass);
|
||||
if(given_pass != password) {
|
||||
LogPrint(
|
||||
@ -224,8 +296,8 @@ void I2PControlSession::handleAuthenticate(const PropertyTree& pt, Response& res
|
||||
return;
|
||||
}
|
||||
const std::string token = generateToken();
|
||||
response.setParam(I2P_CONTROL_PARAM_API, api);
|
||||
response.setParam(I2P_CONTROL_PARAM_TOKEN, token);
|
||||
response.setParam(constants::PARAM_API, api);
|
||||
response.setParam(constants::PARAM_TOKEN, token);
|
||||
|
||||
std::lock_guard<std::mutex> lock(tokensMutex);
|
||||
tokens.insert(std::make_pair(token, util::GetSecondsSinceEpoch()));
|
||||
@ -233,9 +305,9 @@ void I2PControlSession::handleAuthenticate(const PropertyTree& pt, Response& res
|
||||
|
||||
void I2PControlSession::handleEcho(const PropertyTree& pt, Response& response)
|
||||
{
|
||||
const std::string echo = pt.get<std::string>(I2P_CONTROL_PARAM_ECHO);
|
||||
const std::string echo = pt.get<std::string>(constants::PARAM_ECHO);
|
||||
LogPrint(eLogDebug, "I2PControl Echo Echo = ", echo);
|
||||
response.setParam(I2P_CONTROL_PARAM_RESULT, echo);
|
||||
response.setParam(constants::PARAM_RESULT, echo);
|
||||
}
|
||||
|
||||
void I2PControlSession::handleI2PControl(const PropertyTree&, Response&)
|
||||
@ -249,7 +321,7 @@ void I2PControlSession::handleRouterInfo(const PropertyTree& pt, Response& respo
|
||||
{
|
||||
LogPrint(eLogDebug, "I2PControl RouterInfo");
|
||||
for(const auto& pair : pt) {
|
||||
if(pair.first == I2P_CONTROL_PARAM_TOKEN)
|
||||
if(pair.first == constants::PARAM_TOKEN)
|
||||
continue;
|
||||
LogPrint(eLogDebug, pair.first);
|
||||
auto it = routerInfoHandlers.find(pair.first);
|
||||
@ -266,7 +338,7 @@ void I2PControlSession::handleRouterManager(const PropertyTree& pt, Response& re
|
||||
{
|
||||
LogPrint(eLogDebug, "I2PControl RouterManager");
|
||||
for(const auto& pair : pt) {
|
||||
if(pair.first == I2P_CONTROL_PARAM_TOKEN)
|
||||
if(pair.first == constants::PARAM_TOKEN)
|
||||
continue;
|
||||
LogPrint(eLogDebug, pair.first);
|
||||
auto it = routerManagerHandlers.find(pair.first);
|
||||
@ -286,53 +358,112 @@ void I2PControlSession::handleNetworkSetting(const PropertyTree&, Response&)
|
||||
|
||||
void I2PControlSession::handleUptime(Response& response)
|
||||
{
|
||||
response.setParam(I2P_CONTROL_ROUTER_INFO_UPTIME, (int)i2p::context.GetUptime()*1000);
|
||||
response.setParam(constants::ROUTER_INFO_UPTIME, (int)i2p::context.GetUptime()*1000);
|
||||
}
|
||||
|
||||
void I2PControlSession::handleVersion(Response& response)
|
||||
{
|
||||
response.setParam(I2P_CONTROL_ROUTER_INFO_VERSION, VERSION);
|
||||
response.setParam(constants::ROUTER_INFO_VERSION, VERSION);
|
||||
}
|
||||
|
||||
void I2PControlSession::handleStatus(Response& response)
|
||||
{
|
||||
response.setParam(I2P_CONTROL_ROUTER_INFO_STATUS, "???"); // TODO:
|
||||
response.setParam(constants::ROUTER_INFO_STATUS, "???"); // TODO:
|
||||
}
|
||||
|
||||
void I2PControlSession::handleDatapath(Response& response)
|
||||
{
|
||||
response.setParam(
|
||||
constants::ROUTER_INFO_DATAPATH,
|
||||
i2p::util::filesystem::GetDefaultDataDir().string()
|
||||
);
|
||||
}
|
||||
|
||||
void I2PControlSession::handleNetDbKnownPeers(Response& response)
|
||||
{
|
||||
response.setParam(
|
||||
I2P_CONTROL_ROUTER_INFO_NETDB_KNOWNPEERS, i2p::data::netdb.GetNumRouters()
|
||||
constants::ROUTER_INFO_NETDB_KNOWNPEERS, i2p::data::netdb.GetNumRouters()
|
||||
);
|
||||
}
|
||||
|
||||
void I2PControlSession::handleNetDbActivePeers(Response& response)
|
||||
{
|
||||
response.setParam(
|
||||
I2P_CONTROL_ROUTER_INFO_NETDB_ACTIVEPEERS,
|
||||
constants::ROUTER_INFO_NETDB_ACTIVEPEERS,
|
||||
(int)i2p::transport::transports.GetPeers().size()
|
||||
);
|
||||
}
|
||||
|
||||
void I2PControlSession::handleNetDbFloodfills(Response& response)
|
||||
{
|
||||
response.setParam(
|
||||
constants::ROUTER_INFO_NETDB_FLOODFILLS,
|
||||
(int)i2p::data::netdb.GetNumFloodfills()
|
||||
);
|
||||
}
|
||||
|
||||
void I2PControlSession::handleNetDbLeaseSets(Response& response)
|
||||
{
|
||||
response.setParam(
|
||||
constants::ROUTER_INFO_NETDB_LEASESETS,
|
||||
(int)i2p::data::netdb.GetNumLeaseSets()
|
||||
);
|
||||
}
|
||||
|
||||
void I2PControlSession::handleNetStatus(Response& response)
|
||||
{
|
||||
response.setParam(
|
||||
I2P_CONTROL_ROUTER_INFO_NET_STATUS, (int)i2p::context.GetStatus()
|
||||
constants::ROUTER_INFO_NET_STATUS, (int)i2p::context.GetStatus()
|
||||
);
|
||||
}
|
||||
|
||||
void I2PControlSession::handleTunnelsParticipating(Response& response)
|
||||
{
|
||||
response.setParam(
|
||||
I2P_CONTROL_ROUTER_INFO_TUNNELS_PARTICIPATING,
|
||||
constants::ROUTER_INFO_TUNNELS_PARTICIPATING,
|
||||
(int)i2p::tunnel::tunnels.GetTransitTunnels().size()
|
||||
);
|
||||
}
|
||||
|
||||
void I2PControlSession::handleTunnelsCreationSuccess(Response& response)
|
||||
{
|
||||
response.setParam(
|
||||
constants::ROUTER_INFO_TUNNELS_CREATION_SUCCESS,
|
||||
i2p::tunnel::tunnels.GetTunnelCreationSuccessRate()
|
||||
);
|
||||
}
|
||||
|
||||
void I2PControlSession::handleTunnelsInList(Response& response)
|
||||
{
|
||||
JsonObject list;
|
||||
|
||||
for(auto pair : i2p::tunnel::tunnels.GetInboundTunnels()) {
|
||||
const std::string id = std::to_string(pair.first);
|
||||
list[id] = tunnelToJsonObject(pair.second.get());
|
||||
list[id]["bytes"] = JsonObject(
|
||||
static_cast<int>(pair.second->GetNumReceivedBytes())
|
||||
);
|
||||
}
|
||||
response.setParam(constants::ROUTER_INFO_TUNNELS_IN_LIST, list);
|
||||
}
|
||||
|
||||
void I2PControlSession::handleTunnelsOutList(Response& response)
|
||||
{
|
||||
JsonObject list;
|
||||
for(auto tunnel : i2p::tunnel::tunnels.GetOutboundTunnels()) {
|
||||
const std::string id = std::to_string(tunnel->GetTunnelID());
|
||||
list[id] = tunnelToJsonObject(tunnel.get());
|
||||
list[id]["bytes"] = JsonObject(
|
||||
static_cast<int>(tunnel->GetNumSentBytes())
|
||||
);
|
||||
}
|
||||
response.setParam(constants::ROUTER_INFO_TUNNELS_OUT_LIST, list);
|
||||
}
|
||||
|
||||
void I2PControlSession::handleInBandwidth1S(Response& response)
|
||||
{
|
||||
response.setParam(
|
||||
I2P_CONTROL_ROUTER_INFO_BW_IB_1S,
|
||||
constants::ROUTER_INFO_BW_IB_1S,
|
||||
(double)i2p::transport::transports.GetInBandwidth()
|
||||
);
|
||||
}
|
||||
@ -340,7 +471,7 @@ void I2PControlSession::handleInBandwidth1S(Response& response)
|
||||
void I2PControlSession::handleOutBandwidth1S(Response& response)
|
||||
{
|
||||
response.setParam(
|
||||
I2P_CONTROL_ROUTER_INFO_BW_OB_1S,
|
||||
constants::ROUTER_INFO_BW_OB_1S,
|
||||
(double)i2p::transport::transports.GetOutBandwidth()
|
||||
);
|
||||
}
|
||||
@ -348,7 +479,7 @@ void I2PControlSession::handleOutBandwidth1S(Response& response)
|
||||
void I2PControlSession::handleShutdown(Response& response)
|
||||
{
|
||||
LogPrint(eLogInfo, "Shutdown requested");
|
||||
response.setParam(I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN, "");
|
||||
response.setParam(constants::ROUTER_MANAGER_SHUTDOWN, "");
|
||||
// 1 second to make sure response has been sent
|
||||
shutdownTimer.expires_from_now(boost::posix_time::seconds(1));
|
||||
shutdownTimer.async_wait([](const boost::system::error_code&) {
|
||||
@ -361,7 +492,7 @@ void I2PControlSession::handleShutdownGraceful(Response& response)
|
||||
i2p::context.SetAcceptsTunnels(false);
|
||||
int timeout = i2p::tunnel::tunnels.GetTransitTunnelsExpirationTimeout();
|
||||
LogPrint(eLogInfo, "Graceful shutdown requested. Will shutdown after ", timeout, " seconds");
|
||||
response.setParam(I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN_GRACEFUL, "");
|
||||
response.setParam(constants::ROUTER_MANAGER_SHUTDOWN_GRACEFUL, "");
|
||||
shutdownTimer.expires_from_now(boost::posix_time::seconds(timeout + 1));
|
||||
shutdownTimer.async_wait([](const boost::system::error_code&) {
|
||||
Daemon.running = 0;
|
||||
@ -371,7 +502,7 @@ void I2PControlSession::handleShutdownGraceful(Response& response)
|
||||
void I2PControlSession::handleReseed(Response& response)
|
||||
{
|
||||
LogPrint(eLogInfo, "Reseed requested");
|
||||
response.setParam(I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN, "");
|
||||
response.setParam(constants::ROUTER_MANAGER_SHUTDOWN, "");
|
||||
i2p::data::netdb.Reseed();
|
||||
}
|
||||
|
||||
@ -385,7 +516,7 @@ void I2PControlSession::expireTokens(const boost::system::error_code& error)
|
||||
const uint64_t now = util::GetSecondsSinceEpoch();
|
||||
std::lock_guard<std::mutex> lock(tokensMutex);
|
||||
for(auto it = tokens.begin(); it != tokens.end(); ) {
|
||||
if(now - it->second > I2P_CONTROL_TOKEN_LIFETIME)
|
||||
if(now - it->second > constants::TOKEN_LIFETIME)
|
||||
it = tokens.erase(it);
|
||||
else
|
||||
++it;
|
||||
@ -394,7 +525,7 @@ void I2PControlSession::expireTokens(const boost::system::error_code& error)
|
||||
|
||||
void I2PControlSession::startExpireTokensJob()
|
||||
{
|
||||
expireTokensTimer.expires_from_now(boost::posix_time::seconds(I2P_CONTROL_TOKEN_LIFETIME));
|
||||
expireTokensTimer.expires_from_now(boost::posix_time::seconds(constants::TOKEN_LIFETIME));
|
||||
expireTokensTimer.async_wait(std::bind(
|
||||
&I2PControlSession::expireTokens, shared_from_this(), std::placeholders::_1
|
||||
));
|
||||
@ -402,3 +533,4 @@ void I2PControlSession::startExpireTokensJob()
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,52 +9,94 @@
|
||||
#include <boost/asio.hpp>
|
||||
|
||||
namespace i2p {
|
||||
|
||||
// Forward declaration
|
||||
namespace tunnel { class Tunnel; }
|
||||
|
||||
namespace client {
|
||||
namespace i2pcontrol {
|
||||
|
||||
const char I2P_CONTROL_DEFAULT_PASSWORD[] = "itoopie";
|
||||
const uint64_t I2P_CONTROL_TOKEN_LIFETIME = 600; // Token lifetime in seconds
|
||||
const std::size_t I2P_CONTROL_TOKEN_SIZE = 8; // Token size in bytes
|
||||
namespace constants {
|
||||
|
||||
const char I2P_CONTROL_PROPERTY_ID[] = "id";
|
||||
const char I2P_CONTROL_PROPERTY_METHOD[] = "method";
|
||||
const char I2P_CONTROL_PROPERTY_PARAMS[] = "params";
|
||||
const char I2P_CONTROL_PROPERTY_RESULT[] = "result";
|
||||
const char DEFAULT_PASSWORD[] = "itoopie";
|
||||
const uint64_t TOKEN_LIFETIME = 600; // Token lifetime in seconds
|
||||
const std::size_t TOKEN_SIZE = 8; // Token size in bytes
|
||||
|
||||
const char PROPERTY_ID[] = "id";
|
||||
const char PROPERTY_METHOD[] = "method";
|
||||
const char PROPERTY_PARAMS[] = "params";
|
||||
const char PROPERTY_RESULT[] = "result";
|
||||
|
||||
// methods
|
||||
const char I2P_CONTROL_METHOD_AUTHENTICATE[] = "Authenticate";
|
||||
const char I2P_CONTROL_METHOD_ECHO[] = "Echo";
|
||||
const char I2P_CONTROL_METHOD_I2PCONTROL[] = "I2PControl";
|
||||
const char I2P_CONTROL_METHOD_ROUTER_INFO[] = "RouterInfo";
|
||||
const char I2P_CONTROL_METHOD_ROUTER_MANAGER[] = "RouterManager";
|
||||
const char I2P_CONTROL_METHOD_NETWORK_SETTING[] = "NetworkSetting";
|
||||
const char METHOD_AUTHENTICATE[] = "Authenticate";
|
||||
const char METHOD_ECHO[] = "Echo";
|
||||
const char METHOD_I2PCONTROL[] = "I2PControl";
|
||||
const char METHOD_ROUTER_INFO[] = "RouterInfo";
|
||||
const char METHOD_ROUTER_MANAGER[] = "RouterManager";
|
||||
const char METHOD_NETWORK_SETTING[] = "NetworkSetting";
|
||||
|
||||
// params
|
||||
const char I2P_CONTROL_PARAM_API[] = "API";
|
||||
const char I2P_CONTROL_PARAM_PASSWORD[] = "Password";
|
||||
const char I2P_CONTROL_PARAM_TOKEN[] = "Token";
|
||||
const char I2P_CONTROL_PARAM_ECHO[] = "Echo";
|
||||
const char I2P_CONTROL_PARAM_RESULT[] = "Result";
|
||||
const char PARAM_API[] = "API";
|
||||
const char PARAM_PASSWORD[] = "Password";
|
||||
const char PARAM_TOKEN[] = "Token";
|
||||
const char PARAM_ECHO[] = "Echo";
|
||||
const char PARAM_RESULT[] = "Result";
|
||||
|
||||
// I2PControl
|
||||
const char I2P_CONTROL_I2PCONTROL_ADDRESS[] = "i2pcontrol.address";
|
||||
const char I2P_CONTROL_I2PCONTROL_PASSWORD[] = "i2pcontrol.password";
|
||||
const char I2P_CONTROL_I2PCONTROL_PORT[] = "i2pcontrol.port";
|
||||
const char I2PCONTROL_ADDRESS[] = "i2pcontrol.address";
|
||||
const char I2PCONTROL_PASSWORD[] = "i2pcontrol.password";
|
||||
const char I2PCONTROL_PORT[] = "i2pcontrol.port";
|
||||
|
||||
// RouterInfo requests
|
||||
const char I2P_CONTROL_ROUTER_INFO_UPTIME[] = "i2p.router.uptime";
|
||||
const char I2P_CONTROL_ROUTER_INFO_VERSION[] = "i2p.router.version";
|
||||
const char I2P_CONTROL_ROUTER_INFO_STATUS[] = "i2p.router.status";
|
||||
const char I2P_CONTROL_ROUTER_INFO_NETDB_KNOWNPEERS[] = "i2p.router.netdb.knownpeers";
|
||||
const char I2P_CONTROL_ROUTER_INFO_NETDB_ACTIVEPEERS[] = "i2p.router.netdb.activepeers";
|
||||
const char I2P_CONTROL_ROUTER_INFO_NET_STATUS[] = "i2p.router.net.status";
|
||||
const char I2P_CONTROL_ROUTER_INFO_TUNNELS_PARTICIPATING[] = "i2p.router.net.tunnels.participating";
|
||||
const char I2P_CONTROL_ROUTER_INFO_BW_IB_1S[] = "i2p.router.net.bw.inbound.1s";
|
||||
const char I2P_CONTROL_ROUTER_INFO_BW_OB_1S[] = "i2p.router.net.bw.outbound.1s";
|
||||
const char ROUTER_INFO_UPTIME[] = "i2p.router.uptime";
|
||||
const char ROUTER_INFO_VERSION[] = "i2p.router.version";
|
||||
const char ROUTER_INFO_STATUS[] = "i2p.router.status";
|
||||
const char ROUTER_INFO_DATAPATH[] = "i2p.router.datapath";
|
||||
const char ROUTER_INFO_NETDB_KNOWNPEERS[] = "i2p.router.netdb.knownpeers";
|
||||
const char ROUTER_INFO_NETDB_ACTIVEPEERS[] = "i2p.router.netdb.activepeers";
|
||||
const char ROUTER_INFO_NETDB_FLOODFILLS[] = "i2p.router.netdb.floodfills";
|
||||
const char ROUTER_INFO_NETDB_LEASESETS[] = "i2p.router.netdb.leasesets";
|
||||
const char ROUTER_INFO_NET_STATUS[] = "i2p.router.net.status";
|
||||
const char ROUTER_INFO_TUNNELS_PARTICIPATING[] = "i2p.router.net.tunnels.participating";
|
||||
// TODO: Probably better to use the standard GetRate instead
|
||||
const char ROUTER_INFO_TUNNELS_CREATION_SUCCESS[] = "i2p.router.net.tunnels.creationsuccessrate";
|
||||
const char ROUTER_INFO_TUNNELS_IN_LIST[] = "i2p.router.net.tunnels.inbound.list";
|
||||
const char ROUTER_INFO_TUNNELS_OUT_LIST[] = "i2p.router.net.tunnels.outbound.list";
|
||||
const char ROUTER_INFO_BW_IB_1S[] = "i2p.router.net.bw.inbound.1s";
|
||||
const char ROUTER_INFO_BW_OB_1S[] = "i2p.router.net.bw.outbound.1s";
|
||||
|
||||
// RouterManager requests
|
||||
const char I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN[] = "Shutdown";
|
||||
const char I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN_GRACEFUL[] = "ShutdownGraceful";
|
||||
const char I2P_CONTROL_ROUTER_MANAGER_RESEED[] = "Reseed";
|
||||
const char ROUTER_MANAGER_SHUTDOWN[] = "Shutdown";
|
||||
const char ROUTER_MANAGER_SHUTDOWN_GRACEFUL[] = "ShutdownGraceful";
|
||||
const char ROUTER_MANAGER_RESEED[] = "Reseed";
|
||||
|
||||
} // constants
|
||||
|
||||
/**
|
||||
* Represents a Json object, provides functionality to convert to string.
|
||||
*/
|
||||
class JsonObject {
|
||||
|
||||
public:
|
||||
JsonObject() = default;
|
||||
|
||||
JsonObject(const std::string& value);
|
||||
|
||||
JsonObject(int value);
|
||||
|
||||
JsonObject(double value);
|
||||
|
||||
JsonObject& operator[](const std::string& key);
|
||||
|
||||
std::string toString() const;
|
||||
|
||||
private:
|
||||
std::map<std::string, JsonObject> children;
|
||||
std::string value;
|
||||
};
|
||||
|
||||
|
||||
JsonObject tunnelToJsonObject(i2p::tunnel::Tunnel* tunnel);
|
||||
|
||||
/**
|
||||
* "Null" I2P control implementation, does not do actual networking.
|
||||
@ -97,9 +139,22 @@ public:
|
||||
* @todo escape quotes
|
||||
*/
|
||||
void setParam(const std::string& param, const std::string& value);
|
||||
|
||||
/**
|
||||
* Set an ouptut parameter to a specified integer.
|
||||
*/
|
||||
void setParam(const std::string& param, int value);
|
||||
|
||||
/**
|
||||
* Set an ouptut parameter to a specified double.
|
||||
*/
|
||||
void setParam(const std::string& param, double value);
|
||||
|
||||
/**
|
||||
* Set an ouptut parameter to a specified Json object.
|
||||
*/
|
||||
void setParam(const std::string& param, const JsonObject& value);
|
||||
|
||||
void setError(ErrorCode code);
|
||||
void setId(const std::string& identifier);
|
||||
|
||||
@ -113,7 +168,7 @@ public:
|
||||
* the lifetime of this I2PControlSession.
|
||||
*/
|
||||
I2PControlSession(boost::asio::io_service& ios,
|
||||
const std::string& pass = I2P_CONTROL_DEFAULT_PASSWORD);
|
||||
const std::string& pass = constants::DEFAULT_PASSWORD);
|
||||
|
||||
/**
|
||||
* Starts the I2PControlSession.
|
||||
@ -172,10 +227,18 @@ private:
|
||||
void handleUptime(Response& response);
|
||||
void handleVersion(Response& response);
|
||||
void handleStatus(Response& response);
|
||||
void handleDatapath(Response& response);
|
||||
void handleNetDbKnownPeers(Response& response);
|
||||
void handleNetDbActivePeers(Response& response);
|
||||
void handleNetDbFloodfills(Response& response);
|
||||
void handleNetDbLeaseSets(Response& response);
|
||||
void handleNetStatus(Response& response);
|
||||
|
||||
void handleTunnelsParticipating(Response& response);
|
||||
void handleTunnelsCreationSuccess(Response& response);
|
||||
void handleTunnelsInList(Response& response);
|
||||
void handleTunnelsOutList(Response& response);
|
||||
|
||||
void handleInBandwidth1S(Response& response);
|
||||
void handleOutBandwidth1S(Response& response);
|
||||
|
||||
@ -198,6 +261,7 @@ private:
|
||||
boost::asio::deadline_timer expireTokensTimer;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
namespace i2p {
|
||||
namespace client {
|
||||
namespace i2pcontrol {
|
||||
|
||||
I2PControlService::I2PControlService(const std::string& address, int port, const std::string& pass)
|
||||
: m_Session(std::make_shared<I2PControlSession>(m_Service, pass)),
|
||||
@ -168,3 +169,4 @@ void I2PControlService::HandleResponseSent(const boost::system::error_code& ecod
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
namespace i2p {
|
||||
namespace client {
|
||||
namespace i2pcontrol {
|
||||
|
||||
const size_t I2P_CONTROL_MAX_REQUEST_SIZE = 1024;
|
||||
typedef std::array<char, I2P_CONTROL_MAX_REQUEST_SIZE> I2PControlBuffer;
|
||||
@ -51,6 +52,7 @@ private:
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -1,31 +0,0 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFVDCCAzwCCQC2r1XWYtqtAzANBgkqhkiG9w0BAQsFADBsMQswCQYDVQQGEwJY
|
||||
WDELMAkGA1UECAwCWFgxCzAJBgNVBAcMAlhYMRMwEQYDVQQKDApQdXJwbGUgSTJQ
|
||||
MQ0wCwYDVQQLDARJMlBEMR8wHQYJKoZIhvcNAQkBFhBvcmlnbmFsQG1haWwuaTJw
|
||||
MB4XDTE1MDIyMjEzNTgxOFoXDTI1MDIxOTEzNTgxOFowbDELMAkGA1UEBhMCWFgx
|
||||
CzAJBgNVBAgMAlhYMQswCQYDVQQHDAJYWDETMBEGA1UECgwKUHVycGxlIEkyUDEN
|
||||
MAsGA1UECwwESTJQRDEfMB0GCSqGSIb3DQEJARYQb3JpZ25hbEBtYWlsLmkycDCC
|
||||
AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALp3D/gdvFjrMm+IE8tHZCWE
|
||||
hQ6Pp0CCgCGDBC3WQFLqR98bqVPl4UwRG/MKY/LY7Woai06JNmGcpfw0LMoNnHxT
|
||||
bvKtDRe/8kQdhdLHhgIkWKSbMvTAl7uUdV6FzsPgDR0x7scoFVWEhkF0wfmzGF2V
|
||||
yr/WCBQejFPu69z03m5tRQ8Xjp2txWV45RawUmFu50bgbZvLCSLfTkIvxmfJzgPN
|
||||
pJ3sPa/g7TBZl2uEiAu4uaEKvTuuzStOWCGgFaHYFVlTfFXTvmhFMqHfaidtzrlu
|
||||
H35WGrmIWTDl6uGPC5QkSppvkj73rDj5aEyPzWMz5DN3YeECoVSchN+OJJCM6m7+
|
||||
rLFYXghVEp2h+T9O1GBRfcHlQ2E3CrWWvxhmK8dfteJmd501dyNX2paeuIg/aPFO
|
||||
54/8m2r11uyF29hgY8VWLdXtqvwhKuK36PCzofEwDp9QQX8GRsEV4pZTrn4bDhGo
|
||||
kb9BF7TZTqtL3uyiRmIyBXrNNiYlA1Xm4fyKRtxl0mrPaUXdgdnCt3KxOAJ8WM2B
|
||||
7L/kk9U8C/nexHbMxIZfTap49XcUg5dxSO9kOBosIOcCUms8sAzBPDV2tWAByhYF
|
||||
jI/Tutbd3F0+fvcmTcIFOlGbOxKgO2SfwXjv/44g/3LMK6IAMFB9UOc8KhnnJP0f
|
||||
uAHvMXn1ahRs4pM1VizLAgMBAAEwDQYJKoZIhvcNAQELBQADggIBAIOxdaXT+wfu
|
||||
nv/+1hy5T4TlRMNNsuj79ROcy6Mp+JwMG50HjTc0qTlXh8C7nHybDJn4v7DA+Nyn
|
||||
RxT0J5I+Gqn+Na9TaC9mLeX/lwe8/KomyhBWxjrsyWj1V6v/cLO924S2rtcfzMDm
|
||||
l3SFh9YHM1KF/R9N1XYBwtMzr3bupWDnE1yycYp1F4sMLr5SMzMQ0svQpQEM2/y5
|
||||
kly8+eUzryhm+ag9x1686uEG5gxhQ1eHQoZEaClHUOsV+28+d5If7cqcYx9Hf5Tt
|
||||
CiVjJQzdxBF+6GeiJtKxnLtevqlkbyIJt6Cm9/7YIy/ovRGF2AKSYN6oCwmZQ6i1
|
||||
8nRnFq5zE7O94m+GXconWZxy0wVqA6472HThMi7S+Tk/eLYen2ilGY+KCb9a0FH5
|
||||
5MOuWSoJZ8/HfW2VeQmL8EjhWm5F2ybg28wgXK4BOGR3jQi03Fsc+AFidnWxSKo0
|
||||
aiJoPgOsfyu8/fnCcAi07kSmjzUKIWskApgcpGQLNXHFK9mtg7+VA8esRnfLlKtP
|
||||
tJf+nNAPY1sqHfGBzh7WWGWal5RGHF5nEm3ta3oiFF5sMKCJ6C87zVwFkEcRytGC
|
||||
xOGmiG1O1RPrO5NG7rZUaQ4y1OKl2Y1H+nGONzZ3mvoAOvxEq6JtUnU2kZscpPlk
|
||||
fpeOSDoGBYJGbIpzDreBDhxaZrwGq36k
|
||||
-----END CERTIFICATE-----
|
@ -11,6 +11,7 @@ set(CORE_SRC
|
||||
"util/base64.cpp"
|
||||
"util/util.cpp"
|
||||
"util/Log.cpp"
|
||||
"util/HTTP.cpp"
|
||||
"tunnel/TransitTunnel.cpp"
|
||||
"tunnel/Tunnel.cpp"
|
||||
"tunnel/TunnelGateway.cpp"
|
||||
|
246
core/util/HTTP.cpp
Normal file
246
core/util/HTTP.cpp
Normal file
@ -0,0 +1,246 @@
|
||||
#include "HTTP.h"
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <iostream>
|
||||
#include <regex>
|
||||
#include <fstream>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include "Log.h"
|
||||
|
||||
namespace i2p {
|
||||
namespace util {
|
||||
namespace http {
|
||||
|
||||
void Request::parseRequestLine(const std::string& line)
|
||||
{
|
||||
std::stringstream ss(line);
|
||||
ss >> method;
|
||||
ss >> uri;
|
||||
}
|
||||
|
||||
void Request::parseHeaderLine(const std::string& line)
|
||||
{
|
||||
const std::size_t pos = line.find_first_of(':');
|
||||
headers[boost::trim_copy(line.substr(0, pos))] = boost::trim_copy(line.substr(pos + 1));
|
||||
}
|
||||
|
||||
void Request::parseHeader(std::stringstream& ss)
|
||||
{
|
||||
std::string line;
|
||||
while(std::getline(ss, line) && !boost::trim_copy(line).empty())
|
||||
parseHeaderLine(line);
|
||||
|
||||
has_header = boost::trim_copy(line).empty();
|
||||
if(!has_header)
|
||||
header_part = line;
|
||||
else
|
||||
header_part = "";
|
||||
}
|
||||
|
||||
void Request::setIsComplete()
|
||||
{
|
||||
auto it = headers.find("Content-Length");
|
||||
if(it == headers.end()) {
|
||||
// If Content-Length is not set, assume there is no more content
|
||||
// TODO: Support chunked transfer, or explictly reject it
|
||||
is_complete = true;
|
||||
return;
|
||||
}
|
||||
const std::size_t length = std::stoi(it->second);
|
||||
is_complete = content.size() >= length;
|
||||
}
|
||||
|
||||
Request::Request(const std::string& data)
|
||||
{
|
||||
if(!data.empty())
|
||||
has_data = true;
|
||||
|
||||
std::stringstream ss(data);
|
||||
|
||||
std::string line;
|
||||
std::getline(ss, line);
|
||||
|
||||
// Assume the request line is always passed in one go
|
||||
parseRequestLine(line);
|
||||
|
||||
parseHeader(ss);
|
||||
|
||||
if(has_header && ss) {
|
||||
const std::string current = ss.str();
|
||||
content = current.substr(ss.tellg());
|
||||
}
|
||||
|
||||
if(has_header)
|
||||
setIsComplete();
|
||||
}
|
||||
|
||||
std::string Request::getMethod() const
|
||||
{
|
||||
return method;
|
||||
}
|
||||
|
||||
std::string Request::getUri() const
|
||||
{
|
||||
return uri;
|
||||
}
|
||||
|
||||
std::string Request::getHost() const
|
||||
{
|
||||
return host;
|
||||
}
|
||||
|
||||
int Request::getPort() const
|
||||
{
|
||||
return port;
|
||||
}
|
||||
|
||||
std::string Request::getHeader(const std::string& name) const
|
||||
{
|
||||
return headers.at(name);
|
||||
}
|
||||
|
||||
std::string Request::getContent() const
|
||||
{
|
||||
return content;
|
||||
}
|
||||
|
||||
bool Request::hasData() const
|
||||
{
|
||||
return has_data;
|
||||
}
|
||||
|
||||
bool Request::isComplete() const
|
||||
{
|
||||
return is_complete;
|
||||
}
|
||||
|
||||
void Request::clear()
|
||||
{
|
||||
has_data = false;
|
||||
has_header = false;
|
||||
is_complete = false;
|
||||
}
|
||||
|
||||
void Request::update(const std::string& data)
|
||||
{
|
||||
std::stringstream ss(header_part + data);
|
||||
if(!has_header)
|
||||
parseHeader(ss);
|
||||
|
||||
if(has_header && ss) {
|
||||
const std::string current = ss.str();
|
||||
content += current.substr(ss.tellg());
|
||||
}
|
||||
|
||||
if(has_header)
|
||||
setIsComplete();
|
||||
}
|
||||
|
||||
Response::Response(int status, const std::string& content)
|
||||
: status(status), content(content), headers()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Response::setHeader(const std::string& name, const std::string& value)
|
||||
{
|
||||
headers[name] = value;
|
||||
}
|
||||
|
||||
std::string Response::toString() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "HTTP/1.1 " << status << ' ' << getStatusMessage() << "\r\n";
|
||||
for(auto& pair : headers)
|
||||
ss << pair.first << ": " << pair.second << "\r\n";
|
||||
ss << "\r\n" << content;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string Response::getStatusMessage() const
|
||||
{
|
||||
switch(status) {
|
||||
case 105:
|
||||
return "Name Not Resolved";
|
||||
case 200:
|
||||
return "OK";
|
||||
case 400:
|
||||
return "Bad Request";
|
||||
case 404:
|
||||
return "Not Found";
|
||||
case 408:
|
||||
return "Request Timeout";
|
||||
case 500:
|
||||
return "Internal Server Error";
|
||||
case 502:
|
||||
return "Not Implemented";
|
||||
case 504:
|
||||
return "Gateway Timeout";
|
||||
default:
|
||||
return std::string();
|
||||
}
|
||||
}
|
||||
|
||||
void Response::setContentLength()
|
||||
{
|
||||
setHeader("Content-Length", std::to_string(content.size()));
|
||||
}
|
||||
|
||||
std::string preprocessContent(const std::string& content, const std::string& path)
|
||||
{
|
||||
const boost::filesystem::path directory(path); // Given path is assumed to be clean
|
||||
|
||||
static const std::regex re(
|
||||
"<\\!\\-\\-\\s*#include\\s+virtual\\s*\\=\\s*\"([^\"]*)\"\\s*\\-\\->"
|
||||
);
|
||||
|
||||
boost::system::error_code e;
|
||||
|
||||
std::string result;
|
||||
|
||||
std::smatch match;
|
||||
auto it = content.begin();
|
||||
while(std::regex_search(it, content.end(), match, re)) {
|
||||
const auto last = it;
|
||||
std::advance(it, match.position());
|
||||
result.append(last, it);
|
||||
std::advance(it, match.length());
|
||||
|
||||
// Read the contents of the included file
|
||||
std::ifstream ifs(
|
||||
boost::filesystem::canonical(directory / std::string(match[1]), e).string(),
|
||||
std::ios_base::in | std::ios_base::binary
|
||||
);
|
||||
if(e || !ifs)
|
||||
continue;
|
||||
|
||||
std::string data;
|
||||
ifs.seekg(0, ifs.end);
|
||||
data.resize(ifs.tellg());
|
||||
ifs.seekg(0, ifs.beg);
|
||||
ifs.read(&data[0], data.size());
|
||||
|
||||
result += data;
|
||||
}
|
||||
|
||||
// Append all of the remaining content
|
||||
result.append(it, content.end());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string getMimeType(const std::string& filename)
|
||||
{
|
||||
const std::string ext = filename.substr(filename.find_last_of("."));
|
||||
if(ext == ".css")
|
||||
return "text/css";
|
||||
else if(ext == ".js")
|
||||
return "text/javascript";
|
||||
else if(ext == ".html" || ext == ".htm")
|
||||
return "text/html";
|
||||
else
|
||||
return "application/octet-stream";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
104
core/util/HTTP.h
Normal file
104
core/util/HTTP.h
Normal file
@ -0,0 +1,104 @@
|
||||
#ifndef _HTTP_H__
|
||||
#define _HTTP_H__
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
|
||||
namespace i2p {
|
||||
namespace util {
|
||||
namespace http {
|
||||
|
||||
class Request {
|
||||
|
||||
void parseRequestLine(const std::string& line);
|
||||
|
||||
void parseHeaderLine(const std::string& line);
|
||||
|
||||
void parseHeader(std::stringstream& ss);
|
||||
|
||||
void setIsComplete();
|
||||
public:
|
||||
Request() = default;
|
||||
|
||||
Request(const std::string& data);
|
||||
|
||||
std::string getMethod() const;
|
||||
|
||||
std::string getUri() const;
|
||||
|
||||
std::string getHost() const;
|
||||
|
||||
int getPort() const;
|
||||
|
||||
/**
|
||||
* @throw std::out_of_range if no such header exists
|
||||
*/
|
||||
std::string getHeader(const std::string& name) const;
|
||||
|
||||
std::string getContent() const;
|
||||
|
||||
bool hasData() const;
|
||||
|
||||
bool isComplete() const;
|
||||
|
||||
void clear();
|
||||
|
||||
void update(const std::string& data);
|
||||
|
||||
private:
|
||||
std::string header_part;
|
||||
|
||||
std::string method;
|
||||
std::string uri;
|
||||
std::string host;
|
||||
std::string content;
|
||||
int port;
|
||||
std::map<std::string, std::string> headers;
|
||||
bool has_data;
|
||||
bool has_header;
|
||||
bool is_complete;
|
||||
};
|
||||
|
||||
class Response {
|
||||
public:
|
||||
Response() = default;
|
||||
|
||||
Response(int status, const std::string& content = "");
|
||||
|
||||
/**
|
||||
* @note overrides existing header values with the same name
|
||||
*/
|
||||
void setHeader(const std::string& name, const std::string& value);
|
||||
|
||||
std::string toString() const;
|
||||
|
||||
/**
|
||||
* @return the message associated with the satus of this response, or the
|
||||
* empty string if the status number is invalid
|
||||
*/
|
||||
std::string getStatusMessage() const;
|
||||
|
||||
void setContentLength();
|
||||
|
||||
private:
|
||||
int status;
|
||||
std::string content;
|
||||
std::map<std::string, std::string> headers;
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle server side includes.
|
||||
*/
|
||||
std::string preprocessContent(const std::string& content, const std::string& path);
|
||||
|
||||
/**
|
||||
* @return the MIME type based on the extension of the given filename
|
||||
*/
|
||||
std::string getMimeType(const std::string& filename);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // _HTTP_H__
|
@ -18,7 +18,7 @@
|
||||
#if defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__APPLE__) || defined(__OpenBSD__)
|
||||
#include <sys/types.h>
|
||||
#include <ifaddrs.h>
|
||||
#elif defined(WIN32)
|
||||
#elif defined(_WIN32)
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
@ -79,7 +79,7 @@ namespace config {
|
||||
strKey = strKey.substr(0, has_data);
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
#ifdef _WIN32
|
||||
boost::to_lower(strKey);
|
||||
if(boost::algorithm::starts_with(strKey, "/"))
|
||||
strKey = "-" + strKey.substr(1);
|
||||
@ -125,6 +125,11 @@ namespace config {
|
||||
return nDefault;
|
||||
}
|
||||
|
||||
bool HasArg(const std::string& strArg)
|
||||
{
|
||||
return mapArgs.count(strArg);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace filesystem
|
||||
@ -193,28 +198,34 @@ namespace filesystem
|
||||
return pathTunnelsConfigFile;
|
||||
}
|
||||
|
||||
boost::filesystem::path GetWebuiDataDir()
|
||||
{
|
||||
return GetDataDir() / "webui";
|
||||
}
|
||||
|
||||
boost::filesystem::path GetDefaultDataDir()
|
||||
{
|
||||
// Custom path, or default path:
|
||||
// Windows < Vista: C:\Documents and Settings\Username\Application Data\i2pd
|
||||
// Windows >= Vista: C:\Users\Username\AppData\Roaming\i2pd
|
||||
// Mac: ~/Library/Application Support/i2pd
|
||||
// Unix: ~/.i2pd or /var/lib/i2pd is system=1
|
||||
|
||||
#ifdef WIN32
|
||||
// Unix: ~/.i2pd
|
||||
#ifdef I2PD_CUSTOM_DATA_PATH
|
||||
return boost::filesystem::path(std::string(I2PD_CUSTOM_DATA_PATH));
|
||||
#else
|
||||
#ifdef _WIN32
|
||||
// Windows
|
||||
char localAppData[MAX_PATH];
|
||||
SHGetFolderPath(NULL, CSIDL_APPDATA, 0, NULL, localAppData);
|
||||
return boost::filesystem::path(std::string(localAppData) + "\\" + appName);
|
||||
#else
|
||||
if(i2p::util::config::GetArg("-service", 0)) // use system folder
|
||||
return boost::filesystem::path(std::string ("/var/lib/") + appName);
|
||||
boost::filesystem::path pathRet;
|
||||
char* pszHome = getenv("HOME");
|
||||
if(pszHome == NULL || strlen(pszHome) == 0)
|
||||
pathRet = boost::filesystem::path("/");
|
||||
pathRet = boost::filesystem::path("/");
|
||||
else
|
||||
pathRet = boost::filesystem::path(pszHome);
|
||||
#ifdef MAC_OSX
|
||||
#ifdef __APPLE__
|
||||
// Mac
|
||||
pathRet /= "Library/Application Support";
|
||||
boost::filesystem::create_directory(pathRet);
|
||||
@ -223,6 +234,7 @@ namespace filesystem
|
||||
// Unix
|
||||
return pathRet / (std::string (".") + appName);
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -251,6 +263,46 @@ namespace filesystem
|
||||
{
|
||||
return GetDataDir () / "certificates";
|
||||
}
|
||||
|
||||
void InstallFiles()
|
||||
{
|
||||
namespace bfs = boost::filesystem;
|
||||
boost::system::error_code e;
|
||||
const bfs::path source = bfs::canonical(
|
||||
config::GetArg("-install", "webui"), e
|
||||
);
|
||||
|
||||
const bfs::path destination = GetWebuiDataDir();
|
||||
|
||||
if(e || !bfs::is_directory(source))
|
||||
throw std::runtime_error("Given directory is invalid or does not exist");
|
||||
|
||||
// TODO: check that destination is not in source
|
||||
|
||||
try {
|
||||
CopyDir(source, destination);
|
||||
} catch(...) {
|
||||
throw std::runtime_error("Could not copy webui folder to i2pd folder.");
|
||||
}
|
||||
}
|
||||
|
||||
void CopyDir(const boost::filesystem::path& src, const boost::filesystem::path& dest)
|
||||
{
|
||||
namespace bfs = boost::filesystem;
|
||||
|
||||
bfs::create_directory(dest);
|
||||
|
||||
for(bfs::directory_iterator file(src); file != bfs::directory_iterator(); ++file) {
|
||||
const bfs::path current(file->path());
|
||||
if(bfs::is_directory(current))
|
||||
CopyDir(current, dest / current.filename());
|
||||
else
|
||||
bfs::copy_file(
|
||||
current, dest / current.filename(),
|
||||
bfs::copy_option::overwrite_if_exists
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace http
|
||||
@ -515,7 +567,7 @@ namespace net {
|
||||
return mtu;
|
||||
}
|
||||
|
||||
#elif defined(WIN32)
|
||||
#elif defined(_WIN32)
|
||||
int GetMTUWindowsIpv4(sockaddr_in inputAddress, int fallback)
|
||||
{
|
||||
ULONG outBufLen = 0;
|
||||
@ -556,7 +608,7 @@ namespace net {
|
||||
LPSOCKADDR lpAddr = pUnicast->Address.lpSockaddr;
|
||||
sockaddr_in* localInterfaceAddress = (sockaddr_in*) lpAddr;
|
||||
if(localInterfaceAddress->sin_addr.S_un.S_addr == inputAddress.sin_addr.S_un.S_addr) {
|
||||
result = pAddresses->Mtu;
|
||||
auto result = pAddresses->Mtu;
|
||||
FREE(pAddresses);
|
||||
return result;
|
||||
}
|
||||
@ -618,7 +670,7 @@ namespace net {
|
||||
found_address = true;
|
||||
}
|
||||
} if (found_address) {
|
||||
result = pAddresses->Mtu;
|
||||
auto result = pAddresses->Mtu;
|
||||
FREE(pAddresses);
|
||||
pAddresses = nullptr;
|
||||
return result;
|
||||
|
@ -41,6 +41,11 @@ namespace util
|
||||
* @param nDefault the default value to be returned
|
||||
*/
|
||||
const char* GetCharArg(const std::string& strArg, const std::string& nDefault);
|
||||
|
||||
/**
|
||||
* @return true if the argument is set, false otherwise
|
||||
*/
|
||||
bool HasArg(const std::string& strArg);
|
||||
}
|
||||
|
||||
namespace filesystem
|
||||
@ -80,6 +85,10 @@ namespace util
|
||||
*/
|
||||
boost::filesystem::path GetDefaultDataDir();
|
||||
|
||||
/**
|
||||
* @return the default directory for webui data
|
||||
*/
|
||||
boost::filesystem::path GetWebuiDataDir();
|
||||
|
||||
/**
|
||||
* Read a configuration file and store its contents in the given maps.
|
||||
@ -91,6 +100,18 @@ namespace util
|
||||
* @return the path of the certificates directory
|
||||
*/
|
||||
boost::filesystem::path GetCertificatesDir();
|
||||
|
||||
/**
|
||||
* Installs the webui files.
|
||||
* @throw std::runtime_error when installation fails
|
||||
*/
|
||||
void InstallFiles();
|
||||
|
||||
/**
|
||||
* Copies all files and directories in src to dest.
|
||||
* @warning overrides existing files
|
||||
*/
|
||||
void CopyDir(const boost::filesystem::path& src, const boost::filesystem::path& dest);
|
||||
}
|
||||
|
||||
namespace http
|
||||
|
@ -15,7 +15,22 @@ The client should now reseed by itself.
|
||||
|
||||
By default, the web console is located at http://localhost:7070/.
|
||||
|
||||
For a list of cmake options, see build/BUILD_NOTES.md
|
||||
For a list of cmake options, see BUILD_NOTES.md
|
||||
|
||||
Installing the webui
|
||||
====================
|
||||
|
||||
If you build from source the webui files will automatically be copied to your
|
||||
i2pd data path.
|
||||
In some cases (such as when using binaries), you may have to manually install the
|
||||
webui.
|
||||
For this, run:
|
||||
|
||||
$ ./i2pd --install=/path/to/webui
|
||||
|
||||
Or, if the current directory contains a folder named "webui":
|
||||
|
||||
$ ./i2pd --install
|
||||
|
||||
Building Unit Tests
|
||||
===================
|
||||
@ -28,3 +43,15 @@ On Ubuntu/Debian based
|
||||
To build the tests, run
|
||||
|
||||
$ cmake .. -DWITH_TESTS=ON
|
||||
|
||||
CMake Options
|
||||
============
|
||||
Available cmake options:
|
||||
|
||||
* CMAKE_BUILD_TYPE -- build profile (Debug/Release)
|
||||
* WITH_AESNI -- AES-NI support (ON/OFF)
|
||||
* WITH_HARDENING -- enable hardening features (ON/OFF) (gcc only)
|
||||
* WITH_TESTS -- build tests (ON/OFF)
|
||||
* WITH_BENCHMARK -- build bechmarking code (ON/OFF)
|
||||
* WITH_OPTIMIZE -- enable optimization flags (ON/OFF) (not for MSVC)
|
||||
* I2PD_DATA_DIR -- directory where i2pd will store data
|
||||
|
@ -1,12 +1,3 @@
|
||||
Build notes
|
||||
===========
|
||||
|
||||
Available cmake options:
|
||||
|
||||
* CMAKE_BUILD_TYPE -- build profile (Debug/Release)
|
||||
* WITH_AESNI -- AES-NI support (ON/OFF)
|
||||
* WITH_HARDENING -- enable hardening features (ON/OFF) (gcc only)
|
||||
|
||||
Debian
|
||||
------
|
||||
|
||||
@ -25,7 +16,7 @@ Optional packages:
|
||||
FreeBSD
|
||||
-------
|
||||
|
||||
Branch 9.X has gcc v4.2, that knows nothing about required c++11 standart.
|
||||
Branch 9.X has gcc v4.2, that knows nothing about required c++11 standard.
|
||||
|
||||
Required ports:
|
||||
|
||||
|
@ -35,3 +35,4 @@ Cmdline options
|
||||
* --conf= - Config file (default: ~/.i2pd/i2p.conf or /var/lib/i2pd/i2p.conf)
|
||||
This parameter will be silently ignored if the specified config file does not exist.
|
||||
Options specified on the command line take precedence over those in the config file.
|
||||
* --install= - Installs the webui files, see BUILDING.md for details.
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include "util/util.h"
|
||||
#include "util/HTTP.h"
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(UtilityTests)
|
||||
|
||||
@ -88,4 +89,116 @@ BOOST_AUTO_TEST_CASE(ParseUrlPassword)
|
||||
BOOST_CHECK_EQUAL(url("").pass_, "");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(ParseHTTPRequestNoHeaders)
|
||||
{
|
||||
Request req1("GET /index.html HTTP/1.1");
|
||||
Request req2("POST / HTTP/1.0\r\n");
|
||||
BOOST_CHECK_EQUAL(req1.getMethod(), "GET");
|
||||
BOOST_CHECK_EQUAL(req1.getUri(), "/index.html");
|
||||
BOOST_CHECK_EQUAL(req2.getMethod(), "POST");
|
||||
BOOST_CHECK_EQUAL(req2.getUri(), "/");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(ParseHTTPRequestWithHeaders)
|
||||
{
|
||||
Request req1(
|
||||
"GET /index.html HTTP/1.1\r\n"
|
||||
"Host: localhost\r\n"
|
||||
);
|
||||
Request req2(
|
||||
"POST / HTTP/1.1\r\n"
|
||||
"Host: localhost:123 \r\n"
|
||||
);
|
||||
BOOST_CHECK_EQUAL(req1.getHeader("Host"), "localhost");
|
||||
BOOST_CHECK_EQUAL(req2.getHeader("Host"), "localhost:123");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(ParseHTTPRequestWithContent)
|
||||
{
|
||||
Request req1(
|
||||
"GET /index.html HTTP/1.1\r\n"
|
||||
"Host: localhost\r\n\r\n"
|
||||
"Random content."
|
||||
);
|
||||
Request req2(
|
||||
"GET /index.html HTTP/1.0\r\n\r\n"
|
||||
"Random content.\r\nTest content."
|
||||
);
|
||||
BOOST_CHECK_EQUAL(req1.getContent(), "Random content.");
|
||||
BOOST_CHECK_EQUAL(req2.getContent(), "Random content.\r\nTest content.");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(ParseHTTPRequestWithPartialHeaders)
|
||||
{
|
||||
Request req(
|
||||
"GET /index.html HTTP/1.1\r\n"
|
||||
"Host: local"
|
||||
);
|
||||
BOOST_CHECK(req.hasData());
|
||||
BOOST_CHECK(!req.isComplete());
|
||||
BOOST_CHECK_EQUAL(req.getMethod(), "GET");
|
||||
req.update("host\r\n");
|
||||
BOOST_CHECK(req.isComplete());
|
||||
BOOST_CHECK_EQUAL(req.getHeader("Host"), "localhost");
|
||||
req.clear();
|
||||
BOOST_CHECK(!req.hasData());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(ParseHTTPRequestHeadersFirst)
|
||||
{
|
||||
Request req(
|
||||
"GET /index.html HTTP/1.1\r\n"
|
||||
"Content-Length: 5\r\n"
|
||||
"Host: localhost\r\n\r\n"
|
||||
);
|
||||
|
||||
BOOST_CHECK_EQUAL(req.getMethod(), "GET");
|
||||
BOOST_CHECK_EQUAL(req.getHeader("Content-Length"), "5");
|
||||
BOOST_CHECK_EQUAL(req.getHeader("Host"), "localhost");
|
||||
|
||||
BOOST_CHECK(!req.isComplete());
|
||||
req.update("ab");
|
||||
BOOST_CHECK(!req.isComplete());
|
||||
req.update("cde");
|
||||
BOOST_CHECK(req.isComplete());
|
||||
|
||||
BOOST_CHECK_EQUAL(req.getContent(), "abcde");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(HTTPResponseStatusMessage)
|
||||
{
|
||||
BOOST_CHECK_EQUAL(Response(0).getStatusMessage(), "");
|
||||
BOOST_CHECK_EQUAL(Response(105).getStatusMessage(), "Name Not Resolved");
|
||||
BOOST_CHECK_EQUAL(Response(200).getStatusMessage(), "OK");
|
||||
BOOST_CHECK_EQUAL(Response(400).getStatusMessage(), "Bad Request");
|
||||
BOOST_CHECK_EQUAL(Response(404).getStatusMessage(), "Not Found");
|
||||
BOOST_CHECK_EQUAL(Response(408).getStatusMessage(), "Request Timeout");
|
||||
BOOST_CHECK_EQUAL(Response(500).getStatusMessage(), "Internal Server Error");
|
||||
BOOST_CHECK_EQUAL(Response(502).getStatusMessage(), "Not Implemented");
|
||||
BOOST_CHECK_EQUAL(Response(504).getStatusMessage(), "Gateway Timeout");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(WriteHTTPResponse)
|
||||
{
|
||||
Response rsp(200);
|
||||
rsp.setHeader("Connection", "close");
|
||||
BOOST_CHECK_EQUAL(
|
||||
rsp.toString(),
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Connection: close\r\n\r\n"
|
||||
);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(WriteHTTPResponseWithContent)
|
||||
{
|
||||
Response rsp(200, "Test content.");
|
||||
rsp.setHeader("Connection", "close");
|
||||
BOOST_CHECK_EQUAL(
|
||||
rsp.toString(),
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Connection: close\r\n\r\n"
|
||||
"Test content."
|
||||
);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
24
webui/404.html
Normal file
24
webui/404.html
Normal file
@ -0,0 +1,24 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>404 - Page not found</title>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="css/main.css">
|
||||
</head>
|
||||
|
||||
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>404 - Page not found</h1>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<h2 class="content-subhead">The page you were looking for could not be found.</h2>
|
||||
</div>
|
||||
|
||||
<!--#include virtual="menu.html" -->
|
||||
<!--#include virtual="footer.html" -->
|
||||
|
||||
</body>
|
||||
</html>
|
76
webui/css/main.css
Normal file
76
webui/css/main.css
Normal file
@ -0,0 +1,76 @@
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.header {
|
||||
margin: 0px 150px;
|
||||
text-align: center;
|
||||
padding: 2.5em 2em 0px;
|
||||
border-bottom: 1px solid #EEE;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #333;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
h2 {
|
||||
color: #ccc;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.content {
|
||||
margin: 0px 150px auto;
|
||||
padding: 0px 2em;
|
||||
line-height: 1.6em;
|
||||
}
|
||||
|
||||
.content-subhead {
|
||||
margin: 50px 0px 20px;
|
||||
font-weight: 300;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
#menu {
|
||||
width: 150px;
|
||||
position: fixed;
|
||||
top: 0px;
|
||||
bottom: 0px;
|
||||
z-index: 1000;
|
||||
background: #191818 none repeat scroll 0% 0%;
|
||||
overflow-y: auto;
|
||||
display: block;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.menu-heading {
|
||||
background: #1f8dd6;
|
||||
display: block;
|
||||
padding: 10px;
|
||||
color: white;
|
||||
font-variant: small-caps;
|
||||
font-size: 23px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.menu-list {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.menu-link {
|
||||
text-decoration: none;
|
||||
color: #ccc;
|
||||
font-weight: bold;
|
||||
font-size: 15px;
|
||||
}
|
6
webui/footer.html
Normal file
6
webui/footer.html
Normal file
@ -0,0 +1,6 @@
|
||||
<div id="main">
|
||||
<noscript>
|
||||
<div class="header"><h1>Please enable JavaScript!</h1></div>
|
||||
</noscript>
|
||||
</div>
|
||||
|
27
webui/help.html
Normal file
27
webui/help.html
Normal file
@ -0,0 +1,27 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Purple I2P 0.10.0 Webconsole</title>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="css/main.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>I2P help</h1>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<h2 class="content-subhead">Need help? Join us at IRC: #i2pd-dev at irc.freenode.net</h2>
|
||||
<h2 class="content-subhead">
|
||||
<a href="https://github.com/PurpleI2P/i2pd">i2pd at GitHub</a>
|
||||
</h2>
|
||||
<h2 class="content-subhead"><a href="https://geti2p.net/en/">I2P Project</a> </h2>
|
||||
</div>
|
||||
|
||||
<!--#include virtual="menu.html" -->
|
||||
<!--#include virtual="footer.html" -->
|
||||
|
||||
</body>
|
||||
</html>
|
101
webui/index.html
Normal file
101
webui/index.html
Normal file
@ -0,0 +1,101 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Purple I2P 0.10.0 Webconsole</title>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="css/main.css">
|
||||
<script type="text/javascript" src="javascript/I2PControl.js"></script>
|
||||
<script type="text/javascript">
|
||||
function updateRouterInfo(result, session) {
|
||||
if(session.error) {
|
||||
alert("Error: " + session.error["message"]);
|
||||
return;
|
||||
}
|
||||
I2PControl.updateDocument({
|
||||
"version" : result["i2p.router.version"],
|
||||
"status" : I2PControl.statusToString(result["i2p.router.net.status"]),
|
||||
"uptime" : I2PControl.msToString(result["i2p.router.uptime"]),
|
||||
"knownpeers" : result["i2p.router.netdb.knownpeers"],
|
||||
"activepeers" : result["i2p.router.netdb.activepeers"],
|
||||
"tunnels-participating" : result["i2p.router.net.tunnels.participating"],
|
||||
"tunnels-successrate" : result["i2p.router.net.tunnels.creationsuccessrate"] + "%",
|
||||
"bw-in" : result["i2p.router.net.bw.inbound.1s"],
|
||||
"bw-out" : result["i2p.router.net.bw.outbound.1s"]
|
||||
});
|
||||
|
||||
window.setTimeout(function() { requestRouterInfo(session); }, 5000);
|
||||
}
|
||||
|
||||
function requestRouterInfo(session) {
|
||||
session.request("RouterInfo", {
|
||||
"i2p.router.version" : "",
|
||||
"i2p.router.net.status" : "",
|
||||
"i2p.router.uptime" : "",
|
||||
"i2p.router.netdb.knownpeers" : "",
|
||||
"i2p.router.netdb.activepeers" : "",
|
||||
"i2p.router.net.tunnels.participating" : "",
|
||||
"i2p.router.net.tunnels.creationsuccessrate" : "",
|
||||
"i2p.router.net.bw.inbound.1s" : "",
|
||||
"i2p.router.net.bw.outbound.1s" : ""
|
||||
}, updateRouterInfo);
|
||||
}
|
||||
|
||||
window.onload = function() {
|
||||
var session = new I2PControl.Session("itoopie");
|
||||
session.start(function() { requestRouterInfo(session); });
|
||||
document.getElementById("shutdown").onclick = function() {
|
||||
session.request("RouterManager", {"Shutdown" : ""}, function() {});
|
||||
this.disabled = true;
|
||||
};
|
||||
document.getElementById("shutdown-graceful").onclick = function() {
|
||||
session.request("RouterManager", {"ShutdownGraceful" : ""}, function() {});
|
||||
this.disabled = true;
|
||||
};
|
||||
document.getElementById("reseed").onclick = function() {
|
||||
session.request("RouterManager", {"Reseed" : ""}, function() {});
|
||||
this.disabled = true;
|
||||
};
|
||||
};
|
||||
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>i2pd router console</h1>
|
||||
<h2>Version: <span id="version"></span>, uptime: <span id="uptime"></span></h2>
|
||||
<h2>Network status: <span id="status"></span></h2>
|
||||
<p>
|
||||
<button id="shutdown-graceful">graceful shutdown</button>
|
||||
<button id="shutdown">force shutdown</button>
|
||||
<button id="restart" disabled>restart</button>
|
||||
<button id="reseed">reseed</button>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<h2 class="content-subhead">
|
||||
Tunnels participating: <span id="tunnels-participating"></span>
|
||||
</h2>
|
||||
<h2 class="content-subhead">
|
||||
Tunnel create success rate: <span id="tunnels-successrate"></span>
|
||||
</h2>
|
||||
<h2 class="content-subhead">
|
||||
Active peers: <span id="activepeers"></span>
|
||||
</h2>
|
||||
<h2 class="content-subhead">
|
||||
Known peers: <span id="knownpeers"></span>
|
||||
</h2>
|
||||
<h2 class="content-subhead">
|
||||
Bandwidth:
|
||||
in <span id="bw-in"></span> Bps /
|
||||
out <span id="bw-out"></span> Bps
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<!--#include virtual="menu.html" -->
|
||||
<!--#include virtual="footer.html" -->
|
||||
|
||||
</body>
|
||||
</html>
|
102
webui/javascript/I2PControl.js
Normal file
102
webui/javascript/I2PControl.js
Normal file
@ -0,0 +1,102 @@
|
||||
var I2PControl = I2PControl || {}
|
||||
|
||||
I2PControl.Session = function(password) {
|
||||
this.token = "";
|
||||
this.ready = false;
|
||||
this.error = false;
|
||||
this.password = password;
|
||||
};
|
||||
|
||||
I2PControl.Session.prototype = {
|
||||
|
||||
request : function(method, params, handler) {
|
||||
var request = new XMLHttpRequest();
|
||||
request.open("POST", "", true);
|
||||
request.setRequestHeader('Content-Type', 'application/json');
|
||||
var self = this;
|
||||
request.onreadystatechange = function() {
|
||||
if(this.readyState == 4 && this.status == "200" && this.responseText != "") {
|
||||
var data = JSON.parse(this.responseText);
|
||||
if(data.hasOwnProperty("error")) {
|
||||
if(data["error"]["code"] == -32003 || data["error"]["code"] == -32004) {
|
||||
// Get a new token and resend the request
|
||||
self.start(function() {
|
||||
self.request(method, params, handler);
|
||||
});
|
||||
return;
|
||||
}
|
||||
// Cannot fix the error, report it
|
||||
self.error = data["error"];
|
||||
}
|
||||
handler(data.result, self);
|
||||
}
|
||||
};
|
||||
if(this.token != "")
|
||||
params["Token"] = this.token;
|
||||
|
||||
var rpc = {
|
||||
"id" : 0,
|
||||
"method" : method ,
|
||||
"params" : params,
|
||||
"jsonrpc": "2.0"
|
||||
}
|
||||
request.send(JSON.stringify(rpc));
|
||||
},
|
||||
|
||||
start : function(onReady) {
|
||||
var self = this;
|
||||
|
||||
var handleAuthenticate = function(result) {
|
||||
self.token = result["Token"];
|
||||
self.ready = true;
|
||||
onReady();
|
||||
};
|
||||
|
||||
this.request(
|
||||
"Authenticate",
|
||||
{"API" : 1, "Password" : this.password},
|
||||
handleAuthenticate
|
||||
);
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
I2PControl.statusToString = function(status) {
|
||||
switch(status) {
|
||||
case 0: return "OK";
|
||||
case 1: return "TESTING";
|
||||
case 2: return "FIREWALLED";
|
||||
case 3: return "HIDDEN";
|
||||
case 4: return "WARN_FIREWALLED_AND_FAST";
|
||||
case 5: return "WARN_FIREWALLED_AND_FLOODFILL";
|
||||
case 6: return "WARN_FIREWALLED_WITH_INBOUND_TCP";
|
||||
case 7: return "WARN_FIREWALLED_WITH_UDP_DISABLED";
|
||||
case 8: return "ERROR_I2CP";
|
||||
case 9: return "ERROR_CLOCK_SKEW";
|
||||
case 10: return "ERROR_PRIVATE_TCP_ADDRESS";
|
||||
case 11: return "ERROR_SYMMETRIC_NAT";
|
||||
case 12: return "ERROR_UDP_PORT_IN_USE";
|
||||
case 13: return "ERROR_NO_ACTIVE_PEERS_CHECK_CONNECTION_AND_FIREWALL";
|
||||
case 14: return "ERROR_UDP_DISABLED_AND_TCP_UNSET";
|
||||
default: return "UNKNOWN";
|
||||
}
|
||||
};
|
||||
|
||||
I2PControl.msToString = function(mseconds) {
|
||||
var seconds = mseconds / 1000;
|
||||
var numdays = Math.floor(seconds / 86400);
|
||||
var numhours = Math.floor((seconds % 86400) / 3600);
|
||||
var numminutes = Math.floor(((seconds % 86400) % 3600) / 60);
|
||||
var numseconds = ((seconds % 86400) % 3600) % 60;
|
||||
|
||||
return numdays + "d " + numhours + "h " + numminutes + "m " + numseconds + "s";
|
||||
}
|
||||
|
||||
I2PControl.updateDocument = function(values) {
|
||||
|
||||
for(id in values) {
|
||||
if(!values.hasOwnProperty(id))
|
||||
continue;
|
||||
document.getElementById(id).innerHTML = values[id];
|
||||
}
|
||||
};
|
20
webui/menu.html
Normal file
20
webui/menu.html
Normal file
@ -0,0 +1,20 @@
|
||||
<div id="menu">
|
||||
<span class="menu-heading">i2pd</span>
|
||||
<ul class="menu-list">
|
||||
<li class="menu-item">
|
||||
<a href="index.html" class="menu-link">Home</a>
|
||||
</li>
|
||||
<li class="menu-item">
|
||||
<a href="netdb.html" class="menu-link">Network Database</a>
|
||||
</li>
|
||||
<li class="menu-item">
|
||||
<a href="tunnels.html" class="menu-link">Tunnels</a>
|
||||
</li>
|
||||
<li class="menu-item">
|
||||
<a href="config.html" class="menu-link">Configure</a>
|
||||
</li>
|
||||
<li class="menu-item">
|
||||
<a href="help.html" class="menu-link">Help</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
59
webui/netdb.html
Normal file
59
webui/netdb.html
Normal file
@ -0,0 +1,59 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Purple I2P 0.10.0 Webconsole</title>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="css/main.css">
|
||||
<script type="text/javascript" src="javascript/I2PControl.js"></script>
|
||||
<script type="text/javascript">
|
||||
function updateNetDbInfo(result, session) {
|
||||
if(session.error) {
|
||||
alert("Error: " + session.error["message"]);
|
||||
return;
|
||||
}
|
||||
I2PControl.updateDocument({
|
||||
"knownpeers" : result["i2p.router.netdb.knownpeers"],
|
||||
"activepeers" : result["i2p.router.netdb.activepeers"],
|
||||
"floodfills" : result["i2p.router.netdb.floodfills"],
|
||||
"leasesets" : result["i2p.router.netdb.leasesets"],
|
||||
});
|
||||
|
||||
window.setTimeout(function() { requestNetDbInfo(session); }, 10000);
|
||||
}
|
||||
|
||||
function requestNetDbInfo(session) {
|
||||
session.request("RouterInfo", {
|
||||
"i2p.router.netdb.knownpeers" : "",
|
||||
"i2p.router.netdb.activepeers" : "",
|
||||
"i2p.router.netdb.floodfills" : "",
|
||||
"i2p.router.netdb.leasesets" : "",
|
||||
}, updateNetDbInfo);
|
||||
}
|
||||
|
||||
window.onload = function() {
|
||||
var session = new I2PControl.Session("itoopie");
|
||||
session.start(function() { requestNetDbInfo(session); });
|
||||
};
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>i2pd router console</h1>
|
||||
<h2>Network Database Information</h2>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<h2 class="content-subhead">Active peers: <span id="activepeers"></span></h2>
|
||||
<h2 class="content-subhead">Known peers: <span id="knownpeers"></span></h2>
|
||||
<h2 class="content-subhead">Floodfills: <span id="floodfills"></span></h2>
|
||||
</br>
|
||||
<h2 class="content-subhead">LeaseSets: <span id="leasesets"></span></h2>
|
||||
</div>
|
||||
|
||||
<!--#include virtual="menu.html" -->
|
||||
<!--#include virtual="footer.html" -->
|
||||
|
||||
</body>
|
||||
</html>
|
87
webui/tunnels.html
Normal file
87
webui/tunnels.html
Normal file
@ -0,0 +1,87 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Purple I2P 0.10.0 Webconsole</title>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="css/main.css">
|
||||
<script type="text/javascript" src="javascript/I2PControl.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
function buildTable(direction, result) {
|
||||
var table = document.getElementById(direction + "-tunnels").getElementsByTagName("tbody")[0];
|
||||
|
||||
var cmd = "i2p.router.net.tunnels." + direction + ".list";
|
||||
for(id in result[cmd]) {
|
||||
if(!result[cmd].hasOwnProperty(id))
|
||||
continue;
|
||||
var tunnel = result[cmd][id];
|
||||
|
||||
var row = table.insertRow(table.rows.length);
|
||||
row.insertCell(0).appendChild(document.createTextNode(id));
|
||||
row.insertCell(1).appendChild(document.createTextNode(tunnel["state"] ? tunnel["state"] : "running"));
|
||||
row.insertCell(2).appendChild(document.createTextNode(tunnel["layout"]));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
function updateTunnelInfo(result, session) {
|
||||
if(session.error) {
|
||||
alert("Error: " + session.error["message"]);
|
||||
return;
|
||||
}
|
||||
|
||||
buildTable("inbound", result);
|
||||
buildTable("outbound", result);
|
||||
}
|
||||
|
||||
function requestTunnelInfo(session) {
|
||||
session.request("RouterInfo", {
|
||||
"i2p.router.net.tunnels.inbound.list" : "",
|
||||
"i2p.router.net.tunnels.outbound.list" : "",
|
||||
}, updateTunnelInfo);
|
||||
}
|
||||
|
||||
window.onload = function() {
|
||||
var session = new I2PControl.Session("itoopie");
|
||||
session.start(function() { requestTunnelInfo(session); });
|
||||
};
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>i2pd router console</h1>
|
||||
<h2>Tunnel Information</h2>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
|
||||
<h2 class="content-subhead">Inbound Tunnels</h2>
|
||||
<table id="inbound-tunnels">
|
||||
<thead>
|
||||
<th>Tunnel ID</th>
|
||||
<th>Status</th>
|
||||
<th>Overview</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
<h2 class="content-subhead">Outbound Tunnels</h2>
|
||||
<table id="outbound-tunnels">
|
||||
<thead>
|
||||
<th>Tunnel ID</th>
|
||||
<th>Status</th>
|
||||
<th>Overview</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!--#include virtual="menu.html" -->
|
||||
<!--#include virtual="footer.html" -->
|
||||
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user