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.h b/Destination.h index 56c83fb4..e64508c9 100644 --- a/Destination.h +++ b/Destination.h @@ -168,7 +168,6 @@ namespace client // implements LocalDestination const uint8_t * GetEncryptionPrivateKey () const { return m_EncryptionPrivateKey; }; - const uint8_t * GetEncryptionPublicKey () const { return m_EncryptionPublicKey; }; std::shared_ptr GetIdentity () const { return m_Keys.GetPublic (); }; protected: diff --git a/HTTP.cpp b/HTTP.cpp index 3f6fd937..ee1010ec 100644 --- a/HTTP.cpp +++ b/HTTP.cpp @@ -406,11 +406,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..8d10c231 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); /** @@ -89,10 +89,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 +110,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/HTTPServer.cpp b/HTTPServer.cpp index 2af92057..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 () { 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 ee329d37..77330af4 100644 --- a/I2CP.cpp +++ b/I2CP.cpp @@ -1,7 +1,18 @@ +/* +* 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 "I2CP.h" namespace i2p @@ -14,18 +25,114 @@ namespace client { } + 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_MessageID (0) { - ReadProtocolByte (); + RAND_bytes ((uint8_t *)&m_SessionID, 2); } - + I2CPSession::~I2CPSession () { delete[] m_NextMessage; } + void I2CPSession::Start () + { + ReadProtocolByte (); + } + + void I2CPSession::Stop () + { + } + void I2CPSession::ReadProtocolByte () { if (m_Socket) @@ -102,6 +209,12 @@ namespace client void I2CPSession::Terminate () { + if (m_Destination) + { + m_Destination->Stop (); + m_Destination = nullptr; + } + m_Owner.RemoveSession (GetSessionID ()); } void I2CPSession::SendI2CPMessage (uint8_t type, const uint8_t * payload, size_t len) @@ -157,16 +270,258 @@ namespace client void I2CPSession::CreateSessionMessageHandler (const uint8_t * buf, size_t len) { - // TODO - m_Destination = std::make_shared(*this, nullptr, false); + auto identity = std::make_shared(); + size_t offset = identity->FromBuffer (buf, len); + uint16_t optionsSize = bufbe16toh (buf + offset); + offset += 2; + // TODO: extract options + offset += optionsSize; + offset += 8; // date + if (identity->Verify (buf, offset, buf + offset)) // signature + { + m_Destination = std::make_shared(*this, identity, false); + m_Destination->Start (); + SendSessionStatusMessage (1); // created + LogPrint (eLogDebug, "I2CP: session ", m_SessionID, " created"); + } + 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"); + Terminate (); + } + + 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) + { + 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) + { + 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); + SendMessageStatusMessage (nonce, eI2CPMessageStatusAccepted); // accepted + m_Destination->SendMsgTo (buf + offset, payloadLen, identity.GetIdentHash (), nonce); + } + } + else + LogPrint (eLogError, "I2CP: unexpected sessionID ", sessionID); + } + + 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); + if (!buf[10]) // request type = 0 (hash) + { + if (m_Destination) + { + auto ls = m_Destination->FindLeaseSet (buf + 11); + if (ls) + SendHostReplyMessage (requestID, ls->GetIdentity ()); + else + { + auto s = shared_from_this (); + m_Destination->RequestDestination (buf + 11, + [s, requestID](std::shared_ptr leaseSet) + { + s->SendHostReplyMessage (requestID, leaseSet ? leaseSet->GetIdentity () : nullptr); + }); + } + } + else + SendHostReplyMessage (requestID, nullptr); + } + else + { + LogPrint (eLogError, "I2CP: request type ", (int)buf[8], " is not supported"); + 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); + } } - I2CPServer::I2CPServer (const std::string& interface, int port) + 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_CREATE_SESSION_MESSAGE ] = &I2CPSession::CreateSessionMessageHandler; + m_MessagesHandlers[I2CP_CREATE_SESSION_MESSAGE] = &I2CPSession::CreateSessionMessageHandler; + m_MessagesHandlers[I2CP_DESTROY_SESSION_MESSAGE] = &I2CPSession::DestroySessionMessageHandler; + m_MessagesHandlers[I2CP_CREATE_LEASESET_MESSAGE] = &I2CPSession::CreateLeaseSetMessageHandler; + m_MessagesHandlers[I2CP_SEND_MESSAGE_MESSAGE] = &I2CPSession::SendMessageMessageHandler; + m_MessagesHandlers[I2CP_HOST_LOOKUP_MESSAGE] = &I2CPSession::HostLookupMessageHandler; + } + + 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 f677b9ee..4d50b5ef 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" @@ -21,6 +31,23 @@ namespace client 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_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_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; + + enum I2CPMessageStatus + { + eI2CPMessageStatusAccepted = 1, + eI2CPMessageStatusGuaranteedSuccess = 4, + eI2CPMessageStatusGuaranteedFailure = 5, + eI2CPMessageStatusNoLeaseSet = 21 + }; class I2CPSession; class I2CPDestination: public LeaseSetDestination @@ -29,22 +56,32 @@ namespace client I2CPDestination (I2CPSession& owner, std::shared_ptr identity, bool isPublic); + 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 + protected: // implements LocalDestination const uint8_t * GetEncryptionPrivateKey () const { return m_EncryptionPrivateKey; }; - const uint8_t * GetEncryptionPublicKey () const { return m_EncryptionPublicKey; }; std::shared_ptr GetIdentity () const { return m_Identity; }; // I2CP - void HandleDataMessage (const uint8_t * buf, size_t len) { /* TODO */ }; - void CreateNewLeaseSet (std::vector > tunnels) { /* TODO */ }; + 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 ()); } + bool SendMsg (std::shared_ptr msg, std::shared_ptr remote); private: I2CPSession& m_Owner; std::shared_ptr m_Identity; - uint8_t m_EncryptionPublicKey[256], m_EncryptionPrivateKey[256]; + uint8_t m_EncryptionPrivateKey[256]; + uint64_t m_LeaseSetExpirationTime; }; class I2CPServer; @@ -55,9 +92,22 @@ 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 CreateLeaseSetMessageHandler (const uint8_t * buf, size_t len); + void SendMessageMessageHandler (const uint8_t * buf, size_t len); + void HostLookupMessageHandler (const uint8_t * buf, size_t len); private: @@ -66,13 +116,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 SendI2CPMessage (uint8_t type, const uint8_t * payload, size_t len); + 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 SendSessionStatusMessage (uint8_t status); + void SendHostReplyMessage (uint32_t requestID, std::shared_ptr identity); + private: I2CPServer& m_Owner; @@ -81,6 +132,8 @@ namespace client size_t m_NextMessageLen, m_NextMessageOffset; std::shared_ptr m_Destination; + uint16_t m_SessionID; + uint32_t m_MessageID; }; typedef void (I2CPSession::*I2CPMessageHandler)(const uint8_t * buf, size_t len); @@ -89,10 +142,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..9bbe1521 100644 --- a/I2PService.cpp +++ b/I2PService.cpp @@ -3,7 +3,6 @@ #include "ClientContext.h" #include "I2PService.h" - namespace i2p { namespace client @@ -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 2a60ddd3..841acf65 100644 --- a/Identity.h +++ b/Identity.h @@ -179,7 +179,6 @@ 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; 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.linux b/Makefile.linux index 70307267..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 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/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/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/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/i2pd.openrc b/debian/i2pd.openrc index ddcb4003..6253cfa6 100644 --- a/debian/i2pd.openrc +++ b/debian/i2pd.openrc @@ -5,11 +5,9 @@ logfile="/var/log/i2pd.log" mainconf="/etc/i2pd/i2pd.conf" tunconf="/etc/i2pd/tunnels.conf" -. /etc/default/i2pd - name="i2pd" command="/usr/sbin/i2pd" -command_args="--service --daemon --log=file --logfile=$logfile --conf=$mainconf --tunconf=$tunconf" +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" @@ -22,6 +20,22 @@ depend() { } start_pre() { - checkpath -f -o i2pd:adm -w $pidfile - checkpath -f -o i2pd:adm -w $logfile + 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/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/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; +}