/* * Copyright (c) 2013-2020, 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 "Identity.h" #include "Config.h" #include "version.h" #include "Log.h" using namespace boost::program_options; namespace i2p { namespace config { options_description m_OptionsDesc; variables_map m_Options; void Init() { options_description general("General options"); general.add_options() ("help", "Show this message") ("version", "Show i2pd version") ("conf", value()->default_value(""), "Path to main i2pd config file (default: try ~/.i2pd/i2pd.conf or /var/lib/i2pd/i2pd.conf)") ("tunconf", value()->default_value(""), "Path to config with tunnels list and options (default: try ~/.i2pd/tunnels.conf or /var/lib/i2pd/tunnels.conf)") ("tunnelsdir", value()->default_value(""), "Path to extra tunnels' configs folder (default: ~/.i2pd/tunnels.d or /var/lib/i2pd/tunnels.d") ("certsdir", value()->default_value(""), "Path to certificates used for verifying .su3, families (default: ~/.i2pd/certificates or /var/lib/i2pd/certificates") ("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, syslog (stdout if not set)") ("logfile", value()->default_value(""), "Path to logfile (stdout if not set, autodetect if daemon)") ("loglevel", value()->default_value("warn"), "Set the minimal level of log messages (debug, info, warn, error, none)") ("logclftime", bool_switch()->default_value(false), "Write full CLF-formatted date and time to log (default: disabled, write only time)") ("family", value()->default_value(""), "Specify a family, router belongs to") ("datadir", value()->default_value(""), "Path to storage of i2pd data (RI, keys, peer profiles, ...)") ("host", value()->default_value("0.0.0.0"), "External IP") ("ifname", value()->default_value(""), "Network interface to bind to") ("ifname4", value()->default_value(""), "Network interface to bind to for ipv4") ("ifname6", value()->default_value(""), "Network interface to bind to for ipv6") ("nat", bool_switch()->default_value(true), "Should we assume we are behind NAT? (default: enabled)") ("port", value()->default_value(0), "Port to listen for incoming connections (default: auto)") ("ipv4", bool_switch()->default_value(true), "Enable communication through ipv4 (default: enabled)") ("address4", value()->default_value(""), "Local address to bind ipv4 transport sockets to") ("ipv6", bool_switch()->default_value(false), "Enable communication through ipv6 (default: disabled)") ("address6", value()->default_value(""), "Local address to bind ipv6 transport sockets to") ("reservedrange", bool_switch()->default_value(true), "Check remote RI for being in blacklist of reserved IP ranges (default: enabled)") ("netid", value()->default_value(I2PD_NET_ID), "Specify NetID. Main I2P is 2") ("daemon", bool_switch()->default_value(false), "Router will go to background after start (default: disabled)") ("service", bool_switch()->default_value(false), "Router will use system folders like '/var/lib/i2pd' (default: disabled)") ("notransit", bool_switch()->default_value(false), "Router will not accept transit tunnels at startup (default: disabled)") ("floodfill", bool_switch()->default_value(false), "Router will be floodfill (default: disabled)") ("bandwidth", value()->default_value(""), "Bandwidth limit: integer in KBps or letters: L (32), O (256), P (2048), X (>9000)") ("share", value()->default_value(100), "Limit of transit traffic from max bandwidth in percents. (default: 100)") ("ntcp", bool_switch()->default_value(false), "Ignored. Always false") ("ssu", bool_switch()->default_value(true), "Enable SSU transport (default: enabled)") ("ntcpproxy", value()->default_value(""), "Ignored") #ifdef _WIN32 ("svcctl", value()->default_value(""), "Ignored") ("insomnia", bool_switch()->default_value(false), "Prevent system from sleeping (default: disabled)") ("close", value()->default_value("ask"), "Action on close: minimize, exit, ask") #endif ; options_description limits("Limits options"); limits.add_options() ("limits.coresize", value()->default_value(0), "Maximum size of corefile in Kb (0 - use system limit)") ("limits.openfiles", value()->default_value(0), "Maximum number of open files (0 - use system default)") ("limits.transittunnels", value()->default_value(2500), "Maximum active transit sessions (default:2500)") ("limits.ntcpsoft", value()->default_value(0), "Threshold to start probabilistic backoff with ntcp sessions (default: use system limit)") ("limits.ntcphard", value()->default_value(0), "Maximum number of ntcp sessions (default: use system limit)") ("limits.ntcpthreads", value()->default_value(1), "Maximum number of threads used by NTCP DH worker (default: 1)") ; 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") ("http.auth", value()->default_value(false), "Enable Basic HTTP auth for webconsole") ("http.user", value()->default_value("i2pd"), "Username for basic auth") ("http.pass", value()->default_value(""), "Password for basic auth (default: random, see logs)") ("http.strictheaders", value()->default_value(true), "Enable strict host checking on WebUI") ("http.hostname", value()->default_value("localhost"), "Expected hostname for WebUI") ("http.webroot", value()->default_value("/"), "WebUI root path (default: / )") ("http.lang", value()->default_value("english"), "WebUI language (default: english )") ; 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("transient-proxy"), "File to persist HTTP Proxy keys. Transient by default") ("httpproxy.signaturetype", value()-> default_value(i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519), "Signature type for new keys. 7 (EdDSA) by default") ("httpproxy.inbound.length", value()->default_value("3"), "HTTP proxy inbound tunnel length") ("httpproxy.outbound.length", value()->default_value("3"), "HTTP proxy outbound tunnel length") ("httpproxy.inbound.quantity", value()->default_value("5"), "HTTP proxy inbound tunnels quantity") ("httpproxy.outbound.quantity", value()->default_value("5"), "HTTP proxy outbound tunnels quantity") ("httpproxy.latency.min", value()->default_value("0"), "HTTP proxy min latency for tunnels") ("httpproxy.latency.max", value()->default_value("0"), "HTTP proxy max latency for tunnels") ("httpproxy.outproxy", value()->default_value(""), "HTTP proxy upstream out proxy url") ("httpproxy.addresshelper", value()->default_value(true), "Enable or disable addresshelper") ("httpproxy.i2cp.leaseSetType", value()->default_value("3"), "Local destination's LeaseSet type") ("httpproxy.i2cp.leaseSetEncType", value()->default_value("0,4"), "Local destination's LeaseSet encryption type") ("httpproxy.i2cp.leaseSetPrivKey", value()->default_value(""), "LeaseSet private key") ; 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("transient-proxy"), "File to persist SOCKS Proxy keys. Transient by default") ("socksproxy.signaturetype", value()-> default_value(i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519), "Signature type for new keys. 7 (EdDSA) by default") ("socksproxy.inbound.length", value()->default_value("3"), "SOCKS proxy inbound tunnel length") ("socksproxy.outbound.length", value()->default_value("3"), "SOCKS proxy outbound tunnel length") ("socksproxy.inbound.quantity", value()->default_value("5"), "SOCKS proxy inbound tunnels quantity") ("socksproxy.outbound.quantity", value()->default_value("5"), "SOCKS proxy outbound tunnels quantity") ("socksproxy.latency.min", value()->default_value("0"), "SOCKS proxy min latency for tunnels") ("socksproxy.latency.max", value()->default_value("0"), "SOCKS proxy max latency for tunnels") ("socksproxy.outproxy.enabled", value()->default_value(false), "Enable or disable SOCKS outproxy") ("socksproxy.outproxy", value()->default_value("127.0.0.1"), "Upstream outproxy address for SOCKS Proxy") ("socksproxy.outproxyport", value()->default_value(9050), "Upstream outproxy port for SOCKS Proxy") ("socksproxy.i2cp.leaseSetType", value()->default_value("3"), "Local destination's LeaseSet type") ("socksproxy.i2cp.leaseSetEncType", value()->default_value("0,4"), "Local destination's LeaseSet encryption type") ("socksproxy.i2cp.leaseSetPrivKey", value()->default_value(""), "LeaseSet private key") ; options_description sam("SAM bridge options"); sam.add_options() ("sam.enabled", value()->default_value(true), "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") ("sam.singlethread", value()->default_value(true), "Sessions run in the SAM bridge's thread") ; 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 i2cp("I2CP options"); i2cp.add_options() ("i2cp.enabled", value()->default_value(false), "Enable or disable I2CP") ("i2cp.address", value()->default_value("127.0.0.1"), "I2CP listen address") ("i2cp.port", value()->default_value(7654), "I2CP listen port") ("i2cp.singlethread", value()->default_value(true), "Destinations run in the I2CP server's thread") ; 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 certificate") ("i2pcontrol.key", value()->default_value("i2pcontrol.key.pem"), "I2PCP connection certificate key") ; bool upnp_default = false; #if (defined(USE_UPNP) && (defined(WIN32_APP) || defined(ANDROID))) upnp_default = true; // enable UPNP for windows GUI and android by default #endif options_description upnp("UPnP options"); upnp.add_options() ("upnp.enabled", value()->default_value(upnp_default), "Enable or disable UPnP: automatic port forwarding") ("upnp.name", value()->default_value("I2Pd"), "Name i2pd appears in UPnP forwarding list") ; options_description precomputation("Precomputation options"); precomputation.add_options() ("precomputation.elgamal", #if defined(__x86_64__) value()->default_value(false), #else value()->default_value(true), #endif "Enable or disable elgamal precomputation table") ; options_description reseed("Reseed options"); reseed.add_options() ("reseed.verify", value()->default_value(false), "Verify .su3 signature") ("reseed.threshold", value()->default_value(25), "Minimum number of known routers before requesting reseed") ("reseed.floodfill", value()->default_value(""), "Path to router info of floodfill to reseed from") ("reseed.file", value()->default_value(""), "Path to local .su3 file or HTTPS URL to reseed from") ("reseed.zipfile", value()->default_value(""), "Path to local .zip file to reseed from") ("reseed.proxy", value()->default_value(""), "url for reseed proxy, supports http/socks") ("reseed.urls", value()->default_value( "https://reseed.i2p-projekt.de/," "https://reseed.diva.exchange/," "https://reseed-fr.i2pd.xyz/," "https://reseed.memcpy.io/," "https://reseed.onion.im/," "https://i2pseed.creativecowpat.net:8443/," "https://reseed.i2pgit.org/," "https://i2p.novg.net/" ), "Reseed URLs, separated by comma") ("reseed.yggurls", value()->default_value( "http://[324:9de3:fea4:f6ac::ace]:7070/" ), "Reseed URLs through the Yggdrasil, separated by comma") ; options_description addressbook("AddressBook options"); addressbook.add_options() ("addressbook.defaulturl", value()->default_value( "http://shx5vqsw7usdaunyzr2qmes2fq37oumybpudrd4jjj4e4vk4uusa.b32.i2p/hosts.txt" ), "AddressBook subscription URL for initial setup") ("addressbook.subscriptions", value()->default_value("http://reg.i2p/hosts.txt"), "AddressBook subscriptions URLs, separated by comma") ("addressbook.hostsfile", value()->default_value(""), "File to dump addresses in hosts.txt format"); options_description trust("Trust options"); trust.add_options() ("trust.enabled", value()->default_value(false), "Enable explicit trust options") ("trust.family", value()->default_value(""), "Router Family to trust for first hops") ("trust.routers", value()->default_value(""), "Only Connect to these routers") ("trust.hidden", value()->default_value(false), "Should we hide our router from other routers?") ; // Save deprecated websocket options for compatibility options_description websocket("Websocket Options"); websocket.add_options() ("websockets.enabled", value()->default_value(false), "Deprecated option") ("websockets.address", value()->default_value(""), "Deprecated option") ("websockets.port", value()->default_value(0), "Deprecated option") ; options_description exploratory("Exploratory Options"); exploratory.add_options() ("exploratory.inbound.length", value()->default_value(2), "Exploratory inbound tunnel length") ("exploratory.outbound.length", value()->default_value(2), "Exploratory outbound tunnel length") ("exploratory.inbound.quantity", value()->default_value(3), "Exploratory inbound tunnels quantity") ("exploratory.outbound.quantity", value()->default_value(3), "Exploratory outbound tunnels quantity") ; options_description ntcp2("NTCP2 Options"); ntcp2.add_options() ("ntcp2.enabled", value()->default_value(true), "Enable NTCP2 (default: enabled)") ("ntcp2.published", value()->default_value(true), "Publish NTCP2 (default: enabled)") ("ntcp2.port", value()->default_value(0), "Port to listen for incoming NTCP2 connections (default: auto)") ("ntcp2.addressv6", value()->default_value("::"), "Address to publish NTCP2 with") ("ntcp2.proxy", value()->default_value(""), "Proxy URL for NTCP2 transport") ; options_description nettime("Time sync options"); nettime.add_options() ("nettime.enabled", value()->default_value(false), "Disable time sync (default: disabled)") ("nettime.ntpservers", value()->default_value( "0.pool.ntp.org," "1.pool.ntp.org," "2.pool.ntp.org," "3.pool.ntp.org" ), "Comma separated list of NTCP servers") ("nettime.ntpsyncinterval", value()->default_value(72), "NTP sync interval in hours (default: 72)") ; options_description persist("Network information persisting options"); persist.add_options() ("persist.profiles", value()->default_value(true), "Persist peer profiles (default: true)") ("persist.addressbook", value()->default_value(true), "Persist full addresses (default: true)") ; options_description cpuext("CPU encryption extensions options"); cpuext.add_options() ("cpuext.aesni", bool_switch()->default_value(true), "Use auto detection for AESNI CPU extensions. If false, AESNI will be not used") ("cpuext.avx", bool_switch()->default_value(true), "Use auto detection for AVX CPU extensions. If false, AVX will be not used") ("cpuext.force", bool_switch()->default_value(false), "Force usage of CPU extensions. Useful when cpuinfo is not available on virtual machines") ; options_description meshnets("Meshnet transports options"); meshnets.add_options() ("meshnets.yggdrasil", bool_switch()->default_value(false), "Support transports through the Yggdrasil (default: false)") ("meshnets.yggaddress", value()->default_value(""), "Yggdrasil address to publish") ; m_OptionsDesc .add(general) .add(limits) .add(httpserver) .add(httpproxy) .add(socksproxy) .add(sam) .add(bob) .add(i2cp) .add(i2pcontrol) .add(upnp) .add(precomputation) .add(reseed) .add(addressbook) .add(trust) .add(websocket) // deprecated .add(exploratory) .add(ntcp2) .add(nettime) .add(persist) .add(cpuext) .add(meshnets) ; } void ParseCmdline(int argc, char* argv[], bool ignoreUnknown) { 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; if (ignoreUnknown) store(command_line_parser(argc, argv).options(m_OptionsDesc).style (style).allow_unregistered().run(), m_Options); else store(parse_command_line(argc, argv, m_OptionsDesc, style), m_Options); } catch (boost::program_options::error& e) { ThrowFatal ("Error while parsing arguments: ", e.what()); std::cerr << "args: " << e.what() << std::endl; exit(EXIT_FAILURE); } if (!ignoreUnknown && (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); } else if (m_Options.count("version")) { std::cout << "i2pd version " << I2PD_VERSION << " (" << I2P_VERSION << ")" << std::endl; std::cout << "Boost version " << BOOST_VERSION / 100000 << "." // maj. version << BOOST_VERSION / 100 % 1000 << "." // min. version << BOOST_VERSION % 100 // patch version << std::endl; #if defined(OPENSSL_VERSION_TEXT) std::cout << OPENSSL_VERSION_TEXT << std::endl; #endif #if defined(LIBRESSL_VERSION_TEXT) std::cout << LIBRESSL_VERSION_TEXT << std::endl; #endif exit(EXIT_SUCCESS); } } void ParseConfig(const std::string& path) { if (path == "") return; std::ifstream config(path, std::ios::in); if (!config.is_open()) { ThrowFatal ("Missing or unreadable config file: ", path); 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) { ThrowFatal ("Error while parsing config file: ", e.what()); std::cerr << e.what() << std::endl; exit(EXIT_FAILURE); }; } void Finalize() { notify(m_Options); } bool IsDefault(const char *name) { if (!m_Options.count(name)) throw "try to check non-existent option"; if (m_Options[name].defaulted()) return true; return false; } bool GetOptionAsAny(const char *name, boost::any& value) { if (!m_Options.count(name)) return false; value = m_Options[name]; return true; } bool GetOptionAsAny(const std::string& name, boost::any& value) { return GetOptionAsAny (name.c_str (), value); } } // namespace config } // namespace i2p