diff --git a/AddressBook.cpp b/AddressBook.cpp index 00b735ae..754d314a 100644 --- a/AddressBook.cpp +++ b/AddressBook.cpp @@ -396,7 +396,7 @@ namespace client LogPrint (eLogInfo, "Addressbook: ", m_Subscriptions.size (), " subscriptions urls loaded"); } else - LogPrint (eLogWarning, "Addresbook: subscriptions.txt not found"); + LogPrint (eLogWarning, "Addresbook: subscriptions.txt not found in datadir"); } else LogPrint (eLogError, "Addressbook: subscriptions already loaded"); diff --git a/ClientContext.cpp b/ClientContext.cpp index 0d112894..6d05cde1 100644 --- a/ClientContext.cpp +++ b/ClientContext.cpp @@ -2,6 +2,7 @@ #include #include #include +#include "Config.h" #include "util.h" #include "Log.h" #include "Identity.h" @@ -37,46 +38,58 @@ namespace client } std::shared_ptr localDestination; - // proxies - std::string proxyKeys = i2p::util::config::GetArg("-proxykeys", ""); - if (proxyKeys.length () > 0) - { - i2p::data::PrivateKeys keys; - LoadPrivateKeys (keys, proxyKeys); - localDestination = CreateNewLocalDestination (keys, false); + bool httproxy; i2p::config::GetOption("httpproxy.enabled", httproxy); + if (httproxy) { + std::string httpProxyKeys; i2p::config::GetOption("httpproxy.keys", httpProxyKeys); + std::string httpProxyAddr; i2p::config::GetOption("httpproxy.address", httpProxyAddr); + uint16_t httpProxyPort; i2p::config::GetOption("httpproxy.port", httpProxyPort); + LogPrint(eLogInfo, "Clients: starting HTTP Proxy at ", httpProxyAddr, ":", httpProxyPort); + if (httpProxyKeys.length () > 0) + { + i2p::data::PrivateKeys keys; + LoadPrivateKeys (keys, httpProxyKeys); + localDestination = CreateNewLocalDestination (keys, false); + } + m_HttpProxy = new i2p::proxy::HTTPProxy(httpProxyAddr, httpProxyPort, localDestination); + m_HttpProxy->Start(); } - std::string httpProxyAddr = i2p::util::config::GetArg("-httpproxyaddress", "127.0.0.1"); - uint16_t httpProxyPort = i2p::util::config::GetArg("-httpproxyport", 4444); - LogPrint(eLogInfo, "Clients: starting HTTP Proxy at ", httpProxyAddr, ":", httpProxyPort); - m_HttpProxy = new i2p::proxy::HTTPProxy(httpProxyAddr, httpProxyPort, localDestination); - m_HttpProxy->Start(); - std::string socksProxyAddr = i2p::util::config::GetArg("-socksproxyaddress", "127.0.0.1"); - uint16_t socksProxyPort = i2p::util::config::GetArg("-socksproxyport", 4447); - std::string socksOutProxyAddr = i2p::util::config::GetArg("-socksoutproxyaddress", ""); - uint16_t socksOutProxyPort = i2p::util::config::GetArg("-socksoutproxyport", 0); - LogPrint(eLogInfo, "Clients: starting SOCKS Proxy at ", socksProxyAddr, ":", socksProxyPort); - m_SocksProxy = new i2p::proxy::SOCKSProxy(socksProxyAddr, socksProxyPort, socksOutProxyAddr, socksOutProxyPort, localDestination); - m_SocksProxy->Start(); + bool socksproxy; i2p::config::GetOption("socksproxy.enabled", socksproxy); + if (socksproxy) { + std::string socksProxyKeys; i2p::config::GetOption("socksproxy.keys", socksProxyKeys); + std::string socksProxyAddr; i2p::config::GetOption("socksproxy.address", socksProxyAddr); + uint16_t socksProxyPort; i2p::config::GetOption("socksproxy.port", socksProxyPort); + std::string socksOutProxyAddr; i2p::config::GetOption("socksproxy.outproxy", socksOutProxyAddr); + uint16_t socksOutProxyPort; i2p::config::GetOption("socksproxy.outproxyport", socksOutProxyPort); + LogPrint(eLogInfo, "Clients: starting SOCKS Proxy at ", socksProxyAddr, ":", socksProxyPort); + if (socksProxyKeys.length () > 0) + { + i2p::data::PrivateKeys keys; + LoadPrivateKeys (keys, socksProxyKeys); + localDestination = CreateNewLocalDestination (keys, false); + } + m_SocksProxy = new i2p::proxy::SOCKSProxy(socksProxyAddr, socksProxyPort, socksOutProxyAddr, socksOutProxyPort, localDestination); + m_SocksProxy->Start(); + } // I2P tunnels ReadTunnels (); // SAM - std::string samAddr = i2p::util::config::GetArg("-samaddress", "127.0.0.1"); - uint16_t samPort = i2p::util::config::GetArg("-samport", 0); - if (samPort) - { - LogPrint(eLogInfo, "Clients: starting SAM bridge at", samAddr, ":", samPort); + bool sam; i2p::config::GetOption("sam.enabled", sam); + if (sam) { + std::string samAddr; i2p::config::GetOption("sam.address", samAddr); + uint16_t samPort; i2p::config::GetOption("sam.port", samPort); + LogPrint(eLogInfo, "Clients: starting SAM bridge at ", samAddr, ":", samPort); m_SamBridge = new SAMBridge (samAddr, samPort); m_SamBridge->Start (); } // BOB - std::string bobAddr = i2p::util::config::GetArg("-bobaddress", "127.0.0.1"); - uint16_t bobPort = i2p::util::config::GetArg("-bobport", 0); - if (bobPort) - { + bool bob; i2p::config::GetOption("bob.enabled", bob); + if (bob) { + std::string bobAddr; i2p::config::GetOption("bob.address", bobAddr); + uint16_t bobPort; i2p::config::GetOption("bob.port", bobPort); LogPrint(eLogInfo, "Clients: starting BOB command channel at ", bobAddr, ":", bobPort); m_BOBCommandChannel = new BOBCommandChannel (bobAddr, bobPort); m_BOBCommandChannel->Start (); @@ -87,15 +100,19 @@ namespace client void ClientContext::Stop () { - LogPrint(eLogInfo, "Clients: stopping HTTP Proxy"); - m_HttpProxy->Stop(); - delete m_HttpProxy; - m_HttpProxy = nullptr; + if (m_HttpProxy) { + LogPrint(eLogInfo, "Clients: stopping HTTP Proxy"); + m_HttpProxy->Stop(); + delete m_HttpProxy; + m_HttpProxy = nullptr; + } - LogPrint(eLogInfo, "Clients: stopping SOCKS Proxy"); - m_SocksProxy->Stop(); - delete m_SocksProxy; - m_SocksProxy = nullptr; + if (m_SocksProxy) { + LogPrint(eLogInfo, "Clients: stopping SOCKS Proxy"); + m_SocksProxy->Stop(); + delete m_SocksProxy; + m_SocksProxy = nullptr; + } for (auto& it: m_ClientTunnels) { diff --git a/Config.cpp b/Config.cpp new file mode 100644 index 00000000..d1bf7b64 --- /dev/null +++ b/Config.cpp @@ -0,0 +1,228 @@ +/* +* Copyright (c) 2013-2016, 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Config.h" +#include "version.h" + +using namespace boost::program_options; + +namespace i2p { +namespace config { + options_description m_OptionsDesc; + variables_map m_Options; + + /* list of renamed options */ + std::map remapped_options = { + { "tunnelscfg", "tunconf" }, + { "v6", "ipv6" }, + { "httpaddress", "http.address" }, + { "httpport", "http.port" }, + { "httpproxyaddress", "httpproxy.address" }, + { "httpproxyport", "httpproxy.port" }, + { "socksproxyaddress", "socksproxy.address" }, + { "socksproxyport", "socksproxy.port" }, + { "samaddress", "sam.address" }, + { "samport", "sam.port" }, + { "bobaddress", "bob.address" }, + { "bobport", "bob.port" }, + { "i2pcontroladdress", "i2pcontrol.address" }, + { "i2pcontroladdress", "i2pcontrol.port" }, + { "proxykeys", "httpproxy.keys" }, + }; + /* list of options, that loose their argument and become simple switch */ + std::set boolean_options = { + "daemon", "floodfill", "notransit", "service", "ipv6" + }; + + /* this function is a solid piece of shit, remove it after 2.6.0 */ + std::pair old_syntax_parser(const std::string& s) { + std::string name = ""; + std::string value = ""; + std::size_t pos = 0; + /* shortcuts -- -h */ + if (s.length() == 2 && s.at(0) == '-' && s.at(1) != '-') + return make_pair(s.substr(1), ""); + /* old-style -- -log, /log, etc */ + if (s.at(0) == '/' || (s.at(0) == '-' && s.at(1) != '-')) { + if ((pos = s.find_first_of("= ")) != std::string::npos) { + name = s.substr(1, pos - 1); + value = s.substr(pos + 1); + } else { + name = s.substr(1, pos); + value = ""; + } + if (boolean_options.count(name) > 0 && value != "") + std::cerr << "args: don't give an argument to switch option: " << s << std::endl; + if (m_OptionsDesc.find_nothrow(name, false)) { + std::cerr << "args: option " << s << " style is DEPRECATED, use --" << name << " instead" << std::endl; + return std::make_pair(name, value); + } + if (remapped_options.count(name) > 0) { + name = remapped_options[name]; + std::cerr << "args: option " << s << " is DEPRECATED, use --" << name << " instead" << std::endl; + return std::make_pair(name, value); + } /* else */ + } + /* long options -- --help */ + if (s.substr(0, 2) == "--") { + if ((pos = s.find_first_of("= ")) != std::string::npos) { + name = s.substr(2, pos - 2); + value = s.substr(pos + 1); + } else { + name = s.substr(2, pos); + value = ""; + } + if (boolean_options.count(name) > 0 && value != "") { + std::cerr << "args: don't give an argument to switch option: " << s << std::endl; + value = ""; + } + if (m_OptionsDesc.find_nothrow(name, false)) + return std::make_pair(name, value); + if (remapped_options.count(name) > 0) { + name = remapped_options[name]; + std::cerr << "args: option " << s << " is DEPRECATED, use --" << name << " instead" << std::endl; + return std::make_pair(name, value); + } /* else */ + } + std::cerr << "args: unknown option -- " << s << std::endl; + return std::make_pair("", ""); + } + + void Init() { + options_description general("General options"); + general.add_options() + ("help", "Show this message") + ("conf", value()->default_value(""), "Path to main i2pd config file (default: try ~/.i2pd/i2p.conf or /var/lib/i2pd/i2p.conf)") + ("tunconf", value()->default_value(""), "Path to config with tunnels list and options (default: try ~/.i2pd/tunnels.cfg or /var/lib/i2pd/tunnels.cfg)") + ("pidfile", value()->default_value(""), "Path to pidfile (default: ~/i2pd/i2pd.pid or /var/lib/i2pd/i2pd.pid)") + ("log", value()->default_value(""), "Logs destination: stdout, file (stdout if not set, file - otherwise, for compatibility)") + ("logfile", value()->default_value(""), "Path to logfile (stdout if not set, autodetect if daemon)") + ("loglevel", value()->default_value("info"), "Set the minimal level of log messages (debug, info, warn, error)") + ("host", value()->default_value("0.0.0.0"), "External IP") + ("port", value()->default_value(0), "Port to listen for incoming connections (default: auto)") + ("ipv6", value()->zero_tokens()->default_value(false), "Enable communication through ipv6") + ("daemon", value()->zero_tokens()->default_value(false), "Router will go to background after start") + ("service", value()->zero_tokens()->default_value(false), "Router will use system folders like '/var/lib/i2pd'") + ("notransit", value()->zero_tokens()->default_value(false), "Router will not accept transit tunnels at startup") + ("floodfill", value()->zero_tokens()->default_value(false), "Router will be floodfill") + ("bandwidth", value()->default_value('-'), "Bandwidth limiting: L - 32kbps, O - 256Kbps, P - unlimited") +#ifdef _WIN32 + ("svcctl", value()->default_value(""), "Windows service management ('install' or 'remove')") +#endif + ; + + options_description httpserver("HTTP Server options"); + httpserver.add_options() + ("http.enabled", value()->default_value(true), "Enable or disable webconsole") + ("http.address", value()->default_value("127.0.0.1"), "Webconsole listen address") + ("http.port", value()->default_value(7070), "Webconsole listen port") + ; + + options_description httpproxy("HTTP Proxy options"); + httpproxy.add_options() + ("httpproxy.enabled", value()->default_value(true), "Enable or disable HTTP Proxy") + ("httpproxy.address", value()->default_value("127.0.0.1"), "HTTP Proxy listen address") + ("httpproxy.port", value()->default_value(4444), "HTTP Proxy listen port") + ("httpproxy.keys", value()->default_value(""), "File to persist HTTP Proxy keys") + ; + + options_description socksproxy("SOCKS Proxy options"); + socksproxy.add_options() + ("socksproxy.enabled", value()->default_value(true), "Enable or disable SOCKS Proxy") + ("socksproxy.address", value()->default_value("127.0.0.1"), "SOCKS Proxy listen address") + ("socksproxy.port", value()->default_value(4447), "SOCKS Proxy listen port") + ("socksproxy.keys", value()->default_value(""), "File to persist SOCKS Proxy keys") + ; + + options_description sam("SAM bridge options"); + sam.add_options() + ("sam.enabled", value()->default_value(false), "Enable or disable SAM Application bridge") + ("sam.address", value()->default_value("127.0.0.1"), "SAM listen address") + ("sam.port", value()->default_value(7656), "SAM listen port") + ; + + options_description bob("BOB options"); + bob.add_options() + ("bob.enabled", value()->default_value(false), "Enable or disable BOB command channel") + ("bob.address", value()->default_value("127.0.0.1"), "BOB listen address") + ("bob.port", value()->default_value(2827), "BOB listen port") + ; + + options_description i2pcontrol("I2PControl options"); + i2pcontrol.add_options() + ("i2pcontrol.enabled", value()->default_value(false), "Enable or disable I2P Control Protocol") + ("i2pcontrol.address", value()->default_value("127.0.0.1"), "I2PCP listen address") + ("i2pcontrol.port", value()->default_value(7650), "I2PCP listen port") + ("i2pcontrol.password", value()->default_value("itoopie"), "I2PCP access password") + ("i2pcontrol.cert", value()->default_value("i2pcontrol.crt.pem"), "I2PCP connection cerificate") + ("i2pcontrol.key", value()->default_value("i2pcontrol.key.pem"), "I2PCP connection cerificate key") + ; + + m_OptionsDesc + .add(general) + .add(httpserver) + .add(httpproxy) + .add(socksproxy) + .add(sam) + .add(bob) + .add(i2pcontrol) + ; + } + + void ParseCmdline(int argc, char* argv[]) { + try { + auto style = boost::program_options::command_line_style::unix_style + | boost::program_options::command_line_style::allow_long_disguise; + style &= ~ boost::program_options::command_line_style::allow_guessing; + store(parse_command_line(argc, argv, m_OptionsDesc, style, old_syntax_parser), m_Options); + } catch (boost::program_options::error e) { + std::cerr << "args: " << e.what() << std::endl; + exit(EXIT_FAILURE); + } + + if (m_Options.count("help") || m_Options.count("h")) { + std::cout << "i2pd version " << I2PD_VERSION << " (" << I2P_VERSION << ")" << std::endl; + std::cout << m_OptionsDesc; + exit(EXIT_SUCCESS); + } + } + + void ParseConfig(const std::string& path) { + if (path == "") + return; + + std::ifstream config(path, std::ios::in); + + if (!config.is_open()) { + std::cerr << "missing/unreadable config file: " << path << std::endl; + exit(EXIT_FAILURE); + } + + try { + store(boost::program_options::parse_config_file(config, m_OptionsDesc), m_Options); + } catch (boost::program_options::error e) { + std::cerr << e.what() << std::endl; + exit(EXIT_FAILURE); + }; + } + + void Finalize() { + notify(m_Options); + }; +} // namespace config +} // namespace i2p diff --git a/Config.h b/Config.h new file mode 100644 index 00000000..51a12d70 --- /dev/null +++ b/Config.h @@ -0,0 +1,100 @@ +#ifndef CONFIG_H +#define CONFIG_H + +#include +#include +#include + +/** + * Functions to parse and store i2pd parameters + * + * General usage flow: + * Init() -- early as possible + * ParseCmdline() -- somewhere close to main() + * ParseConfig() -- after detecting path to config + * Finalize() -- right after all Parse*() functions called + * GetOption() -- may be called after Finalize() + */ + +namespace i2p { +namespace config { + extern boost::program_options::variables_map m_Options; + + /** + * @brief Initialize list of acceptable parameters + * + * Should be called before any Parse* functions. + */ + void Init(); + + /** + * @brief Parse cmdline parameters, and show help if requested + * @param argc Cmdline arguments count, should be passed from main(). + * @param argv Cmdline parameters array, should be passed from main() + * + * If --help is given in parameters, shows it's list with description + * terminates the program with exitcode 0. + * + * In case of parameter misuse boost throws an exception. + * We internally handle type boost::program_options::unknown_option, + * and then terminate program with exitcode 1. + * + * Other exceptions will be passed to higher level. + */ + void ParseCmdline(int argc, char* argv[]); + + /** + * @brief Load and parse given config file + * @param path Path to config file + * + * If error occured when opening file path is points to, + * we show the error message and terminate program. + * + * In case of parameter misuse boost throws an exception. + * We internally handle type boost::program_options::unknown_option, + * and then terminate program with exitcode 1. + * + * Other exceptions will be passed to higher level. + */ + void ParseConfig(const std::string& path); + + /** + * @brief Used to combine options from cmdline, config and default values + */ + void Finalize(); + + /* @brief Accessor to parameters by name + * @param name Name of the requested parameter + * @param value Variable where to store option + * @return this function returns false if parameter not found + * + * @example uint16_t port; GetOption("sam.port", port); + */ + template + bool GetOption(const char *name, T& value) { + if (!m_Options.count(name)) + return false; + value = m_Options[name].as(); + return true; + } + + /** + * @brief Set value of given parameter + * @param name Name of settable parameter + * @param value New parameter value + * @return true if value set up successful, false otherwise + * + * @example uint16_t port = 2827; SetOption("bob.port", port); + */ + template + bool SetOption(const char *name, const T& value) { + if (!m_Options.count(name)) + return false; + m_Options[name] = value; + notify(m_Options); + return true; + } +} +} + +#endif // CONFIG_H diff --git a/Daemon.cpp b/Daemon.cpp index ee221080..b9d8bd44 100644 --- a/Daemon.cpp +++ b/Daemon.cpp @@ -3,6 +3,7 @@ #include "Daemon.h" +#include "Config.h" #include "Log.h" #include "Base.h" #include "version.h" @@ -50,78 +51,121 @@ namespace i2p bool Daemon_Singleton::IsService () const { + bool service = false; #ifndef _WIN32 - return i2p::util::config::GetArg("-service", 0); -#else - return false; + i2p::config::GetOption("service", service); #endif + return service; } bool Daemon_Singleton::init(int argc, char* argv[]) { + i2p::config::Init(); + i2p::config::ParseCmdline(argc, argv); + + std::string config = i2p::util::filesystem::GetConfigFile().string(); + std::string tunconf = i2p::util::filesystem::GetTunnelsConfigFile().string(); + std::string datadir = i2p::util::filesystem::GetDataDir().string(); + + i2p::config::ParseConfig(config); + i2p::config::Finalize(); + i2p::crypto::InitCrypto (); - i2p::util::config::OptionParser(argc, argv); i2p::context.Init (); - LogPrint(eLogInfo, "i2pd v", VERSION, " starting"); - LogPrint(eLogDebug, "FS: data directory: ", i2p::util::filesystem::GetDataDir().string()); - i2p::util::config::ReadConfigFile(i2p::util::filesystem::GetConfigFile()); + i2p::config::GetOption("daemon", isDaemon); + + // TODO: move log init here - isDaemon = i2p::util::config::GetArg("-daemon", 0); - isLogging = i2p::util::config::GetArg("-log", (int)isDaemon); + LogPrint(eLogInfo, "i2pd v", VERSION, " starting"); + LogPrint(eLogDebug, "FS: main config file: ", config); + LogPrint(eLogDebug, "FS: tunnels config: ", tunconf); + LogPrint(eLogDebug, "FS: data directory: ", datadir); - int port = i2p::util::config::GetArg("-port", 0); + uint16_t port; i2p::config::GetOption("port", port); if (port) - i2p::context.UpdatePort (port); - std::string host = i2p::util::config::GetArg("-host", ""); - if (host != "") + { + LogPrint(eLogInfo, "Daemon: accepting incoming connections at port ", port); + i2p::context.UpdatePort (port); + } + + std::string host; i2p::config::GetOption("host", host); + if (host != "0.0.0.0") + { + LogPrint(eLogInfo, "Daemon: setting address for incoming connections to ", host); i2p::context.UpdateAddress (boost::asio::ip::address::from_string (host)); + } + + bool ipv6; i2p::config::GetOption("ipv6", ipv6); + bool transit; i2p::config::GetOption("notransit", transit); + i2p::context.SetSupportsV6 (ipv6); + i2p::context.SetAcceptsTunnels (!transit); + + bool isFloodfill; i2p::config::GetOption("floodfill", isFloodfill); + char bandwidth; i2p::config::GetOption("bandwidth", bandwidth); - i2p::context.SetSupportsV6 (i2p::util::config::GetArg("-v6", 0)); - i2p::context.SetAcceptsTunnels (!i2p::util::config::GetArg("-notransit", 0)); - bool isFloodfill = i2p::util::config::GetArg("-floodfill", 0); - i2p::context.SetFloodfill (isFloodfill); - auto bandwidth = i2p::util::config::GetArg("-bandwidth", ""); - if (bandwidth.length () > 0) + if (isFloodfill) { - if (bandwidth[0] > 'O') - i2p::context.SetExtraBandwidth (); - else if (bandwidth[0] > 'L') - i2p::context.SetHighBandwidth (); - else + LogPrint(eLogInfo, "Daemon: router will be floodfill"); + i2p::context.SetFloodfill (true); + } + if (bandwidth != '-') + { + LogPrint(eLogInfo, "Daemon: bandwidth set to ", bandwidth); + if (bandwidth > 'O') + i2p::context.SetExtraBandwidth (); + else if (bandwidth > 'L') + i2p::context.SetHighBandwidth (); + else i2p::context.SetLowBandwidth (); } - else if (isFloodfill) + else if (isFloodfill) + { + LogPrint(eLogInfo, "Daemon: floodfill bandwidth set to 'extra'"); i2p::context.SetExtraBandwidth (); - LogPrint(eLogDebug, "Daemon: CMD parameters:"); - for (int i = 0; i < argc; ++i) - LogPrint(eLogDebug, i, ": ", argv[i]); + } + else + i2p::context.SetLowBandwidth (); return true; } bool Daemon_Singleton::start() { - // initialize log - if (isLogging) - { - std::string logfile_path = IsService () ? "/var/log/i2pd" : i2p::util::filesystem::GetDataDir().string(); + std::string logs = ""; i2p::config::GetOption("log", logs); + std::string logfile = ""; i2p::config::GetOption("logfile", logfile); + std::string loglevel = ""; i2p::config::GetOption("loglevel", loglevel); + + if (isDaemon && (logs == "" || logs == "stdout")) + logs = "file"; + + if (logs == "file") + { + if (logfile == "") + { + // use autodetect of logfile + logfile = IsService () ? "/var/log" : i2p::util::filesystem::GetDataDir().string(); #ifndef _WIN32 - logfile_path.append("/i2pd.log"); + logfile.append("/i2pd.log"); #else - logfile_path.append("\\i2pd.log"); + logfile.append("\\i2pd.log"); #endif - StartLog (logfile_path); + } + StartLog (logfile); + } else { + // use stdout + StartLog (""); } - else - StartLog (""); // write to stdout - g_Log->SetLogLevel(i2p::util::config::GetArg("-loglevel", "info")); + SetLogLevel(loglevel); - std::string httpAddr = i2p::util::config::GetArg("-httpaddress", "127.0.0.1"); - uint16_t httpPort = i2p::util::config::GetArg("-httpport", 7070); - LogPrint(eLogInfo, "Daemon: staring HTTP Server at ", httpAddr, ":", httpPort); - d.httpServer = std::unique_ptr(new i2p::util::HTTPServer(httpAddr, httpPort)); - d.httpServer->Start(); + bool http; i2p::config::GetOption("http.enabled", http); + if (http) { + std::string httpAddr; i2p::config::GetOption("http.address", httpAddr); + uint16_t httpPort; i2p::config::GetOption("http.port", httpPort); + LogPrint(eLogInfo, "Daemon: starting HTTP Server at ", httpAddr, ":", httpPort); + d.httpServer = std::unique_ptr(new i2p::util::HTTPServer(httpAddr, httpPort)); + d.httpServer->Start(); + } LogPrint(eLogInfo, "Daemon: starting NetDB"); i2p::data::netdb.Start(); @@ -140,10 +184,10 @@ namespace i2p i2p::client::context.Start (); // I2P Control Protocol - std::string i2pcpAddr = i2p::util::config::GetArg("-i2pcontroladdress", "127.0.0.1"); - uint16_t i2pcpPort = i2p::util::config::GetArg("-i2pcontrolport", 0); - if (i2pcpPort) - { + bool i2pcontrol; i2p::config::GetOption("i2pcontrol.enabled", i2pcontrol); + if (i2pcontrol) { + std::string i2pcpAddr; i2p::config::GetOption("i2pcontrol.address", i2pcpAddr); + uint16_t i2pcpPort; i2p::config::GetOption("i2pcontrol.port", i2pcpPort); LogPrint(eLogInfo, "Daemon: starting I2PControl at ", i2pcpAddr, ":", i2pcpPort); d.m_I2PControlService = std::unique_ptr(new i2p::client::I2PControlService (i2pcpAddr, i2pcpPort)); d.m_I2PControlService->Start (); @@ -166,9 +210,11 @@ namespace i2p i2p::transport::transports.Stop(); LogPrint(eLogInfo, "Daemon: stopping NetDB"); i2p::data::netdb.Stop(); - LogPrint(eLogInfo, "Daemon: stopping HTTP Server"); - d.httpServer->Stop(); - d.httpServer = nullptr; + if (d.httpServer) { + LogPrint(eLogInfo, "Daemon: stopping HTTP Server"); + d.httpServer->Stop(); + d.httpServer = nullptr; + } if (d.m_I2PControlService) { LogPrint(eLogInfo, "Daemon: stopping I2PControl"); diff --git a/DaemonLinux.cpp b/DaemonLinux.cpp index 306fc7ca..89df5306 100644 --- a/DaemonLinux.cpp +++ b/DaemonLinux.cpp @@ -8,10 +8,10 @@ #include #include +#include "Config.h" #include "Log.h" #include "util.h" - void handle_signal(int sig) { switch (sig) @@ -28,7 +28,6 @@ void handle_signal(int sig) } } - namespace i2p { namespace util @@ -74,7 +73,7 @@ namespace i2p // Pidfile // this code is c-styled and a bit ugly, but we need fd for locking pidfile - pidfile = i2p::util::config::GetArg("-pidfile", ""); + std::string pidfile; i2p::config::GetOption("pidfile", pidfile); if (pidfile == "") { pidfile = IsService () ? "/var/run" : i2p::util::filesystem::GetDataDir().string(); pidfile.append("/i2pd.pid"); @@ -120,7 +119,6 @@ namespace i2p return Daemon_Singleton::stop(); } - } } diff --git a/DaemonWin32.cpp b/DaemonWin32.cpp index e09bf077..4ab65040 100644 --- a/DaemonWin32.cpp +++ b/DaemonWin32.cpp @@ -1,3 +1,4 @@ +#include "Config.h" #include "Daemon.h" #include "util.h" #include "Log.h" @@ -23,7 +24,7 @@ namespace i2p else isDaemon = 0; - std::string serviceControl = i2p::util::config::GetArg("-svcctl", ""); + std::string serviceControl; i2p::config::GetOption("svcctl", serviceControl); if (serviceControl == "install") { LogPrint(eLogInfo, "WinSVC: installing ", SERVICE_NAME, " as service"); diff --git a/I2NPProtocol.cpp b/I2NPProtocol.cpp index 2cd2b0b2..c31a4479 100644 --- a/I2NPProtocol.cpp +++ b/I2NPProtocol.cpp @@ -80,6 +80,15 @@ namespace i2p LogPrint (eLogError, "I2NP: message length ", len, " exceeds max length"); return msg; } + + std::shared_ptr CopyI2NPMessage (std::shared_ptr msg) + { + if (!msg) return nullptr; + auto newMsg = NewI2NPMessage (msg->len); + newMsg->offset = msg->offset; + *newMsg = *msg; + return newMsg; + } std::shared_ptr CreateDeliveryStatusMsg (uint32_t msgID) { diff --git a/I2NPProtocol.h b/I2NPProtocol.h index 898396d0..173b57fb 100644 --- a/I2NPProtocol.h +++ b/I2NPProtocol.h @@ -174,7 +174,7 @@ namespace tunnel from = other.from; return *this; } - + // for SSU only uint8_t * GetSSUHeader () { return buf + offset + I2NP_HEADER_SIZE - I2NP_SHORT_HEADER_SIZE; }; void FromSSU (uint32_t msgID) // we have received SSU message and convert it to regular @@ -215,7 +215,8 @@ namespace tunnel std::shared_ptr CreateI2NPMessage (I2NPMessageType msgType, const uint8_t * buf, size_t len, uint32_t replyMsgID = 0); std::shared_ptr CreateI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr from = nullptr); - + std::shared_ptr CopyI2NPMessage (std::shared_ptr msg); + std::shared_ptr CreateDeliveryStatusMsg (uint32_t msgID); std::shared_ptr CreateRouterInfoDatabaseLookupMsg (const uint8_t * key, const uint8_t * from, uint32_t replyTunnelID, bool exploratory = false, std::set * excludedPeers = nullptr); diff --git a/I2PControl.cpp b/I2PControl.cpp index ad81ec93..776cfe86 100644 --- a/I2PControl.cpp +++ b/I2PControl.cpp @@ -1,5 +1,3 @@ -// There is bug in boost 1.49 with gcc 4.7 coming with Debian Wheezy -#define GCC47_BOOST149 ((BOOST_VERSION == 104900) && (__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) #include #include #include @@ -8,10 +6,15 @@ #include #include #include + +// There is bug in boost 1.49 with gcc 4.7 coming with Debian Wheezy +#define GCC47_BOOST149 ((BOOST_VERSION == 104900) && (__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) #if !GCC47_BOOST149 #include #endif + #include "Log.h" +#include "Config.h" #include "NetDb.h" #include "RouterContext.h" #include "Daemon.h" @@ -26,56 +29,62 @@ namespace i2p namespace client { I2PControlService::I2PControlService (const std::string& address, int port): - m_Password (I2P_CONTROL_DEFAULT_PASSWORD), m_IsRunning (false), m_Thread (nullptr), + m_IsRunning (false), m_Thread (nullptr), m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), port)), m_SSLContext (m_Service, boost::asio::ssl::context::sslv23), m_ShutdownTimer (m_Service) { - LoadConfig (); - // certificate + i2p::config::GetOption("i2pcontrol.password", m_Password); + + // certificate / keys + std::string i2pcp_crt; i2p::config::GetOption("i2pcontrol.cert", i2pcp_crt); + std::string i2pcp_key; i2p::config::GetOption("i2pcontrol.key", i2pcp_key); auto path = GetPath (); - if (!boost::filesystem::exists (path)) - { - if (!boost::filesystem::create_directory (path)) - LogPrint (eLogError, "Failed to create i2pcontrol directory"); - } - if (!boost::filesystem::exists (path / I2P_CONTROL_KEY_FILE) || - !boost::filesystem::exists (path / I2P_CONTROL_CERT_FILE)) + // TODO: move this to i2p::fs::expand + if (i2pcp_crt.at(0) != '/') + i2pcp_crt.insert(0, (path / "/").string()); + if (i2pcp_key.at(0) != '/') + i2pcp_key.insert(0, (path / "/").string()); + if (!boost::filesystem::exists (i2pcp_crt) || + !boost::filesystem::exists (i2pcp_key)) { - // create new certificate - CreateCertificate (); - LogPrint (eLogInfo, "I2PControl certificates created"); + LogPrint (eLogInfo, "I2PControl: creating new certificate for control connection"); + CreateCertificate (i2pcp_crt.c_str(), i2pcp_key.c_str()); + } else { + LogPrint(eLogDebug, "I2PControl: using cert from ", i2pcp_crt); } m_SSLContext.set_options (boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2 | boost::asio::ssl::context::single_dh_use); - m_SSLContext.use_certificate_file ((path / I2P_CONTROL_CERT_FILE).string (), boost::asio::ssl::context::pem); - m_SSLContext.use_private_key_file ((path / I2P_CONTROL_KEY_FILE).string (), boost::asio::ssl::context::pem); + m_SSLContext.use_certificate_file (i2pcp_crt, boost::asio::ssl::context::pem); + m_SSLContext.use_private_key_file (i2pcp_key, boost::asio::ssl::context::pem); // handlers - m_MethodHandlers[I2P_CONTROL_METHOD_AUTHENTICATE] = &I2PControlService::AuthenticateHandler; - m_MethodHandlers[I2P_CONTROL_METHOD_ECHO] = &I2PControlService::EchoHandler; - m_MethodHandlers[I2P_CONTROL_METHOD_I2PCONTROL] = &I2PControlService::I2PControlHandler; - m_MethodHandlers[I2P_CONTROL_METHOD_ROUTER_INFO] = &I2PControlService::RouterInfoHandler; - m_MethodHandlers[I2P_CONTROL_METHOD_ROUTER_MANAGER] = &I2PControlService::RouterManagerHandler; - m_MethodHandlers[I2P_CONTROL_METHOD_NETWORK_SETTING] = &I2PControlService::NetworkSettingHandler; + m_MethodHandlers["Authenticate"] = &I2PControlService::AuthenticateHandler; + m_MethodHandlers["Echo"] = &I2PControlService::EchoHandler; + m_MethodHandlers["I2PControl"] = &I2PControlService::I2PControlHandler; + m_MethodHandlers["RouterInfo"] = &I2PControlService::RouterInfoHandler; + m_MethodHandlers["RouterManager"] = &I2PControlService::RouterManagerHandler; + m_MethodHandlers["NetworkSetting"] = &I2PControlService::NetworkSettingHandler; // I2PControl - m_I2PControlHandlers[I2P_CONTROL_I2PCONTROL_PASSWORD] = &I2PControlService::PasswordHandler; + m_I2PControlHandlers["i2pcontrol.password"] = &I2PControlService::PasswordHandler; // RouterInfo - m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_UPTIME] = &I2PControlService::UptimeHandler; - m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_VERSION] = &I2PControlService::VersionHandler; - m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_STATUS] = &I2PControlService::StatusHandler; - m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_NETDB_KNOWNPEERS] = &I2PControlService::NetDbKnownPeersHandler; - m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_NETDB_ACTIVEPEERS] = &I2PControlService::NetDbActivePeersHandler; - m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_NET_STATUS] = &I2PControlService::NetStatusHandler; - m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_TUNNELS_PARTICIPATING] = &I2PControlService::TunnelsParticipatingHandler; - m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_BW_IB_1S] = &I2PControlService::InboundBandwidth1S ; - m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_BW_OB_1S] = &I2PControlService::OutboundBandwidth1S ; + m_RouterInfoHandlers["i2p.router.uptime"] = &I2PControlService::UptimeHandler; + m_RouterInfoHandlers["i2p.router.version"] = &I2PControlService::VersionHandler; + m_RouterInfoHandlers["i2p.router.status"] = &I2PControlService::StatusHandler; + m_RouterInfoHandlers["i2p.router.netdb.knownpeers"] = &I2PControlService::NetDbKnownPeersHandler; + m_RouterInfoHandlers["i2p.router.netdb.activepeers"] = &I2PControlService::NetDbActivePeersHandler; + m_RouterInfoHandlers["i2p.router.net.bw.inbound.1s"] = &I2PControlService::InboundBandwidth1S; + m_RouterInfoHandlers["i2p.router.net.bw.outbound.1s"] = &I2PControlService::OutboundBandwidth1S; + m_RouterInfoHandlers["i2p.router.net.status"] = &I2PControlService::NetStatusHandler; + m_RouterInfoHandlers["i2p.router.net.tunnels.participating"] = &I2PControlService::TunnelsParticipatingHandler; + m_RouterInfoHandlers["i2p.router.net.total.received.bytes"] = &I2PControlService::NetTotalReceivedBytes; + m_RouterInfoHandlers["i2p.router.net.total.sent.bytes"] = &I2PControlService::NetTotalSentBytes; // RouterManager - m_RouterManagerHandlers[I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN] = &I2PControlService::ShutdownHandler; - m_RouterManagerHandlers[I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN_GRACEFUL] = &I2PControlService::ShutdownGracefulHandler; - m_RouterManagerHandlers[I2P_CONTROL_ROUTER_MANAGER_RESEED] = &I2PControlService::ReseedHandler; + m_RouterManagerHandlers["Reseed"] = &I2PControlService::ReseedHandler; + m_RouterManagerHandlers["Shutdown"] = &I2PControlService::ShutdownHandler; + m_RouterManagerHandlers["ShutdownGraceful"] = &I2PControlService::ShutdownGracefulHandler; } I2PControlService::~I2PControlService () @@ -83,49 +92,6 @@ namespace client Stop (); } - void I2PControlService::LoadConfig () - { - auto path = GetPath (); - if (!boost::filesystem::exists (path)) - { - if (!boost::filesystem::create_directory (path)) - LogPrint (eLogError, "Failed to create i2pcontrol directory"); - } - boost::property_tree::ptree pt; - auto filename = path / I2P_CONTROL_CONFIG_FILE; - bool isNew = true; - if (boost::filesystem::exists (filename)) - { - try - { - boost::property_tree::read_ini (filename.string (), pt); - isNew = false; - } - catch (std::exception& ex) - { - LogPrint (eLogError, "Can't read ", filename, ": ", ex.what ()); - } - } - m_Password = pt.get (I2P_CONTROL_I2PCONTROL_PASSWORD, I2P_CONTROL_DEFAULT_PASSWORD); - if (isNew) SaveConfig (); - } - - void I2PControlService::SaveConfig () - { - boost::property_tree::ptree pt; - pt.put (I2P_CONTROL_I2PCONTROL_PASSWORD, m_Password); - auto filename = GetPath () / I2P_CONTROL_CONFIG_FILE; - // we take care about directory in LoadConfig - try - { - boost::property_tree::write_ini (filename.string (), pt); - } - catch (std::exception& ex) - { - LogPrint (eLogError, "Can't write ", filename, ": ", ex.what ()); - } - } - void I2PControlService::Start () { if (!m_IsRunning) @@ -141,28 +107,25 @@ namespace client if (m_IsRunning) { m_IsRunning = false; - m_Acceptor.cancel (); + m_Acceptor.cancel (); m_Service.stop (); if (m_Thread) - { - m_Thread->join (); + { + m_Thread->join (); delete m_Thread; m_Thread = nullptr; - } + } } } - void I2PControlService::Run () - { + void I2PControlService::Run () + { while (m_IsRunning) { - try - { + try { m_Service.run (); - } - catch (std::exception& ex) - { - LogPrint (eLogError, "I2PControl: ", ex.what ()); + } catch (std::exception& ex) { + LogPrint (eLogError, "I2PControl: runtime exception: ", ex.what ()); } } } @@ -179,13 +142,12 @@ namespace client if (ecode != boost::asio::error::operation_aborted) Accept (); - if (!ecode) - { - LogPrint (eLogInfo, "New I2PControl request from ", socket->lowest_layer ().remote_endpoint ()); - Handshake (socket); + if (ecode) { + LogPrint (eLogError, "I2PControl: accept error: ", ecode.message ()); + return; } - else - LogPrint (eLogError, "I2PControl accept error: ", ecode.message ()); + LogPrint (eLogDebug, "I2PControl: new request from ", socket->lowest_layer ().remote_endpoint ()); + Handshake (socket); } void I2PControlService::Handshake (std::shared_ptr socket) @@ -196,13 +158,12 @@ namespace client void I2PControlService::HandleHandshake (const boost::system::error_code& ecode, std::shared_ptr socket) { - if (!ecode) - { - //std::this_thread::sleep_for (std::chrono::milliseconds(5)); - ReadRequest (socket); + if (ecode) { + LogPrint (eLogError, "I2PControl: handshake error: ", ecode.message ()); + return; } - else - LogPrint (eLogError, "I2PControl handshake error: ", ecode.message ()); + //std::this_thread::sleep_for (std::chrono::milliseconds(5)); + ReadRequest (socket); } void I2PControlService::ReadRequest (std::shared_ptr socket) @@ -210,24 +171,22 @@ namespace client auto request = std::make_shared(); socket->async_read_some ( #if defined(BOOST_ASIO_HAS_STD_ARRAY) - boost::asio::buffer (*request), + boost::asio::buffer (*request), #else - boost::asio::buffer (request->data (), request->size ()), -#endif - std::bind(&I2PControlService::HandleRequestReceived, this, + boost::asio::buffer (request->data (), request->size ()), +#endif + std::bind(&I2PControlService::HandleRequestReceived, this, std::placeholders::_1, std::placeholders::_2, socket, request)); } void I2PControlService::HandleRequestReceived (const boost::system::error_code& ecode, - size_t bytes_transferred, std::shared_ptr socket, + size_t bytes_transferred, std::shared_ptr socket, std::shared_ptr buf) { - if (ecode) - { - LogPrint (eLogError, "I2PControl read error: ", ecode.message ()); - } - else - { + if (ecode) { + LogPrint (eLogError, "I2PControl: read error: ", ecode.message ()); + return; + } else { try { bool isHtml = !memcmp (buf->data (), "POST", 4); @@ -238,12 +197,12 @@ namespace client std::string header; size_t contentLength = 0; while (!ss.eof () && header != "\r") - { + { std::getline(ss, header); auto colon = header.find (':'); if (colon != std::string::npos && header.substr (0, colon) == "Content-Length") contentLength = std::stoi (header.substr (colon + 1)); - } + } if (ss.eof ()) { LogPrint (eLogError, "Malformed I2PControl request. HTTP header expected"); @@ -251,10 +210,10 @@ namespace client } std::streamoff rem = contentLength + ss.tellg () - bytes_transferred; // more bytes to read if (rem > 0) - { + { bytes_transferred = boost::asio::read (*socket, boost::asio::buffer (buf->data (), rem)); ss.write (buf->data (), bytes_transferred); - } + } } #if GCC47_BOOST149 LogPrint (eLogError, "json_read is not supported due bug in boost 1.49 with gcc 4.7"); @@ -262,17 +221,17 @@ namespace client boost::property_tree::ptree pt; boost::property_tree::read_json (ss, pt); - std::string method = pt.get(I2P_CONTROL_PROPERTY_METHOD); + std::string id = pt.get("id"); + std::string method = pt.get("method"); auto it = m_MethodHandlers.find (method); if (it != m_MethodHandlers.end ()) { std::ostringstream response; - response << "{\"id\":" << pt.get(I2P_CONTROL_PROPERTY_ID) << ",\"result\":{"; - - (this->*(it->second))(pt.get_child (I2P_CONTROL_PROPERTY_PARAMS), response); + response << "{\"id\":" << id << ",\"result\":{"; + (this->*(it->second))(pt.get_child ("params"), response); response << "},\"jsonrpc\":\"2.0\"}"; SendResponse (socket, buf, response, isHtml); - } + } else LogPrint (eLogWarning, "Unknown I2PControl method ", method); #endif @@ -291,21 +250,21 @@ namespace client void I2PControlService::InsertParam (std::ostringstream& ss, const std::string& name, int value) const { ss << "\"" << name << "\":" << value; - } + } void I2PControlService::InsertParam (std::ostringstream& ss, const std::string& name, const std::string& value) const { - ss << "\"" << name << "\":"; + ss << "\"" << name << "\":"; if (value.length () > 0) ss << "\"" << value << "\""; else ss << "null"; - } - + } + void I2PControlService::InsertParam (std::ostringstream& ss, const std::string& name, double value) const { ss << "\"" << name << "\":" << std::fixed << std::setprecision(2) << value; - } + } void I2PControlService::SendResponse (std::shared_ptr socket, std::shared_ptr buf, std::ostringstream& response, bool isHtml) @@ -321,48 +280,49 @@ namespace client header << "Date: "; auto facet = new boost::local_time::local_time_facet ("%a, %d %b %Y %H:%M:%S GMT"); header.imbue(std::locale (header.getloc(), facet)); - header << boost::posix_time::second_clock::local_time() << "\r\n"; + header << boost::posix_time::second_clock::local_time() << "\r\n"; header << "\r\n"; offset = header.str ().size (); memcpy (buf->data (), header.str ().c_str (), offset); - } + } memcpy (buf->data () + offset, response.str ().c_str (), len); - boost::asio::async_write (*socket, boost::asio::buffer (buf->data (), offset + len), + boost::asio::async_write (*socket, boost::asio::buffer (buf->data (), offset + len), boost::asio::transfer_all (), - std::bind(&I2PControlService::HandleResponseSent, this, + std::bind(&I2PControlService::HandleResponseSent, this, std::placeholders::_1, std::placeholders::_2, socket, buf)); } void I2PControlService::HandleResponseSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, std::shared_ptr socket, std::shared_ptr buf) { - if (ecode) - LogPrint (eLogError, "I2PControl write error: ", ecode.message ()); + if (ecode) { + LogPrint (eLogError, "I2PControl: write error: ", ecode.message ()); + } } // handlers void I2PControlService::AuthenticateHandler (const boost::property_tree::ptree& params, std::ostringstream& results) { - int api = params.get (I2P_CONTROL_PARAM_API); - auto password = params.get (I2P_CONTROL_PARAM_PASSWORD); - LogPrint (eLogDebug, "I2PControl Authenticate API=", api, " Password=", password); + int api = params.get ("API"); + auto password = params.get ("Password"); + LogPrint (eLogDebug, "I2PControl: Authenticate API=", api, " Password=", password); if (password != m_Password) { LogPrint (eLogError, "I2PControl: Authenticate - Invalid password: ", password); return; } - InsertParam (results, I2P_CONTROL_PARAM_API, api); + InsertParam (results, "API", api); results << ","; std::string token = boost::lexical_cast(i2p::util::GetSecondsSinceEpoch ()); m_Tokens.insert (token); - InsertParam (results, I2P_CONTROL_PARAM_TOKEN, token); + InsertParam (results, "Token", token); } void I2PControlService::EchoHandler (const boost::property_tree::ptree& params, std::ostringstream& results) { - auto echo = params.get (I2P_CONTROL_PARAM_ECHO); + auto echo = params.get ("Echo"); LogPrint (eLogDebug, "I2PControl Echo Echo=", echo); - InsertParam (results, I2P_CONTROL_PARAM_RESULT, echo); + InsertParam (results, "Result", echo); } @@ -370,120 +330,129 @@ namespace client void I2PControlService::I2PControlHandler (const boost::property_tree::ptree& params, std::ostringstream& results) { - LogPrint (eLogDebug, "I2PControl I2PControl"); for (auto& it: params) { - LogPrint (eLogDebug, it.first); + LogPrint (eLogDebug, "I2PControl: I2PControl request: ", it.first); auto it1 = m_I2PControlHandlers.find (it.first); if (it1 != m_I2PControlHandlers.end ()) { - (this->*(it1->second))(it.second.data ()); - InsertParam (results, it.first, ""); + (this->*(it1->second))(it.second.data ()); + InsertParam (results, it.first, ""); } else - LogPrint (eLogError, "I2PControl I2PControl unknown request ", it.first); + LogPrint (eLogError, "I2PControl: I2PControl unknown request: ", it.first); } } void I2PControlService::PasswordHandler (const std::string& value) { - LogPrint (eLogDebug, "I2PControl new password=", value); + LogPrint (eLogWarning, "I2PControl: new password=", value, ", to make it persistent you should update your config!"); m_Password = value; m_Tokens.clear (); - SaveConfig (); } // RouterInfo void I2PControlService::RouterInfoHandler (const boost::property_tree::ptree& params, std::ostringstream& results) { - LogPrint (eLogDebug, "I2PControl RouterInfo"); for (auto it = params.begin (); it != params.end (); it++) { - LogPrint (eLogDebug, it->first); + LogPrint (eLogDebug, "I2PControl: RouterInfo request: ", it->first); auto it1 = m_RouterInfoHandlers.find (it->first); if (it1 != m_RouterInfoHandlers.end ()) { - if (it != params.begin ()) results << ","; - (this->*(it1->second))(results); - } + if (it != params.begin ()) results << ","; + (this->*(it1->second))(results); + } else - LogPrint (eLogError, "I2PControl RouterInfo unknown request ", it->first); + LogPrint (eLogError, "I2PControl: RouterInfo unknown request ", it->first); } } void I2PControlService::UptimeHandler (std::ostringstream& results) { - InsertParam (results, I2P_CONTROL_ROUTER_INFO_UPTIME, (int)i2p::context.GetUptime ()*1000); + InsertParam (results, "i2p.router.uptime", (int)i2p::context.GetUptime ()*1000); } void I2PControlService::VersionHandler (std::ostringstream& results) { - InsertParam (results, I2P_CONTROL_ROUTER_INFO_VERSION, VERSION); + InsertParam (results, "i2p.router.version", VERSION); } void I2PControlService::StatusHandler (std::ostringstream& results) { - InsertParam (results, I2P_CONTROL_ROUTER_INFO_STATUS, "???"); // TODO: + InsertParam (results, "i2p.router.status", "???"); // TODO: } - + void I2PControlService::NetDbKnownPeersHandler (std::ostringstream& results) { - InsertParam (results, I2P_CONTROL_ROUTER_INFO_NETDB_KNOWNPEERS, i2p::data::netdb.GetNumRouters ()); + InsertParam (results, "i2p.router.netdb.knownpeers", i2p::data::netdb.GetNumRouters ()); } void I2PControlService::NetDbActivePeersHandler (std::ostringstream& results) { - InsertParam (results, I2P_CONTROL_ROUTER_INFO_NETDB_ACTIVEPEERS, (int)i2p::transport::transports.GetPeers ().size ()); + InsertParam (results, "i2p.router.netdb.activepeers", (int)i2p::transport::transports.GetPeers ().size ()); } void I2PControlService::NetStatusHandler (std::ostringstream& results) { - InsertParam (results, I2P_CONTROL_ROUTER_INFO_NET_STATUS, (int)i2p::context.GetStatus ()); + InsertParam (results, "i2p.router.net.status", (int)i2p::context.GetStatus ()); } void I2PControlService::TunnelsParticipatingHandler (std::ostringstream& results) { - InsertParam (results, I2P_CONTROL_ROUTER_INFO_TUNNELS_PARTICIPATING, (int)i2p::tunnel::tunnels.GetTransitTunnels ().size ()); + int transit = i2p::tunnel::tunnels.GetTransitTunnels ().size (); + InsertParam (results, "i2p.router.net.tunnels.participating", transit); } void I2PControlService::InboundBandwidth1S (std::ostringstream& results) { - InsertParam (results, I2P_CONTROL_ROUTER_INFO_BW_IB_1S, (double)i2p::transport::transports.GetInBandwidth ()); + double bw = i2p::transport::transports.GetInBandwidth (); + InsertParam (results, "i2p.router.net.bw.inbound.1s", bw); } void I2PControlService::OutboundBandwidth1S (std::ostringstream& results) { - InsertParam (results, I2P_CONTROL_ROUTER_INFO_BW_OB_1S, (double)i2p::transport::transports.GetOutBandwidth ()); + double bw = i2p::transport::transports.GetOutBandwidth (); + InsertParam (results, "i2p.router.net.bw.outbound.1s", bw); + } + + void I2PControlService::NetTotalReceivedBytes (std::ostringstream& results) + { + InsertParam (results, "i2p.router.net.total.received.bytes", (double)i2p::transport::transports.GetTotalReceivedBytes ()); + } + + void I2PControlService::NetTotalSentBytes (std::ostringstream& results) + { + InsertParam (results, "i2p.router.net.total.sent.bytes", (double)i2p::transport::transports.GetTotalSentBytes ()); } // RouterManager - + void I2PControlService::RouterManagerHandler (const boost::property_tree::ptree& params, std::ostringstream& results) { - LogPrint (eLogDebug, "I2PControl RouterManager"); for (auto it = params.begin (); it != params.end (); it++) { if (it != params.begin ()) results << ","; - LogPrint (eLogDebug, it->first); + LogPrint (eLogDebug, "I2PControl: RouterManager request: ", it->first); auto it1 = m_RouterManagerHandlers.find (it->first); - if (it1 != m_RouterManagerHandlers.end ()) + if (it1 != m_RouterManagerHandlers.end ()) { (this->*(it1->second))(results); - else - LogPrint (eLogError, "I2PControl RouterManager unknown request ", it->first); + } else + LogPrint (eLogError, "I2PControl: RouterManager unknown request: ", it->first); } - } + } - void I2PControlService::ShutdownHandler (std::ostringstream& results) + void I2PControlService::ShutdownHandler (std::ostringstream& results) { - LogPrint (eLogInfo, "Shutdown requested"); - InsertParam (results, I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN, ""); + LogPrint (eLogInfo, "I2PControl: Shutdown requested"); + InsertParam (results, "Shutdown", ""); m_ShutdownTimer.expires_from_now (boost::posix_time::seconds(1)); // 1 second to make sure response has been sent m_ShutdownTimer.async_wait ( [](const boost::system::error_code& ecode) { - Daemon.running = 0; + Daemon.running = 0; }); } @@ -491,42 +460,42 @@ namespace client { i2p::context.SetAcceptsTunnels (false); int timeout = i2p::tunnel::tunnels.GetTransitTunnelsExpirationTimeout (); - LogPrint (eLogInfo, "Graceful shutdown requested. Will shutdown after ", timeout, " seconds"); - InsertParam (results, I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN_GRACEFUL, ""); + LogPrint (eLogInfo, "I2PControl: Graceful shutdown requested, ", timeout, " seconds remains"); + InsertParam (results, "ShutdownGraceful", ""); m_ShutdownTimer.expires_from_now (boost::posix_time::seconds(timeout + 1)); // + 1 second m_ShutdownTimer.async_wait ( [](const boost::system::error_code& ecode) { - Daemon.running = 0; + Daemon.running = 0; }); } void I2PControlService::ReseedHandler (std::ostringstream& results) { - LogPrint (eLogInfo, "Reseed requested"); - InsertParam (results, I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN, ""); + LogPrint (eLogInfo, "I2PControl: Reseed requested"); + InsertParam (results, "Reseed", ""); i2p::data::netdb.Reseed (); } // network setting void I2PControlService::NetworkSettingHandler (const boost::property_tree::ptree& params, std::ostringstream& results) { - LogPrint (eLogDebug, "I2PControl NetworkSetting"); for (auto it = params.begin (); it != params.end (); it++) { if (it != params.begin ()) results << ","; - LogPrint (eLogDebug, it->first); + LogPrint (eLogDebug, "I2PControl: NetworkSetting request: ", it->first); auto it1 = m_NetworkSettingHandlers.find (it->first); - if (it1 != m_NetworkSettingHandlers.end ()) + if (it1 != m_NetworkSettingHandlers.end ()) { (this->*(it1->second))(it->second.data (), results); - else - LogPrint (eLogError, "I2PControl NetworkSetting unknown request ", it->first); + } else + LogPrint (eLogError, "I2PControl: NetworkSetting unknown request: ", it->first); } } // certificate - void I2PControlService::CreateCertificate () + void I2PControlService::CreateCertificate (const char *crt_path, const char *key_path) { + FILE *f = NULL; EVP_PKEY * pkey = EVP_PKEY_new (); RSA * rsa = RSA_new (); BIGNUM * e = BN_dup (i2p::crypto::GetRSAE ()); @@ -538,42 +507,38 @@ namespace client X509 * x509 = X509_new (); ASN1_INTEGER_set (X509_get_serialNumber (x509), 1); X509_gmtime_adj (X509_get_notBefore (x509), 0); - X509_gmtime_adj (X509_get_notAfter (x509), I2P_CONTROL_CERTIFICATE_VALIDITY*24*60*60); // expiration - X509_set_pubkey (x509, pkey); // public key + X509_gmtime_adj (X509_get_notAfter (x509), I2P_CONTROL_CERTIFICATE_VALIDITY*24*60*60); // expiration + X509_set_pubkey (x509, pkey); // public key X509_NAME * name = X509_get_subject_name (x509); X509_NAME_add_entry_by_txt (name, "C", MBSTRING_ASC, (unsigned char *)"RU", -1, -1, 0); // country (Russia by default) X509_NAME_add_entry_by_txt (name, "O", MBSTRING_ASC, (unsigned char *)I2P_CONTROL_CERTIFICATE_ORGANIZATION, -1, -1, 0); // organization X509_NAME_add_entry_by_txt (name, "CN", MBSTRING_ASC, (unsigned char *)I2P_CONTROL_CERTIFICATE_COMMON_NAME, -1, -1, 0); // common name X509_set_issuer_name (x509, name); // set issuer to ourselves X509_sign (x509, pkey, EVP_sha1 ()); // sign - // save key and certificate - // keys - auto filename = GetPath () / I2P_CONTROL_KEY_FILE; - FILE * f= fopen (filename.string ().c_str (), "wb"); - if (f) - { - PEM_write_PrivateKey (f, pkey, NULL, NULL, 0, NULL, NULL); + + // save cert + if ((f = fopen (crt_path, "wb")) != NULL) { + LogPrint (eLogInfo, "I2PControl: saving new cert to ", crt_path); + PEM_write_X509 (f, x509); fclose (f); + } else { + LogPrint (eLogError, "I2PControl: can't write cert: ", strerror(errno)); } - else - LogPrint (eLogError, "Can't open file ", filename); - // certificate - filename = GetPath () / I2P_CONTROL_CERT_FILE; - f= fopen (filename.string ().c_str (), "wb"); - if (f) - { - PEM_write_X509 (f, x509); + + // save key + if ((f = fopen (key_path, "wb")) != NULL) { + LogPrint (eLogInfo, "I2PControl: saving cert key to : ", key_path); + PEM_write_PrivateKey (f, pkey, NULL, NULL, 0, NULL, NULL); fclose (f); + } else { + LogPrint (eLogError, "I2PControl: can't write key: ", strerror(errno)); } - else - LogPrint (eLogError, "Can't open file ", filename); - X509_free (x509); + X509_free (x509); + } else { + LogPrint (eLogError, "I2PControl: can't create RSA key for certificate"); } - else - LogPrint (eLogError, "Couldn't create RSA key for certificate"); EVP_PKEY_free (pkey); } - } } diff --git a/I2PControl.h b/I2PControl.h index 10b6b651..18592068 100644 --- a/I2PControl.h +++ b/I2PControl.h @@ -20,56 +20,8 @@ namespace i2p namespace client { const size_t I2P_CONTROL_MAX_REQUEST_SIZE = 1024; - typedef std::array I2PControlBuffer; - - const char I2P_CONTROL_PATH[] = "ipcontrol"; - const char I2P_CONTROL_KEY_FILE[] = "key.pem"; - const char I2P_CONTROL_CERT_FILE[] = "cert.pem"; - const char I2P_CONTROL_CONFIG_FILE[] = "i2pcontrol.conf"; - const char I2P_CONTROL_DEFAULT_PASSWORD[] = "itoopie"; - - 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"; - - // 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"; - - // 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"; - - // 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"; - - // 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"; - - // 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"; - - // Certificate + typedef std::array I2PControlBuffer; + const long I2P_CONTROL_CERTIFICATE_VALIDITY = 365*10; // 10 years const char I2P_CONTROL_CERTIFICATE_COMMON_NAME[] = "i2pd.i2pcontrol"; const char I2P_CONTROL_CERTIFICATE_ORGANIZATION[] = "Purple I2P"; @@ -87,25 +39,21 @@ namespace client private: - void LoadConfig (); - void SaveConfig (); - void Run (); void Accept (); - void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr socket); + void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr socket); void Handshake (std::shared_ptr socket); void HandleHandshake (const boost::system::error_code& ecode, std::shared_ptr socket); void ReadRequest (std::shared_ptr socket); - void HandleRequestReceived (const boost::system::error_code& ecode, size_t bytes_transferred, + void HandleRequestReceived (const boost::system::error_code& ecode, size_t bytes_transferred, std::shared_ptr socket, std::shared_ptr buf); void SendResponse (std::shared_ptr socket, std::shared_ptr buf, std::ostringstream& response, bool isHtml); void HandleResponseSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, std::shared_ptr socket, std::shared_ptr buf); - boost::filesystem::path GetPath () const { return i2p::util::filesystem::GetDefaultDataDir() / I2P_CONTROL_PATH; }; - void CreateCertificate (); - + boost::filesystem::path GetPath () const { return i2p::util::filesystem::GetDefaultDataDir(); }; + void CreateCertificate (const char *crt_path, const char *key_path); private: @@ -121,7 +69,7 @@ namespace client void I2PControlHandler (const boost::property_tree::ptree& params, std::ostringstream& results); void RouterInfoHandler (const boost::property_tree::ptree& params, std::ostringstream& results); void RouterManagerHandler (const boost::property_tree::ptree& params, std::ostringstream& results); - void NetworkSettingHandler (const boost::property_tree::ptree& params, std::ostringstream& results); + void NetworkSettingHandler (const boost::property_tree::ptree& params, std::ostringstream& results); // I2PControl typedef void (I2PControlService::*I2PControlRequestHandler)(const std::string& value); @@ -133,11 +81,13 @@ namespace client void VersionHandler (std::ostringstream& results); void StatusHandler (std::ostringstream& results); void NetDbKnownPeersHandler (std::ostringstream& results); - void NetDbActivePeersHandler (std::ostringstream& results); - void NetStatusHandler (std::ostringstream& results); + void NetDbActivePeersHandler (std::ostringstream& results); + void NetStatusHandler (std::ostringstream& results); void TunnelsParticipatingHandler (std::ostringstream& results); void InboundBandwidth1S (std::ostringstream& results); void OutboundBandwidth1S (std::ostringstream& results); + void NetTotalReceivedBytes (std::ostringstream& results); + void NetTotalSentBytes (std::ostringstream& results); // RouterManager typedef void (I2PControlService::*RouterManagerRequestHandler)(std::ostringstream& results); @@ -146,20 +96,20 @@ namespace client void ReseedHandler (std::ostringstream& results); // NetworkSetting - typedef void (I2PControlService::*NetworkSettingRequestHandler)(const std::string& value, std::ostringstream& results); + typedef void (I2PControlService::*NetworkSettingRequestHandler)(const std::string& value, std::ostringstream& results); private: std::string m_Password; bool m_IsRunning; - std::thread * m_Thread; + std::thread * m_Thread; boost::asio::io_service m_Service; boost::asio::ip::tcp::acceptor m_Acceptor; boost::asio::ssl::context m_SSLContext; boost::asio::deadline_timer m_ShutdownTimer; std::set m_Tokens; - + std::map m_MethodHandlers; std::map m_I2PControlHandlers; std::map m_RouterInfoHandlers; diff --git a/I2PTunnel.cpp b/I2PTunnel.cpp index 9eccc1bd..e936834d 100644 --- a/I2PTunnel.cpp +++ b/I2PTunnel.cpp @@ -134,7 +134,7 @@ namespace client void I2PTunnelConnection::Write (const uint8_t * buf, size_t len) { - m_Socket->async_send (boost::asio::buffer (buf, len), + boost::asio::async_write (*m_Socket, boost::asio::buffer (buf, len), boost::asio::transfer_all (), std::bind (&I2PTunnelConnection::HandleWrite, shared_from_this (), std::placeholders::_1)); } @@ -184,11 +184,14 @@ namespace client std::getline(m_InHeader, line); if (!m_InHeader.fail ()) { - if (line.find ("Host:") != std::string::npos) - m_OutHeader << "Host: " << m_Host << "\r\n"; - else - m_OutHeader << line << "\n"; if (line == "\r") endOfHeader = true; + else + { + if (line.find ("Host:") != std::string::npos) + m_OutHeader << "Host: " << m_Host << "\r\n"; + else + m_OutHeader << line << "\n"; + } } else break; @@ -203,7 +206,8 @@ namespace client if (endOfHeader) { - m_OutHeader << m_InHeader.str (); // data right after header + m_OutHeader << "\r\n"; // end of header + m_OutHeader << m_InHeader.str ().substr (m_InHeader.tellg ()); // data right after header m_HeaderSent = true; I2PTunnelConnection::Write ((uint8_t *)m_OutHeader.str ().c_str (), m_OutHeader.str ().length ()); } diff --git a/Identity.cpp b/Identity.cpp index 7c8e38be..ac49b711 100644 --- a/Identity.cpp +++ b/Identity.cpp @@ -199,7 +199,7 @@ namespace data } memcpy (&m_StandardIdentity, buf, DEFAULT_IDENTITY_SIZE); - delete[] m_ExtendedBuffer; + delete[] m_ExtendedBuffer; m_ExtendedBuffer = nullptr; m_ExtendedLen = bufbe16toh (m_StandardIdentity.certificate + 1); if (m_ExtendedLen) { @@ -211,6 +211,7 @@ namespace data else { LogPrint (eLogError, "Identity: Certificate length ", m_ExtendedLen, " exceeds buffer length ", len - DEFAULT_IDENTITY_SIZE); + m_ExtendedLen = 0; return 0; } } diff --git a/LeaseSet.cpp b/LeaseSet.cpp index fb6ac60b..716ee6d2 100644 --- a/LeaseSet.cpp +++ b/LeaseSet.cpp @@ -85,13 +85,24 @@ namespace data if (readIdentity || !m_Identity) m_Identity = std::make_shared(m_Buffer, m_BufferLen); size_t size = m_Identity->GetFullLen (); + if (size > m_BufferLen) + { + LogPrint (eLogError, "LeaseSet: identity length ", size, " exceeds buffer size ", m_BufferLen); + m_IsValid = false; + return; + } memcpy (m_EncryptionKey, m_Buffer + size, 256); size += 256; // encryption key size += m_Identity->GetSigningPublicKeyLen (); // unused signing key uint8_t num = m_Buffer[size]; size++; // num LogPrint (eLogDebug, "LeaseSet: read num=", (int)num); - if (!num) m_IsValid = false; + if (!num || num > MAX_NUM_LEASES) + { + LogPrint (eLogError, "LeaseSet: incorrect number of leases", (int)num); + m_IsValid = false; + return; + } // process leases const uint8_t * leases = m_Buffer + size; diff --git a/LeaseSet.h b/LeaseSet.h index 8135a7a1..aa9b73ad 100644 --- a/LeaseSet.h +++ b/LeaseSet.h @@ -31,7 +31,8 @@ namespace data } }; - const int MAX_LS_BUFFER_SIZE = 3072; + const int MAX_LS_BUFFER_SIZE = 3072; + const uint8_t MAX_NUM_LEASES = 16; class LeaseSet: public RoutingDestination { public: diff --git a/Log.h b/Log.h index c4b0f276..f941e6a7 100644 --- a/Log.h +++ b/Log.h @@ -95,6 +95,12 @@ inline void StopLog () } } +inline void SetLogLevel (const std::string& level) +{ + if (g_Log) + g_Log->SetLogLevel(level); +} + template void LogPrint (std::stringstream& s, TValue arg) { diff --git a/Makefile.mingw b/Makefile.mingw index 57fe094b..591a1256 100644 --- a/Makefile.mingw +++ b/Makefile.mingw @@ -1,7 +1,11 @@ CXX = g++ CXXFLAGS = -O2 -D_MT -DWIN32 -D_WINDOWS -DWIN32_LEAN_AND_MEAN NEEDED_CXXFLAGS = -std=c++11 -BOOST_SUFFIX = -mgw48-mt-1_59 -INCFLAGS = -I/usr/include/ -I/usr/local/include/ -I/c/dev/openssl/include -I/c/dev/boost/include/boost-1_59 +BOOST_SUFFIX = -mt +INCFLAGS = -I/usr/include/ -I/usr/local/include/ LDFLAGS = -Wl,-rpath,/usr/local/lib -L/usr/local/lib -L/c/dev/openssl -L/c/dev/boost/lib LDLIBS = -Wl,-Bstatic -lboost_system$(BOOST_SUFFIX) -Wl,-Bstatic -lboost_date_time$(BOOST_SUFFIX) -Wl,-Bstatic -lboost_filesystem$(BOOST_SUFFIX) -Wl,-Bstatic -lboost_regex$(BOOST_SUFFIX) -Wl,-Bstatic -lboost_program_options$(BOOST_SUFFIX) -Wl,-Bstatic -lssl -Wl,-Bstatic -lcrypto -Wl,-Bstatic -lz -Wl,-Bstatic -lwsock32 -Wl,-Bstatic -lws2_32 -Wl,-Bstatic -lgdi32 -Wl,-Bstatic -liphlpapi -static-libgcc -static-libstdc++ -Wl,-Bstatic -lstdc++ -Wl,-Bstatic -lpthread + +ifeq ($(USE_AESNI),1) + CPU_FLAGS = -maes -DAESNI +endif diff --git a/RouterContext.cpp b/RouterContext.cpp index 75406daf..962cf3ee 100644 --- a/RouterContext.cpp +++ b/RouterContext.cpp @@ -1,5 +1,6 @@ #include #include +#include "Config.h" #include "Crypto.h" #include "Timestamp.h" #include "I2NPProtocol.h" @@ -43,11 +44,14 @@ namespace i2p { i2p::data::RouterInfo routerInfo; routerInfo.SetRouterIdentity (GetIdentity ()); - int port = i2p::util::config::GetArg("-port", 0); + uint16_t port; i2p::config::GetOption("port", port); if (!port) port = rand () % (30777 - 9111) + 9111; // I2P network ports range - routerInfo.AddSSUAddress (i2p::util::config::GetArg("-host", "127.0.0.1").c_str (), port, routerInfo.GetIdentHash ()); - routerInfo.AddNTCPAddress (i2p::util::config::GetArg("-host", "127.0.0.1").c_str (), port); + std::string host; i2p::config::GetOption("host", host); + if (host == "0.0.0.0") + host = "127.0.0.1"; // replace default address with safe value + routerInfo.AddSSUAddress (host.c_str(), port, routerInfo.GetIdentHash ()); + routerInfo.AddNTCPAddress (host.c_str(), port); routerInfo.SetCaps (i2p::data::RouterInfo::eReachable | i2p::data::RouterInfo::eSSUTesting | i2p::data::RouterInfo::eSSUIntroducer); // LR, BC routerInfo.SetProperty ("netId", std::to_string (I2PD_NET_ID)); diff --git a/RouterInfo.cpp b/RouterInfo.cpp index 8f2b7bfe..64b675a7 100644 --- a/RouterInfo.cpp +++ b/RouterInfo.cpp @@ -81,7 +81,7 @@ namespace data { s.seekg (0,std::ios::end); m_BufferLen = s.tellg (); - if (m_BufferLen < 40) + if (m_BufferLen < 40 || m_BufferLen > MAX_RI_BUFFER_SIZE) { LogPrint(eLogError, "RouterInfo: File", m_FullPath, " is malformed"); return false; @@ -109,13 +109,25 @@ namespace data { m_RouterIdentity = std::make_shared(m_Buffer, m_BufferLen); size_t identityLen = m_RouterIdentity->GetFullLen (); + if (identityLen >= m_BufferLen) + { + LogPrint (eLogError, "RouterInfo: identity length ", identityLen, " exceeds buffer size ", m_BufferLen); + m_IsUnreachable = true; + return; + } std::stringstream str (std::string ((char *)m_Buffer + identityLen, m_BufferLen - identityLen)); ReadFromStream (str); + if (!str) + { + LogPrint (eLogError, "RouterInfo: malformed message"); + m_IsUnreachable = true; + return; + } if (verifySignature) { // verify signature - int l = m_BufferLen - m_RouterIdentity->GetSignatureLen (); - if (!m_RouterIdentity->Verify ((uint8_t *)m_Buffer, l, (uint8_t *)m_Buffer + l)) + int l = m_BufferLen - m_RouterIdentity->GetSignatureLen (); + if (l < 0 || !m_RouterIdentity->Verify ((uint8_t *)m_Buffer, l, (uint8_t *)m_Buffer + l)) { LogPrint (eLogError, "RouterInfo: signature verification failed"); m_IsUnreachable = true; @@ -130,7 +142,7 @@ namespace data m_Timestamp = be64toh (m_Timestamp); // read addresses uint8_t numAddresses; - s.read ((char *)&numAddresses, sizeof (numAddresses)); + s.read ((char *)&numAddresses, sizeof (numAddresses)); if (!s) return; bool introducers = false; for (int i = 0; i < numAddresses; i++) { @@ -149,7 +161,7 @@ namespace data address.port = 0; address.mtu = 0; uint16_t size, r = 0; - s.read ((char *)&size, sizeof (size)); + s.read ((char *)&size, sizeof (size)); if (!s) return; size = be16toh (size); while (r < size) { @@ -214,17 +226,18 @@ namespace data else if (!strcmp (key, "ikey")) Base64ToByteStream (value, strlen (value), introducer.iKey, 32); } + if (!s) return; } if (isValidAddress) m_Addresses.push_back(address); } // read peers uint8_t numPeers; - s.read ((char *)&numPeers, sizeof (numPeers)); + s.read ((char *)&numPeers, sizeof (numPeers)); if (!s) return; s.seekg (numPeers*32, std::ios_base::cur); // TODO: read peers // read properties uint16_t size, r = 0; - s.read ((char *)&size, sizeof (size)); + s.read ((char *)&size, sizeof (size)); if (!s) return; size = be16toh (size); while (r < size) { @@ -250,6 +263,7 @@ namespace data LogPrint (eLogError, "Unexpected netid=", value); m_IsUnreachable = true; } + if (!s) return; } if (!m_SupportedTransports || !m_Addresses.size() || (UsesIntroducer () && !introducers)) diff --git a/RouterInfo.h b/RouterInfo.h index a750cfea..2d32edef 100644 --- a/RouterInfo.h +++ b/RouterInfo.h @@ -182,7 +182,7 @@ namespace data std::string m_FullPath; std::shared_ptr m_RouterIdentity; uint8_t * m_Buffer; - int m_BufferLen; + size_t m_BufferLen; uint64_t m_Timestamp; std::vector
m_Addresses; std::map m_Properties; diff --git a/SAM.h b/SAM.h index e82e8b7d..07142c8c 100644 --- a/SAM.h +++ b/SAM.h @@ -41,7 +41,7 @@ namespace client const char SAM_DEST_REPLY_I2P_ERROR[] = "DEST REPLY RESULT=I2P_ERROR\n"; const char SAM_NAMING_LOOKUP[] = "NAMING LOOKUP"; const char SAM_NAMING_REPLY[] = "NAMING REPLY RESULT=OK NAME=ME VALUE=%s\n"; - const char SAM_DATAGRAM_RECEIVED[] = "DATAGRAM RECEIVED DESTINATION=%s SIZE=%zu\n"; + const char SAM_DATAGRAM_RECEIVED[] = "DATAGRAM RECEIVED DESTINATION=%s SIZE=%lu\n"; const char SAM_NAMING_REPLY_INVALID_KEY[] = "NAMING REPLY RESULT=INVALID_KEY NAME=%s\n"; const char SAM_NAMING_REPLY_KEY_NOT_FOUND[] = "NAMING REPLY RESULT=INVALID_KEY_NOT_FOUND NAME=%s\n"; const char SAM_PARAM_MIN[] = "MIN"; diff --git a/SSUData.cpp b/SSUData.cpp index f1282c3d..0ccf25a8 100644 --- a/SSUData.cpp +++ b/SSUData.cpp @@ -157,7 +157,7 @@ namespace transport memcpy (frag + 1, buf, 3); buf += 3; uint32_t fragmentInfo = bufbe32toh (frag); // fragment info - uint16_t fragmentSize = fragmentInfo & 0x1FFF; // bits 0 - 13 + uint16_t fragmentSize = fragmentInfo & 0x3FFF; // bits 0 - 13 bool isLast = fragmentInfo & 0x010000; // bit 16 uint8_t fragmentNum = fragmentInfo >> 17; // bits 23 - 17 if (fragmentSize >= SSU_V4_MAX_PACKET_SIZE) diff --git a/Transports.cpp b/Transports.cpp index 8d0ad4a6..acffd865 100644 --- a/Transports.cpp +++ b/Transports.cpp @@ -499,8 +499,9 @@ namespace transport { m_Service.post([session, this]() { - if (!session->GetRemoteIdentity ()) return; - auto ident = session->GetRemoteIdentity ()->GetIdentHash (); + auto remoteIdentity = session->GetRemoteIdentity (); + if (!remoteIdentity) return; + auto ident = remoteIdentity->GetIdentHash (); auto it = m_Peers.find (ident); if (it != m_Peers.end ()) { @@ -520,8 +521,9 @@ namespace transport { m_Service.post([session, this]() { - if (!session->GetRemoteIdentity ()) return; - auto ident = session->GetRemoteIdentity ()->GetIdentHash (); + auto remoteIdentity = session->GetRemoteIdentity (); + if (!remoteIdentity) return; + auto ident = remoteIdentity->GetIdentHash (); auto it = m_Peers.find (ident); if (it != m_Peers.end ()) { diff --git a/Tunnel.cpp b/Tunnel.cpp index 60030916..dbe87a2f 100644 --- a/Tunnel.cpp +++ b/Tunnel.cpp @@ -494,7 +494,7 @@ namespace tunnel if (IsRouterInfoMsg (msg) || typeID == eI2NPDatabaseSearchReply) // transit DatabaseStore my contain new/updated RI // or DatabaseSearchReply with new routers - i2p::data::netdb.PostI2NPMsg (msg); + i2p::data::netdb.PostI2NPMsg (CopyI2NPMessage (msg)); tunnel->SendTunnelDataMsg (msg); } diff --git a/TunnelEndpoint.cpp b/TunnelEndpoint.cpp index 0b730d35..a3907ce5 100644 --- a/TunnelEndpoint.cpp +++ b/TunnelEndpoint.cpp @@ -237,6 +237,11 @@ namespace tunnel } auto typeID = msg.data->GetTypeID (); LogPrint (eLogDebug, "TunnelMessage: handle fragment of ", msg.data->GetLength (), " bytes, msg type ", (int)typeID); + // catch RI or reply with new list of routers + if ((IsRouterInfoMsg (msg.data) || typeID == eI2NPDatabaseSearchReply) && + !m_IsInbound && msg.deliveryType != eDeliveryTypeLocal) + i2p::data::netdb.PostI2NPMsg (CopyI2NPMessage (msg.data)); + switch (msg.deliveryType) { case eDeliveryTypeLocal: @@ -257,10 +262,6 @@ namespace tunnel default: LogPrint (eLogError, "TunnelMessage: Unknown delivery type ", (int)msg.deliveryType); }; - // catch RI or reply with new list of routers - if ((IsRouterInfoMsg (msg.data) || typeID == eI2NPDatabaseSearchReply) && - !m_IsInbound && msg.deliveryType != eDeliveryTypeLocal) - i2p::data::netdb.PostI2NPMsg (msg.data); } } } diff --git a/api.cpp b/api.cpp index 9a9c5c46..cd527550 100644 --- a/api.cpp +++ b/api.cpp @@ -1,5 +1,6 @@ #include #include +#include "Config.h" #include "Log.h" #include "NetDb.h" #include "Transports.h" @@ -18,7 +19,9 @@ namespace api void InitI2P (int argc, char* argv[], const char * appName) { i2p::util::filesystem::SetAppName (appName); - i2p::util::config::OptionParser(argc, argv); + i2p::config::Init (); + i2p::config::ParseCmdline (argc, argv); + i2p::config::Finalize (); i2p::crypto::InitCrypto (); i2p::context.Init (); } diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt index cbcfc275..646e8a6e 100644 --- a/build/CMakeLists.txt +++ b/build/CMakeLists.txt @@ -17,6 +17,7 @@ set ( CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules" ) set ( CMAKE_SOURCE_DIR ".." ) set (LIBI2PD_SRC + "${CMAKE_SOURCE_DIR}/Config.cpp" "${CMAKE_SOURCE_DIR}/Crypto.cpp" "${CMAKE_SOURCE_DIR}/Garlic.cpp" "${CMAKE_SOURCE_DIR}/I2NPProtocol.cpp" @@ -90,11 +91,6 @@ if (WITH_UPNP) endif () endif () -# Default build is Debug -if (NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE Debug) -endif () - # compiler flags customization (by vendor) if (MSVC) add_definitions( -D_WIN32_WINNT=_WIN32_WINNT_WINXP -DWIN32_LEAN_AND_MEAN -DNOMINMAX ) #-DOPENSSL_NO_SSL2 -DOPENSSL_USE_DEPRECATED @@ -209,7 +205,7 @@ else() endif () if (WITH_PCH) - include_directories(${CMAKE_BINARY_DIR}) + include_directories(BEFORE ${CMAKE_BINARY_DIR}) add_library(stdafx STATIC "${CMAKE_SOURCE_DIR}/stdafx.cpp") if(MSVC) target_compile_options(stdafx PRIVATE /Ycstdafx.h /Zm155) @@ -246,11 +242,9 @@ if(NOT DEFINED OPENSSL_INCLUDE_DIR) message(SEND_ERROR "Could not find OpenSSL. Please download and install it first!") endif() -find_package ( MiniUPnPc ) -if (MINIUPNPC_FOUND) - include_directories( ${MINIUPNPC_INCLUDE_DIR} ) -else () - set(WITH_UPNP OFF) +if (WITH_UPNP) + find_package ( MiniUPnPc REQUIRED ) + include_directories( SYSTEM ${MINIUPNPC_INCLUDE_DIR} ) endif() find_package ( ZLIB ) @@ -289,7 +283,7 @@ endif () link_directories(${CMAKE_CURRENT_BINARY_DIR}/zlib/lib ${ZLIB_ROOT}/lib) # load includes -include_directories( ${Boost_INCLUDE_DIRS} ${OPENSSL_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR} ) +include_directories( SYSTEM ${Boost_INCLUDE_DIRS} ${OPENSSL_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR} ) # show summary message(STATUS "---------------------------------------") diff --git a/debian/i2pd.conf b/debian/i2pd.conf index a6e9c23f..4a518916 100644 --- a/debian/i2pd.conf +++ b/debian/i2pd.conf @@ -1,16 +1,19 @@ -floodfill=0 -v6=0 +ipv6 -httpproxyaddress=127.0.0.1 -httpproxyport=4444 +[httpproxy] +address = 127.0.0.1 +port = 4444 # other services (disabled by default) # -# samaddress=127.0.0.1 -# samport=7656 +#[sam] +#address = 127.0.0.1 +#port = 7656 # -# bobaddress=127.0.0.1 -# bobport=2827 +#[bob] +#address = 127.0.0.1 +#port = 2827 # -# i2pcontroladdress=127.0.0.1 -# i2pcontrolport=7650 +#[i2pcontrol] +#address = 127.0.0.1 +#port = 7650 diff --git a/debian/i2pd.init b/debian/i2pd.init index 609a3407..d87aa000 100644 --- a/debian/i2pd.init +++ b/debian/i2pd.init @@ -46,7 +46,7 @@ do_start() start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --chuid "$USER" --test > /dev/null \ || return 1 start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --chuid "$USER" -- \ - --service=1 --daemon=1 --log=1 --conf=$I2PCONF --tunnelscfg=$TUNCONF \ + --service --daemon --log --conf=$I2PCONF --tunconf=$TUNCONF \ --port=$I2PD_PORT $DAEMON_OPTS > /dev/null 2>&1 \ || return 2 return $? diff --git a/debian/i2pd.upstart b/debian/i2pd.upstart index 0527935d..d1536ea3 100644 --- a/debian/i2pd.upstart +++ b/debian/i2pd.upstart @@ -4,7 +4,7 @@ start on runlevel [2345] stop on runlevel [016] or unmounting-filesystem # these can be overridden in /etc/init/i2pd.override -env I2P_HOST="1.2.3.4" -env I2P_PORT="4567" +env I2PD_HOST="1.2.3.4" +env I2PD_PORT="4567" -exec /usr/sbin/i2pd --daemon=0 --log=1 --host=$I2P_HOST --port=$I2P_PORT +exec /usr/sbin/i2pd --daemon --log --host=$I2PD_HOST --port=$I2PD_PORT diff --git a/debian/postinst b/debian/postinst index 4be6bf3f..1de58c1e 100755 --- a/debian/postinst +++ b/debian/postinst @@ -1,6 +1,7 @@ #!/bin/sh set -e +LOGFILE='/var/log/i2pd.log' I2PDHOME='/var/lib/i2pd' I2PDUSER='i2pd' @@ -16,8 +17,9 @@ case "$1" in adduser --system --quiet --group --home $I2PDHOME $I2PDUSER fi - touch /var/log/i2pd.log - chown -f ${I2PDUSER}:adm /var/log/i2pd.log + touch $LOGFILE + chmod 640 $LOGFILE + chown -f ${I2PDUSER}:adm $LOGFILE mkdir -p -m0750 $I2PDHOME chown -f -R -P ${I2PDUSER}:${I2PDUSER} ${I2PDHOME} ;; diff --git a/docs/build_notes_windows.md b/docs/build_notes_windows.md index 3779eca0..c72b8989 100644 --- a/docs/build_notes_windows.md +++ b/docs/build_notes_windows.md @@ -23,105 +23,24 @@ paths like /c/dev/ for C:\dev\. msys2 ----- -Get it from https://msys2.github.io and update it as described -there. Use the installer appropriate for the bitness of your Windows -OS. You will be able to build 32-bit applications if you install -64-bit version of msys2. For 64-bit, use *mingw-w64-x86_64* prefix -instead of *mingw-w64-i686* for the packages mentioned below, and use -*/mingw64* as CMake find root. - +Get install file msys2-i686-20150916.exe from https://msys2.github.io. +open MSys2Shell (from Start menu). Install all prerequisites and download i2pd source: ```bash -pacman -S mingw-w64-i686-boost mingw-w64-i686-openssl mingw-w64-i686-gcc mingw-w64-i686-miniupnpc cmake git +pacman -S mingw-w64-i686-boost mingw-w64-i686-openssl mingw-w64-i686-gcc git make mkdir -p /c/dev/i2pd cd /c/dev/i2pd git clone https://github.com/PurpleI2P/i2pd.git cd i2pd -``` - -Check with `git status` that you are on *openssl* branch. If it is not -the case, do `git checkout openssl`. - -```sh -git pull origin openssl --ff-only # to update sources if you are rebuilding after a while -mkdir -p mingw32.build # CMake build folder -cd mingw32.build export PATH=/mingw32/bin:/usr/bin # we need compiler on PATH which is usually heavily cluttered on Windows -cmake ../build -G "Unix Makefiles" -DWITH_UPNP=ON -DWITH_PCH=ON \ - -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX:PATH=../mingw32.stage -DCMAKE_FIND_ROOT_PATH=/mingw32 +make ``` If your processor has [AES instruction set](https://en.wikipedia.org/wiki/AES_instruction_set), -you may try adding `-DWITH_AESNI=ON`. No check is done however, it -will compile but will crash with `Illegal instruction` if not supported. - -Make sure CMake found proper libraries and compiler. This might be the -case if you have Strawberry Perl installed as it alters PATH and you -failed to override it like mentioned above. You should see something -like - -``` --- The C compiler identification is GNU 5.2.0 --- The CXX compiler identification is GNU 5.2.0 --- Check for working C compiler: /mingw32/bin/gcc.exe --- Check for working C compiler: /mingw32/bin/gcc.exe -- works --- Detecting C compiler ABI info --- Detecting C compiler ABI info - done --- Detecting C compile features --- Detecting C compile features - done --- Check for working CXX compiler: /mingw32/bin/c++.exe --- Check for working CXX compiler: /mingw32/bin/c++.exe -- works --- Detecting CXX compiler ABI info --- Detecting CXX compiler ABI info - done --- Detecting CXX compile features --- Detecting CXX compile features - done --- Performing Test CXX11_SUPPORTED --- Performing Test CXX11_SUPPORTED - Success --- Performing Test CXX0X_SUPPORTED --- Performing Test CXX0X_SUPPORTED - Success --- Looking for include file pthread.h --- Looking for include file pthread.h - found --- Looking for pthread_create --- Looking for pthread_create - found --- Found Threads: TRUE --- Boost version: 1.59.0 --- Found the following Boost libraries: --- system --- filesystem --- regex --- program_options --- date_time --- thread --- chrono --- Found OpenSSL: /mingw32/lib/libssl.dll.a;/mingw32/lib/libcrypto.dll.a (found version "1.0.2d") --- Found MiniUPnP headers: /mingw32/include --- Found ZLIB: /mingw32/lib/libz.dll.a (found version "1.2.8") --- --------------------------------------- --- Build type : RelWithDebInfo --- Compiler vendor : GNU --- Compiler version : 5.2.0 --- Compiler path : /mingw32/bin/c++.exe --- Install prefix: : ../mingw32.stage --- Options: --- AESNI : OFF --- HARDENING : OFF --- LIBRARY : ON --- BINARY : ON --- STATIC BUILD : OFF --- UPnP : ON --- PCH : ON --- --------------------------------------- --- Configuring done --- Generating done --- Build files have been written to: /c/dev/i2pd/i2pd/mingw32.build -``` - -Now it is time to compile everything. If you have a multicore processor -you can add `-j` flag. - - make -j4 install +you use `make USE_AESNI=1`. No check is done however, it +will compile, but it might crash with `Illegal instruction` if not supported. You should be able to run ./i2pd . If you need to start from the new shell, consider starting *MinGW-w64 Win32 Shell* instead of *MSYS2 Shell* as diff --git a/docs/config_opts_after_2.3.0.md b/docs/config_opts_after_2.3.0.md new file mode 100644 index 00000000..1b2adcdb --- /dev/null +++ b/docs/config_opts_after_2.3.0.md @@ -0,0 +1,43 @@ +Изменения обработки параметров в релизах > 2.3.0 +------------------------------------------------ + +Система параметров отличается от того, что было ранее и достаточно сильно: + +* изменения имён и стиля параметров + +Все параметры теперь в виде --help (gnu-style), у некоторых есть шорткаты в виде -h (unix-style). +Это касается всех систем, в том числе винды. + +--daemon=1 и подобное -> просто --daemon, без параметра. Нет опции - false, есть - true +--notransit=1 -> --notransit, то же что и выше: есть опция - false, нет - true +--v6 -> --ipv6 (первое было похоже на версию какого-то своего протокола, типа socksproxy --v5) +--tunnelscfg -> --tunconf (имя параметра было слишком длинным, cfg переделан на conf - единообразно с --conf) +--sockskeys -> разделён на два, для socks и httpproxy по-отдельности + +* поддержка секций в основном конфиге + +Выглядит это так: + + # основные опции + pidfile = /var/run/i2pd.pid + # + # настройки конкретного модуля + [httproxy] + address = 1.2.3.4 + port = 4446 + keys = httproxy-keys.dat + # и так далее + [sam] + enabled = no + addresss = 127.0.0.2 + # ^^ переопределяется только адрес, остальное берётся из дефолта + +Точно так же сейчас работает конфиг туннелей: секция до точки - имя, после - параметр + +* поддержка выключения отдельных сервисов "на корню" см sam.enabled и подобное + +Это позволило задать дефолт для номера порта и не писать его руками для включения. + +* добавлен --help (см #110) + +* присутствует некая валидация параметров, --port=abcd - не прокатит, --port=100500 - тоже diff --git a/docs/configuration.md b/docs/configuration.md index 4fbdd559..3f3c5386 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -4,46 +4,67 @@ i2pd configuration Command line options -------------------- -* --port= - The port to listen on -* --httpaddress= - The address to listen on (HTTP server) -* --httpport= - The port to listen on (HTTP server) -* --loglevel= - Log messages above this level (debug, *info, warn, error) -* --pidfile= - Where to write pidfile (dont write by default) -* --daemon= - Enable or disable daemon mode. 1 for yes, 0 for no. 0 by default -* --log= - Enable or disable logging to the file. 1 for daemon, 0 for non-daemon by default -* --svcctl= - Windows service management (--svcctl="install" or --svcctl="remove") -* --service= - 1 if uses system folders (/var/run/i2pd.pid, /var/log/i2pd/i2pd.log, /var/lib/i2pd). -* --v6= - 1 if supports communication through ipv6, off by default -* --floodfill= - 1 if router is floodfill, off by default -* --bandwidth= - L if bandwidth is limited to 32Kbs/sec, O - to 256Kbs/sec, P - unlimited -* --notransit= - 1 if router doesn't accept transit tunnels at startup. 0 by default -* --httpproxyaddress= - The address to listen on (HTTP Proxy) -* --httpproxyport= - The port to listen on (HTTP Proxy) 4446 by default -* --socksproxyaddress= - The address to listen on (SOCKS Proxy) -* --socksproxyport= - The port to listen on (SOCKS Proxy). 4447 by default -* --proxykeys= - optional keys file for proxy local destination (both HTTP and SOCKS) -* --samaddress= - The address to listen on (SAM bridge) -* --samport= - Port of SAM bridge. Usually 7656. SAM is off if not specified -* --bobaddress= - The address to listen on (BOB command channel) -* --bobport= - Port of BOB command channel. Usually 2827. BOB is off if not specified -* --i2pcontroladdress= - The address to listen on (I2P control service) -* --i2pcontrolport= - Port of I2P control service. Usually 7650. I2PControl is off if not specified -* --tunnelscfg= - Tunnels Config file (default: ~/.i2pd/tunnels.cfg or /var/lib/i2pd/tunnels.cfg) * --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. +* --tunconf= - Tunnels config file (default: ~/.i2pd/tunnels.cfg or /var/lib/i2pd/tunnels.cfg) +* --pidfile= - Where to write pidfile (dont write by default) +* --log= - Logs destination: stdout, file (stdout if not set, file - otherwise, for compatibility) +* --logfile= - Path to logfile (default - autodetect) +* --loglevel= - Log messages above this level (debug, *info, warn, error) +* --host= - The external IP +* --port= - The port to listen on +* --daemon - Router will go to background after start +* --service - Router will use system folders like '/var/lib/i2pd' +* --ipv6 - Enable communication through ipv6 +* --notransit - Router will not accept transit tunnels at startup +* --floodfill - Router will be floodfill +* --bandwidth= - L if bandwidth is limited to 32Kbs/sec, O - to 256Kbs/sec, P - unlimited +* --svcctl= - Windows service management (--svcctl="install" or --svcctl="remove") + +* --http.address= - The address to listen on (HTTP server) +* --http.port= - The port to listen on (HTTP server) + +* --httpproxy.address= - The address to listen on (HTTP Proxy) +* --httpproxy.port= - The port to listen on (HTTP Proxy) 4446 by default +* --httpproxy.keys= - optional keys file for proxy local destination (both HTTP and SOCKS) + +* --socksproxy.address= - The address to listen on (SOCKS Proxy) +* --socksproxy.port= - The port to listen on (SOCKS Proxy). 4447 by default +* --socksproxy.keys= - optional keys file for proxy local destination (both HTTP and SOCKS) +* --socksproxy.outproxy= - Address of outproxy. requests outside i2p will go there +* --socksproxy.outproxyport= - Outproxy remote port + +* --sam.address= - The address to listen on (SAM bridge) +* --sam.port= - Port of SAM bridge. Usually 7656. SAM is off if not specified + +* --bob.address= - The address to listen on (BOB command channel) +* --bob.port= - Port of BOB command channel. Usually 2827. BOB is off if not specified + +* --i2pcontrol.address= - The address to listen on (I2P control service) +* --i2pcontrol.port= - Port of I2P control service. Usually 7650. I2PControl is off if not specified Config files ------------ INI-like, syntax is the following : = . Comments are "#", not ";" as you may expect. See [boost ticket](https://svn.boost.org/trac/boost/ticket/808) -All command-line parameters are allowed as keys, for example: +All command-line parameters are allowed as keys, but note for those which contains dot (.). + +For example: i2p.conf: - log = 1 - v6 = 0 + # comment + log = yes + ipv6 = yes + # settings for specific module + [httpproxy] + port = 4444 + # ^^ this will be --httproxy.port= in cmdline + # another one + [sam] + enabled = yes tunnels.cfg (filename of this config is subject of change): diff --git a/filelist.mk b/filelist.mk index 147ef259..ce3a12f5 100644 --- a/filelist.mk +++ b/filelist.mk @@ -8,9 +8,9 @@ LIB_SRC = \ LIB_CLIENT_SRC = \ AddressBook.cpp BOB.cpp ClientContext.cpp I2PTunnel.cpp I2PService.cpp \ - SAM.cpp SOCKS.cpp HTTPProxy.cpp + SAM.cpp SOCKS.cpp HTTPProxy.cpp Config.cpp # also: Daemon{Linux,Win32}.cpp will be added later DAEMON_SRC = \ - HTTPServer.cpp I2PControl.cpp UPnP.cpp Daemon.cpp i2pd.cpp + HTTPServer.cpp I2PControl.cpp UPnP.cpp Daemon.cpp Config.cpp i2pd.cpp diff --git a/util.cpp b/util.cpp index e894c44f..1ea5fcd7 100644 --- a/util.cpp +++ b/util.cpp @@ -13,6 +13,7 @@ #include #include #include +#include "Config.h" #include "util.h" #include "Log.h" @@ -66,86 +67,6 @@ namespace i2p { namespace util { - -namespace config -{ - std::map mapArgs; - - void OptionParser(int argc, const char* const argv[]) - { - mapArgs.clear(); - for (int i = 1; i < argc; i++) - { - std::string strKey (argv[i]); - std::string strValue; - size_t has_data = strKey.find('='); - if (has_data != std::string::npos) - { - strValue = strKey.substr(has_data+1); - strKey = strKey.substr(0, has_data); - } - -#ifdef WIN32 - boost::to_lower(strKey); - if (boost::algorithm::starts_with(strKey, "/")) - strKey = "-" + strKey.substr(1); -#endif - if (strKey[0] != '-') - break; - - mapArgs[strKey] = strValue; - } - - BOOST_FOREACH(PAIRTYPE(const std::string,std::string)& entry, mapArgs) - { - std::string name = entry.first; - - // interpret --foo as -foo (as long as both are not set) - if (name.find("--") == 0) - { - std::string singleDash(name.begin()+1, name.end()); - if (mapArgs.count(singleDash) == 0) - mapArgs[singleDash] = entry.second; - name = singleDash; - } - } - } - - std::string GetArg(const std::string& strArg, const std::string& strDefault) - { - if (mapArgs.count(strArg)) - return mapArgs[strArg]; - return strDefault; - } - - int GetArg(const std::string& strArg, int nDefault) - { - if (mapArgs.count(strArg)) - return atoi(mapArgs[strArg].c_str()); - return nDefault; - } - - void ReadConfigFile(boost::filesystem::path path) - { - boost::filesystem::ifstream streamConfig(path); - if (!streamConfig.good()) - return; // No i2pd.conf file is OK - - std::set setOptions; - setOptions.insert("*"); - - for (boost::program_options::detail::config_file_iterator it(streamConfig, setOptions), end; it != end; ++it) - { - // Don't overwrite existing settings so command line settings override i2pd.conf - std::string strKey = std::string("-") + it->string_key; - if (mapArgs.count(strKey) == 0) - { - mapArgs[strKey] = it->value[0]; - } - } - } -} - namespace filesystem { std::string appName ("i2pd"); @@ -166,8 +87,10 @@ namespace filesystem // TODO: datadir parameter is useless because GetDataDir is called before OptionParser // and mapArgs is not initialized yet - /*if (i2p::util::config::mapArgs.count("-datadir")) - path = boost::filesystem::system_complete(i2p::util::config::mapArgs["-datadir"]); + /* + std::string datadir; i2p::config::GetOption("datadir", datadir); + if (datadir != "") + path = boost::filesystem::system_complete(datadir); else */ path = GetDefaultDataDir(); @@ -200,17 +123,34 @@ namespace filesystem boost::filesystem::path GetConfigFile() { - boost::filesystem::path pathConfigFile(i2p::util::config::GetArg("-conf", "i2p.conf")); - if (!pathConfigFile.is_complete()) pathConfigFile = GetDataDir() / pathConfigFile; - return pathConfigFile; + std::string config; i2p::config::GetOption("conf", config); + if (config != "") { + /* config file set with cmdline */ + boost::filesystem::path path(config); + return path; + } + /* else - try autodetect */ + boost::filesystem::path path("i2p.conf"); + path = GetDataDir() / path; + if (!boost::filesystem::exists(path)) + path = ""; /* reset */ + return path; } boost::filesystem::path GetTunnelsConfigFile() { - boost::filesystem::path pathTunnelsConfigFile(i2p::util::config::GetArg("-tunnelscfg", "tunnels.cfg")); - if (!pathTunnelsConfigFile.is_complete()) - pathTunnelsConfigFile = GetDataDir() / pathTunnelsConfigFile; - return pathTunnelsConfigFile; + std::string tunconf; i2p::config::GetOption("tunconf", tunconf); + if (tunconf != "") { + /* config file set with cmdline */ + boost::filesystem::path path(tunconf); + return path; + } + /* else - try autodetect */ + boost::filesystem::path path("tunnels.cfg"); + path = GetDataDir() / path; + if (!boost::filesystem::exists(path)) + path = ""; /* reset */ + return path; } boost::filesystem::path GetDefaultDataDir() @@ -225,7 +165,8 @@ namespace filesystem SHGetFolderPath(NULL, CSIDL_APPDATA, 0, NULL, localAppData); return boost::filesystem::path(std::string(localAppData) + "\\" + appName); #else /* UNIX */ - if (i2p::util::config::GetArg("-service", 0)) // use system folder + bool service; i2p::config::GetOption("service", service); + if (service) // use system folder return boost::filesystem::path(std::string ("/var/lib/") + appName); boost::filesystem::path pathRet; char* pszHome = getenv("HOME"); diff --git a/util.h b/util.h index 905c2a8d..0377ef8d 100644 --- a/util.h +++ b/util.h @@ -14,14 +14,6 @@ namespace i2p { namespace util { - namespace config - { - void OptionParser(int argc, const char* const argv[]); - int GetArg(const std::string& strArg, int nDefault); - std::string GetArg(const std::string& strArg, const std::string& strDefault); - void ReadConfigFile(boost::filesystem::path path); - } - namespace filesystem { void SetAppName (const std::string& name); diff --git a/version.h b/version.h index a0fff346..49b52b96 100644 --- a/version.h +++ b/version.h @@ -7,7 +7,7 @@ #define MAKE_VERSION(a,b,c) STRINGIZE(a) "." STRINGIZE(b) "." STRINGIZE(c) #define I2PD_VERSION_MAJOR 2 -#define I2PD_VERSION_MINOR 3 +#define I2PD_VERSION_MINOR 4 #define I2PD_VERSION_MICRO 0 #define I2PD_VERSION_PATCH 0 #define I2PD_VERSION MAKE_VERSION(I2PD_VERSION_MAJOR, I2PD_VERSION_MINOR, I2PD_VERSION_MICRO)