diff --git a/.travis.yml b/.travis.yml index c791187d..d83cdbc0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,7 +17,6 @@ addons: - libboost-date-time-dev - libboost-filesystem-dev - libboost-program-options-dev - - libboost-regex-dev - libboost-system-dev - libboost-thread-dev - libminiupnpc-dev diff --git a/AddressBook.cpp b/AddressBook.cpp index 5a07b3c8..0dbb42d7 100644 --- a/AddressBook.cpp +++ b/AddressBook.cpp @@ -159,7 +159,7 @@ namespace client int AddressBookFilesystemStorage::Save (const std::map& addresses) { - if (addresses.size() == 0) { + if (addresses.empty()) { LogPrint(eLogWarning, "Addressbook: not saving empty addressbook"); return 0; } diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 00000000..9a32e42f --- /dev/null +++ b/ChangeLog @@ -0,0 +1,86 @@ +# for this file format description, +# see https://github.com/olivierlacan/keep-a-changelog + +## [2.8.0] - UNRELEASED +### Changed +- Proxy refactoring & speedup +- I2PControl refactoring & fixes (proper jsonrpc responses on errors) +- boost::regex no more needed + +### Fixed +- initscripts: added openrc one, in sysv-ish make I2PD_PORT optional + +## [2.7.0] - 2016-05-18 +### Added +- Precomputed El-Gamal/DH tables +- Configurable limit of transit tunnels + +### Changed +- Speed-up of assymetric crypto for non-x64 platforms +- Refactoring of web-console + +## [2.6.0] - 2016-03-31 +### Added +- Gracefull shutdown on SIGINT +- Numeric bandwidth limits (was: by router class) +- Jumpservices in web-console +- Logging to syslog +- Tray icon for windows application + +### Changed +- Logs refactoring +- Improved statistics in web-console + +### Deprecated: +- Renamed main/tunnels config files (will use old, if found, but emits warning) + +## [2.5.1] - 2016-03-10 +### Fixed +- Doesn't create ~/.i2pd dir if missing + +## [2.5.0] - 2016-03-04 +### Added +- IRC server tunnels +- SOCKS outproxy support +- Support for gzipped addressbook updates +- Support for router families + +### Changed +- Shared RTT/RTO between streams +- Filesystem work refactoring + +## [2.4.0] - 2016-02-03 +### Added +- X-I2P-* headers for server http-tunnels +- I2CP options for I2P tunnels +- Show I2P tunnels in webconsole + +### Changed +- Refactoring of cmdline/config parsing + +## [2.3.0] - 2016-01-12 +### Added +- Support for new router bandwidth class codes (P and X) +- I2PControl supports external webui +- Added --pidfile and --notransit parameters +- Ability to specify signature type for i2p tunnel + +### Changed +- Fixed multiple floodfill-related bugs +- New webconsole layout + +## [2.2.0] - 2015-12-22 +### Added +- Ability to connect to router without ip via introducer + +### Changed +- Persist temporary encryption keys for local destinations +- Performance improvements for EdDSA +- New addressbook structure + +## [2.1.0] - 2015-11-12 +### Added +- Implementation of EdDSA + +### Changed +- EdDSA is default signature type for new RouterInfos diff --git a/ClientContext.cpp b/ClientContext.cpp index df0de4e5..db1c8e6a 100644 --- a/ClientContext.cpp +++ b/ClientContext.cpp @@ -114,6 +114,24 @@ namespace client } } + // I2CP + bool i2cp; i2p::config::GetOption("i2cp.enabled", i2cp); + if (i2cp) + { + std::string i2cpAddr; i2p::config::GetOption("i2cp.address", i2cpAddr); + uint16_t i2cpPort; i2p::config::GetOption("i2cp.port", i2cpPort); + LogPrint(eLogInfo, "Clients: starting I2CP at ", i2cpAddr, ":", i2cpPort); + try + { + m_I2CPServer = new I2CPServer (i2cpAddr, i2cpPort); + m_I2CPServer->Start (); + } + catch (std::exception& e) + { + LogPrint(eLogError, "Clients: Exception in I2CP: ", e.what()); + } + } + m_AddressBook.StartResolvers (); } @@ -165,6 +183,14 @@ namespace client m_BOBCommandChannel = nullptr; } + if (m_I2CPServer) + { + LogPrint(eLogInfo, "Clients: stopping I2CP"); + m_I2CPServer->Stop (); + delete m_I2CPServer; + m_I2CPServer = nullptr; + } + LogPrint(eLogInfo, "Clients: stopping AddressBook"); m_AddressBook.Stop (); for (auto it: m_Destinations) diff --git a/Config.cpp b/Config.cpp index 44dec286..e6d44d59 100644 --- a/Config.cpp +++ b/Config.cpp @@ -178,6 +178,13 @@ namespace config { ("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") + ; + options_description i2pcontrol("I2PControl options"); i2pcontrol.add_options() ("i2pcontrol.enabled", value()->default_value(false), "Enable or disable I2P Control Protocol") @@ -207,6 +214,7 @@ namespace config { .add(socksproxy) .add(sam) .add(bob) + .add(i2cp) .add(i2pcontrol) .add(precomputation) ; diff --git a/Config.h b/Config.h index d79a9c47..6b2af717 100644 --- a/Config.h +++ b/Config.h @@ -68,7 +68,7 @@ namespace config { * @param value Variable where to store option * @return this function returns false if parameter not found * - * @example uint16_t port; GetOption("sam.port", port); + * Example: uint16_t port; GetOption("sam.port", port); */ template bool GetOption(const char *name, T& value) { @@ -84,7 +84,7 @@ namespace config { * @param value New parameter value * @return true if value set up successful, false otherwise * - * @example uint16_t port = 2827; SetOption("bob.port", port); + * Example: uint16_t port = 2827; SetOption("bob.port", port); */ template bool SetOption(const char *name, const T& value) { diff --git a/Daemon.cpp b/Daemon.cpp index c98bce05..7ca28a6f 100644 --- a/Daemon.cpp +++ b/Daemon.cpp @@ -45,10 +45,10 @@ namespace i2p #endif }; - Daemon_Singleton::Daemon_Singleton() : running(1), d(*new Daemon_Singleton_Private()) {}; + Daemon_Singleton::Daemon_Singleton() : isDaemon(false), running(true), d(*new Daemon_Singleton_Private()) {} Daemon_Singleton::~Daemon_Singleton() { delete &d; - }; + } bool Daemon_Singleton::IsService () const { diff --git a/Daemon.h b/Daemon.h index 031686f7..977d9258 100644 --- a/Daemon.h +++ b/Daemon.h @@ -22,9 +22,7 @@ namespace i2p virtual bool stop(); virtual void run () {}; - bool isLogging; bool isDaemon; - bool running; protected: diff --git a/DaemonLinux.cpp b/DaemonLinux.cpp index b408fc70..1cc0fa85 100644 --- a/DaemonLinux.cpp +++ b/DaemonLinux.cpp @@ -45,7 +45,7 @@ namespace i2p { bool DaemonLinux::start() { - if (isDaemon == 1) + if (isDaemon) { pid_t pid; pid = fork(); @@ -73,10 +73,10 @@ namespace i2p return false; } - // close stdin/stdout/stderr descriptors - freopen("/dev/null", "r", stdin); - freopen("/dev/null", "w", stdout); - freopen("/dev/null", "w", stderr); + // point std{in,out,err} descriptors to /dev/null + stdin = freopen("/dev/null", "r", stdin); + stdout = freopen("/dev/null", "w", stdout); + stderr = freopen("/dev/null", "w", stderr); } // Pidfile diff --git a/DaemonWin32.cpp b/DaemonWin32.cpp index a6d91da4..3afb70ce 100644 --- a/DaemonWin32.cpp +++ b/DaemonWin32.cpp @@ -45,7 +45,7 @@ namespace i2p return false; } - if (isDaemon == 1) + if (isDaemon) { LogPrint(eLogDebug, "Daemon: running as service"); I2PService service(SERVICE_NAME); diff --git a/Destination.cpp b/Destination.cpp index 40c8768e..bb67b601 100644 --- a/Destination.cpp +++ b/Destination.cpp @@ -130,10 +130,6 @@ namespace client if (!m_IsRunning) { m_IsRunning = true; - if (m_IsPublic) - PersistTemporaryKeys (); - else - i2p::crypto::GenerateElGamalKeyPair(m_EncryptionPrivateKey, m_EncryptionPublicKey); m_Pool->SetLocalDestination (shared_from_this ()); m_Pool->SetActive (true); m_Thread = new std::thread (std::bind (&LeaseSetDestination::Run, shared_from_this ())); @@ -204,14 +200,21 @@ namespace client return m_LeaseSet; } + void LeaseSetDestination::SetLeaseSet (i2p::data::LocalLeaseSet * newLeaseSet) + { + m_LeaseSet.reset (newLeaseSet); + if (m_IsPublic) + { + m_PublishVerificationTimer.cancel (); + Publish (); + } + } + void LeaseSetDestination::UpdateLeaseSet () { int numTunnels = m_Pool->GetNumInboundTunnels () + 2; // 2 backup tunnels if (numTunnels > i2p::data::MAX_NUM_LEASES) numTunnels = i2p::data::MAX_NUM_LEASES; // 16 tunnels maximum - auto leaseSet = new i2p::data::LocalLeaseSet (GetIdentity (), GetEncryptionPublicKey (), - m_Pool->GetInboundTunnels (numTunnels)); - Sign (leaseSet->GetBuffer (), leaseSet->GetBufferLen () - leaseSet->GetSignatureLen (), leaseSet->GetSignature ()); // TODO - m_LeaseSet.reset (leaseSet); + CreateNewLeaseSet (m_Pool->GetInboundTunnels (numTunnels)); } bool LeaseSetDestination::SubmitSessionKey (const uint8_t * key, const uint8_t * tag) @@ -391,11 +394,6 @@ namespace client { i2p::garlic::GarlicDestination::SetLeaseSetUpdated (); UpdateLeaseSet (); - if (m_IsPublic) - { - m_PublishVerificationTimer.cancel (); - Publish (); - } } void LeaseSetDestination::Publish () @@ -642,36 +640,16 @@ namespace client else it++; } - } - - void LeaseSetDestination::PersistTemporaryKeys () - { - std::string ident = GetIdentHash().ToBase32(); - std::string path = i2p::fs::DataDirPath("destinations", (ident + ".dat")); - std::ifstream f(path, std::ifstream::binary); - - if (f) { - f.read ((char *)m_EncryptionPublicKey, 256); - f.read ((char *)m_EncryptionPrivateKey, 256); - return; - } - - LogPrint (eLogInfo, "Destination: Creating new temporary keys for address ", ident, ".b32.i2p"); - i2p::crypto::GenerateElGamalKeyPair(m_EncryptionPrivateKey, m_EncryptionPublicKey); - - std::ofstream f1 (path, std::ofstream::binary | std::ofstream::out); - if (f1) { - f1.write ((char *)m_EncryptionPublicKey, 256); - f1.write ((char *)m_EncryptionPrivateKey, 256); - return; - } - LogPrint(eLogError, "Destinations: Can't save keys to ", path); } ClientDestination::ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map * params): LeaseSetDestination (isPublic, params), m_Keys (keys), m_DatagramDestination (nullptr) { + if (isPublic) + PersistTemporaryKeys (); + else + i2p::crypto::GenerateElGamalKeyPair(m_EncryptionPrivateKey, m_EncryptionPublicKey); if (isPublic) LogPrint (eLogInfo, "Destination: Local address ", GetIdentHash().ToBase32 (), " created"); } @@ -840,5 +818,37 @@ namespace client ret.push_back (it1.second); return ret; } + + void ClientDestination::PersistTemporaryKeys () + { + std::string ident = GetIdentHash().ToBase32(); + std::string path = i2p::fs::DataDirPath("destinations", (ident + ".dat")); + std::ifstream f(path, std::ifstream::binary); + + if (f) { + f.read ((char *)m_EncryptionPublicKey, 256); + f.read ((char *)m_EncryptionPrivateKey, 256); + return; + } + + LogPrint (eLogInfo, "Destination: Creating new temporary keys for address ", ident, ".b32.i2p"); + i2p::crypto::GenerateElGamalKeyPair(m_EncryptionPrivateKey, m_EncryptionPublicKey); + + std::ofstream f1 (path, std::ofstream::binary | std::ofstream::out); + if (f1) { + f1.write ((char *)m_EncryptionPublicKey, 256); + f1.write ((char *)m_EncryptionPrivateKey, 256); + return; + } + LogPrint(eLogError, "Destinations: Can't save keys to ", path); + } + + void ClientDestination::CreateNewLeaseSet (std::vector > tunnels) + { + auto leaseSet = new i2p::data::LocalLeaseSet (GetIdentity (), m_EncryptionPublicKey, tunnels); + // sign + Sign (leaseSet->GetBuffer (), leaseSet->GetBufferLen () - leaseSet->GetSignatureLen (), leaseSet->GetSignature ()); // TODO + SetLeaseSet (leaseSet); + } } } diff --git a/Destination.h b/Destination.h index e2531699..e64508c9 100644 --- a/Destination.h +++ b/Destination.h @@ -81,10 +81,6 @@ namespace client bool RequestDestination (const i2p::data::IdentHash& dest, RequestComplete requestComplete = nullptr); void CancelDestinationRequest (const i2p::data::IdentHash& dest); - // implements LocalDestination - const uint8_t * GetEncryptionPrivateKey () const { return m_EncryptionPrivateKey; }; - const uint8_t * GetEncryptionPublicKey () const { return m_EncryptionPublicKey; }; - // implements GarlicDestination std::shared_ptr GetLeaseSet (); std::shared_ptr GetTunnelPool () const { return m_Pool; } @@ -98,8 +94,10 @@ namespace client protected: + void SetLeaseSet (i2p::data::LocalLeaseSet * newLeaseSet); // I2CP virtual void HandleDataMessage (const uint8_t * buf, size_t len) = 0; + virtual void CreateNewLeaseSet (std::vector > tunnels) = 0; private: @@ -117,13 +115,9 @@ namespace client void HandleRequestTimoutTimer (const boost::system::error_code& ecode, const i2p::data::IdentHash& dest); void HandleCleanupTimer (const boost::system::error_code& ecode); void CleanupRemoteLeaseSets (); - - void PersistTemporaryKeys (); private: - uint8_t m_EncryptionPublicKey[256], m_EncryptionPrivateKey[256]; - volatile bool m_IsRunning; std::thread * m_Thread; boost::asio::io_service m_Service; @@ -156,7 +150,8 @@ namespace client bool Stop (); const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; }; - + void Sign (const uint8_t * buf, int len, uint8_t * signature) const { m_Keys.Sign (buf, len, signature); }; + // streaming std::shared_ptr CreateStreamingDestination (int port, bool gzip = true); // additional std::shared_ptr GetStreamingDestination (int port = 0) const; @@ -166,28 +161,31 @@ namespace client void AcceptStreams (const i2p::stream::StreamingDestination::Acceptor& acceptor); void StopAcceptingStreams (); bool IsAcceptingStreams () const; - + // datagram i2p::datagram::DatagramDestination * GetDatagramDestination () const { return m_DatagramDestination; }; i2p::datagram::DatagramDestination * CreateDatagramDestination (); - // implements LocalDestination - std::shared_ptr GetIdentity () const { return m_Keys.GetPublic (); }; - void Sign (const uint8_t * buf, int len, uint8_t * signature) const { m_Keys.Sign (buf, len, signature); }; + // implements LocalDestination + const uint8_t * GetEncryptionPrivateKey () const { return m_EncryptionPrivateKey; }; + std::shared_ptr GetIdentity () const { return m_Keys.GetPublic (); }; protected: // I2CP void HandleDataMessage (const uint8_t * buf, size_t len); + void CreateNewLeaseSet (std::vector > tunnels); private: std::shared_ptr GetSharedFromThis () - { return std::static_pointer_cast(shared_from_this ()); } + { return std::static_pointer_cast(shared_from_this ()); } + void PersistTemporaryKeys (); private: i2p::data::PrivateKeys m_Keys; + uint8_t m_EncryptionPublicKey[256], m_EncryptionPrivateKey[256]; std::shared_ptr m_StreamingDestination; // default std::map > m_StreamingDestinationsByPorts; diff --git a/HTTP.cpp b/HTTP.cpp index 3f6fd937..a23f5a72 100644 --- a/HTTP.cpp +++ b/HTTP.cpp @@ -253,21 +253,12 @@ namespace http { if (pos >= eoh) break; } - auto it = headers.find("Host"); - if (it != headers.end ()) { - host = it->second; - } else if (version == "HTTP/1.1") { - return -1; /* 'Host' header required for HTTP/1.1 */ - } else if (url.host != "") { - host = url.host; - } return eoh + strlen(HTTP_EOH); } std::string HTTPReq::to_string() { std::stringstream ss; ss << method << " " << uri << " " << version << CRLF; - ss << "Host: " << host << CRLF; for (auto & h : headers) { ss << h.first << ": " << h.second << CRLF; } @@ -406,11 +397,10 @@ namespace http { bool MergeChunkedResponse (std::istream& in, std::ostream& out) { std::string hexLen; - long int len; while (!in.eof ()) { std::getline (in, hexLen); errno = 0; - len = strtoul(hexLen.c_str(), (char **) NULL, 16); + long int len = strtoul(hexLen.c_str(), (char **) NULL, 16); if (errno != 0) return false; /* conversion error */ if (len == 0) diff --git a/HTTP.h b/HTTP.h index f227271f..19d0612e 100644 --- a/HTTP.h +++ b/HTTP.h @@ -38,7 +38,7 @@ namespace http { * @brief Tries to parse url from string * @return true on success, false on invalid url */ - bool parse (const char *str, size_t len = 0); + bool parse (const char *str, std::size_t len = 0); bool parse (const std::string& url); /** @@ -69,7 +69,6 @@ namespace http { std::string version; std::string method; std::string uri; - std::string host; HTTPReq (): version("HTTP/1.0"), method("GET"), uri("/") {}; @@ -89,10 +88,12 @@ namespace http { std::string version; std::string status; unsigned short int code; - /** simplifies response generation - * If this variable is set: - * a) Content-Length header will be added if missing - * b) contents of body will be included in response + /** + * @brief Simplifies response generation + * + * If this variable is set, on @a to_string() call: + * * Content-Length header will be added if missing, + * * contents of @a body will be included in generated response */ std::string body; @@ -108,9 +109,9 @@ namespace http { /** * @brief Serialize HTTP response to string - * @note If version is set to HTTP/1.1, and Date header is missing, + * @note If @a version is set to HTTP/1.1, and Date header is missing, * it will be generated based on current time and added to headers - * @note If body member is set and Content-Length header is missing, + * @note If @a body is set and Content-Length header is missing, * this header will be added, based on body's length */ std::string to_string(); diff --git a/HTTPProxy.cpp b/HTTPProxy.cpp index f681f365..056e3170 100644 --- a/HTTPProxy.cpp +++ b/HTTPProxy.cpp @@ -1,16 +1,8 @@ #include #include #include -#include #include #include -#include -#include -#include -#include - -#include "I2PService.h" -#include "Destination.h" #include "HTTPProxy.h" #include "util.h" #include "Identity.h" @@ -20,6 +12,7 @@ #include "I2PEndian.h" #include "I2PTunnel.h" #include "Config.h" +#include "HTTP.h" namespace i2p { @@ -43,7 +36,7 @@ namespace proxy void HandleSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered); void Terminate(); void AsyncSockRead(); - void HTTPRequestFailed(const char *message); + void HTTPRequestFailed(/*std::string message*/); void RedirectToJumpService(); void ExtractRequest(); bool IsI2PAddress(); @@ -56,7 +49,6 @@ namespace proxy uint8_t m_http_buff[http_buffer_size]; std::shared_ptr m_sock; std::string m_request; //Data left to be sent - std::string m_Response; std::string m_url; //URL std::string m_method; //Method std::string m_version; //HTTP version @@ -99,17 +91,10 @@ namespace proxy /* All hope is lost beyond this point */ //TODO: handle this apropriately - void HTTPProxyHandler::HTTPRequestFailed(const char *message) + void HTTPProxyHandler::HTTPRequestFailed(/*HTTPProxyHandler::errTypes error*/) { - std::size_t size = std::strlen(message); - std::stringstream ss; - ss << "HTTP/1.0 500 Internal Server Error\r\n" - << "Content-Type: text/plain\r\n"; - ss << "Content-Length: " << std::to_string(size + 2) << "\r\n" - << "\r\n"; /* end of headers */ - ss << message << "\r\n"; - m_Response = ss.str(); - boost::asio::async_write(*m_sock, boost::asio::buffer(m_Response), + static std::string response = "HTTP/1.0 500 Internal Server Error\r\nContent-type: text/html\r\nContent-length: 0\r\n"; + boost::asio::async_write(*m_sock, boost::asio::buffer(response,response.size()), std::bind(&HTTPProxyHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1)); } @@ -120,8 +105,7 @@ namespace proxy uint16_t httpPort; i2p::config::GetOption("http.port", httpPort); response << "HTTP/1.1 302 Found\r\nLocation: http://" << httpAddr << ":" << httpPort << "/?page=jumpservices&address=" << m_address << "\r\n\r\n"; - m_Response = response.str (); - boost::asio::async_write(*m_sock, boost::asio::buffer(m_Response), + boost::asio::async_write(*m_sock, boost::asio::buffer(response.str (),response.str ().length ()), std::bind(&HTTPProxyHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1)); } @@ -133,21 +117,13 @@ namespace proxy void HTTPProxyHandler::ExtractRequest() { LogPrint(eLogDebug, "HTTPProxy: request: ", m_method, " ", m_url); - std::string server=""; - std::string port="80"; - boost::regex rHTTP("http://(.*?)(:(\\d+))?(/.*)"); - boost::smatch m; - std::string path; - if(boost::regex_search(m_url, m, rHTTP, boost::match_extra)) - { - server=m[1].str(); - if (m[2].str() != "") port=m[3].str(); - path=m[4].str(); - } - LogPrint(eLogDebug, "HTTPProxy: server: ", server, ", port: ", port, ", path: ", path); - m_address = server; - m_port = boost::lexical_cast(port); - m_path = path; + i2p::http::URL url; + url.parse (m_url); + m_address = url.host; + m_port = url.port; + m_path = url.path; + if (!m_port) m_port = 80; + LogPrint(eLogDebug, "HTTPProxy: server: ", m_address, ", port: ", m_port, ", path: ", m_path); } bool HTTPProxyHandler::ValidateHTTPRequest() @@ -155,7 +131,7 @@ namespace proxy if ( m_version != "HTTP/1.0" && m_version != "HTTP/1.1" ) { LogPrint(eLogError, "HTTPProxy: unsupported version: ", m_version); - HTTPRequestFailed("unsupported HTTP version"); + HTTPRequestFailed(); //TODO: send right stuff return false; } return true; @@ -292,13 +268,13 @@ namespace proxy case '\n': EnterState(DONE); break; default: LogPrint(eLogError, "HTTPProxy: rejected invalid request ending with: ", ((int)*http_buff)); - HTTPRequestFailed("rejected invalid request"); + HTTPRequestFailed(); //TODO: add correct code return false; } break; default: LogPrint(eLogError, "HTTPProxy: invalid state: ", m_state); - HTTPRequestFailed("invalid parser state"); + HTTPRequestFailed(); //TODO: add correct code 500 return false; } http_buff++; @@ -354,7 +330,7 @@ namespace proxy else { LogPrint (eLogError, "HTTPProxy: error when creating the stream, check the previous warnings for more info"); - HTTPRequestFailed("error when creating the stream, check logs"); + HTTPRequestFailed(); // TODO: Send correct error message host unreachable } } @@ -367,5 +343,6 @@ namespace proxy { return std::make_shared (this, socket); } + } } diff --git a/HTTPProxy.h b/HTTPProxy.h index 0356adb5..b5ed77b9 100644 --- a/HTTPProxy.h +++ b/HTTPProxy.h @@ -1,6 +1,13 @@ #ifndef HTTP_PROXY_H__ #define HTTP_PROXY_H__ +#include +#include +#include +#include +#include "I2PService.h" +#include "Destination.h" + namespace i2p { namespace proxy diff --git a/HTTPServer.cpp b/HTTPServer.cpp index 5d54711d..7b7b8bcc 100644 --- a/HTTPServer.cpp +++ b/HTTPServer.cpp @@ -27,156 +27,6 @@ namespace i2p { namespace http { - const char *itoopieImage = - "\"ICToopie"; - const char *itoopieFavicon = "data:image/png;base64," "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv" @@ -667,7 +517,7 @@ namespace http { i2p::config::GetOption("http.auth", needAuth); i2p::config::GetOption("http.user", user); i2p::config::GetOption("http.pass", pass); - }; + } void HTTPConnection::Receive () { @@ -755,12 +605,14 @@ namespace http { // Html5 head start ShowPageHead (s); - if (req.uri.find("page=") != std::string::npos) + if (req.uri.find("page=") != std::string::npos) { HandlePage (req, res, s); - else if (req.uri.find("cmd=") != std::string::npos) + } else if (req.uri.find("cmd=") != std::string::npos) { HandleCommand (req, res, s); - else + } else { ShowStatus (s); + res.add_header("Refresh", "5"); + } ShowPageTail (s); res.code = 200; @@ -841,7 +693,9 @@ namespace http { return; } s << "SUCCESS: Command accepted

\r\n"; - s << "Back to commands list"; + s << "Back to commands list
\r\n"; + s << "

You will be redirected in 5 seconds"; + res.add_header("Refresh", "5; url=/?page=commands"); } void HTTPConnection::SendReply (HTTPRes& reply, std::string& content) diff --git a/HTTPServer.h b/HTTPServer.h index 2635c3be..bf7f5c65 100644 --- a/HTTPServer.h +++ b/HTTPServer.h @@ -3,7 +3,6 @@ namespace i2p { namespace http { - extern const char *itoopieImage; extern const char *itoopieFavicon; const size_t HTTP_CONNECTION_BUFFER_SIZE = 8192; diff --git a/I2CP.cpp b/I2CP.cpp index 5c718a48..71188ec8 100644 --- a/I2CP.cpp +++ b/I2CP.cpp @@ -1,29 +1,139 @@ +/* +* 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 "I2PEndian.h" #include "Log.h" +#include "Timestamp.h" +#include "LeaseSet.h" +#include "ClientContext.h" +#include "Transports.h" +#include "Signature.h" #include "I2CP.h" - namespace i2p { namespace client { - I2CPDestination::I2CPDestination (I2CPSession& owner, std::shared_ptr identity, bool isPublic): - LeaseSetDestination (isPublic), m_Owner (owner), m_Identity (identity) + I2CPDestination::I2CPDestination (std::shared_ptr owner, std::shared_ptr identity, bool isPublic, const std::map& params): + LeaseSetDestination (isPublic, ¶ms), m_Owner (owner), m_Identity (identity) { } + void I2CPDestination::SetEncryptionPrivateKey (const uint8_t * key) + { + memcpy (m_EncryptionPrivateKey, key, 256); + } + + void I2CPDestination::HandleDataMessage (const uint8_t * buf, size_t len) + { + uint32_t length = bufbe32toh (buf); + if (length > len - 4) length = len - 4; + m_Owner->SendMessagePayloadMessage (buf + 4, length); + } + + void I2CPDestination::CreateNewLeaseSet (std::vector > tunnels) + { + i2p::data::LocalLeaseSet ls (m_Identity, m_EncryptionPrivateKey, tunnels); // we don't care about encryption key + m_LeaseSetExpirationTime = ls.GetExpirationTime (); + uint8_t * leases = ls.GetLeases (); + leases[-1] = tunnels.size (); + htobe16buf (leases - 3, m_Owner->GetSessionID ()); + size_t l = 2/*sessionID*/ + 1/*num leases*/ + i2p::data::LEASE_SIZE*tunnels.size (); + m_Owner->SendI2CPMessage (I2CP_REQUEST_VARIABLE_LEASESET_MESSAGE, leases - 3, l); + } + + void I2CPDestination::LeaseSetCreated (const uint8_t * buf, size_t len) + { + auto ls = new i2p::data::LocalLeaseSet (m_Identity, buf, len); + ls->SetExpirationTime (m_LeaseSetExpirationTime); + SetLeaseSet (ls); + } + + void I2CPDestination::SendMsgTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint32_t nonce) + { + auto msg = NewI2NPMessage (); + uint8_t * buf = msg->GetPayload (); + htobe32buf (buf, len); + memcpy (buf + 4, payload, len); + msg->len += len + 4; + msg->FillI2NPMessageHeader (eI2NPData); + auto remote = FindLeaseSet (ident); + if (remote) + GetService ().post (std::bind (&I2CPDestination::SendMsg, GetSharedFromThis (), msg, remote)); + else + { + auto s = GetSharedFromThis (); + RequestDestination (ident, + [s, msg, nonce](std::shared_ptr ls) + { + if (ls) + { + bool sent = s->SendMsg (msg, ls); + s->m_Owner->SendMessageStatusMessage (nonce, sent ? eI2CPMessageStatusGuaranteedSuccess : eI2CPMessageStatusGuaranteedFailure); + } + else + s->m_Owner->SendMessageStatusMessage (nonce, eI2CPMessageStatusNoLeaseSet); + }); + } + } + + bool I2CPDestination::SendMsg (std::shared_ptr msg, std::shared_ptr remote) + { + auto outboundTunnel = GetTunnelPool ()->GetNextOutboundTunnel (); + auto leases = remote->GetNonExpiredLeases (); + if (!leases.empty () && outboundTunnel) + { + std::vector msgs; + uint32_t i = rand () % leases.size (); + auto garlic = WrapMessage (remote, msg, true); + msgs.push_back (i2p::tunnel::TunnelMessageBlock + { + i2p::tunnel::eDeliveryTypeTunnel, + leases[i]->tunnelGateway, leases[i]->tunnelID, + garlic + }); + outboundTunnel->SendTunnelDataMsg (msgs); + return true; + } + else + { + if (outboundTunnel) + LogPrint (eLogWarning, "I2CP: Failed to send message. All leases expired"); + else + LogPrint (eLogWarning, "I2CP: Failed to send message. No outbound tunnels"); + return false; + } + } + I2CPSession::I2CPSession (I2CPServer& owner, std::shared_ptr socket): m_Owner (owner), m_Socket (socket), - m_NextMessage (nullptr), m_NextMessageLen (0), m_NextMessageOffset (0) + m_NextMessage (nullptr), m_NextMessageLen (0), m_NextMessageOffset (0), + m_SessionID (0), m_MessageID (0), m_IsSendAccepted (true) + { + } + + I2CPSession::~I2CPSession () + { + delete[] m_NextMessage; + } + + void I2CPSession::Start () { ReadProtocolByte (); } - I2CPSession::~I2CPSession () + void I2CPSession::Stop () { - delete[] m_NextMessage; + Terminate (); } void I2CPSession::ReadProtocolByte () @@ -34,7 +144,7 @@ namespace client m_Socket->async_read_some (boost::asio::buffer (m_Buffer, 1), [s](const boost::system::error_code& ecode, std::size_t bytes_transferred) { - if (!ecode && bytes_transferred > 0 && s->m_Buffer[0] == I2CP_PRTOCOL_BYTE) + if (!ecode && bytes_transferred > 0 && s->m_Buffer[0] == I2CP_PROTOCOL_BYTE) s->Receive (); else s->Terminate (); @@ -54,25 +164,34 @@ namespace client Terminate (); else { - size_t offset = 0; + size_t offset = 0; // from m_Buffer if (m_NextMessage) { - if (m_NextMessageOffset + bytes_transferred <= m_NextMessageLen) + if (m_NextMessageOffset + bytes_transferred < m_NextMessageLen) { memcpy (m_NextMessage + m_NextMessageOffset, m_Buffer, bytes_transferred); m_NextMessageOffset += bytes_transferred; + offset = bytes_transferred; } else { + // m_NextMessage complete offset = m_NextMessageLen - m_NextMessageOffset; memcpy (m_NextMessage + m_NextMessageOffset, m_Buffer, offset); HandleNextMessage (m_NextMessage); delete[] m_NextMessage; + m_NextMessage = nullptr; } } while (offset < bytes_transferred) { auto msgLen = bufbe32toh (m_Buffer + offset + I2CP_HEADER_LENGTH_OFFSET) + I2CP_HEADER_SIZE; + if (msgLen > 0xFFFF) // 64K + { + LogPrint (eLogError, "I2CP: message length ", msgLen, " exceeds 64K. Terminated"); + Terminate (); + return; + } if (msgLen <= bytes_transferred - offset) { HandleNextMessage (m_Buffer + offset); @@ -102,17 +221,463 @@ namespace client void I2CPSession::Terminate () { + if (m_Destination) + { + m_Destination->Stop (); + m_Destination = nullptr; + } + if (m_Socket) + { + m_Socket->close (); + m_Socket = nullptr; + } + m_Owner.RemoveSession (GetSessionID ()); + } + + void I2CPSession::SendI2CPMessage (uint8_t type, const uint8_t * payload, size_t len) + { + auto l = len + I2CP_HEADER_SIZE; + uint8_t * buf = new uint8_t[l]; + htobe32buf (buf + I2CP_HEADER_LENGTH_OFFSET, len); + buf[I2CP_HEADER_TYPE_OFFSET] = type; + memcpy (buf + I2CP_HEADER_SIZE, payload, len); + boost::asio::async_write (*m_Socket, boost::asio::buffer (buf, l), boost::asio::transfer_all (), + std::bind(&I2CPSession::HandleI2CPMessageSent, shared_from_this (), + std::placeholders::_1, std::placeholders::_2, buf)); + } + + void I2CPSession::HandleI2CPMessageSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, const uint8_t * buf) + { + delete[] buf; + if (ecode && ecode != boost::asio::error::operation_aborted) + Terminate (); + } + + std::string I2CPSession::ExtractString (const uint8_t * buf, size_t len) + { + uint8_t l = buf[0]; + if (l > len) l = len; + return std::string ((const char *)(buf + 1), l); + } + + size_t I2CPSession::PutString (uint8_t * buf, size_t len, const std::string& str) + { + auto l = str.length (); + if (l + 1 >= len) l = len - 1; + if (l > 255) l = 255; // 1 byte max + buf[0] = l; + memcpy (buf + 1, str.c_str (), l); + return l + 1; + } + + void I2CPSession::ExtractMapping (const uint8_t * buf, size_t len, std::map& mapping) + // TODO: move to Base.cpp + { + size_t offset = 0; + while (offset < len) + { + std::string param = ExtractString (buf + offset, len - offset); + offset += param.length (); + if (buf[offset] != '=') + { + LogPrint (eLogWarning, "I2CP: Unexpected character ", buf[offset], " instead '=' after ", param); + break; + } + offset++; + + std::string value = ExtractString (buf + offset, len - offset); + offset += value.length (); + if (buf[offset] != ';') + { + LogPrint (eLogWarning, "I2CP: Unexpected character ", buf[offset], " instead ';' after ", value); + break; + } + offset++; + mapping.insert (std::make_pair (param, value)); + } } void I2CPSession::GetDateMessageHandler (const uint8_t * buf, size_t len) { + // get version + auto version = ExtractString (buf, len); + auto l = version.length () + 1 + 8; + uint8_t * payload = new uint8_t[l]; + // set date + auto ts = i2p::util::GetMillisecondsSinceEpoch (); + htobe64buf (payload, ts); + // echo vesrion back + PutString (payload + 8, l - 8, version); + SendI2CPMessage (I2CP_SET_DATE_MESSAGE, payload, l); + delete[] payload; } - I2CPServer::I2CPServer (const std::string& interface, int port) + void I2CPSession::CreateSessionMessageHandler (const uint8_t * buf, size_t len) + { + RAND_bytes ((uint8_t *)&m_SessionID, 2); + auto identity = std::make_shared(); + size_t offset = identity->FromBuffer (buf, len); + if (!offset) + { + LogPrint (eLogError, "I2CP: create session maformed identity"); + SendSessionStatusMessage (3); // invalid + return; + } + uint16_t optionsSize = bufbe16toh (buf + offset); + offset += 2; + if (optionsSize > len - offset) + { + LogPrint (eLogError, "I2CP: options size ", optionsSize, "exceeds message size"); + SendSessionStatusMessage (3); // invalid + return; + } + std::map params; + ExtractMapping (buf + offset, optionsSize, params); + offset += optionsSize; // options + if (params[I2CP_PARAM_MESSAGE_RELIABILITY] == "none") m_IsSendAccepted = false; + + offset += 8; // date + if (identity->Verify (buf, offset, buf + offset)) // signature + { + bool isPublic = true; + if (params[I2CP_PARAM_DONT_PUBLISH_LEASESET] == "true") isPublic = false; + if (!m_Destination) + { + m_Destination = std::make_shared(shared_from_this (), identity, isPublic, params); + SendSessionStatusMessage (1); // created + LogPrint (eLogDebug, "I2CP: session ", m_SessionID, " created"); + m_Destination->Start (); + } + else + { + LogPrint (eLogError, "I2CP: session already exists"); + SendSessionStatusMessage (4); // refused + } + } + else + { + LogPrint (eLogError, "I2CP: create session signature verification falied"); + SendSessionStatusMessage (3); // invalid + } + } + + void I2CPSession::DestroySessionMessageHandler (const uint8_t * buf, size_t len) + { + SendSessionStatusMessage (0); // destroy + LogPrint (eLogDebug, "I2CP: session ", m_SessionID, " destroyed"); + if (m_Destination) + { + m_Destination->Stop (); + m_Destination = 0; + } + } + + void I2CPSession::ReconfigureSessionMessageHandler (const uint8_t * buf, size_t len) + { + // TODO: implement actual reconfiguration + SendSessionStatusMessage (2); // updated + } + + void I2CPSession::SendSessionStatusMessage (uint8_t status) + { + uint8_t buf[3]; + htobe16buf (buf, m_SessionID); + buf[2] = status; + SendI2CPMessage (I2CP_SESSION_STATUS_MESSAGE, buf, 3); + } + + void I2CPSession::SendMessageStatusMessage (uint32_t nonce, I2CPMessageStatus status) + { + if (!nonce) return; // don't send status with zero nonce + uint8_t buf[15]; + htobe16buf (buf, m_SessionID); + htobe32buf (buf + 2, m_MessageID++); + buf[6] = (uint8_t)status; + memset (buf + 7, 0, 4); // size + htobe32buf (buf + 11, nonce); + SendI2CPMessage (I2CP_MESSAGE_STATUS_MESSAGE, buf, 15); + } + + void I2CPSession::CreateLeaseSetMessageHandler (const uint8_t * buf, size_t len) + { + uint16_t sessionID = bufbe16toh (buf); + if (sessionID == m_SessionID) + { + size_t offset = 2; + if (m_Destination) + { + offset += i2p::crypto::DSA_PRIVATE_KEY_LENGTH; // skip signing private key + // we always assume this field as 20 bytes (DSA) regardless actual size + // instead of + //offset += m_Destination->GetIdentity ()->GetSigningPrivateKeyLen (); + m_Destination->SetEncryptionPrivateKey (buf + offset); + offset += 256; + m_Destination->LeaseSetCreated (buf + offset, len - offset); + } + } + else + LogPrint (eLogError, "I2CP: unexpected sessionID ", sessionID); + } + + void I2CPSession::SendMessageMessageHandler (const uint8_t * buf, size_t len) + { + uint16_t sessionID = bufbe16toh (buf); + if (sessionID == m_SessionID) + { + size_t offset = 2; + if (m_Destination) + { + i2p::data::IdentityEx identity; + offset += identity.FromBuffer (buf + offset, len - offset); + uint32_t payloadLen = bufbe32toh (buf + offset); + offset += 4; + uint32_t nonce = bufbe32toh (buf + offset + payloadLen); + if (m_IsSendAccepted) + SendMessageStatusMessage (nonce, eI2CPMessageStatusAccepted); // accepted + m_Destination->SendMsgTo (buf + offset, payloadLen, identity.GetIdentHash (), nonce); + } + } + else + LogPrint (eLogError, "I2CP: unexpected sessionID ", sessionID); + } + + void I2CPSession::SendMessageExpiresMessageHandler (const uint8_t * buf, size_t len) + { + SendMessageMessageHandler (buf, len - 8); // ignore flags(2) and expiration(6) + } + + void I2CPSession::HostLookupMessageHandler (const uint8_t * buf, size_t len) + { + uint16_t sessionID = bufbe16toh (buf); + if (sessionID == m_SessionID) + { + uint32_t requestID = bufbe32toh (buf + 2); + //uint32_t timeout = bufbe32toh (buf + 6); + i2p::data::IdentHash ident; + switch (buf[10]) + { + case 0: // hash + ident = i2p::data::IdentHash (buf + 11); + break; + case 1: // address + { + auto name = ExtractString (buf + 11, len - 11); + if (!i2p::client::context.GetAddressBook ().GetIdentHash (name, ident)) + { + LogPrint (eLogError, "I2CP: address ", name, " not found"); + SendHostReplyMessage (requestID, nullptr); + return; + } + break; + } + default: + LogPrint (eLogError, "I2CP: request type ", (int)buf[10], " is not supported"); + SendHostReplyMessage (requestID, nullptr); + return; + } + + if (m_Destination) + { + auto ls = m_Destination->FindLeaseSet (ident); + if (ls) + SendHostReplyMessage (requestID, ls->GetIdentity ()); + else + { + auto s = shared_from_this (); + m_Destination->RequestDestination (ident, + [s, requestID](std::shared_ptr leaseSet) + { + s->SendHostReplyMessage (requestID, leaseSet ? leaseSet->GetIdentity () : nullptr); + }); + } + } + else + SendHostReplyMessage (requestID, nullptr); + } + else + LogPrint (eLogError, "I2CP: unexpected sessionID ", sessionID); + } + + void I2CPSession::SendHostReplyMessage (uint32_t requestID, std::shared_ptr identity) + { + if (identity) + { + size_t l = identity->GetFullLen () + 7; + uint8_t * buf = new uint8_t[l]; + htobe16buf (buf, m_SessionID); + htobe32buf (buf + 2, requestID); + buf[6] = 0; // result code + identity->ToBuffer (buf + 7, l - 7); + SendI2CPMessage (I2CP_HOST_REPLY_MESSAGE, buf, l); + delete[] buf; + } + else + { + uint8_t buf[7]; + htobe16buf (buf, m_SessionID); + htobe32buf (buf + 2, requestID); + buf[6] = 1; // result code + SendI2CPMessage (I2CP_HOST_REPLY_MESSAGE, buf, 7); + } + } + + void I2CPSession::DestLookupMessageHandler (const uint8_t * buf, size_t len) + { + if (m_Destination) + { + auto ls = m_Destination->FindLeaseSet (buf); + if (ls) + { + auto l = ls->GetIdentity ()->GetFullLen (); + uint8_t * identBuf = new uint8_t[l]; + ls->GetIdentity ()->ToBuffer (identBuf, l); + SendI2CPMessage (I2CP_DEST_REPLY_MESSAGE, identBuf, l); + delete[] identBuf; + } + else + { + auto s = shared_from_this (); + i2p::data::IdentHash ident (buf); + m_Destination->RequestDestination (ident, + [s, ident](std::shared_ptr leaseSet) + { + if (leaseSet) // found + { + auto l = leaseSet->GetIdentity ()->GetFullLen (); + uint8_t * identBuf = new uint8_t[l]; + leaseSet->GetIdentity ()->ToBuffer (identBuf, l); + s->SendI2CPMessage (I2CP_DEST_REPLY_MESSAGE, identBuf, l); + delete[] identBuf; + } + else + s->SendI2CPMessage (I2CP_DEST_REPLY_MESSAGE, ident, 32); // not found + }); + } + } + else + SendI2CPMessage (I2CP_DEST_REPLY_MESSAGE, buf, 32); + } + + void I2CPSession::GetBandwidthLimitsMessageHandler (const uint8_t * buf, size_t len) + { + uint8_t limits[64]; + memset (limits, 0, 64); + htobe32buf (limits, i2p::transport::transports.GetInBandwidth ()); // inbound + htobe32buf (limits + 4, i2p::transport::transports.GetOutBandwidth ()); // outbound + SendI2CPMessage (I2CP_BANDWIDTH_LIMITS_MESSAGE, limits, 64); + } + + void I2CPSession::SendMessagePayloadMessage (const uint8_t * payload, size_t len) + { + // we don't use SendI2CPMessage to eliminate additional copy + auto l = len + 10 + I2CP_HEADER_SIZE; + uint8_t * buf = new uint8_t[l]; + htobe32buf (buf + I2CP_HEADER_LENGTH_OFFSET, len + 10); + buf[I2CP_HEADER_TYPE_OFFSET] = I2CP_MESSAGE_PAYLOAD_MESSAGE; + htobe16buf (buf + I2CP_HEADER_SIZE, m_SessionID); + htobe32buf (buf + I2CP_HEADER_SIZE + 2, m_MessageID++); + htobe32buf (buf + I2CP_HEADER_SIZE + 6, len); + memcpy (buf + I2CP_HEADER_SIZE + 10, payload, len); + boost::asio::async_write (*m_Socket, boost::asio::buffer (buf, l), boost::asio::transfer_all (), + std::bind(&I2CPSession::HandleI2CPMessageSent, shared_from_this (), + std::placeholders::_1, std::placeholders::_2, buf)); + } + + I2CPServer::I2CPServer (const std::string& interface, int port): + m_IsRunning (false), m_Thread (nullptr), + m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(interface), port)) { memset (m_MessagesHandlers, 0, sizeof (m_MessagesHandlers)); - m_MessagesHandlers[I2CP_GET_DATE_MESSAGE] = &I2CPSession::GetDateMessageHandler; + m_MessagesHandlers[I2CP_GET_DATE_MESSAGE] = &I2CPSession::GetDateMessageHandler; + m_MessagesHandlers[I2CP_CREATE_SESSION_MESSAGE] = &I2CPSession::CreateSessionMessageHandler; + m_MessagesHandlers[I2CP_DESTROY_SESSION_MESSAGE] = &I2CPSession::DestroySessionMessageHandler; + m_MessagesHandlers[I2CP_RECONFIGURE_SESSION_MESSAGE] = &I2CPSession::ReconfigureSessionMessageHandler; + m_MessagesHandlers[I2CP_CREATE_LEASESET_MESSAGE] = &I2CPSession::CreateLeaseSetMessageHandler; + m_MessagesHandlers[I2CP_SEND_MESSAGE_MESSAGE] = &I2CPSession::SendMessageMessageHandler; + m_MessagesHandlers[I2CP_SEND_MESSAGE_EXPIRES_MESSAGE] = &I2CPSession::SendMessageExpiresMessageHandler; + m_MessagesHandlers[I2CP_HOST_LOOKUP_MESSAGE] = &I2CPSession::HostLookupMessageHandler; + m_MessagesHandlers[I2CP_DEST_LOOKUP_MESSAGE] = &I2CPSession::DestLookupMessageHandler; + m_MessagesHandlers[I2CP_GET_BANDWIDTH_LIMITS_MESSAGE] = &I2CPSession::GetBandwidthLimitsMessageHandler; } + + I2CPServer::~I2CPServer () + { + if (m_IsRunning) + Stop (); + } + + void I2CPServer::Start () + { + Accept (); + m_IsRunning = true; + m_Thread = new std::thread (std::bind (&I2CPServer::Run, this)); + } + + void I2CPServer::Stop () + { + m_IsRunning = false; + m_Acceptor.cancel (); + for (auto it: m_Sessions) + it.second->Stop (); + m_Sessions.clear (); + m_Service.stop (); + if (m_Thread) + { + m_Thread->join (); + delete m_Thread; + m_Thread = nullptr; + } + } + + void I2CPServer::Run () + { + while (m_IsRunning) + { + try + { + m_Service.run (); + } + catch (std::exception& ex) + { + LogPrint (eLogError, "I2CP: runtime exception: ", ex.what ()); + } + } + } + + void I2CPServer::Accept () + { + auto newSocket = std::make_shared (m_Service); + m_Acceptor.async_accept (*newSocket, std::bind (&I2CPServer::HandleAccept, this, + std::placeholders::_1, newSocket)); + } + + void I2CPServer::HandleAccept(const boost::system::error_code& ecode, std::shared_ptr socket) + { + if (!ecode && socket) + { + boost::system::error_code ec; + auto ep = socket->remote_endpoint (ec); + if (!ec) + { + LogPrint (eLogDebug, "I2CP: new connection from ", ep); + auto session = std::make_shared(*this, socket); + m_Sessions[session->GetSessionID ()] = session; + session->Start (); + } + else + LogPrint (eLogError, "I2CP: incoming connection error ", ec.message ()); + } + else + LogPrint (eLogError, "I2CP: accept error: ", ecode.message ()); + + if (ecode != boost::asio::error::operation_aborted) + Accept (); + } + + void I2CPServer::RemoveSession (uint16_t sessionID) + { + m_Sessions.erase (sessionID); + } } } diff --git a/I2CP.h b/I2CP.h index 020fd22a..f77607ba 100644 --- a/I2CP.h +++ b/I2CP.h @@ -1,9 +1,19 @@ +/* +* 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 +*/ + #ifndef I2CP_H__ #define I2CP_H__ #include #include #include +#include +#include #include #include "Destination.h" @@ -11,7 +21,7 @@ namespace i2p { namespace client { - const uint8_t I2CP_PRTOCOL_BYTE = 0x2A; + const uint8_t I2CP_PROTOCOL_BYTE = 0x2A; const size_t I2CP_SESSION_BUFFER_SIZE = 4096; const size_t I2CP_HEADER_LENGTH_OFFSET = 0; @@ -19,27 +29,69 @@ namespace client const size_t I2CP_HEADER_SIZE = I2CP_HEADER_TYPE_OFFSET + 1; const uint8_t I2CP_GET_DATE_MESSAGE = 32; + const uint8_t I2CP_SET_DATE_MESSAGE = 33; + const uint8_t I2CP_CREATE_SESSION_MESSAGE = 1; + const uint8_t I2CP_RECONFIGURE_SESSION_MESSAGE = 2; + const uint8_t I2CP_SESSION_STATUS_MESSAGE = 20; + const uint8_t I2CP_DESTROY_SESSION_MESSAGE = 3; + const uint8_t I2CP_REQUEST_VARIABLE_LEASESET_MESSAGE = 37; + const uint8_t I2CP_CREATE_LEASESET_MESSAGE = 4; + const uint8_t I2CP_SEND_MESSAGE_MESSAGE = 5; + const uint8_t I2CP_SEND_MESSAGE_EXPIRES_MESSAGE = 36; + const uint8_t I2CP_MESSAGE_PAYLOAD_MESSAGE = 31; + const uint8_t I2CP_MESSAGE_STATUS_MESSAGE = 22; + const uint8_t I2CP_HOST_LOOKUP_MESSAGE = 38; + const uint8_t I2CP_HOST_REPLY_MESSAGE = 39; + const uint8_t I2CP_DEST_LOOKUP_MESSAGE = 34; + const uint8_t I2CP_DEST_REPLY_MESSAGE = 35; + const uint8_t I2CP_GET_BANDWIDTH_LIMITS_MESSAGE = 8; + const uint8_t I2CP_BANDWIDTH_LIMITS_MESSAGE = 23; + + enum I2CPMessageStatus + { + eI2CPMessageStatusAccepted = 1, + eI2CPMessageStatusGuaranteedSuccess = 4, + eI2CPMessageStatusGuaranteedFailure = 5, + eI2CPMessageStatusNoLeaseSet = 21 + }; + + // params + const char I2CP_PARAM_DONT_PUBLISH_LEASESET[] = "i2cp.dontPublishLeaseSet"; + const char I2CP_PARAM_MESSAGE_RELIABILITY[] = "i2cp.messageReliability"; class I2CPSession; class I2CPDestination: public LeaseSetDestination { public: - I2CPDestination (I2CPSession& owner, std::shared_ptr identity, bool isPublic); + I2CPDestination (std::shared_ptr owner, std::shared_ptr identity, bool isPublic, const std::map& params); + + void SetEncryptionPrivateKey (const uint8_t * key); + void LeaseSetCreated (const uint8_t * buf, size_t len); // called from I2CPSession + void SendMsgTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint32_t nonce); // called from I2CPSession + + // implements LocalDestination + const uint8_t * GetEncryptionPrivateKey () const { return m_EncryptionPrivateKey; }; + std::shared_ptr GetIdentity () const { return m_Identity; }; protected: - // implements LocalDestination - std::shared_ptr GetIdentity () const { return m_Identity; }; - void Sign (const uint8_t * buf, int len, uint8_t * signature) const { /* TODO */}; - // I2CP - void HandleDataMessage (const uint8_t * buf, size_t len) {}; + void HandleDataMessage (const uint8_t * buf, size_t len); + void CreateNewLeaseSet (std::vector > tunnels); private: - I2CPSession& m_Owner; + std::shared_ptr GetSharedFromThis () + { return std::static_pointer_cast(shared_from_this ()); } + bool SendMsg (std::shared_ptr msg, std::shared_ptr remote); + + private: + + std::shared_ptr m_Owner; std::shared_ptr m_Identity; + uint8_t m_EncryptionPrivateKey[256]; + uint64_t m_LeaseSetExpirationTime; }; class I2CPServer; @@ -50,8 +102,26 @@ namespace client I2CPSession (I2CPServer& owner, std::shared_ptr socket); ~I2CPSession (); + void Start (); + void Stop (); + uint16_t GetSessionID () const { return m_SessionID; }; + + // called from I2CPDestination + void SendI2CPMessage (uint8_t type, const uint8_t * payload, size_t len); + void SendMessagePayloadMessage (const uint8_t * payload, size_t len); + void SendMessageStatusMessage (uint32_t nonce, I2CPMessageStatus status); + // message handlers void GetDateMessageHandler (const uint8_t * buf, size_t len); + void CreateSessionMessageHandler (const uint8_t * buf, size_t len); + void DestroySessionMessageHandler (const uint8_t * buf, size_t len); + void ReconfigureSessionMessageHandler (const uint8_t * buf, size_t len); + void CreateLeaseSetMessageHandler (const uint8_t * buf, size_t len); + void SendMessageMessageHandler (const uint8_t * buf, size_t len); + void SendMessageExpiresMessageHandler (const uint8_t * buf, size_t len); + void HostLookupMessageHandler (const uint8_t * buf, size_t len); + void DestLookupMessageHandler (const uint8_t * buf, size_t len); + void GetBandwidthLimitsMessageHandler (const uint8_t * buf, size_t len); private: @@ -60,6 +130,14 @@ namespace client void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); void HandleNextMessage (const uint8_t * buf); void Terminate (); + + void HandleI2CPMessageSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, const uint8_t * buf); + std::string ExtractString (const uint8_t * buf, size_t len); + size_t PutString (uint8_t * buf, size_t len, const std::string& str); + void ExtractMapping (const uint8_t * buf, size_t len, std::map& mapping); + + void SendSessionStatusMessage (uint8_t status); + void SendHostReplyMessage (uint32_t requestID, std::shared_ptr identity); private: @@ -69,6 +147,9 @@ namespace client size_t m_NextMessageLen, m_NextMessageOffset; std::shared_ptr m_Destination; + uint16_t m_SessionID; + uint32_t m_MessageID; + bool m_IsSendAccepted; }; typedef void (I2CPSession::*I2CPMessageHandler)(const uint8_t * buf, size_t len); @@ -77,10 +158,30 @@ namespace client public: I2CPServer (const std::string& interface, int port); + ~I2CPServer (); + + void Start (); + void Stop (); + boost::asio::io_service& GetService () { return m_Service; }; + + void RemoveSession (uint16_t sessionID); + + private: + + void Run (); + + void Accept (); + void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr socket); private: - I2CPMessageHandler m_MessagesHandlers[256]; + I2CPMessageHandler m_MessagesHandlers[256]; + std::map > m_Sessions; + + bool m_IsRunning; + std::thread * m_Thread; + boost::asio::io_service m_Service; + boost::asio::ip::tcp::acceptor m_Acceptor; public: diff --git a/I2PService.cpp b/I2PService.cpp index 0aefc172..4f907f18 100644 --- a/I2PService.cpp +++ b/I2PService.cpp @@ -3,7 +3,6 @@ #include "ClientContext.h" #include "I2PService.h" - namespace i2p { namespace client @@ -28,7 +27,7 @@ namespace client m_LocalDestination->CreateStream (streamRequestComplete, identHash, port); else { - LogPrint (eLogWarning, "I2PService: Remote destination ", dest, " not found"); + LogPrint (eLogWarning, "I2PService: Remote destination not found: ", dest); streamRequestComplete (nullptr); } } @@ -71,7 +70,7 @@ namespace client std::bind(&TCPIPPipe::HandleUpstreamReceived, shared_from_this(), std::placeholders::_1, std::placeholders::_2)); } else { - LogPrint(eLogError, "TCPIPPipe: no upstream socket for read"); + LogPrint(eLogError, "TCPIPPipe: upstream receive: no socket"); } } @@ -82,14 +81,14 @@ namespace client std::bind(&TCPIPPipe::HandleDownstreamReceived, shared_from_this(), std::placeholders::_1, std::placeholders::_2)); } else { - LogPrint(eLogError, "TCPIPPipe: no downstream socket for read"); + LogPrint(eLogError, "TCPIPPipe: downstream receive: no socket"); } } void TCPIPPipe::UpstreamWrite(const uint8_t * buf, size_t len) { if (m_up) { - LogPrint(eLogDebug, "TCPIPPipe: write upstream ", (int)len); + LogPrint(eLogDebug, "TCPIPPipe: upstream: ", (int) len, " bytes written"); boost::asio::async_write(*m_up, boost::asio::buffer(buf, len), boost::asio::transfer_all(), std::bind(&TCPIPPipe::HandleUpstreamWrite, @@ -97,14 +96,14 @@ namespace client std::placeholders::_1) ); } else { - LogPrint(eLogError, "tcpip pipe upstream socket null"); + LogPrint(eLogError, "TCPIPPipe: upstream write: no socket"); } } void TCPIPPipe::DownstreamWrite(const uint8_t * buf, size_t len) { if (m_down) { - LogPrint(eLogDebug, "TCPIPPipe: write downstream ", (int)len); + LogPrint(eLogDebug, "TCPIPPipe: downstream: ", (int) len, " bytes written"); boost::asio::async_write(*m_down, boost::asio::buffer(buf, len), boost::asio::transfer_all(), std::bind(&TCPIPPipe::HandleDownstreamWrite, @@ -112,16 +111,16 @@ namespace client std::placeholders::_1) ); } else { - LogPrint(eLogError, "tcpip pipe downstream socket null"); + LogPrint(eLogError, "TCPIPPipe: downstream write: no socket"); } } void TCPIPPipe::HandleDownstreamReceived(const boost::system::error_code & ecode, std::size_t bytes_transfered) { - LogPrint(eLogDebug, "TCPIPPipe downstream got ", (int) bytes_transfered); + LogPrint(eLogDebug, "TCPIPPipe: downstream: ", (int) bytes_transfered, " bytes received"); if (ecode) { - LogPrint(eLogError, "TCPIPPipe Downstream read error:" , ecode.message()); + LogPrint(eLogError, "TCPIPPipe: downstream read error:" , ecode.message()); if (ecode != boost::asio::error::operation_aborted) Terminate(); } else { @@ -135,7 +134,7 @@ namespace client void TCPIPPipe::HandleDownstreamWrite(const boost::system::error_code & ecode) { if (ecode) { - LogPrint(eLogError, "TCPIPPipe Downstream write error:" , ecode.message()); + LogPrint(eLogError, "TCPIPPipe: downstream write error:" , ecode.message()); if (ecode != boost::asio::error::operation_aborted) Terminate(); } @@ -143,7 +142,7 @@ namespace client void TCPIPPipe::HandleUpstreamWrite(const boost::system::error_code & ecode) { if (ecode) { - LogPrint(eLogError, "TCPIPPipe Upstream write error:" , ecode.message()); + LogPrint(eLogError, "TCPIPPipe: upstream write error:" , ecode.message()); if (ecode != boost::asio::error::operation_aborted) Terminate(); } @@ -151,9 +150,9 @@ namespace client void TCPIPPipe::HandleUpstreamReceived(const boost::system::error_code & ecode, std::size_t bytes_transfered) { - LogPrint(eLogDebug, "TCPIPPipe upstream got ", (int) bytes_transfered); + LogPrint(eLogDebug, "TCPIPPipe: upstream ", (int)bytes_transfered, " bytes received"); if (ecode) { - LogPrint(eLogError, "TCPIPPipe Upstream read error:" , ecode.message()); + LogPrint(eLogError, "TCPIPPipe: upstream read error:" , ecode.message()); if (ecode != boost::asio::error::operation_aborted) Terminate(); } else { @@ -206,6 +205,5 @@ namespace client LogPrint (eLogError, "I2PService: ", GetName(), " closing socket on accept because: ", ecode.message ()); } } - } } diff --git a/Identity.h b/Identity.h index 541a7801..841acf65 100644 --- a/Identity.h +++ b/Identity.h @@ -179,9 +179,7 @@ namespace data virtual ~LocalDestination() {}; virtual const uint8_t * GetEncryptionPrivateKey () const = 0; - virtual const uint8_t * GetEncryptionPublicKey () const = 0; virtual std::shared_ptr GetIdentity () const = 0; - virtual void Sign (const uint8_t * buf, int len, uint8_t * signature) const = 0; const IdentHash& GetIdentHash () const { return GetIdentity ()->GetIdentHash (); }; }; diff --git a/LeaseSet.cpp b/LeaseSet.cpp index 5efe2b16..16a470e0 100644 --- a/LeaseSet.cpp +++ b/LeaseSet.cpp @@ -213,6 +213,7 @@ namespace data m_Buffer[offset] = num; offset++; // leases + m_Leases = m_Buffer + offset; auto currentTime = i2p::util::GetMillisecondsSinceEpoch (); for (int i = 0; i < num; i++) { @@ -231,6 +232,14 @@ namespace data // we don't sign it yet. must be signed later on } + LocalLeaseSet::LocalLeaseSet (std::shared_ptr identity, const uint8_t * buf, size_t len): + m_ExpirationTime (0), m_Identity (identity) + { + m_BufferLen = len; + m_Buffer = new uint8_t[m_BufferLen]; + memcpy (m_Buffer, buf, len); + } + bool LocalLeaseSet::IsExpired () const { auto ts = i2p::util::GetMillisecondsSinceEpoch (); diff --git a/LeaseSet.h b/LeaseSet.h index 289ae25c..c174ac39 100644 --- a/LeaseSet.h +++ b/LeaseSet.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include "Identity.h" @@ -88,14 +89,19 @@ namespace data public: LocalLeaseSet (std::shared_ptr identity, const uint8_t * encryptionPublicKey, std::vector > tunnels); + LocalLeaseSet (std::shared_ptr identity, const uint8_t * buf, size_t len); ~LocalLeaseSet () { delete[] m_Buffer; }; const uint8_t * GetBuffer () const { return m_Buffer; }; uint8_t * GetSignature () { return m_Buffer + m_BufferLen - GetSignatureLen (); }; size_t GetBufferLen () const { return m_BufferLen; }; size_t GetSignatureLen () const { return m_Identity->GetSignatureLen (); }; + uint8_t * GetLeases () { return m_Leases; }; + const IdentHash& GetIdentHash () const { return m_Identity->GetIdentHash (); }; bool IsExpired () const; + uint64_t GetExpirationTime () const { return m_ExpirationTime; }; + void SetExpirationTime (uint64_t expirationTime) { m_ExpirationTime = expirationTime; }; bool operator== (const LeaseSet& other) const { return m_BufferLen == other.GetBufferLen () && !memcmp (other.GetBuffer (), other.GetBuffer (), m_BufferLen); }; @@ -104,7 +110,7 @@ namespace data uint64_t m_ExpirationTime; // in milliseconds std::shared_ptr m_Identity; - uint8_t * m_Buffer; + uint8_t * m_Buffer, * m_Leases; size_t m_BufferLen; }; } diff --git a/Log.cpp b/Log.cpp index 36fcebf3..590f3d0f 100644 --- a/Log.cpp +++ b/Log.cpp @@ -12,7 +12,7 @@ namespace i2p { namespace log { Log logger; /** - * @enum Maps our loglevel to their symbolic name + * @brief Maps our loglevel to their symbolic name */ static const char * g_LogLevelStr[eNumLogLevels] = { @@ -56,7 +56,7 @@ namespace log { #endif case eLogFile: case eLogStream: - m_LogStream->flush(); + if (m_LogStream) m_LogStream->flush(); break; default: /* do nothing */ @@ -107,10 +107,11 @@ namespace log { #endif case eLogFile: case eLogStream: - *m_LogStream << TimeAsString(msg->timestamp) - << "@" << short_tid - << "/" << g_LogLevelStr[msg->level] - << " - " << msg->text << std::endl; + if (m_LogStream) + *m_LogStream << TimeAsString(msg->timestamp) + << "@" << short_tid + << "/" << g_LogLevelStr[msg->level] + << " - " << msg->text << std::endl; break; case eLogStdout: default: @@ -130,10 +131,13 @@ namespace log { Process(); } - void Log::SendTo (const std::string& path) { + void Log::SendTo (const std::string& path) + { + if (m_LogStream) m_LogStream = nullptr; // close previous auto flags = std::ofstream::out | std::ofstream::app; auto os = std::make_shared (path, flags); - if (os->is_open ()) { + if (os->is_open ()) + { m_Logfile = path; m_Destination = eLogFile; m_LogStream = os; diff --git a/Log.h b/Log.h index 33bd5868..6762899f 100644 --- a/Log.h +++ b/Log.h @@ -86,7 +86,7 @@ namespace log { LogLevel GetLogLevel () { return m_MinLevel; }; /** - * @brief Sets minimal alloed level for log messages + * @brief Sets minimal allowed level for log messages * @param level String with wanted minimal msg level */ void SetLogLevel (const std::string& level); @@ -101,7 +101,7 @@ namespace log { * @brief Sets log destination to given output stream * @param os Output stream */ - void SendTo (std::shared_ptr s); + void SendTo (std::shared_ptr os); #ifndef _WIN32 /** @@ -129,7 +129,8 @@ namespace log { }; /** - * @struct Log message container + * @struct LogMsg + * @brief Log message container * * We creating it somewhere with LogPrint(), * then put in MsgQueue for later processing. diff --git a/Makefile b/Makefile index e3807d93..4cc313a9 100644 --- a/Makefile +++ b/Makefile @@ -76,6 +76,7 @@ $(ARLIB_CLIENT): $(patsubst %.cpp,obj/%.o,$(LIB_CLIENT_SRC)) clean: rm -rf obj + rm -rf docs/generated $(RM) $(I2PD) $(SHLIB) $(ARLIB) $(SHLIB_CLIENT) $(ARLIB_CLIENT) strip: $(I2PD) $(SHLIB_CLIENT) $(SHLIB) @@ -86,9 +87,13 @@ dist: git archive --format=tar.gz -9 --worktree-attributes \ --prefix=i2pd_$(LATEST_TAG)/ $(LATEST_TAG) -o i2pd_$(LATEST_TAG).tar.gz +doxygen: + doxygen -s docs/Doxyfile + .PHONY: all .PHONY: clean .PHONY: deps +.PHONY: doxygen .PHONY: dist .PHONY: api .PHONY: api_client diff --git a/Makefile.bsd b/Makefile.bsd index 255233f1..d6871f07 100644 --- a/Makefile.bsd +++ b/Makefile.bsd @@ -9,4 +9,4 @@ CXXFLAGS = -O2 NEEDED_CXXFLAGS = -std=c++11 -D_GLIBCXX_USE_NANOSLEEP=1 INCFLAGS = -I/usr/include/ -I/usr/local/include/ LDFLAGS = -Wl,-rpath,/usr/local/lib -L/usr/local/lib -LDLIBS = -lcrypto -lssl -lz -lboost_system -lboost_date_time -lboost_filesystem -lboost_regex -lboost_program_options -lpthread +LDLIBS = -lcrypto -lssl -lz -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread diff --git a/Makefile.homebrew b/Makefile.homebrew index 163b7950..6ce513fe 100644 --- a/Makefile.homebrew +++ b/Makefile.homebrew @@ -6,7 +6,7 @@ CXX = clang++ CXXFLAGS = -g -Wall -std=c++11 -DMAC_OSX INCFLAGS = -I${SSLROOT}/include -I${BOOSTROOT}/include LDFLAGS = -L${SSLROOT}/lib -L${BOOSTROOT}/lib -LDLIBS = -lz -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_regex -lboost_program_options -lpthread +LDLIBS = -lz -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread ifeq ($(USE_UPNP),1) LDFLAGS += -ldl diff --git a/Makefile.linux b/Makefile.linux index 791382c6..e00fd705 100644 --- a/Makefile.linux +++ b/Makefile.linux @@ -1,5 +1,5 @@ # set defaults instead redefine -CXXFLAGS ?= -g -Wall +CXXFLAGS ?= -g -Wall -Wextra -Wno-unused-parameter -pedantic INCFLAGS ?= ## NOTE: The NEEDED_CXXFLAGS are here so that custom CXXFLAGS can be specified at build time @@ -32,7 +32,6 @@ ifeq ($(USE_STATIC),yes) LDLIBS = $(LIBDIR)/libboost_system.a LDLIBS += $(LIBDIR)/libboost_date_time.a LDLIBS += $(LIBDIR)/libboost_filesystem.a - LDLIBS += $(LIBDIR)/libboost_regex.a LDLIBS += $(LIBDIR)/libboost_program_options.a LDLIBS += $(LIBDIR)/libcrypto.a LDLIBS += $(LIBDIR)/libssl.a @@ -40,7 +39,7 @@ ifeq ($(USE_STATIC),yes) LDLIBS += -lpthread -static-libstdc++ -static-libgcc USE_AESNI := no else - LDLIBS = -lcrypto -lssl -lz -lboost_system -lboost_date_time -lboost_filesystem -lboost_regex -lboost_program_options -lpthread + LDLIBS = -lcrypto -lssl -lz -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread endif # UPNP Support (miniupnpc 1.5 or 1.6) diff --git a/Makefile.mingw b/Makefile.mingw index 5fe3c479..0390d66a 100644 --- a/Makefile.mingw +++ b/Makefile.mingw @@ -13,7 +13,6 @@ 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 \ diff --git a/Makefile.osx b/Makefile.osx index 71f95a7f..ef236c9a 100644 --- a/Makefile.osx +++ b/Makefile.osx @@ -3,7 +3,7 @@ CXXFLAGS = -g -Wall -std=c++11 -DMAC_OSX #CXXFLAGS = -g -O2 -Wall -std=c++11 INCFLAGS = -I/usr/local/include -I/usr/local/ssl/include LDFLAGS = -Wl,-rpath,/usr/local/lib -L/usr/local/lib -L/usr/local/ssl/lib -LDLIBS = -lz -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_regex -lboost_program_options -lpthread +LDLIBS = -lz -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread ifeq ($(USE_UPNP),1) LDFLAGS += -ldl diff --git a/NetDb.cpp b/NetDb.cpp index 3aeff92f..d2afc50a 100644 --- a/NetDb.cpp +++ b/NetDb.cpp @@ -849,7 +849,8 @@ namespace data template std::shared_ptr NetDb::GetRandomRouter (Filter filter) const { - if (!m_RouterInfos.size ()) return 0; + if (m_RouterInfos.empty()) + return 0; uint32_t ind = rand () % m_RouterInfos.size (); for (int j = 0; j < 2; j++) { diff --git a/Reseed.cpp b/Reseed.cpp index ddefc460..6f27891d 100644 --- a/Reseed.cpp +++ b/Reseed.cpp @@ -1,7 +1,6 @@ #include #include #include -#include #include #include #include diff --git a/RouterInfo.cpp b/RouterInfo.cpp index 2e76127c..0ef2f623 100644 --- a/RouterInfo.cpp +++ b/RouterInfo.cpp @@ -353,7 +353,7 @@ namespace data if (m_Caps & eReachable) caps += CAPS_FLAG_REACHABLE; // reachable if (m_Caps & eUnreachable) caps += CAPS_FLAG_UNREACHABLE; // unreachable - SetProperty ("caps", caps.c_str ()); + SetProperty ("caps", caps); } void RouterInfo::WriteToStream (std::ostream& s) diff --git a/Streaming.cpp b/Streaming.cpp index ab0a6df0..1a6fdbca 100644 --- a/Streaming.cpp +++ b/Streaming.cpp @@ -232,6 +232,11 @@ namespace stream bool acknowledged = false; auto ts = i2p::util::GetMillisecondsSinceEpoch (); uint32_t ackThrough = packet->GetAckThrough (); + if (ackThrough > m_SequenceNumber) + { + LogPrint (eLogError, "Streaming: Unexpected ackThrough=", ackThrough, " > seqn=", m_SequenceNumber); + return; + } int nackCount = packet->GetNACKCount (); for (auto it = m_SentPackets.begin (); it != m_SentPackets.end ();) { @@ -336,9 +341,9 @@ namespace stream htobe32buf (packet + size, m_SequenceNumber++); size += 4; // sequenceNum if (isNoAck) - htobe32buf (packet + size, m_LastReceivedSequenceNumber); - else htobuf32 (packet + size, 0); + else + htobe32buf (packet + size, m_LastReceivedSequenceNumber); size += 4; // ack Through packet[size] = 0; size++; // NACK count @@ -521,7 +526,7 @@ namespace stream size += 4; // receiveStreamID htobe32buf (packet + size, m_SequenceNumber++); size += 4; // sequenceNum - htobe32buf (packet + size, m_LastReceivedSequenceNumber); + htobe32buf (packet + size, m_LastReceivedSequenceNumber >= 0 ? m_LastReceivedSequenceNumber : 0); size += 4; // ack Through packet[size] = 0; size++; // NACK count diff --git a/Transports.cpp b/Transports.cpp index 057e2472..2d3d423f 100644 --- a/Transports.cpp +++ b/Transports.cpp @@ -606,7 +606,7 @@ namespace transport std::shared_ptr Transports::GetRandomPeer () const { - if (!m_Peers.size ()) return nullptr; + if (m_Peers.empty ()) return nullptr; std::unique_lock l(m_PeersMutex); auto it = m_Peers.begin (); std::advance (it, rand () % m_Peers.size ()); diff --git a/Tunnel.cpp b/Tunnel.cpp index ebaf98c8..1403330b 100644 --- a/Tunnel.cpp +++ b/Tunnel.cpp @@ -358,7 +358,7 @@ namespace tunnel std::shared_ptr Tunnels::GetNextOutboundTunnel () { - if (!m_OutboundTunnels.size ()) return nullptr; + if (m_OutboundTunnels.empty ()) return nullptr; uint32_t ind = rand () % m_OutboundTunnels.size (), i = 0; std::shared_ptr tunnel; for (auto it: m_OutboundTunnels) diff --git a/Tunnel.h b/Tunnel.h index fe6022ac..43417e5d 100644 --- a/Tunnel.h +++ b/Tunnel.h @@ -73,7 +73,7 @@ namespace tunnel bool HandleTunnelBuildResponse (uint8_t * msg, size_t len); - virtual void Print (std::stringstream& s) const {}; + virtual void Print (std::stringstream&) const {}; // implements TunnelBase void SendTunnelDataMsg (std::shared_ptr msg); diff --git a/appveyor.yml b/appveyor.yml index 6600714d..6018bea0 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -125,7 +125,7 @@ install: - cd %BOOST_ROOT% - if defined msvc if not exist "stage%bitness%\lib\%boostlib%boost_system-vc%msvc%0-mt%boostdbg%*" ( bootstrap > c:\projects\instdir\build_boost.log - && b2 toolset=msvc-%msvc%.0 %boost_variant% link=%type% runtime-link=%type% address-model=%bitness% --build-type=minimal --with-filesystem --with-program_options --with-regex --with-date_time --stagedir=stage%bitness% >> c:\projects\instdir\build_boost.log + && b2 toolset=msvc-%msvc%.0 %boost_variant% link=%type% runtime-link=%type% address-model=%bitness% --build-type=minimal --with-filesystem --with-program_options --with-date_time --stagedir=stage%bitness% >> c:\projects\instdir\build_boost.log || type c:\projects\instdir\build_boost.log ) - if defined msvc if not exist C:\stage\OpenSSL-Win%bitness%-vc%msvc%-%type%\ ( diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt index 7944e2ec..4a1bfe2b 100644 --- a/build/CMakeLists.txt +++ b/build/CMakeLists.txt @@ -242,7 +242,7 @@ endif() target_link_libraries(i2pdclient libi2pd) -find_package ( Boost COMPONENTS system filesystem regex program_options date_time REQUIRED ) +find_package ( Boost COMPONENTS system filesystem program_options date_time REQUIRED ) if(NOT DEFINED Boost_INCLUDE_DIRS) message(SEND_ERROR "Boost is not found, or your boost version was bellow 1.46. Please download Boost!") endif() diff --git a/build/Dockerfile b/build/Dockerfile index f570bd79..751fe956 100644 --- a/build/Dockerfile +++ b/build/Dockerfile @@ -1,7 +1,7 @@ FROM ubuntu RUN apt-get update && apt-get install -y libboost-dev libboost-filesystem-dev \ - libboost-program-options-dev libboost-regex-dev libboost-date-time-dev \ + libboost-program-options-dev libboost-date-time-dev \ libssl-dev git build-essential RUN git clone https://github.com/PurpleI2P/i2pd.git diff --git a/debian/changelog b/debian/changelog index 52bca793..5ac732e0 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +i2pd (2.7.0-1) unstable; urgency=low + + * updated to version 2.7.0/0.9.25 + + -- hagen Wed, 18 May 2016 01:11:04 +0000 + i2pd (2.2.0-2) unstable; urgency=low * updated to version 2.2.0 diff --git a/debian/control b/debian/control index ceca7a5f..78906ba4 100644 --- a/debian/control +++ b/debian/control @@ -4,7 +4,6 @@ Priority: extra Maintainer: hagen Build-Depends: debhelper (>= 9.0.0), dpkg-dev (>= 1.16.1~), gcc (>= 4.7) | clang (>= 3.3), - libboost-regex-dev, libboost-system-dev (>= 1.46), libboost-date-time-dev, libboost-filesystem-dev, diff --git a/debian/i2pd.default b/debian/i2pd.default index bf6eb005..28b0e621 100644 --- a/debian/i2pd.default +++ b/debian/i2pd.default @@ -4,6 +4,7 @@ I2PD_ENABLED="yes" # port to listen for incoming connections +# comment this line if you want to use value from config I2PD_PORT="4567" # Additional options that are passed to the Daemon. diff --git a/debian/i2pd.init b/debian/i2pd.init index 8cfee8d4..02b37546 100644 --- a/debian/i2pd.init +++ b/debian/i2pd.init @@ -41,6 +41,10 @@ do_start() return 2 fi + if [ -n "$I2PD_PORT" ]; then + DAEMON_OPTS="--port $I2PD_PORT $DAEMON_OPTS" + fi + touch "$PIDFILE" chown -f $USER:adm "$PIDFILE" @@ -51,7 +55,7 @@ do_start() || return 1 start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --chuid "$USER" -- \ --service --daemon --log=file --logfile=$LOGFILE --conf=$I2PCONF --tunconf=$TUNCONF \ - --port=$I2PD_PORT $DAEMON_OPTS > /dev/null 2>&1 \ + $DAEMON_OPTS > /dev/null 2>&1 \ || return 2 return $? } diff --git a/debian/i2pd.openrc b/debian/i2pd.openrc new file mode 100644 index 00000000..6253cfa6 --- /dev/null +++ b/debian/i2pd.openrc @@ -0,0 +1,41 @@ +#!/sbin/openrc-run + +pidfile="/var/run/i2pd.pid" +logfile="/var/log/i2pd.log" +mainconf="/etc/i2pd/i2pd.conf" +tunconf="/etc/i2pd/tunnels.conf" + +name="i2pd" +command="/usr/sbin/i2pd" +command_args="--service --daemon --log=file --logfile=$logfile --conf=$mainconf --tunconf=$tunconf --pidfile=$pidfile" +description="i2p router written in C++" +required_dirs="/var/lib/i2pd" +required_files="$mainconf" +start_stop_daemon_args="--chuid i2pd" + +depend() { + need mountall + use net + after bootmisc +} + +start_pre() { + if [ -r /etc/default/i2pd ]; then + . /etc/default/i2pd + fi + + if [ "x$I2PD_ENABLED" != "xyes" ]; then + ewarn "i2pd disabled in /etc/default/i2pd" + exit 1 + fi + + checkpath -f -o i2pd:adm $logfile + checkpath -f -o i2pd:adm $pidfile + + if [ -n "$I2PD_PORT" -a "$I2PD_PORT" -gt 0 ]; then + command_args="$command_args --port=$I2PD_PORT" + fi + if [ -n "$DAEMON_OPTS" ]; then + command_args="$command_args $DAEMON_OPTS" + fi +} diff --git a/docs/Doxyfile b/docs/Doxyfile new file mode 100644 index 00000000..f2e29995 --- /dev/null +++ b/docs/Doxyfile @@ -0,0 +1,259 @@ +DOXYFILE_ENCODING = UTF-8 +PROJECT_NAME = "i2pd" +PROJECT_NUMBER = +PROJECT_BRIEF = "load-balanced unspoofable packet switching network" +PROJECT_LOGO = +OUTPUT_DIRECTORY = docs/generated +CREATE_SUBDIRS = NO +ALLOW_UNICODE_NAMES = NO +OUTPUT_LANGUAGE = English +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ABBREVIATE_BRIEF = +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = YES +STRIP_FROM_PATH = +STRIP_FROM_INC_PATH = +SHORT_NAMES = NO +JAVADOC_AUTOBRIEF = NO +QT_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = NO +INHERIT_DOCS = YES +SEPARATE_MEMBER_PAGES = NO +TAB_SIZE = 4 +ALIASES = +TCL_SUBST = +OPTIMIZE_OUTPUT_FOR_C = NO +OPTIMIZE_OUTPUT_JAVA = NO +OPTIMIZE_FOR_FORTRAN = NO +OPTIMIZE_OUTPUT_VHDL = NO +EXTENSION_MAPPING = +MARKDOWN_SUPPORT = YES +AUTOLINK_SUPPORT = YES +BUILTIN_STL_SUPPORT = NO +CPP_CLI_SUPPORT = NO +SIP_SUPPORT = NO +IDL_PROPERTY_SUPPORT = YES +DISTRIBUTE_GROUP_DOC = NO +SUBGROUPING = YES +INLINE_GROUPED_CLASSES = NO +INLINE_SIMPLE_STRUCTS = NO +TYPEDEF_HIDES_STRUCT = NO +LOOKUP_CACHE_SIZE = 0 +EXTRACT_ALL = NO +EXTRACT_PRIVATE = NO +EXTRACT_PACKAGE = NO +EXTRACT_STATIC = NO +EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_METHODS = NO +EXTRACT_ANON_NSPACES = NO +HIDE_UNDOC_MEMBERS = NO +HIDE_UNDOC_CLASSES = NO +HIDE_FRIEND_COMPOUNDS = NO +HIDE_IN_BODY_DOCS = NO +INTERNAL_DOCS = NO +CASE_SENSE_NAMES = YES +HIDE_SCOPE_NAMES = NO +SHOW_INCLUDE_FILES = YES +SHOW_GROUPED_MEMB_INC = NO +FORCE_LOCAL_INCLUDES = NO +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +SORT_BRIEF_DOCS = NO +SORT_MEMBERS_CTORS_1ST = NO +SORT_GROUP_NAMES = NO +SORT_BY_SCOPE_NAME = NO +STRICT_PROTO_MATCHING = NO +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +SHOW_USED_FILES = YES +SHOW_FILES = YES +SHOW_NAMESPACES = YES +FILE_VERSION_FILTER = +LAYOUT_FILE = +CITE_BIB_FILES = +QUIET = YES +WARNINGS = YES +WARN_IF_UNDOCUMENTED = NO +WARN_IF_DOC_ERROR = YES +WARN_NO_PARAMDOC = NO +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = +INPUT = +INPUT_ENCODING = UTF-8 +FILE_PATTERNS = *.cpp *.h +RECURSIVE = NO +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = +EXCLUDE_SYMBOLS = +EXAMPLE_PATH = +EXAMPLE_PATTERNS = +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = +INPUT_FILTER = +FILTER_PATTERNS = +FILTER_SOURCE_FILES = NO +FILTER_SOURCE_PATTERNS = +USE_MDFILE_AS_MAINPAGE = +SOURCE_BROWSER = NO +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = YES +REFERENCED_BY_RELATION = NO +REFERENCES_RELATION = NO +REFERENCES_LINK_SOURCE = YES +SOURCE_TOOLTIPS = YES +USE_HTAGS = NO +VERBATIM_HEADERS = NO +CLANG_ASSISTED_PARSING = NO +CLANG_OPTIONS = +ALPHABETICAL_INDEX = YES +COLS_IN_ALPHA_INDEX = 5 +IGNORE_PREFIX = +GENERATE_HTML = YES +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_EXTRA_STYLESHEET = +HTML_EXTRA_FILES = +HTML_COLORSTYLE_HUE = 220 +HTML_COLORSTYLE_SAT = 100 +HTML_COLORSTYLE_GAMMA = 80 +HTML_TIMESTAMP = YES +HTML_DYNAMIC_SECTIONS = NO +HTML_INDEX_NUM_ENTRIES = 100 +GENERATE_DOCSET = NO +DOCSET_FEEDNAME = "Doxygen generated docs" +DOCSET_BUNDLE_ID = org.doxygen.Project +DOCSET_PUBLISHER_ID = org.doxygen.Publisher +DOCSET_PUBLISHER_NAME = Publisher +GENERATE_HTMLHELP = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +CHM_INDEX_ENCODING = +BINARY_TOC = NO +TOC_EXPAND = NO +GENERATE_QHP = NO +QCH_FILE = +QHP_NAMESPACE = org.doxygen.Project +QHP_VIRTUAL_FOLDER = doc +QHP_CUST_FILTER_NAME = +QHP_CUST_FILTER_ATTRS = +QHP_SECT_FILTER_ATTRS = +QHG_LOCATION = +GENERATE_ECLIPSEHELP = NO +ECLIPSE_DOC_ID = org.doxygen.Project +DISABLE_INDEX = NO +GENERATE_TREEVIEW = NO +ENUM_VALUES_PER_LINE = 4 +TREEVIEW_WIDTH = 250 +EXT_LINKS_IN_WINDOW = NO +FORMULA_FONTSIZE = 10 +FORMULA_TRANSPARENT = YES +USE_MATHJAX = NO +MATHJAX_FORMAT = HTML-CSS +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest +MATHJAX_EXTENSIONS = +MATHJAX_CODEFILE = +SEARCHENGINE = YES +SERVER_BASED_SEARCH = NO +EXTERNAL_SEARCH = NO +SEARCHENGINE_URL = +SEARCHDATA_FILE = searchdata.xml +EXTERNAL_SEARCH_ID = +EXTRA_SEARCH_MAPPINGS = +GENERATE_LATEX = NO +LATEX_OUTPUT = latex +LATEX_CMD_NAME = latex +MAKEINDEX_CMD_NAME = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = a4 +EXTRA_PACKAGES = +LATEX_HEADER = +LATEX_FOOTER = +LATEX_EXTRA_FILES = +PDF_HYPERLINKS = YES +USE_PDFLATEX = YES +LATEX_BATCHMODE = NO +LATEX_HIDE_INDICES = NO +LATEX_SOURCE_CODE = NO +LATEX_BIB_STYLE = plain +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_SUBDIR = +MAN_LINKS = NO +GENERATE_XML = NO +XML_OUTPUT = xml +XML_PROGRAMLISTING = YES +GENERATE_DOCBOOK = NO +DOCBOOK_OUTPUT = docbook +DOCBOOK_PROGRAMLISTING = NO +GENERATE_AUTOGEN_DEF = NO +GENERATE_PERLMOD = NO +PERLMOD_LATEX = NO +PERLMOD_PRETTY = YES +PERLMOD_MAKEVAR_PREFIX = +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = NO +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +EXTERNAL_PAGES = YES +PERL_PATH = /usr/bin/perl +CLASS_DIAGRAMS = YES +MSCGEN_PATH = +DIA_PATH = +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = NO +DOT_NUM_THREADS = 0 +DOT_FONTNAME = Helvetica +DOT_FONTSIZE = 10 +DOT_FONTPATH = +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +GROUP_GRAPHS = YES +UML_LOOK = NO +UML_LIMIT_NUM_FIELDS = 10 +TEMPLATE_RELATIONS = NO +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +CALL_GRAPH = NO +CALLER_GRAPH = NO +GRAPHICAL_HIERARCHY = YES +DIRECTORY_GRAPH = YES +DOT_IMAGE_FORMAT = png +INTERACTIVE_SVG = NO +DOT_PATH = +DOTFILE_DIRS = +MSCFILE_DIRS = +DIAFILE_DIRS = +PLANTUML_JAR_PATH = +DOT_GRAPH_MAX_NODES = 50 +MAX_DOT_GRAPH_DEPTH = 0 +DOT_TRANSPARENT = NO +DOT_MULTI_TARGETS = NO +GENERATE_LEGEND = YES +DOT_CLEANUP = YES diff --git a/docs/build_notes_cross.md b/docs/build_notes_cross.md index d819ba34..78d60e6a 100644 --- a/docs/build_notes_cross.md +++ b/docs/build_notes_cross.md @@ -22,7 +22,7 @@ Proceed with building Boost normal way, but let's define dedicated staging direc ```sh ./bootstrap.sh ./b2 toolset=gcc-mingw target-os=windows variant=release link=static runtime-link=static address-model=64 \ - --build-type=minimal --with-filesystem --with-program_options --with-regex --with-date_time \ + --build-type=minimal --with-filesystem --with-program_options --with-date_time \ --stagedir=stage-mingw-64 cd .. ``` diff --git a/docs/build_notes_unix.md b/docs/build_notes_unix.md index 05605343..cdde1ee7 100644 --- a/docs/build_notes_unix.md +++ b/docs/build_notes_unix.md @@ -46,7 +46,6 @@ sudo apt-get install \ libboost-date-time-dev \ libboost-filesystem-dev \ libboost-program-options-dev \ - libboost-regex-dev \ libboost-system-dev \ libboost-thread-dev \ libssl-dev diff --git a/docs/build_notes_windows.md b/docs/build_notes_windows.md index 81e0dfc2..921a6110 100644 --- a/docs/build_notes_windows.md +++ b/docs/build_notes_windows.md @@ -110,11 +110,11 @@ prompt to build Boost) and run the following: cd C:\dev\boost bootstrap - b2 toolset=msvc-12.0 --build-type=complete --with-filesystem --with-program_options --with-regex --with-date_time + b2 toolset=msvc-12.0 --build-type=complete --with-filesystem --with-program_options --with-date_time If you are on 64-bit Windows and you want to build 64-bit version as well - b2 toolset=msvc-12.0 --build-type=complete --stagedir=stage64 address-model=64 --with-filesystem --with-program_options --with-regex --with-date_time + b2 toolset=msvc-12.0 --build-type=complete --stagedir=stage64 address-model=64 --with-filesystem --with-program_options --with-date_time After Boost is compiled, set the environment variable `BOOST_ROOT` to the directory Boost was unpacked to, e.g., C:\dev\boost. diff --git a/docs/configuration.md b/docs/configuration.md index 11e8b4a8..9ab85b46 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -58,7 +58,11 @@ All options below still possible in cmdline, but better write it in config file: * --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 -* --sam.enabled= - If BOB is enabled. false by default +* --bob.enabled= - If BOB is enabled. false by default + +* --i2cp.address= - The address to listen on +* --i2cp.port= - Port of I2CP server. Usually 7654. IPCP is off if not specified +* --i2cp.enabled= - If I2CP is enabled. false by default. Other services don't requeire I2CP * --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 diff --git a/docs/itoopieImage.png b/docs/itoopieImage.png new file mode 100644 index 00000000..a5dc7b68 Binary files /dev/null and b/docs/itoopieImage.png differ diff --git a/stdafx.h b/stdafx.h index ed13bf8b..42490354 100644 --- a/stdafx.h +++ b/stdafx.h @@ -33,7 +33,6 @@ #include #include -#include #include #include #include diff --git a/tests/Makefile b/tests/Makefile index 199b7353..957d4632 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -1,6 +1,7 @@ CXXFLAGS += -Wall -Wextra -pedantic -O0 -g -std=c++11 -D_GLIBCXX_USE_NANOSLEEP=1 -TESTS = test-http-url test-http-req test-http-res test-http-url_decode +TESTS = test-http-url test-http-req test-http-res test-http-url_decode \ + test-http-merge_chunked all: $(TESTS) run diff --git a/tests/test-http-merge_chunked.cpp b/tests/test-http-merge_chunked.cpp new file mode 100644 index 00000000..ba587a45 --- /dev/null +++ b/tests/test-http-merge_chunked.cpp @@ -0,0 +1,25 @@ +#include +#include "../HTTP.h" + +using namespace i2p::http; + +int main() { + const char *buf = + "4\r\n" + "HTTP\r\n" + "A\r\n" + " response \r\n" + "E\r\n" + "with \r\n" + "chunks.\r\n" + "0\r\n" + "\r\n" + ; + std::stringstream in(buf); + std::stringstream out; + + assert(MergeChunkedResponse(in, out) == true); + assert(out.str() == "HTTP response with \r\nchunks."); + + return 0; +} diff --git a/version.h b/version.h index bee17044..5d07c596 100644 --- a/version.h +++ b/version.h @@ -16,7 +16,7 @@ #define I2P_VERSION_MAJOR 0 #define I2P_VERSION_MINOR 9 -#define I2P_VERSION_MICRO 25 +#define I2P_VERSION_MICRO 26 #define I2P_VERSION_PATCH 0 #define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO)