diff --git a/daemon/Daemon.cpp b/daemon/Daemon.cpp index 84f197fb..ba5afe91 100644 --- a/daemon/Daemon.cpp +++ b/daemon/Daemon.cpp @@ -33,6 +33,9 @@ #include "Timestamp.h" #include "I18N.h" +#include "TCPServer.h" +#include + namespace i2p { namespace util @@ -43,6 +46,7 @@ namespace util Daemon_Singleton_Private() {}; ~Daemon_Singleton_Private() {}; + std::unique_ptr tcpServer; std::unique_ptr httpServer; std::unique_ptr m_I2PControlService; std::unique_ptr UPnP; @@ -67,7 +71,17 @@ namespace util DaemonDataDir = path; } - bool Daemon_Singleton::init(int argc, char* argv[]) { + bool Daemon_Singleton::init(int argc, char* argv[]) + { + this->onTcpServer = false; + + if(argv[1]) + { + std::string arg1(argv[1]); + if(arg1 == "@:start") + this->onTcpServer = true; + } + return init(argc, argv, nullptr); } @@ -321,7 +335,7 @@ namespace util if (http) { std::string httpAddr; i2p::config::GetOption("http.address", httpAddr); uint16_t httpPort; i2p::config::GetOption("http.port", httpPort); - LogPrint(eLogInfo, "Daemon: Starting Webconsole at ", httpAddr, ":", httpPort); + LogPrint(eLogInfo, "Daemon: Starting Webconsole at ", httpAddr, ":", httpPort); try { d.httpServer = std::unique_ptr(new i2p::http::HTTPServer(httpAddr, httpPort)); @@ -360,11 +374,20 @@ namespace util ThrowFatal ("Unable to start I2PControl service at ", i2pcpAddr, ":", i2pcpPort, ": ", ex.what ()); } } + + // Start Console I2PD. + if(this->onTcpServer == true) + { + LogPrint(eLogInfo, "Daemon: Starting TCP server (Demo.)"); + d.tcpServer = std::unique_ptr(new i2p::tcp::TCPServer()); + d.tcpServer->Start(); + } + return true; } bool Daemon_Singleton::stop() - { + { LogPrint(eLogInfo, "Daemon: Shutting down"); LogPrint(eLogInfo, "Daemon: Stopping Client"); i2p::client::context.Stop(); @@ -378,7 +401,6 @@ namespace util d.UPnP->Stop (); d.UPnP = nullptr; } - if (d.m_NTPSync) { d.m_NTPSync->Stop (); @@ -393,7 +415,7 @@ namespace util LogPrint(eLogInfo, "Daemon: Stopping HTTP Server"); d.httpServer->Stop(); d.httpServer = nullptr; - } + } if (d.m_I2PControlService) { LogPrint(eLogInfo, "Daemon: Stopping I2PControl"); @@ -402,7 +424,12 @@ namespace util } i2p::crypto::TerminateCrypto (); i2p::log::Logger().Stop(); - + + // Stop Console I2PD. + LogPrint(eLogInfo, "Daemon: Starting TCP server (Demo.)"); + d.tcpServer->Stop(); + d.tcpServer->Printf(); + return true; } } diff --git a/daemon/Daemon.h b/daemon/Daemon.h index 26d4a047..6743a113 100644 --- a/daemon/Daemon.h +++ b/daemon/Daemon.h @@ -29,10 +29,10 @@ namespace util virtual void run () {}; virtual void setDataDir (std::string path); - + + bool onTcpServer; bool isDaemon; bool running; - protected: Daemon_Singleton (); @@ -114,12 +114,11 @@ namespace util void run (); private: - std::string pidfile; int pidFH; public: - + bool onTcpServer; int gracefulShutdownInterval; // in seconds }; #endif diff --git a/daemon/HTTPServer.cpp b/daemon/HTTPServer.cpp index 3d5ff1a0..cda73c15 100644 --- a/daemon/HTTPServer.cpp +++ b/daemon/HTTPServer.cpp @@ -956,15 +956,7 @@ namespace http { void ShowI2PTunnels (std::stringstream& s) { std::string webroot; i2p::config::GetOption("http.webroot", webroot); - s << "" << tr("Client Tunnels") << ":
\r\n
\r\n"; - for (auto& it: i2p::client::context.GetClientTunnels ()) - { - auto& ident = it.second->GetLocalDestination ()->GetIdentHash(); - s << "
"; - s << it.second->GetName () << " ⇐ "; - s << i2p::client::context.GetAddressBook ().ToAddress(ident); - s << "
\r\n"<< std::endl; - } + s << "" << tr("Client Tunnels") << ":
\r\n
\r\n"; auto httpProxy = i2p::client::context.GetHttpProxy (); if (httpProxy) { @@ -984,7 +976,18 @@ namespace http { s << "
\r\n"<< std::endl; } s << "
\r\n"; - + auto& clientTunnels = i2p::client::context.GetClientTunnels(); + if (!clientTunnels.empty ()) + { + for (auto& it: clientTunnels) + { + auto& ident = it.second->GetLocalDestination ()->GetIdentHash(); + s << "
"; + s << it.second->GetName () << " ⇐ "; + s << i2p::client::context.GetAddressBook ().ToAddress(ident); + s << "
\r\n"<< std::endl; + } + } auto& serverTunnels = i2p::client::context.GetServerTunnels (); if (!serverTunnels.empty ()) { s << "
\r\n" << tr("Server Tunnels") << ":
\r\n
\r\n"; diff --git a/daemon/TCPServer.cpp b/daemon/TCPServer.cpp new file mode 100644 index 00000000..839780ef --- /dev/null +++ b/daemon/TCPServer.cpp @@ -0,0 +1,543 @@ +/* +* Copyright (c) 2023, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ +#include "TCPServer.h" +#include +#include +#include +//#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "Base.h" +#include "FS.h" +#include "Log.h" +#include "Config.h" +#include "Tunnel.h" +#include "Transports.h" +#include "NetDb.hpp" +#include "LeaseSet.h" +#include "Destination.h" +#include "RouterContext.h" +#include "ClientContext.h" +#include "Daemon.h" +#include "util.h" +#include "ECIESX25519AEADRatchetSession.h" +#include "I18N.h" + +#ifdef WIN32_APP +#include "Win32App.h" +#endif + +// For image, style and info +#include "version.h" + +namespace i2p +{ +namespace tcp +{ +// Private region.------------------------------------------------------------------------------- +// Part: Connection. + void TCPServer::_socker() + { + bzero((char*)&servAddr, sizeof(servAddr)); + servAddr.sin_family = AF_INET; + servAddr.sin_addr.s_addr = inet_addr(IpAddress); + servAddr.sin_port = htons(port); + + serverSd = socket(AF_INET, SOCK_STREAM, 0); + if(serverSd < 0) + { + std::cerr << "[!] - Error establishing the server socket" << std::endl; + exit(0); + } + } + + void TCPServer::_bind() + { + bindStatus = bind(serverSd, (struct sockaddr*) &servAddr, sizeof(servAddr)); + if(bindStatus < 0) + { + std::cerr << "[!] - Error binding socket to local address" << std::endl; + codeStop = 2; + } + + std::cout << "[*] - Waiting for a client to connect..." << std::endl; + listen(serverSd, 1); + } + + void TCPServer::_accept() + { + newSockAddrSize = sizeof(newSockAddr); + newSd = accept(serverSd, (sockaddr *)&newSockAddr, &newSockAddrSize); + + if(newSd < 0) + { + std::cerr << "[!] - Error accepting request from client!" << std::endl; + codeStop = 3; + } + else + { + std::cout << "[*] - Connected with client!" << std::endl; + } + } + + void TCPServer::_close() + { + std::cout << "[-] - Start close socket..." << std::endl; + gettimeofday(&end1, NULL); + close(newSd); + close(serverSd); + std::cout << "[-] - End close socket..." << std::endl; + } +//End part: Connection. +// Part: Static. + static void showUptime(std::stringstream& s, int seconds) + { + int num; + + if ((num = seconds / 86400) > 0) { + s << ntr("%d day", "%d days", num, num) << ", "; + seconds -= num * 86400; + } + if ((num = seconds / 3600) > 0) { + s << ntr("%d hour", "%d hours", num, num) << ", "; + seconds -= num * 3600; + } + if ((num = seconds / 60) > 0) { + s << ntr("%d minute", "%d minutes", num, num) << ", "; + seconds -= num * 60; + } + s << ntr("%d second", "%d seconds", seconds, seconds); + } + + static void showStatus(std::stringstream& s, i2p::RouterStatus status) + { + switch (status) + { + case eRouterStatusOK: s << tr("OK."); break; + case eRouterStatusTesting: s << tr("Testing."); break; + case eRouterStatusFirewalled: s << tr("Firewalled."); break; + case eRouterStatusUnknown: s << tr("Unknown."); break; + case eRouterStatusProxy: s << tr("Proxy."); break; + case eRouterStatusMesh: s << tr("Mesh."); break; + default: s << tr("Unknown (default)."); + } + } + + static void showTraffic(std::stringstream& s, uint64_t bytes) + { + s << std::fixed << std::setprecision(2); + auto numKBytes = (double) bytes / 1024; + if (numKBytes < 1024) + s << tr(/* tr: Kibibyte */ "%.2f KiB", numKBytes); + else if (numKBytes < 1024 * 1024) + s << tr(/* tr: Mebibyte */ "%.2f MiB", numKBytes / 1024); + else + s << tr(/* tr: Gibibyte */ "%.2f GiB", numKBytes / 1024 / 1024); + } + + template + static void ShowTransportSessions (std::stringstream& s, const Sessions& sessions, const std::string name) + { + auto comp = [](typename Sessions::mapped_type a, typename Sessions::mapped_type b) + { + return a->GetRemoteEndpoint() < b->GetRemoteEndpoint(); + }; + + std::set sortedSessions(comp); + for (const auto& it : sessions) + { + auto ret = sortedSessions.insert(it.second); + if (!ret.second) + LogPrint(eLogError, "TCPPServer: Duplicate remote endpoint detected: ", (*ret.first)->GetRemoteEndpoint()); + } + + std::stringstream tmp_s, tmp_s6; uint16_t cnt = 0, cnt6 = 0; + for (const auto& it: sortedSessions) + { + auto endpoint = it->GetRemoteEndpoint(); + + if (it && it->IsEstablished() && endpoint.address().is_v4 ()) + { + tmp_s << i2p::data::GetIdentHashAbbreviation(it->GetRemoteIdentity()->GetIdentHash()) << ": " + << endpoint.address ().to_string () << ":" << endpoint.port (); + + tmp_s << " [" << it->GetNumSentBytes () << ":" << it->GetNumReceivedBytes () << "]"; + + if (it->GetRelayTag ()) + tmp_s << " [itag:" << it->GetRelayTag () << "]"; + + if (it->GetSendQueueSize () > 0) + tmp_s << " [queue:" << it->GetSendQueueSize () << "]"; + + tmp_s << std::endl; + cnt++; + } + if (it && it->IsEstablished() && endpoint.address().is_v6 ()) + { + tmp_s6 << i2p::data::GetIdentHashAbbreviation(it->GetRemoteIdentity()->GetIdentHash()) << ": " + << "[" << endpoint.address ().to_string () << "]:" << endpoint.port (); + + tmp_s6 << " [" << it->GetNumSentBytes () << ":" << it->GetNumReceivedBytes () << "]"; + + if (it->GetRelayTag ()) + tmp_s6 << " [itag:" << it->GetRelayTag () << "]"; + + if (it->GetSendQueueSize () > 0) + tmp_s6 << " [queue:" << it->GetSendQueueSize () << "]"; + + tmp_s6 << std::endl; + cnt6++; + } + } + if (!tmp_s.str().empty()) + { + s << name << "(" << cnt << ")\n" << tmp_s.str() << "\n"; + } + if (!tmp_s6.str().empty()) + { + s << name << "v6 ( " << cnt6 << " )\n" << tmp_s6.str() << "\n"; + } + } +//End part: Loacal. +//End private region.--------------------------------------------------------------------------- +// Publick region------------------------------------------------------------------------------- + TCPServer::TCPServer(int _port) + { + port = _port; + } + + TCPServer::~TCPServer() + { + _close(); + //std::cout << "********Session********" << std::endl; + //std::cout << "[i] - Bytes written: " << bytesWritten << " | Bytes read: " << bytesRead << std::endl; + //std::cout << "[i] - Elapsed time: " << (end1.tv_sec - start1.tv_sec) << " secs." << std::endl; + //std::cout << "[-] - Connection closed..." << std::endl; + } + + void TCPServer::Printf() + { + std::cout << "[*] - Address: 127.0.0.1 (default)" << std::endl; + std::cout << "[*] - Port: " << port << std::endl; + } + + void TCPServer::Start() + { + while(codeStop == 0) + { + std::cout << "[i] - Init socket." << std::endl; + _socker(); + + std::cout << "[i] - Start bind." << std::endl; + _bind(); + + std::cout << "[i] - Server status: \"RUN\"." << std::endl; + _accept(); + + start1.tv_sec = 0; + end1.tv_sec = 0; + + gettimeofday(&start1, NULL); + + while(resetBit) + { + try + { + //receive a message from the client (listen) + std::cout << "[i] - Awaiting client response..." << std::endl; + memset(&msg, 0, sizeof(msg));//clear the buffer + bytesRead += recv(newSd, (char*)&msg, sizeof(msg), 0); + + if(!strcmp(msg, "@:clear") || !strcmp(msg, "@:cls")) + { + system("clear"); + strcpy(msg, "Bot -> test status: \"Server clear\" - ok."); + bytesWritten += send(newSd, (char*)&msg, strlen(msg), 0); + } + else if(!strcmp(msg, "@:hi")) + { + std::cout << "[*] - Client send command \"hi\"." << std::endl; + strcpy(msg, "Bot -> status msg: \"Good!\" ;)"); + bytesWritten += send(newSd, (char*)&msg, strlen(msg), 0); + } + else if(!strcmp(msg, "@:info")) + { + try + { + std::cout << "[*] - Client send command \"info\"." << std::endl; + + std::stringstream s; + + s << "\n\n---=== Base info I2PD. ===---\n"; + + // Output: Status. + s << "\n"; + i2p::RouterStatus status = i2p::context.GetStatus(); + s << "> Status: "; + showStatus(s, status); + + // Output: Uptime. + s << "\n"; + int uptime = i2p::context.GetUptime(); + s << "> Uptime: "; + showUptime(s, uptime); + + // Output: Tunnel creation success rate. + s << "\n"; + s << "> Tunnel creation success rate: " << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate() << "%"; + + // Output: Received. + s << "\n"; + s << "> Received: "; + showTraffic(s, i2p::transport::transports.GetTotalReceivedBytes()); + s << " ("< Sent: "; + showTraffic (s, i2p::transport::transports.GetTotalSentBytes()); + s << " ("< Transit: "; + showTraffic(s, i2p::transport::transports.GetTotalTransitTransmittedBytes()); + s << " ("< Router Ident: " << i2p::context.GetRouterInfo().GetIdentHashBase64(); + + // Output: Base info. + s << "\n"; + s << "> Router Caps: " << i2p::context.GetRouterInfo().GetProperty("caps"); + + s << "\n"; + s << "> Version: " << VERSION; + + s << "\n"; + s << "> Routers: " << i2p::data::netdb.GetNumRouters() << "."; + + s << "\n"; + s << "> Floodfills: " << i2p::data::netdb.GetNumFloodfills () << "."; + + s << "\n"; + s << "> LeaseSets: " << i2p::data::netdb.GetNumLeaseSets () << "."; + + size_t clientTunnelCount = i2p::tunnel::tunnels.CountOutboundTunnels(); + clientTunnelCount += i2p::tunnel::tunnels.CountInboundTunnels(); + size_t transitTunnelCount = i2p::tunnel::tunnels.CountTransitTunnels(); + + s << "\n"; + s << "> Client Tunnels: " << std::to_string(clientTunnelCount) << ".";; + + s << "\n"; + s << "> Transit Tunnels: " << std::to_string(transitTunnelCount) << "."; + + s << "\n\n---=== Info tunnelse I2PD. ===---\n\n"; + + s << "Client Tunnels:"; + auto httpProxy = i2p::client::context.GetHttpProxy(); + if(httpProxy) + { + s << "\n"; + auto& ident = httpProxy->GetLocalDestination()->GetIdentHash(); + s << "HTTP Proxy - " << i2p::client::context.GetAddressBook().ToAddress(ident);; + } + + auto socksProxy = i2p::client::context.GetSocksProxy(); + if(socksProxy) + { + s << "\n"; + auto& ident = socksProxy->GetLocalDestination()->GetIdentHash(); + s << "SOCKS Proxy - " << i2p::client::context.GetAddressBook().ToAddress(ident); + } + + auto& clientTunnels = i2p::client::context.GetClientTunnels(); + if(!clientTunnels.empty()) + { + s << "\n"; + for (auto& it: clientTunnels) + { + auto& ident = it.second->GetLocalDestination()->GetIdentHash(); + s << it.second->GetName() << " - "; + s << i2p::client::context.GetAddressBook().ToAddress(ident) << "\n"; + } + } + + auto& serverTunnels = i2p::client::context.GetServerTunnels(); + if(!serverTunnels.empty()) + { + s << "\n"; + s << "Server Tunnels:" << "\n"; + for(auto& it: serverTunnels) + { + auto& ident = it.second->GetLocalDestination()->GetIdentHash(); + s << it.second->GetName() << " - "; + s << i2p::client::context.GetAddressBook().ToAddress(ident); + s << ":" << it.second->GetLocalPort() << "\n"; + } + } + + auto& clientForwards = i2p::client::context.GetClientForwards(); + if(!clientForwards.empty()) + { + s << "\n"; + s << "Client Forwards:" << "\n"; + for (auto& it: clientForwards) + { + auto& ident = it.second->GetLocalDestination ()->GetIdentHash(); + s << it.second->GetName() << " - "; + s << i2p::client::context.GetAddressBook().ToAddress(ident) << "\n"; + } + } + + auto& serverForwards = i2p::client::context.GetServerForwards(); + if(!serverForwards.empty()) + { + s << "\n"; + s << "Server Forwards:\n"; + for(auto& it: serverForwards) + { + auto& ident = it.second->GetLocalDestination ()->GetIdentHash(); + s << it.second->GetName() << " - "; + s << i2p::client::context.GetAddressBook().ToAddress(ident) << "\n"; + } + } + + // Convert and send msg. + const std::string tmp = s.str(); + strcpy(msg, tmp.c_str()); + //std::cout << "[i] - " << msg; + bytesWritten += send(newSd, (char*)&msg, strlen(msg), 0); + } + catch(const char* msg) + { + std::cout << msg << std::endl; + } + } + else if(!strcmp(msg, "@:tr")) + { + std::cout << "[*] - Client send command \"transport\"." << std::endl; + std::stringstream s; + s << "\n\n---=== Transport info I2PD. ===---\n"; + + auto ntcp2Server = i2p::transport::transports.GetNTCP2Server(); + if (ntcp2Server) + { + auto sessions = ntcp2Server->GetNTCP2Sessions(); + if (!sessions.empty ()) + ShowTransportSessions(s, sessions, "NTCP2"); + } + auto ssu2Server = i2p::transport::transports.GetSSU2Server(); + if (ssu2Server) + { + auto sessions = ssu2Server->GetSSU2Sessions(); + if (!sessions.empty()) + ShowTransportSessions(s, sessions, "SSU2"); + } + + // Convert and send msg. + const std::string tmp = s.str(); + strcpy(msg, tmp.c_str()); + //std::cout << "[i] - " << msg << std::endl; + bytesWritten += send(newSd, (char*)&msg, strlen(msg), 0); + } + else if(!strcmp(msg, "@:?")) + { + std::stringstream s; + std::cout << "\n[*] - Client send command \"call helper\"." << std::endl; + s << "\nHelper server v0.0.2\n"; + s << "@:info - Output base info about i2pd router.\n"; + s << "@:tr - Output info transport i2pd.\n"; + s << "@:exit - close console i2pd client.\n"; + s << "@:stop - kill i2pd service and exit.\n"; + s << "[+] - End help."; + + const std::string tmp = s.str(); + strcpy(msg, tmp.c_str()); + bytesWritten += send(newSd, (char*)&msg, strlen(msg), 0); + } + else if(!strcmp(msg, "@:stop")) + { + std::cout << "[*] - Client send command \"stop\"." << std::endl; + strcpy(msg, "Bot -> Server is stop."); + bytesWritten += send(newSd, (char*)&msg, strlen(msg), 0); + codeStop = 1; + system("killall -s 9 ./i2pd"); + break; + } + else if(strcmp(msg, "")) + { + std::cout << "[*] - Client send command: " << std::endl; + strcpy(msg, "Bot -> command status: \"Bad!\" :("); + bytesWritten += send(newSd, (char*)&msg, strlen(msg), 0); + } + else + { + std::cout << "[!] - Disconnecting from the client!" << std::endl; + std::cout << "[-] - Restart server..." << std::endl; + break; + } + + //Output send msg (side server). + //std::cout << "[>] - Server send: " << msg << std::endl; + } + catch(const char* msg) + { + std::cout << "[!] - Disconnecting from the client." << std::endl; + std::cout << msg << std::endl; + break; + } + } + + _close(); + + if(codeStop >= 1) + { + std::cout << "[!] - Exit TCP server... \n" << "Stop code = " << codeStop << "\n"; + break; + } + } + } + + void TCPServer::Stop() + { + codeStop = 1; + resetBit = false; + std::cout << "[-] - Call distruct TCPServer, stop." << std::endl; + _close(); + } + + int TCPServer::GetCodeStop() + { + return codeStop; + } +} // tcp +} // i2p diff --git a/daemon/TCPServer.h b/daemon/TCPServer.h new file mode 100644 index 00000000..1ff76fe1 --- /dev/null +++ b/daemon/TCPServer.h @@ -0,0 +1,73 @@ +/* +* Copyright (c) 2023, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + +#ifndef TCP_SERVER_H__ +#define TCP_SERVER_H__ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include "HTTP.h" + +namespace i2p +{ +namespace tcp +{ + +class TCPServer +{ +private: + const char *IpAddress = "127.0.0.1"; + int num = 1; + int port = 49151; + char msg[8192]; + bool resetBit = true; + int codeStop = 0; + + sockaddr_in servAddr; + int serverSd; + int bindStatus; + + sockaddr_in newSockAddr; + socklen_t newSockAddrSize; + int newSd; + + struct timeval start1; + struct timeval end1; + + int bytesRead = 0; + int bytesWritten = 0; + + void _socker(); + void _bind(); + void _accept(); + void _recv(); + void _send(); + void _close(); + +public: + TCPServer(int _port); + TCPServer(){}; + ~TCPServer(); + + void Printf(); + void Start(); + void Run(); + void Stop(); + int GetCodeStop(); +}; + +} // tcp +} // i2p +#endif /* TCP_SERVER_H__ */