diff --git a/AddressBook.cpp b/AddressBook.cpp index 5e7510a5..3cf2bc56 100644 --- a/AddressBook.cpp +++ b/AddressBook.cpp @@ -203,7 +203,7 @@ namespace client } //--------------------------------------------------------------------- - AddressBook::AddressBook (): m_Storage(new AddressBookFilesystemStorage), m_IsLoaded (false), m_IsDownloading (false), + AddressBook::AddressBook (): m_Storage(nullptr), m_IsLoaded (false), m_IsDownloading (false), m_DefaultSubscription (nullptr), m_SubscriptionsUpdateTimer (nullptr) { } @@ -215,6 +215,8 @@ namespace client void AddressBook::Start () { + if (!m_Storage) + m_Storage = new AddressBookFilesystemStorage; m_Storage->Init(); LoadHosts (); /* try storage, then hosts.txt, then download */ StartSubscriptions (); diff --git a/Base.cpp b/Base.cpp index e0b6af07..35aae437 100644 --- a/Base.cpp +++ b/Base.cpp @@ -1,5 +1,6 @@ #include -#include "Log.h" +#include + #include "Base.h" namespace i2p @@ -284,107 +285,6 @@ namespace data } return ret; } - - GzipInflator::GzipInflator (): m_IsDirty (false) - { - memset (&m_Inflator, 0, sizeof (m_Inflator)); - inflateInit2 (&m_Inflator, MAX_WBITS + 16); // gzip - } - - GzipInflator::~GzipInflator () - { - inflateEnd (&m_Inflator); - } - - size_t GzipInflator::Inflate (const uint8_t * in, size_t inLen, uint8_t * out, size_t outLen) - { - if (m_IsDirty) inflateReset (&m_Inflator); - m_IsDirty = true; - m_Inflator.next_in = const_cast(in); - m_Inflator.avail_in = inLen; - m_Inflator.next_out = out; - m_Inflator.avail_out = outLen; - int err; - if ((err = inflate (&m_Inflator, Z_NO_FLUSH)) == Z_STREAM_END) - return outLen - m_Inflator.avail_out; - else - { - LogPrint (eLogError, "Decompression error ", err); - return 0; - } - } - - bool GzipInflator::Inflate (const uint8_t * in, size_t inLen, std::ostream& s) - { - m_IsDirty = true; - uint8_t * out = new uint8_t[GZIP_CHUNK_SIZE]; - m_Inflator.next_in = const_cast(in); - m_Inflator.avail_in = inLen; - int ret; - do - { - m_Inflator.next_out = out; - m_Inflator.avail_out = GZIP_CHUNK_SIZE; - ret = inflate (&m_Inflator, Z_NO_FLUSH); - if (ret < 0) - { - LogPrint (eLogError, "Decompression error ", ret); - inflateEnd (&m_Inflator); - s.setstate(std::ios_base::failbit); - break; - } - else - s.write ((char *)out, GZIP_CHUNK_SIZE - m_Inflator.avail_out); - } - while (!m_Inflator.avail_out); // more data to read - delete[] out; - return ret == Z_STREAM_END || ret < 0; - } - - void GzipInflator::Inflate (std::istream& in, std::ostream& out) - { - uint8_t * buf = new uint8_t[GZIP_CHUNK_SIZE]; - while (!in.eof ()) - { - in.read ((char *)buf, GZIP_CHUNK_SIZE); - Inflate (buf, in.gcount (), out); - } - delete[] buf; - } - - GzipDeflator::GzipDeflator (): m_IsDirty (false) - { - memset (&m_Deflator, 0, sizeof (m_Deflator)); - deflateInit2 (&m_Deflator, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY); // 15 + 16 sets gzip - } - - GzipDeflator::~GzipDeflator () - { - deflateEnd (&m_Deflator); - } - - void GzipDeflator::SetCompressionLevel (int level) - { - deflateParams (&m_Deflator, level, Z_DEFAULT_STRATEGY); - } - - size_t GzipDeflator::Deflate (const uint8_t * in, size_t inLen, uint8_t * out, size_t outLen) - { - if (m_IsDirty) deflateReset (&m_Deflator); - m_IsDirty = true; - m_Deflator.next_in = const_cast(in); - m_Deflator.avail_in = inLen; - m_Deflator.next_out = out; - m_Deflator.avail_out = outLen; - int err; - if ((err = deflate (&m_Deflator, Z_FINISH)) == Z_STREAM_END) - return outLen - m_Deflator.avail_out; - else - { - LogPrint (eLogError, "Compression error ", err); - return 0; - } - } } } diff --git a/Base.h b/Base.h index 0c38725e..66192d1b 100644 --- a/Base.h +++ b/Base.h @@ -2,15 +2,11 @@ #define BASE_H__ #include -#include #include -#include #include -namespace i2p -{ -namespace data -{ +namespace i2p { +namespace data { size_t ByteStreamToBase64 (const uint8_t * InBuffer, size_t InCount, char * OutBuffer, size_t len); size_t Base64ToByteStream (const char * InBuffer, size_t InCount, uint8_t * OutBuffer, size_t len ); const char * GetBase32SubstitutionTable (); @@ -23,112 +19,7 @@ namespace data Compute the size for a buffer to contain encoded base64 given that the size of the input is input_size bytes */ size_t Base64EncodingBufferSize(const size_t input_size); - - template - class Tag - { - public: - - Tag (const uint8_t * buf) { memcpy (m_Buf, buf, sz); }; - Tag (const Tag& ) = default; -#ifndef _WIN32 // FIXME!!! msvs 2013 can't compile it - Tag (Tag&& ) = default; -#endif - Tag () = default; - - Tag& operator= (const Tag& ) = default; -#ifndef _WIN32 - Tag& operator= (Tag&& ) = default; -#endif - - uint8_t * operator()() { return m_Buf; }; - const uint8_t * operator()() const { return m_Buf; }; - - operator uint8_t * () { return m_Buf; }; - operator const uint8_t * () const { return m_Buf; }; - - const uint64_t * GetLL () const { return ll; }; - - bool operator== (const Tag& other) const { return !memcmp (m_Buf, other.m_Buf, sz); }; - bool operator< (const Tag& other) const { return memcmp (m_Buf, other.m_Buf, sz) < 0; }; - - bool IsZero () const - { - for (int i = 0; i < sz/8; i++) - if (ll[i]) return false; - return true; - } - - std::string ToBase64 () const - { - char str[sz*2]; - int l = i2p::data::ByteStreamToBase64 (m_Buf, sz, str, sz*2); - str[l] = 0; - return std::string (str); - } - - std::string ToBase32 () const - { - char str[sz*2]; - int l = i2p::data::ByteStreamToBase32 (m_Buf, sz, str, sz*2); - str[l] = 0; - return std::string (str); - } - - void FromBase32 (const std::string& s) - { - i2p::data::Base32ToByteStream (s.c_str (), s.length (), m_Buf, sz); - } - - void FromBase64 (const std::string& s) - { - i2p::data::Base64ToByteStream (s.c_str (), s.length (), m_Buf, sz); - } - - private: - - union // 8 bytes alignment - { - uint8_t m_Buf[sz]; - uint64_t ll[sz/8]; - }; - }; - - const size_t GZIP_CHUNK_SIZE = 16384; - class GzipInflator - { - public: - - GzipInflator (); - ~GzipInflator (); - - size_t Inflate (const uint8_t * in, size_t inLen, uint8_t * out, size_t outLen); - bool Inflate (const uint8_t * in, size_t inLen, std::ostream& s); - // return true when finshed or error, s failbit will be set in case of error - void Inflate (std::istream& in, std::ostream& out); - - private: - - z_stream m_Inflator; - bool m_IsDirty; - }; - - class GzipDeflator - { - public: - - GzipDeflator (); - ~GzipDeflator (); - - void SetCompressionLevel (int level); - size_t Deflate (const uint8_t * in, size_t inLen, uint8_t * out, size_t outLen); - - private: - - z_stream m_Deflator; - bool m_IsDirty; - }; -} -} +} // data +} // i2p #endif diff --git a/ChangeLog b/ChangeLog index 9a32e42f..520978df 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,14 +1,23 @@ # for this file format description, # see https://github.com/olivierlacan/keep-a-changelog -## [2.8.0] - UNRELEASED +## [2.9.0] - UNRELEASED ### Changed - Proxy refactoring & speedup + +## [2.8.0] - 2016-06-20 +### Added +- Basic Android support +- I2CP implementation +- 'doxygen' target + +### Changed - 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 +- properly close NTCP sessions (memleak) ## [2.7.0] - 2016-05-18 ### Added diff --git a/Config.cpp b/Config.cpp index e6d44d59..2a7668c5 100644 --- a/Config.cpp +++ b/Config.cpp @@ -26,83 +26,6 @@ namespace config { options_description m_OptionsDesc; variables_map m_Options; - /* list of renamed options */ - std::map remapped_options = { - { "tunnelscfg", "tunconf" }, - { "v6", "ipv6" }, - { "httpaddress", "http.address" }, - { "httpport", "http.port" }, - { "httpproxyaddress", "httpproxy.address" }, - { "httpproxyport", "httpproxy.port" }, - { "socksproxyaddress", "socksproxy.address" }, - { "socksproxyport", "socksproxy.port" }, - { "samaddress", "sam.address" }, - { "samport", "sam.port" }, - { "bobaddress", "bob.address" }, - { "bobport", "bob.port" }, - { "i2pcontroladdress", "i2pcontrol.address" }, - { "i2pcontroladdress", "i2pcontrol.port" }, - { "proxykeys", "httpproxy.keys" }, - }; - /* list of options, that loose their argument and become simple switch */ - std::set boolean_options = { - "daemon", "floodfill", "notransit", "service", "ipv6" - }; - - /* this function is a solid piece of shit, remove it after 2.6.0 */ - std::pair old_syntax_parser(const std::string& s) { - std::string name = ""; - std::string value = ""; - std::size_t pos = 0; - /* shortcuts -- -h */ - if (s.length() == 2 && s.at(0) == '-' && s.at(1) != '-') - return make_pair(s.substr(1), ""); - /* old-style -- -log, /log, etc */ - if (s.at(0) == '/' || (s.at(0) == '-' && s.at(1) != '-')) { - if ((pos = s.find_first_of("= ")) != std::string::npos) { - name = s.substr(1, pos - 1); - value = s.substr(pos + 1); - } else { - name = s.substr(1, pos); - value = ""; - } - if (boolean_options.count(name) > 0 && value != "") - std::cerr << "args: don't give an argument to switch option: " << s << std::endl; - if (m_OptionsDesc.find_nothrow(name, false)) { - std::cerr << "args: option " << s << " style is DEPRECATED, use --" << name << " instead" << std::endl; - return std::make_pair(name, value); - } - if (remapped_options.count(name) > 0) { - name = remapped_options[name]; - std::cerr << "args: option " << s << " is DEPRECATED, use --" << name << " instead" << std::endl; - return std::make_pair(name, value); - } /* else */ - } - /* long options -- --help */ - if (s.substr(0, 2) == "--") { - if ((pos = s.find_first_of("= ")) != std::string::npos) { - name = s.substr(2, pos - 2); - value = s.substr(pos + 1); - } else { - name = s.substr(2, pos); - value = ""; - } - if (boolean_options.count(name) > 0 && value != "") { - std::cerr << "args: don't give an argument to switch option: " << s << std::endl; - value = ""; - } - if (m_OptionsDesc.find_nothrow(name, false)) - return std::make_pair(name, value); - if (remapped_options.count(name) > 0) { - name = remapped_options[name]; - std::cerr << "args: option " << s << " is DEPRECATED, use --" << name << " instead" << std::endl; - return std::make_pair(name, value); - } /* else */ - } - std::cerr << "args: unknown option -- " << s << std::endl; - return std::make_pair("", ""); - } - void Init() { options_description general("General options"); general.add_options() @@ -225,7 +148,7 @@ namespace config { auto style = boost::program_options::command_line_style::unix_style | boost::program_options::command_line_style::allow_long_disguise; style &= ~ boost::program_options::command_line_style::allow_guessing; - store(parse_command_line(argc, argv, m_OptionsDesc, style, old_syntax_parser), m_Options); + store(parse_command_line(argc, argv, m_OptionsDesc, style), m_Options); } catch (boost::program_options::error& e) { std::cerr << "args: " << e.what() << std::endl; exit(EXIT_FAILURE); diff --git a/Crypto.h b/Crypto.h index 7ce202ce..a66f51b7 100644 --- a/Crypto.h +++ b/Crypto.h @@ -9,7 +9,9 @@ #include #include #include + #include "Base.h" +#include "Tag.h" namespace i2p { diff --git a/DaemonWin32.cpp b/DaemonWin32.cpp index 3afb70ce..6eb43dc0 100644 --- a/DaemonWin32.cpp +++ b/DaemonWin32.cpp @@ -1,113 +1,115 @@ -#include -#include "Config.h" -#include "Daemon.h" -#include "util.h" -#include "Log.h" - -#ifdef _WIN32 - -#include "Win32/Win32Service.h" -#ifdef WIN32_APP -#include "Win32/Win32App.h" -#endif - -namespace i2p -{ - namespace util - { - bool DaemonWin32::init(int argc, char* argv[]) - { - setlocale(LC_CTYPE, ""); - SetConsoleCP(1251); - SetConsoleOutputCP(1251); - - if (!Daemon_Singleton::init(argc, argv)) - return false; - - std::string serviceControl; i2p::config::GetOption("svcctl", serviceControl); - if (serviceControl == "install") - { - LogPrint(eLogInfo, "WinSVC: installing ", SERVICE_NAME, " as service"); - InstallService( - SERVICE_NAME, // Name of service - SERVICE_DISPLAY_NAME, // Name to display - SERVICE_START_TYPE, // Service start type - SERVICE_DEPENDENCIES, // Dependencies - SERVICE_ACCOUNT, // Service running account - SERVICE_PASSWORD // Password of the account - ); - return false; - } - else if (serviceControl == "remove") - { - LogPrint(eLogInfo, "WinSVC: uninstalling ", SERVICE_NAME, " service"); - UninstallService(SERVICE_NAME); - return false; - } - +#include +#include +#include "Config.h" +#include "Daemon.h" +#include "util.h" +#include "Log.h" + +#ifdef _WIN32 + +#include "Win32/Win32Service.h" +#ifdef WIN32_APP +#include "Win32/Win32App.h" +#endif + +namespace i2p +{ + namespace util + { + bool DaemonWin32::init(int argc, char* argv[]) + { + setlocale(LC_CTYPE, ""); + SetConsoleCP(1251); + SetConsoleOutputCP(1251); + setlocale(LC_ALL, "Russian"); + + if (!Daemon_Singleton::init(argc, argv)) + return false; + + std::string serviceControl; i2p::config::GetOption("svcctl", serviceControl); + if (serviceControl == "install") + { + LogPrint(eLogInfo, "WinSVC: installing ", SERVICE_NAME, " as service"); + InstallService( + SERVICE_NAME, // Name of service + SERVICE_DISPLAY_NAME, // Name to display + SERVICE_START_TYPE, // Service start type + SERVICE_DEPENDENCIES, // Dependencies + SERVICE_ACCOUNT, // Service running account + SERVICE_PASSWORD // Password of the account + ); + return false; + } + else if (serviceControl == "remove") + { + LogPrint(eLogInfo, "WinSVC: uninstalling ", SERVICE_NAME, " service"); + UninstallService(SERVICE_NAME); + return false; + } + if (isDaemon) - { - LogPrint(eLogDebug, "Daemon: running as service"); - I2PService service(SERVICE_NAME); - if (!I2PService::Run(service)) - { - LogPrint(eLogError, "Daemon: Service failed to run w/err 0x%08lx\n", GetLastError()); - return false; - } - return false; - } - else - LogPrint(eLogDebug, "Daemon: running as user"); - - return true; - } - - bool DaemonWin32::start() - { - setlocale(LC_CTYPE, ""); - SetConsoleCP(1251); - SetConsoleOutputCP(1251); - setlocale(LC_ALL, "Russian"); -#ifdef WIN32_APP - if (!i2p::win32::StartWin32App ()) return false; - - // override log - i2p::config::SetOption("log", std::string ("file")); -#endif - bool ret = Daemon_Singleton::start(); - if (ret && i2p::log::Logger().GetLogType() == eLogFile) - { - // TODO: find out where this garbage to console comes from - SetStdHandle(STD_OUTPUT_HANDLE, INVALID_HANDLE_VALUE); - SetStdHandle(STD_ERROR_HANDLE, INVALID_HANDLE_VALUE); - } - bool insomnia; i2p::config::GetOption("insomnia", insomnia); - if (insomnia) - SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED); - return ret; - } - - bool DaemonWin32::stop() - { -#ifdef WIN32_APP - i2p::win32::StopWin32App (); -#endif - return Daemon_Singleton::stop(); - } - - void DaemonWin32::run () - { -#ifdef WIN32_APP - i2p::win32::RunWin32App (); -#else - while (running) - { - std::this_thread::sleep_for (std::chrono::seconds(1)); - } - -#endif - } - } -} - -#endif + { + LogPrint(eLogDebug, "Daemon: running as service"); + I2PService service(SERVICE_NAME); + if (!I2PService::Run(service)) + { + LogPrint(eLogError, "Daemon: Service failed to run w/err 0x%08lx\n", GetLastError()); + return false; + } + return false; + } + else + LogPrint(eLogDebug, "Daemon: running as user"); + + return true; + } + + bool DaemonWin32::start() + { + setlocale(LC_CTYPE, ""); + SetConsoleCP(1251); + SetConsoleOutputCP(1251); + setlocale(LC_ALL, "Russian"); +#ifdef WIN32_APP + if (!i2p::win32::StartWin32App ()) return false; + + // override log + i2p::config::SetOption("log", std::string ("file")); +#endif + bool ret = Daemon_Singleton::start(); + if (ret && i2p::log::Logger().GetLogType() == eLogFile) + { + // TODO: find out where this garbage to console comes from + SetStdHandle(STD_OUTPUT_HANDLE, INVALID_HANDLE_VALUE); + SetStdHandle(STD_ERROR_HANDLE, INVALID_HANDLE_VALUE); + } + bool insomnia; i2p::config::GetOption("insomnia", insomnia); + if (insomnia) + SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED); + return ret; + } + + bool DaemonWin32::stop() + { +#ifdef WIN32_APP + i2p::win32::StopWin32App (); +#endif + return Daemon_Singleton::stop(); + } + + void DaemonWin32::run () + { +#ifdef WIN32_APP + i2p::win32::RunWin32App (); +#else + while (running) + { + std::this_thread::sleep_for (std::chrono::seconds(1)); + } + +#endif + } + } +} + +#endif diff --git a/Destination.cpp b/Destination.cpp index bb67b601..6cc24537 100644 --- a/Destination.cpp +++ b/Destination.cpp @@ -31,7 +31,7 @@ namespace client { int len = i2p::util::lexical_cast(it->second, inboundTunnelLen); - if (len > 0) + if (len >= 0) { inboundTunnelLen = len; } @@ -42,7 +42,7 @@ namespace client { int len = i2p::util::lexical_cast(it->second, outboundTunnelLen); - if (len > 0) + if (len >= 0) { outboundTunnelLen = len; } diff --git a/Garlic.cpp b/Garlic.cpp index c1100f64..17740fae 100644 --- a/Garlic.cpp +++ b/Garlic.cpp @@ -7,6 +7,7 @@ #include "I2NPProtocol.h" #include "Tunnel.h" #include "TunnelPool.h" +#include "Transports.h" #include "Timestamp.h" #include "Log.h" #include "Garlic.h" @@ -514,22 +515,34 @@ namespace garlic buf += 32; uint32_t gwTunnel = bufbe32toh (buf); buf += 4; - std::shared_ptr tunnel; - if (from && from->GetTunnelPool ()) - tunnel = from->GetTunnelPool ()->GetNextOutboundTunnel (); - if (tunnel) // we have send it through an outbound tunnel - { - auto msg = CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from); - tunnel->SendTunnelDataMsg (gwHash, gwTunnel, msg); - } - else - LogPrint (eLogWarning, "Garlic: No outbound tunnels available for garlic clove"); + auto msg = CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from); + if (from) // received through an inbound tunnel + { + std::shared_ptr tunnel; + if (from->GetTunnelPool ()) + tunnel = from->GetTunnelPool ()->GetNextOutboundTunnel (); + else + LogPrint (eLogError, "Garlic: Tunnel pool is not set for inbound tunnel"); + if (tunnel) // we have send it through an outbound tunnel + tunnel->SendTunnelDataMsg (gwHash, gwTunnel, msg); + else + LogPrint (eLogWarning, "Garlic: No outbound tunnels available for garlic clove"); + } + else // received directly + i2p::transport::transports.SendMessage (gwHash, i2p::CreateTunnelGatewayMsg (gwTunnel, msg)); // send directly break; } case eGarlicDeliveryTypeRouter: - LogPrint (eLogWarning, "Garlic: type router not supported"); + { + uint8_t * ident = buf; buf += 32; - break; + if (!from) // received directly + i2p::transport::transports.SendMessage (ident, + CreateI2NPMessage (buf, GetI2NPMessageLength (buf))); + else + LogPrint (eLogWarning, "Garlic: type router for inbound tunnels not supported"); + break; + } default: LogPrint (eLogWarning, "Garlic: unknown delivery type ", (int)deliveryType); } diff --git a/Gzip.cpp b/Gzip.cpp new file mode 100644 index 00000000..da9f06b1 --- /dev/null +++ b/Gzip.cpp @@ -0,0 +1,108 @@ +/* +* 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 /* memset */ +#include + +#include "Gzip.h" + +namespace i2p { +namespace data { + const size_t GZIP_CHUNK_SIZE = 16384; + + GzipInflator::GzipInflator (): m_IsDirty (false) + { + memset (&m_Inflator, 0, sizeof (m_Inflator)); + inflateInit2 (&m_Inflator, MAX_WBITS + 16); // gzip + } + + GzipInflator::~GzipInflator () + { + inflateEnd (&m_Inflator); + } + + size_t GzipInflator::Inflate (const uint8_t * in, size_t inLen, uint8_t * out, size_t outLen) + { + if (m_IsDirty) inflateReset (&m_Inflator); + m_IsDirty = true; + m_Inflator.next_in = const_cast(in); + m_Inflator.avail_in = inLen; + m_Inflator.next_out = out; + m_Inflator.avail_out = outLen; + int err; + if ((err = inflate (&m_Inflator, Z_NO_FLUSH)) == Z_STREAM_END) { + return outLen - m_Inflator.avail_out; + } + return 0; + } + + void GzipInflator::Inflate (const uint8_t * in, size_t inLen, std::ostream& os) + { + m_IsDirty = true; + uint8_t * out = new uint8_t[GZIP_CHUNK_SIZE]; + m_Inflator.next_in = const_cast(in); + m_Inflator.avail_in = inLen; + int ret; + do { + m_Inflator.next_out = out; + m_Inflator.avail_out = GZIP_CHUNK_SIZE; + ret = inflate (&m_Inflator, Z_NO_FLUSH); + if (ret < 0) { + inflateEnd (&m_Inflator); + os.setstate(std::ios_base::failbit); + break; + } + os.write ((char *)out, GZIP_CHUNK_SIZE - m_Inflator.avail_out); + } while (!m_Inflator.avail_out); // more data to read + delete[] out; + } + + void GzipInflator::Inflate (std::istream& in, std::ostream& out) + { + uint8_t * buf = new uint8_t[GZIP_CHUNK_SIZE]; + while (!in.eof ()) + { + in.read ((char *) buf, GZIP_CHUNK_SIZE); + Inflate (buf, in.gcount (), out); + } + delete[] buf; + } + + GzipDeflator::GzipDeflator (): m_IsDirty (false) + { + memset (&m_Deflator, 0, sizeof (m_Deflator)); + deflateInit2 (&m_Deflator, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY); // 15 + 16 sets gzip + } + + GzipDeflator::~GzipDeflator () + { + deflateEnd (&m_Deflator); + } + + void GzipDeflator::SetCompressionLevel (int level) + { + deflateParams (&m_Deflator, level, Z_DEFAULT_STRATEGY); + } + + size_t GzipDeflator::Deflate (const uint8_t * in, size_t inLen, uint8_t * out, size_t outLen) + { + if (m_IsDirty) deflateReset (&m_Deflator); + m_IsDirty = true; + m_Deflator.next_in = const_cast(in); + m_Deflator.avail_in = inLen; + m_Deflator.next_out = out; + m_Deflator.avail_out = outLen; + int err; + if ((err = deflate (&m_Deflator, Z_FINISH)) == Z_STREAM_END) { + return outLen - m_Deflator.avail_out; + } /* else */ + return 0; + } +} // data +} // i2p diff --git a/Gzip.h b/Gzip.h new file mode 100644 index 00000000..35661abe --- /dev/null +++ b/Gzip.h @@ -0,0 +1,44 @@ +#ifndef GZIP_H__ +#define GZIP_H__ + +#include + +namespace i2p { +namespace data { + class GzipInflator + { + public: + + GzipInflator (); + ~GzipInflator (); + + size_t Inflate (const uint8_t * in, size_t inLen, uint8_t * out, size_t outLen); + /** @note @a os failbit will be set in case of error */ + void Inflate (const uint8_t * in, size_t inLen, std::ostream& os); + void Inflate (std::istream& in, std::ostream& out); + + private: + + z_stream m_Inflator; + bool m_IsDirty; + }; + + class GzipDeflator + { + public: + + GzipDeflator (); + ~GzipDeflator (); + + void SetCompressionLevel (int level); + size_t Deflate (const uint8_t * in, size_t inLen, uint8_t * out, size_t outLen); + + private: + + z_stream m_Deflator; + bool m_IsDirty; + }; +} // data +} // i2p + +#endif /* GZIP_H__ */ diff --git a/HTTP.cpp b/HTTP.cpp index eca21fde..7fd87c55 100644 --- a/HTTP.cpp +++ b/HTTP.cpp @@ -81,7 +81,8 @@ namespace http { } /* user[:pass] */ pos_c = url.find('@', pos_p); - if (pos_c != std::string::npos) { + std::size_t pos_slash = url.find('/', pos_p); + if (pos_c != std::string::npos && (pos_slash == std::string::npos || pos_slash > pos_c)) { std::size_t delim = url.find(':', pos_p); if (delim != std::string::npos && delim < pos_c) { user = url.substr(pos_p, delim - pos_p); @@ -90,7 +91,7 @@ namespace http { } else { user = url.substr(pos_p, pos_c - pos_p); } - pos_p = pos_c + 1; + pos_p = pos_c + 1; } /* hostname[:port][/path] */ pos_c = url.find_first_of(":/", pos_p); @@ -276,7 +277,7 @@ namespace http { return false; } - long int HTTPMsg::length() { + long int HTTPMsg::content_length() { unsigned long int length = 0; auto it = headers.find("Content-Length"); if (it == headers.end()) diff --git a/HTTP.h b/HTTP.h index 19d0612e..bce55026 100644 --- a/HTTP.h +++ b/HTTP.h @@ -62,7 +62,7 @@ namespace http { void del_header(const char *name); /** @brief Returns declared message length or -1 if unknown */ - long int length(); + long int content_length(); }; struct HTTPReq : HTTPMsg { diff --git a/HTTPProxy.cpp b/HTTPProxy.cpp index 48fa0ae6..db58d1dc 100644 --- a/HTTPProxy.cpp +++ b/HTTPProxy.cpp @@ -3,6 +3,13 @@ #include #include #include +#include +#include +#include +#include + +#include "I2PService.h" +#include "Destination.h" #include "HTTPProxy.h" #include "util.h" #include "Identity.h" @@ -14,12 +21,19 @@ #include "Config.h" #include "HTTP.h" -namespace i2p -{ -namespace proxy -{ +namespace i2p { +namespace proxy { + bool str_rmatch(std::string & str, const char *suffix) { + auto pos = str.rfind (suffix); + if (pos == std::string::npos) + return false; /* not found */ + if (str.length() == (pos + std::strlen(suffix))) + return true; /* match */ + return false; + } + static const size_t http_buffer_size = 8192; - class HTTPProxyHandler: public i2p::client::I2PServiceHandler, public std::enable_shared_from_this + class HTTPReqHandler: public i2p::client::I2PServiceHandler, public std::enable_shared_from_this { private: enum state @@ -36,10 +50,9 @@ namespace proxy void HandleSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered); void Terminate(); void AsyncSockRead(); - void HTTPRequestFailed(/*std::string message*/); - void RedirectToJumpService(); + void HTTPRequestFailed(const char *message); + void RedirectToJumpService(std::string & host); void ExtractRequest(); - bool IsI2PAddress(); bool ValidateHTTPRequest(); void HandleJumpServices(); bool CreateHTTPRequest(uint8_t *http_buff, std::size_t len); @@ -59,26 +72,26 @@ namespace proxy public: - HTTPProxyHandler(HTTPProxyServer * parent, std::shared_ptr sock) : + HTTPReqHandler(HTTPProxy * parent, std::shared_ptr sock) : I2PServiceHandler(parent), m_sock(sock) { EnterState(GET_METHOD); } - ~HTTPProxyHandler() { Terminate(); } + ~HTTPReqHandler() { Terminate(); } void Handle () { AsyncSockRead(); } }; - void HTTPProxyHandler::AsyncSockRead() + void HTTPReqHandler::AsyncSockRead() { LogPrint(eLogDebug, "HTTPProxy: async sock read"); - if(m_sock) { - m_sock->async_receive(boost::asio::buffer(m_http_buff, http_buffer_size), - std::bind(&HTTPProxyHandler::HandleSockRecv, shared_from_this(), - std::placeholders::_1, std::placeholders::_2)); - } else { + if (!m_sock) { LogPrint(eLogError, "HTTPProxy: no socket for read"); + return; } + m_sock->async_receive(boost::asio::buffer(m_http_buff, http_buffer_size), + std::bind(&HTTPReqHandler::HandleSockRecv, shared_from_this(), + std::placeholders::_1, std::placeholders::_2)); } - void HTTPProxyHandler::Terminate() { + void HTTPReqHandler::Terminate() { if (Kill()) return; if (m_sock) { @@ -91,30 +104,45 @@ namespace proxy /* All hope is lost beyond this point */ //TODO: handle this apropriately - void HTTPProxyHandler::HTTPRequestFailed(/*HTTPProxyHandler::errTypes error*/) + void HTTPReqHandler::HTTPRequestFailed(const char *message) { - static std::string response = "HTTP/1.0 500 Internal Server Error\r\nContent-type: text/html\r\nContent-length: 0\r\n\r\n"; - boost::asio::async_write(*m_sock, boost::asio::buffer(response,response.size()), - std::bind(&HTTPProxyHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1)); + i2p::http::HTTPRes res; + res.code = 500; + res.add_header("Content-Type", "text/plain"); + res.add_header("Connection", "close"); + res.body = message; + res.body += "\r\n"; + std::string response = res.to_string(); + boost::asio::async_write(*m_sock, boost::asio::buffer(response), + std::bind(&HTTPReqHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1)); } - void HTTPProxyHandler::RedirectToJumpService(/*HTTPProxyHandler::errTypes error*/) + void HTTPReqHandler::RedirectToJumpService(std::string & host) { - std::stringstream response; - std::string httpAddr; i2p::config::GetOption("http.address", httpAddr); - uint16_t httpPort; i2p::config::GetOption("http.port", httpPort); + i2p::http::HTTPRes res; + i2p::http::URL url; - response << "HTTP/1.1 302 Found\r\nLocation: http://" << httpAddr << ":" << httpPort << "/?page=jumpservices&address=" << m_address << "\r\n\r\n"; - boost::asio::async_write(*m_sock, boost::asio::buffer(response.str (),response.str ().length ()), - std::bind(&HTTPProxyHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1)); + /* TODO: don't redirect to webconsole, it's not always work, handle jumpservices here */ + i2p::config::GetOption("http.address", url.host); + i2p::config::GetOption("http.port", url.port); + url.path = "/"; + url.query = "page=jumpservices&address="; + url.query += host; + + res.code = 302; /* redirect */ + res.add_header("Location", url.to_string().c_str()); + + std::string response = res.to_string(); + boost::asio::async_write(*m_sock, boost::asio::buffer(response), + std::bind(&HTTPReqHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1)); } - void HTTPProxyHandler::EnterState(HTTPProxyHandler::state nstate) + void HTTPReqHandler::EnterState(HTTPReqHandler::state nstate) { m_state = nstate; } - void HTTPProxyHandler::ExtractRequest() + void HTTPReqHandler::ExtractRequest() { LogPrint(eLogDebug, "HTTPProxy: request: ", m_method, " ", m_url); i2p::http::URL url; @@ -127,18 +155,18 @@ namespace proxy LogPrint(eLogDebug, "HTTPProxy: server: ", m_address, ", port: ", m_port, ", path: ", m_path); } - bool HTTPProxyHandler::ValidateHTTPRequest() + bool HTTPReqHandler::ValidateHTTPRequest() { if ( m_version != "HTTP/1.0" && m_version != "HTTP/1.1" ) { LogPrint(eLogError, "HTTPProxy: unsupported version: ", m_version); - HTTPRequestFailed(); //TODO: send right stuff + HTTPRequestFailed("unsupported HTTP version"); return false; } return true; } - void HTTPProxyHandler::HandleJumpServices() + void HTTPReqHandler::HandleJumpServices() { static const char * helpermark1 = "?i2paddresshelper="; static const char * helpermark2 = "&i2paddresshelper="; @@ -170,31 +198,20 @@ namespace proxy m_path.erase(addressHelperPos); } - bool HTTPProxyHandler::IsI2PAddress() - { - auto pos = m_address.rfind (".i2p"); - if (pos != std::string::npos && (pos+4) == m_address.length ()) - { - return true; - } - return false; - } - - bool HTTPProxyHandler::CreateHTTPRequest(uint8_t *http_buff, std::size_t len) + bool HTTPReqHandler::CreateHTTPRequest(uint8_t *http_buff, std::size_t len) { ExtractRequest(); //TODO: parse earlier if (!ValidateHTTPRequest()) return false; HandleJumpServices(); i2p::data::IdentHash identHash; - if (IsI2PAddress ()) + if (str_rmatch(m_address, ".i2p")) { if (!i2p::client::context.GetAddressBook ().GetIdentHash (m_address, identHash)){ - RedirectToJumpService(); + RedirectToJumpService(m_address); return false; } } - m_request = m_method; m_request.push_back(' '); @@ -235,7 +252,7 @@ namespace proxy return true; } - bool HTTPProxyHandler::HandleData(uint8_t *http_buff, std::size_t len) + bool HTTPReqHandler::HandleData(uint8_t *http_buff, std::size_t len) { while (len > 0) { @@ -269,13 +286,13 @@ namespace proxy case '\n': EnterState(DONE); break; default: LogPrint(eLogError, "HTTPProxy: rejected invalid request ending with: ", ((int)*http_buff)); - HTTPRequestFailed(); //TODO: add correct code + HTTPRequestFailed("rejected invalid request"); return false; } break; default: LogPrint(eLogError, "HTTPProxy: invalid state: ", m_state); - HTTPRequestFailed(); //TODO: add correct code 500 + HTTPRequestFailed("invalid parser state"); return false; } http_buff++; @@ -286,7 +303,7 @@ namespace proxy return true; } - void HTTPProxyHandler::HandleSockRecv(const boost::system::error_code & ecode, std::size_t len) + void HTTPReqHandler::HandleSockRecv(const boost::system::error_code & ecode, std::size_t len) { LogPrint(eLogDebug, "HTTPProxy: sock recv: ", len, " bytes"); if(ecode) @@ -301,49 +318,45 @@ namespace proxy if (m_state == DONE) { LogPrint(eLogDebug, "HTTPProxy: requested: ", m_url); - GetOwner()->CreateStream (std::bind (&HTTPProxyHandler::HandleStreamRequestComplete, + GetOwner()->CreateStream (std::bind (&HTTPReqHandler::HandleStreamRequestComplete, shared_from_this(), std::placeholders::_1), m_address, m_port); } else AsyncSockRead(); } - } - void HTTPProxyHandler::SentHTTPFailed(const boost::system::error_code & ecode) + void HTTPReqHandler::SentHTTPFailed(const boost::system::error_code & ecode) { if (ecode) LogPrint (eLogError, "HTTPProxy: Closing socket after sending failure because: ", ecode.message ()); Terminate(); } - void HTTPProxyHandler::HandleStreamRequestComplete (std::shared_ptr stream) + void HTTPReqHandler::HandleStreamRequestComplete (std::shared_ptr stream) { - if (stream) - { - if (Kill()) return; - LogPrint (eLogInfo, "HTTPProxy: New I2PTunnel connection"); - auto connection = std::make_shared(GetOwner(), m_sock, stream); - GetOwner()->AddHandler (connection); - connection->I2PConnect (reinterpret_cast(m_request.data()), m_request.size()); - Done(shared_from_this()); - } - else - { + if (!stream) { LogPrint (eLogError, "HTTPProxy: error when creating the stream, check the previous warnings for more info"); - HTTPRequestFailed(); // TODO: Send correct error message host unreachable + HTTPRequestFailed("error when creating the stream, check logs"); + return; } + if (Kill()) + return; + LogPrint (eLogDebug, "HTTPProxy: New I2PTunnel connection"); + auto connection = std::make_shared(GetOwner(), m_sock, stream); + GetOwner()->AddHandler (connection); + connection->I2PConnect (reinterpret_cast(m_request.data()), m_request.size()); + Done (shared_from_this()); } - HTTPProxyServer::HTTPProxyServer(const std::string& address, int port, std::shared_ptr localDestination): + HTTPProxy::HTTPProxy(const std::string& address, int port, std::shared_ptr localDestination): TCPIPAcceptor(address, port, localDestination ? localDestination : i2p::client::context.GetSharedLocalDestination ()) { } - std::shared_ptr HTTPProxyServer::CreateHandler(std::shared_ptr socket) + std::shared_ptr HTTPProxy::CreateHandler(std::shared_ptr socket) { - return std::make_shared (this, socket); + return std::make_shared (this, socket); } - -} -} +} // http +} // i2p diff --git a/HTTPProxy.h b/HTTPProxy.h index b5ed77b9..29b997eb 100644 --- a/HTTPProxy.h +++ b/HTTPProxy.h @@ -1,32 +1,21 @@ #ifndef HTTP_PROXY_H__ #define HTTP_PROXY_H__ -#include -#include -#include -#include -#include "I2PService.h" -#include "Destination.h" - -namespace i2p -{ -namespace proxy -{ - class HTTPProxyServer: public i2p::client::TCPIPAcceptor +namespace i2p { +namespace proxy { + class HTTPProxy: public i2p::client::TCPIPAcceptor { public: - HTTPProxyServer(const std::string& address, int port, std::shared_ptr localDestination = nullptr); - ~HTTPProxyServer() {}; + HTTPProxy(const std::string& address, int port, std::shared_ptr localDestination = nullptr); + ~HTTPProxy() {}; protected: // Implements TCPIPAcceptor std::shared_ptr CreateHandler(std::shared_ptr socket); const char* GetName() { return "HTTP Proxy"; } }; - - typedef HTTPProxyServer HTTPProxy; -} -} +} // http +} // i2p #endif diff --git a/HTTPServer.cpp b/HTTPServer.cpp index b40cef27..cfcf4261 100644 --- a/HTTPServer.cpp +++ b/HTTPServer.cpp @@ -58,6 +58,9 @@ namespace http { " .tunnel.another { color: #434343; }\r\n" " caption { font-size: 1.5em; text-align: center; color: #894C84; }\r\n" " table { width: 100%; border-collapse: collapse; text-align: center; }\r\n" + " .private { background: black; color: black; } .private:hover { background: black; color: white } \r\n" + " .slide p, .slide [type='checkbox']{ display:none; } \r\n" + " .slide [type='checkbox']:checked ~ p { display:block; } \r\n" "\r\n"; const char HTTP_PAGE_TUNNELS[] = "tunnels"; @@ -126,8 +129,12 @@ namespace http { s << "\r\n" "\r\n" /* TODO: Add support for locale */ - " \r\n" - " \r\n" /* TODO: Find something to parse html/template system. This is horrible. */ + " \r\n" /* TODO: Find something to parse html/template system. This is horrible. */ +#if (!defined(WIN32)) + " \r\n" +#else + " \r\n" +#endif " \r\n" " Purple I2P " VERSION " Webconsole\r\n" << cssStyles << @@ -201,6 +208,10 @@ namespace http { s << numKBytesSent / 1024 / 1024 << " GiB"; s << " (" << (double) i2p::transport::transports.GetOutBandwidth () / 1024 << " KiB/s)
\r\n"; s << "Data path: " << i2p::fs::GetDataDir() << "
\r\n
\r\n"; + s << "
\r\n\r\n

\r\n"; + s << "Router Ident: " << i2p::context.GetRouterInfo().GetIdentHashBase64() << "
\r\n"; + s << "Router Family: " << i2p::context.GetRouterInfo().GetProperty("family") << "
\r\n"; + s << "Router Caps: " << i2p::context.GetRouterInfo().GetProperty("caps") << "
\r\n"; s << "Our external address:" << "
\r\n" ; for (auto address : i2p::context.GetRouterInfo().GetAddresses()) { @@ -223,6 +234,7 @@ namespace http { } s << address->host.to_string() << ":" << address->port << "
\r\n"; } + s << "

\r\n
\r\n"; s << "
\r\nRouters: " << i2p::data::netdb.GetNumRouters () << " "; s << "Floodfills: " << i2p::data::netdb.GetNumFloodfills () << " "; s << "LeaseSets: " << i2p::data::netdb.GetNumLeaseSets () << "
\r\n"; @@ -612,7 +624,7 @@ namespace http { HandleCommand (req, res, s); } else { ShowStatus (s); - //res.add_header("Refresh", "5"); + res.add_header("Refresh", "10"); } ShowPageTail (s); diff --git a/I2CP.cpp b/I2CP.cpp index f506a312..4884583e 100644 --- a/I2CP.cpp +++ b/I2CP.cpp @@ -114,7 +114,7 @@ namespace client } } - I2CPSession::I2CPSession (I2CPServer& owner, std::shared_ptr socket): + I2CPSession::I2CPSession (I2CPServer& owner, std::shared_ptr socket): m_Owner (owner), m_Socket (socket), m_Payload (nullptr), m_SessionID (0xFFFF), m_MessageID (0), m_IsSendAccepted (true) { @@ -583,7 +583,12 @@ namespace client 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)) + m_Acceptor (m_Service, +#ifdef ANDROID + I2CPSession::proto::endpoint(std::string (1, '\0') + interface)) // leading 0 for abstract address +#else + I2CPSession::proto::endpoint(boost::asio::ip::address::from_string(interface), port)) +#endif { memset (m_MessagesHandlers, 0, sizeof (m_MessagesHandlers)); m_MessagesHandlers[I2CP_GET_DATE_MESSAGE] = &I2CPSession::GetDateMessageHandler; @@ -644,12 +649,13 @@ namespace client void I2CPServer::Accept () { - auto newSocket = std::make_shared (m_Service); + 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) + void I2CPServer::HandleAccept(const boost::system::error_code& ecode, + std::shared_ptr socket) { if (!ecode && socket) { diff --git a/I2CP.h b/I2CP.h index 6fc0e846..4964c575 100644 --- a/I2CP.h +++ b/I2CP.h @@ -99,7 +99,14 @@ namespace client { public: - I2CPSession (I2CPServer& owner, std::shared_ptr socket); +#ifdef ANDROID + typedef boost::asio::local::stream_protocol proto; +#else + typedef boost::asio::ip::tcp proto; +#endif + + I2CPSession (I2CPServer& owner, std::shared_ptr socket); + ~I2CPSession (); void Start (); @@ -144,7 +151,7 @@ namespace client private: I2CPServer& m_Owner; - std::shared_ptr m_Socket; + std::shared_ptr m_Socket; uint8_t m_Header[I2CP_HEADER_SIZE], * m_Payload; size_t m_PayloadLen; @@ -173,7 +180,8 @@ namespace client void Run (); void Accept (); - void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr socket); + + void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr socket); private: @@ -183,7 +191,7 @@ namespace client bool m_IsRunning; std::thread * m_Thread; boost::asio::io_service m_Service; - boost::asio::ip::tcp::acceptor m_Acceptor; + I2CPSession::proto::acceptor m_Acceptor; public: diff --git a/I2PControl.cpp b/I2PControl.cpp index c0c87fd4..aa3c55e8 100644 --- a/I2PControl.cpp +++ b/I2PControl.cpp @@ -205,7 +205,7 @@ namespace client } /* append to json chunk of data from 1st request */ json.write(buf->begin() + len, bytes_transferred - len); - remains = req.length() - len; + remains = req.content_length() - len; /* if request has Content-Length header, fetch rest of data and store to json buffer */ while (remains > 0) { len = ((long int) buf->size() < remains) ? buf->size() : remains; diff --git a/Makefile b/Makefile index 4cc313a9..fe8ae7e3 100644 --- a/Makefile +++ b/Makefile @@ -11,6 +11,7 @@ include filelist.mk USE_AESNI := yes USE_STATIC := no +USE_UPNP := no ifeq ($(UNAME),Darwin) DAEMON_SRC += DaemonLinux.cpp diff --git a/Makefile.homebrew b/Makefile.homebrew index 6ce513fe..f57f6495 100644 --- a/Makefile.homebrew +++ b/Makefile.homebrew @@ -8,7 +8,7 @@ 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_program_options -lpthread -ifeq ($(USE_UPNP),1) +ifeq ($(USE_UPNP),yes) LDFLAGS += -ldl CXXFLAGS += -DUSE_UPNP endif diff --git a/Makefile.linux b/Makefile.linux index e00fd705..1376260a 100644 --- a/Makefile.linux +++ b/Makefile.linux @@ -33,18 +33,18 @@ ifeq ($(USE_STATIC),yes) LDLIBS += $(LIBDIR)/libboost_date_time.a LDLIBS += $(LIBDIR)/libboost_filesystem.a LDLIBS += $(LIBDIR)/libboost_program_options.a - LDLIBS += $(LIBDIR)/libcrypto.a LDLIBS += $(LIBDIR)/libssl.a + LDLIBS += $(LIBDIR)/libcrypto.a LDLIBS += $(LIBDIR)/libz.a - LDLIBS += -lpthread -static-libstdc++ -static-libgcc + LDLIBS += -lpthread -static-libstdc++ -static-libgcc -lrt USE_AESNI := no else LDLIBS = -lcrypto -lssl -lz -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread endif # UPNP Support (miniupnpc 1.5 or 1.6) -ifeq ($(USE_UPNP),1) - LDFLAGS += -ldl +ifeq ($(USE_UPNP),yes) + LDFLAGS += -lminiupnpc CXXFLAGS += -DUSE_UPNP endif diff --git a/Makefile.mingw b/Makefile.mingw index b859ebb3..5cf16bf5 100644 --- a/Makefile.mingw +++ b/Makefile.mingw @@ -1,47 +1,47 @@ -USE_WIN32_APP=yes -CXX = g++ -WINDRES = windres -CXXFLAGS = -Os -D_MT -DWIN32 -D_WINDOWS -DWIN32_LEAN_AND_MEAN -NEEDED_CXXFLAGS = -std=c++11 -BOOST_SUFFIX = -mt -INCFLAGS = -I/usr/include/ -I/usr/local/include/ -LDFLAGS = -Wl,-rpath,/usr/local/lib \ - -L/usr/local/lib \ - -L/c/dev/openssl \ - -L/c/dev/boost/lib -LDLIBS = \ - -Wl,-Bstatic -lboost_system$(BOOST_SUFFIX) \ - -Wl,-Bstatic -lboost_date_time$(BOOST_SUFFIX) \ - -Wl,-Bstatic -lboost_filesystem$(BOOST_SUFFIX) \ - -Wl,-Bstatic -lboost_program_options$(BOOST_SUFFIX) \ - -Wl,-Bstatic -lssl \ - -Wl,-Bstatic -lcrypto \ - -Wl,-Bstatic -lz \ - -Wl,-Bstatic -lwsock32 \ - -Wl,-Bstatic -lws2_32 \ - -Wl,-Bstatic -lgdi32 \ - -Wl,-Bstatic -liphlpapi \ - -static-libgcc -static-libstdc++ \ - -Wl,-Bstatic -lstdc++ \ - -Wl,-Bstatic -lpthread - -ifeq ($(USE_WIN32_APP), yes) - CXXFLAGS += -DWIN32_APP - LDFLAGS += -mwindows -s - DAEMON_RC += Win32/Resource.rc - DAEMON_OBJS += $(patsubst %.rc,obj/%.o,$(DAEMON_RC)) -endif - -# UPNP Support -ifeq ($(USE_UPNP),1) - CXXFLAGS += -DUSE_UPNP -endif - -ifeq ($(USE_AESNI),1) - CPU_FLAGS = -maes -DAESNI -else - CPU_FLAGS = -msse -endif - -obj/%.o : %.rc - $(WINDRES) -i $< -o $@ +USE_WIN32_APP=yes +CXX = g++ +WINDRES = windres +CXXFLAGS = -Os -D_MT -DWIN32 -D_WINDOWS -DWIN32_LEAN_AND_MEAN +NEEDED_CXXFLAGS = -std=c++11 +BOOST_SUFFIX = -mt +INCFLAGS = -I/usr/include/ -I/usr/local/include/ +LDFLAGS = -Wl,-rpath,/usr/local/lib \ + -L/usr/local/lib + +# UPNP Support +ifeq ($(USE_UPNP),yes) + CXXFLAGS += -DUSE_UPNP -DMINIUPNP_STATICLIB + LDLIBS = -Wl,-Bstatic -lminiupnpc +endif + +LDLIBS += \ + -Wl,-Bstatic -lboost_system$(BOOST_SUFFIX) \ + -Wl,-Bstatic -lboost_date_time$(BOOST_SUFFIX) \ + -Wl,-Bstatic -lboost_filesystem$(BOOST_SUFFIX) \ + -Wl,-Bstatic -lboost_program_options$(BOOST_SUFFIX) \ + -Wl,-Bstatic -lssl \ + -Wl,-Bstatic -lcrypto \ + -Wl,-Bstatic -lz \ + -Wl,-Bstatic -lwsock32 \ + -Wl,-Bstatic -lws2_32 \ + -Wl,-Bstatic -lgdi32 \ + -Wl,-Bstatic -liphlpapi \ + -static-libgcc -static-libstdc++ \ + -Wl,-Bstatic -lstdc++ \ + -Wl,-Bstatic -lpthread + +ifeq ($(USE_WIN32_APP), yes) + CXXFLAGS += -DWIN32_APP + LDFLAGS += -mwindows -s + DAEMON_RC += Win32/Resource.rc + DAEMON_OBJS += $(patsubst %.rc,obj/%.o,$(DAEMON_RC)) +endif + +ifeq ($(USE_AESNI),yes) + CPU_FLAGS = -maes -DAESNI +else + CPU_FLAGS = -msse +endif + +obj/%.o : %.rc + $(WINDRES) -i $< -o $@ diff --git a/Makefile.osx b/Makefile.osx index ef236c9a..f40ce1af 100644 --- a/Makefile.osx +++ b/Makefile.osx @@ -5,7 +5,7 @@ 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_program_options -lpthread -ifeq ($(USE_UPNP),1) +ifeq ($(USE_UPNP),yes) LDFLAGS += -ldl CXXFLAGS += -DUSE_UPNP endif diff --git a/NTCPSession.cpp b/NTCPSession.cpp index ae020f5f..953c0707 100644 --- a/NTCPSession.cpp +++ b/NTCPSession.cpp @@ -1,6 +1,6 @@ #include #include -#include + #include "I2PEndian.h" #include "Base.h" #include "Crypto.h" @@ -430,7 +430,7 @@ namespace transport } else { - LogPrint (eLogInfo, "NTCP: Server session from ", m_Socket.remote_endpoint (), " connected"); + LogPrint (eLogDebug, "NTCP: Server session from ", m_Socket.remote_endpoint (), " connected"); m_Server.AddNTCPSession (shared_from_this ()); Connected (); @@ -942,7 +942,7 @@ namespace transport { if (ecode) { - LogPrint (eLogError, "NTCP: Connect error: ", ecode.message ()); + LogPrint (eLogError, "NTCP: Can't connect to ", conn->GetSocket ().remote_endpoint (), ": ", ecode.message ()); if (ecode != boost::asio::error::operation_aborted) i2p::data::netdb.SetUnreachable (conn->GetRemoteIdentity ()->GetIdentHash (), true); conn->Terminate (); diff --git a/NetDb.cpp b/NetDb.cpp index d2afc50a..59b10f26 100644 --- a/NetDb.cpp +++ b/NetDb.cpp @@ -2,7 +2,7 @@ #include #include #include -#include + #include "I2PEndian.h" #include "Base.h" #include "Crypto.h" @@ -450,6 +450,12 @@ namespace data } offset += 32; } + // we must send reply back before this check + if (ident == i2p::context.GetIdentHash ()) + { + LogPrint (eLogError, "NetDb: database store with own RouterInfo received, dropped"); + return; + } size_t payloadOffset = offset; bool updated = false; @@ -488,11 +494,16 @@ namespace data memcpy (payload + DATABASE_STORE_HEADER_SIZE, buf + payloadOffset, msgLen); floodMsg->FillI2NPMessageHeader (eI2NPDatabaseStore); std::set excluded; - for (int i = 0; i < 3; i++) + excluded.insert (i2p::context.GetIdentHash ()); // don't flood to itself + excluded.insert (ident); // don't flood back + for (int i = 0; i < 3; i++) { auto floodfill = GetClosestFloodfill (ident, excluded); if (floodfill) - transports.SendMessage (floodfill->GetIdentHash (), floodMsg); + { + transports.SendMessage (floodfill->GetIdentHash (), CopyI2NPMessage(floodMsg)); + excluded.insert (floodfill->GetIdentHash ()); + } else break; } @@ -610,7 +621,7 @@ namespace data uint32_t replyTunnelID = 0; if (flag & DATABASE_LOOKUP_DELIVERY_FLAG) //reply to tunnel { - replyTunnelID = bufbe32toh (buf + 64); + replyTunnelID = bufbe32toh (buf + 65); excluded += 4; } uint16_t numExcluded = bufbe16toh (excluded); @@ -671,7 +682,7 @@ namespace data if (!replyMsg) { - LogPrint (eLogWarning, "NetDb: Requested ", key, " not found. ", numExcluded, " excluded"); + LogPrint (eLogWarning, "NetDb: Requested ", key, " not found, ", numExcluded, " peers excluded"); // find or cleate response std::vector closestFloodfills; bool found = false; diff --git a/NetDb.h b/NetDb.h index 1c062358..823dbb54 100644 --- a/NetDb.h +++ b/NetDb.h @@ -8,7 +8,9 @@ #include #include #include + #include "Base.h" +#include "Gzip.h" #include "FS.h" #include "Queue.h" #include "I2NPProtocol.h" diff --git a/SSU.cpp b/SSU.cpp index ddfd1501..d635a7f9 100644 --- a/SSU.cpp +++ b/SSU.cpp @@ -231,7 +231,7 @@ namespace transport session = std::make_shared (*this, packet->from); session->WaitForConnect (); (*sessions)[packet->from] = session; - LogPrint (eLogInfo, "SSU: new session from ", packet->from.address ().to_string (), ":", packet->from.port (), " created"); + LogPrint (eLogDebug, "SSU: new session from ", packet->from.address ().to_string (), ":", packet->from.port (), " created"); } } session->ProcessNextMessage (packet->buf, packet->len, packet->from); @@ -312,7 +312,7 @@ namespace transport auto session = std::make_shared (*this, remoteEndpoint, router, peerTest); sessions[remoteEndpoint] = session; // connect - LogPrint (eLogInfo, "SSU: Creating new session to [", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()), "] ", + LogPrint (eLogDebug, "SSU: Creating new session to [", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()), "] ", remoteEndpoint.address ().to_string (), ":", remoteEndpoint.port ()); session->Connect (); } @@ -364,10 +364,10 @@ namespace transport } if (introducerSession) // session found - LogPrint (eLogInfo, "SSU: Session to introducer already exists"); + LogPrint (eLogWarning, "SSU: Session to introducer already exists"); else // create new { - LogPrint (eLogInfo, "SSU: Creating new session to introducer"); + LogPrint (eLogDebug, "SSU: Creating new session to introducer ", introducer->iHost); boost::asio::ip::udp::endpoint introducerEndpoint (introducer->iHost, introducer->iPort); introducerSession = std::make_shared (*this, introducerEndpoint, router); m_Sessions[introducerEndpoint] = introducerSession; diff --git a/SSUData.cpp b/SSUData.cpp index 0ccf25a8..2bd65682 100644 --- a/SSUData.cpp +++ b/SSUData.cpp @@ -241,7 +241,7 @@ namespace transport if (!msg->IsExpired ()) m_Handler.PutNextMessage (msg); else - LogPrint (eLogInfo, "SSU: message expired"); + LogPrint (eLogDebug, "SSU: message expired"); } else LogPrint (eLogWarning, "SSU: Message ", msgID, " already received"); @@ -425,6 +425,7 @@ namespace transport if (ecode != boost::asio::error::operation_aborted) { uint32_t ts = i2p::util::GetSecondsSinceEpoch (); + int numResent = 0; for (auto it = m_SentMessages.begin (); it != m_SentMessages.end ();) { if (ts >= it->second->nextResendTime) @@ -437,6 +438,7 @@ namespace transport try { m_Session.Send (f->buf, f->len); // resend + numResent++; } catch (boost::system::system_error& ec) { @@ -457,7 +459,13 @@ namespace transport else it++; } - ScheduleResend (); + if (numResent < MAX_OUTGOING_WINDOW_SIZE) + ScheduleResend (); + else + { + LogPrint (eLogError, "SSU: resend window exceeds max size. Session terminated"); + m_Session.Close (); + } } } diff --git a/SSUData.h b/SSUData.h index 392bfce6..02135350 100644 --- a/SSUData.h +++ b/SSUData.h @@ -29,6 +29,7 @@ namespace transport const int DECAY_INTERVAL = 20; // in seconds const int INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT = 30; // in seconds const unsigned int MAX_NUM_RECEIVED_MESSAGES = 1000; // how many msgID we store for duplicates check + const int MAX_OUTGOING_WINDOW_SIZE = 200; // how many unacked message we can store // data flags const uint8_t DATA_FLAG_EXTENDED_DATA_INCLUDED = 0x02; const uint8_t DATA_FLAG_WANT_REPLY = 0x04; diff --git a/SSUSession.cpp b/SSUSession.cpp index cf56ca15..9b480888 100644 --- a/SSUSession.cpp +++ b/SSUSession.cpp @@ -814,7 +814,7 @@ namespace transport if (!ecode) { // timeout expired - LogPrint (eLogWarning, "SSU: session was not established after ", SSU_CONNECT_TIMEOUT, " seconds"); + LogPrint (eLogWarning, "SSU: session with ", m_RemoteEndpoint, " was not established after ", SSU_CONNECT_TIMEOUT, " seconds"); Failed (); } } @@ -891,7 +891,7 @@ namespace transport { if (ecode != boost::asio::error::operation_aborted) { - LogPrint (eLogWarning, "SSU: no activity for ", SSU_TERMINATION_TIMEOUT, " seconds"); + LogPrint (eLogWarning, "SSU: no activity with ", m_RemoteEndpoint, " for ", SSU_TERMINATION_TIMEOUT, " seconds"); Failed (); } } diff --git a/Streaming.cpp b/Streaming.cpp index 1a6fdbca..2d3c92ce 100644 --- a/Streaming.cpp +++ b/Streaming.cpp @@ -87,7 +87,7 @@ namespace stream return; } - LogPrint (eLogDebug, "Streaming: Received seqn=", receivedSeqn); + LogPrint (eLogDebug, "Streaming: Received seqn=", receivedSeqn, " on sSID=", m_SendStreamID); if (receivedSeqn == m_LastReceivedSequenceNumber + 1) { // we have received next in sequence message @@ -129,13 +129,13 @@ namespace stream if (receivedSeqn <= m_LastReceivedSequenceNumber) { // we have received duplicate - LogPrint (eLogWarning, "Streaming: Duplicate message ", receivedSeqn, " received"); + LogPrint (eLogWarning, "Streaming: Duplicate message ", receivedSeqn, " on sSID=", m_SendStreamID); SendQuickAck (); // resend ack for previous message again delete packet; // packet dropped } else { - LogPrint (eLogWarning, "Streaming: Missing messages from ", m_LastReceivedSequenceNumber + 1, " to ", receivedSeqn - 1); + LogPrint (eLogWarning, "Streaming: Missing messages on sSID=", m_SendStreamID, ": from ", m_LastReceivedSequenceNumber + 1, " to ", receivedSeqn - 1); // save message and wait for missing message again SavePacket (packet); if (m_LastReceivedSequenceNumber >= 0) @@ -183,7 +183,7 @@ namespace stream m_RemoteIdentity = std::make_shared(optionData, packet->GetOptionSize ()); optionData += m_RemoteIdentity->GetFullLen (); if (!m_RemoteLeaseSet) - LogPrint (eLogDebug, "Streaming: Incoming stream from ", m_RemoteIdentity->GetIdentHash ().ToBase64 ()); + LogPrint (eLogDebug, "Streaming: Incoming stream from ", m_RemoteIdentity->GetIdentHash ().ToBase64 (), ", sSID=", m_SendStreamID, ", rSID=", m_RecvStreamID); } if (flags & PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED) @@ -263,7 +263,7 @@ namespace stream uint64_t rtt = ts - sentPacket->sendTime; m_RTT = (m_RTT*seqn + rtt)/(seqn + 1); m_RTO = m_RTT*1.5; // TODO: implement it better - LogPrint (eLogDebug, "Packet ", seqn, " acknowledged rtt=", rtt); + LogPrint (eLogDebug, "Streaming: Packet ", seqn, " acknowledged rtt=", rtt); m_SentPackets.erase (it++); delete sentPacket; acknowledged = true; @@ -395,8 +395,11 @@ namespace stream } if (packets.size () > 0) { - m_IsAckSendScheduled = false; - m_AckSendTimer.cancel (); + if (m_SavedPackets.empty ()) // no NACKS + { + m_IsAckSendScheduled = false; + m_AckSendTimer.cancel (); + } bool isEmpty = m_SentPackets.empty (); auto ts = i2p::util::GetMillisecondsSinceEpoch (); for (auto it: packets) @@ -448,7 +451,7 @@ namespace stream auto seqn = it->GetSeqn (); if (numNacks + (seqn - nextSeqn) >= 256) { - LogPrint (eLogError, "Number of NACKs exceeds 256. seqn=", seqn, " nextSeqn=", nextSeqn); + LogPrint (eLogError, "Streaming: Number of NACKs exceeds 256. seqn=", seqn, " nextSeqn=", nextSeqn); htobe32buf (packet + 12, nextSeqn); // change ack Through break; } @@ -489,7 +492,7 @@ namespace stream m_Status = eStreamStatusClosing; Close (); // recursion if (m_Status == eStreamStatusClosing) //still closing - LogPrint (eLogInfo, "Streaming: Trying to send stream data before closing"); + LogPrint (eLogDebug, "Streaming: Trying to send stream data before closing, sSID=", m_SendStreamID); break; case eStreamStatusReset: SendClose (); @@ -511,7 +514,7 @@ namespace stream m_LocalDestination.DeleteStream (shared_from_this ()); break; default: - LogPrint (eLogWarning, "Streaming: Unexpected stream status ", (int)m_Status); + LogPrint (eLogWarning, "Streaming: Unexpected stream status ", (int)m_Status, "sSID=", m_SendStreamID); }; } @@ -543,7 +546,7 @@ namespace stream p->len = size; m_Service.post (std::bind (&Stream::SendPacket, shared_from_this (), p)); - LogPrint (eLogDebug, "Streaming: FIN sent"); + LogPrint (eLogDebug, "Streaming: FIN sent, sSID=", m_SendStreamID); } size_t Stream::ConcatenatePackets (uint8_t * buf, size_t len) @@ -597,7 +600,7 @@ namespace stream UpdateCurrentRemoteLease (); if (!m_RemoteLeaseSet) { - LogPrint (eLogError, "Streaming: Can't send packets, missing remote LeaseSet"); + LogPrint (eLogError, "Streaming: Can't send packets, missing remote LeaseSet, sSID=", m_SendStreamID); return; } } @@ -622,7 +625,7 @@ namespace stream m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ()->GetTunnelPool ()->GetNewOutboundTunnel (m_CurrentOutboundTunnel); if (!m_CurrentOutboundTunnel) { - LogPrint (eLogError, "Streaming: No outbound tunnels in the pool"); + LogPrint (eLogError, "Streaming: No outbound tunnels in the pool, sSID=", m_SendStreamID); return; } @@ -646,7 +649,7 @@ namespace stream m_CurrentOutboundTunnel->SendTunnelDataMsg (msgs); } else - LogPrint (eLogWarning, "Streaming: All leases are expired"); + LogPrint (eLogWarning, "Streaming: All leases are expired, sSID=", m_SendStreamID); } @@ -665,7 +668,7 @@ namespace stream // check for resend attempts if (m_NumResendAttempts >= MAX_NUM_RESEND_ATTEMPTS) { - LogPrint (eLogWarning, "Streaming: packet was not ACKed after ", MAX_NUM_RESEND_ATTEMPTS, " attempts, terminate"); + LogPrint (eLogWarning, "Streaming: packet was not ACKed after ", MAX_NUM_RESEND_ATTEMPTS, " attempts, terminate, sSID=", m_SendStreamID); m_Status = eStreamStatusReset; Close (); return; @@ -700,13 +703,13 @@ namespace stream case 4: if (m_RoutingSession) m_RoutingSession->SetSharedRoutingPath (nullptr); UpdateCurrentRemoteLease (); // pick another lease - LogPrint (eLogWarning, "Streaming: Another remote lease has been selected for stream"); + LogPrint (eLogWarning, "Streaming: Another remote lease has been selected for stream with sSID=", m_SendStreamID); break; case 3: // pick another outbound tunnel if (m_RoutingSession) m_RoutingSession->SetSharedRoutingPath (nullptr); m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ()->GetTunnelPool ()->GetNextOutboundTunnel (m_CurrentOutboundTunnel); - LogPrint (eLogWarning, "Streaming: Another outbound tunnel has been selected for stream"); + LogPrint (eLogWarning, "Streaming: Another outbound tunnel has been selected for stream with sSID=", m_SendStreamID); break; default: ; } @@ -722,7 +725,7 @@ namespace stream { if (m_LastReceivedSequenceNumber < 0) { - LogPrint (eLogWarning, "Streaming: SYN has not been recived after ", ACK_SEND_TIMEOUT, " milliseconds after follow on, terminate"); + LogPrint (eLogWarning, "Streaming: SYN has not been recived after ", ACK_SEND_TIMEOUT, " milliseconds after follow on, terminate sSID=", m_SendStreamID); m_Status = eStreamStatusReset; Close (); return; @@ -825,7 +828,7 @@ namespace stream it->second->HandleNextPacket (packet); else { - LogPrint (eLogError, "Streaming: Unknown stream sendStreamID=", sendStreamID); + LogPrint (eLogError, "Streaming: Unknown stream sSID=", sendStreamID); delete packet; } } @@ -841,7 +844,7 @@ namespace stream auto it = m_SavedPackets.find (receiveStreamID); if (it != m_SavedPackets.end ()) { - LogPrint (eLogDebug, "Streaming: Processing ", it->second.size (), " saved packets for receiveStreamID=", receiveStreamID); + LogPrint (eLogDebug, "Streaming: Processing ", it->second.size (), " saved packets for rSID=", receiveStreamID); for (auto it1: it->second) incomingStream->HandleNextPacket (it1); m_SavedPackets.erase (it); @@ -860,7 +863,7 @@ namespace stream m_PendingIncomingTimer.expires_from_now (boost::posix_time::seconds(PENDING_INCOMING_TIMEOUT)); m_PendingIncomingTimer.async_wait (std::bind (&StreamingDestination::HandlePendingIncomingTimer, shared_from_this (), std::placeholders::_1)); - LogPrint (eLogDebug, "Streaming: Pending incoming stream added"); + LogPrint (eLogDebug, "Streaming: Pending incoming stream added, rSID=", receiveStreamID); } else { diff --git a/Tag.h b/Tag.h new file mode 100644 index 00000000..b6f94de7 --- /dev/null +++ b/Tag.h @@ -0,0 +1,90 @@ +/* +* 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 TAG_H__ +#define TAG_H__ + +#include /* memcpy */ + +#include "Base.h" + +namespace i2p { +namespace data { + template + class Tag + { + public: + + Tag (const uint8_t * buf) { memcpy (m_Buf, buf, sz); }; + Tag (const Tag& ) = default; +#ifndef _WIN32 // FIXME!!! msvs 2013 can't compile it + Tag (Tag&& ) = default; +#endif + Tag () = default; + + Tag& operator= (const Tag& ) = default; +#ifndef _WIN32 + Tag& operator= (Tag&& ) = default; +#endif + + uint8_t * operator()() { return m_Buf; }; + const uint8_t * operator()() const { return m_Buf; }; + + operator uint8_t * () { return m_Buf; }; + operator const uint8_t * () const { return m_Buf; }; + + const uint64_t * GetLL () const { return ll; }; + + bool operator== (const Tag& other) const { return !memcmp (m_Buf, other.m_Buf, sz); }; + bool operator< (const Tag& other) const { return memcmp (m_Buf, other.m_Buf, sz) < 0; }; + + bool IsZero () const + { + for (int i = 0; i < sz/8; i++) + if (ll[i]) return false; + return true; + } + + std::string ToBase64 () const + { + char str[sz*2]; + int l = i2p::data::ByteStreamToBase64 (m_Buf, sz, str, sz*2); + str[l] = 0; + return std::string (str); + } + + std::string ToBase32 () const + { + char str[sz*2]; + int l = i2p::data::ByteStreamToBase32 (m_Buf, sz, str, sz*2); + str[l] = 0; + return std::string (str); + } + + void FromBase32 (const std::string& s) + { + i2p::data::Base32ToByteStream (s.c_str (), s.length (), m_Buf, sz); + } + + void FromBase64 (const std::string& s) + { + i2p::data::Base64ToByteStream (s.c_str (), s.length (), m_Buf, sz); + } + + private: + + union // 8 bytes alignment + { + uint8_t m_Buf[sz]; + uint64_t ll[sz/8]; + }; + }; +} // data +} // i2p + +#endif /* TAG_H__ */ diff --git a/TransitTunnel.cpp b/TransitTunnel.cpp index 0d54fc11..dfe01a05 100644 --- a/TransitTunnel.cpp +++ b/TransitTunnel.cpp @@ -92,7 +92,7 @@ namespace tunnel { if (isEndpoint) { - LogPrint (eLogInfo, "TransitTunnel: endpoint ", receiveTunnelID, " created"); + LogPrint (eLogDebug, "TransitTunnel: endpoint ", receiveTunnelID, " created"); return std::make_shared (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey); } else if (isGateway) @@ -102,7 +102,7 @@ namespace tunnel } else { - LogPrint (eLogInfo, "TransitTunnel: ", receiveTunnelID, "->", nextTunnelID, " created"); + LogPrint (eLogDebug, "TransitTunnel: ", receiveTunnelID, "->", nextTunnelID, " created"); return std::make_shared (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey); } } diff --git a/Tunnel.cpp b/Tunnel.cpp index 961cc97e..5da18542 100644 --- a/Tunnel.cpp +++ b/Tunnel.cpp @@ -217,6 +217,7 @@ namespace tunnel if (msg) { m_NumReceivedBytes += msg->GetLength (); + msg->from = shared_from_this (); HandleI2NPMessage (msg); } } @@ -768,6 +769,22 @@ namespace tunnel return newTunnel; } + std::shared_ptr Tunnels::CreateInboundTunnel (std::shared_ptr config, std::shared_ptr outboundTunnel) + { + if (config) + return CreateTunnel(config, outboundTunnel); + else + return CreateZeroHopsInboundTunnel (); + } + + std::shared_ptr Tunnels::CreateOutboundTunnel (std::shared_ptr config) + { + if (config) + return CreateTunnel(config); + else + return CreateZeroHopsOutboundTunnel (); + } + void Tunnels::AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr tunnel) { m_PendingInboundTunnels[replyMsgID] = tunnel; @@ -815,20 +832,22 @@ namespace tunnel } - void Tunnels::CreateZeroHopsInboundTunnel () + std::shared_ptr Tunnels::CreateZeroHopsInboundTunnel () { auto inboundTunnel = std::make_shared (); inboundTunnel->SetState (eTunnelStateEstablished); m_InboundTunnels.push_back (inboundTunnel); m_Tunnels[inboundTunnel->GetTunnelID ()] = inboundTunnel; + return inboundTunnel; } - void Tunnels::CreateZeroHopsOutboundTunnel () + std::shared_ptr Tunnels::CreateZeroHopsOutboundTunnel () { auto outboundTunnel = std::make_shared (); outboundTunnel->SetState (eTunnelStateEstablished); m_OutboundTunnels.push_back (outboundTunnel); // we don't insert into m_Tunnels + return outboundTunnel; } int Tunnels::GetTransitTunnelsExpirationTimeout () @@ -861,12 +880,6 @@ namespace tunnel // TODO: locking return m_OutboundTunnels.size(); } - -#ifdef ANDROID_ARM7A - template std::shared_ptr Tunnels::CreateTunnel(std::shared_ptr, std::shared_ptr); - template std::shared_ptr Tunnels::CreateTunnel(std::shared_ptr, std::shared_ptr); -#endif - } } diff --git a/Tunnel.h b/Tunnel.h index 43417e5d..5bc8b195 100644 --- a/Tunnel.h +++ b/Tunnel.h @@ -176,10 +176,10 @@ namespace tunnel void AddTransitTunnel (std::shared_ptr tunnel); void AddOutboundTunnel (std::shared_ptr newTunnel); void AddInboundTunnel (std::shared_ptr newTunnel); + std::shared_ptr CreateInboundTunnel (std::shared_ptr config, std::shared_ptr outboundTunnel); + std::shared_ptr CreateOutboundTunnel (std::shared_ptr config); void PostTunnelData (std::shared_ptr msg); void PostTunnelData (const std::vector >& msgs); - template - std::shared_ptr CreateTunnel (std::shared_ptr config, std::shared_ptr outboundTunnel = nullptr); void AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr tunnel); void AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr tunnel); std::shared_ptr CreateTunnelPool (int numInboundHops, @@ -189,6 +189,9 @@ namespace tunnel private: + template + std::shared_ptr CreateTunnel (std::shared_ptr config, std::shared_ptr outboundTunnel = nullptr); + template std::shared_ptr GetPendingTunnel (uint32_t replyMsgID, const std::map >& pendingTunnels); @@ -204,8 +207,8 @@ namespace tunnel void ManagePendingTunnels (PendingTunnels& pendingTunnels); void ManageTunnelPools (); - void CreateZeroHopsInboundTunnel (); - void CreateZeroHopsOutboundTunnel (); + std::shared_ptr CreateZeroHopsInboundTunnel (); + std::shared_ptr CreateZeroHopsOutboundTunnel (); private: diff --git a/TunnelConfig.h b/TunnelConfig.h index 0340254e..7546c9b2 100644 --- a/TunnelConfig.h +++ b/TunnelConfig.h @@ -159,6 +159,11 @@ namespace tunnel return num; } + bool IsEmpty () const + { + return !m_FirstHop; + } + virtual bool IsInbound () const { return m_FirstHop->isGateway; } virtual uint32_t GetTunnelID () const diff --git a/TunnelPool.cpp b/TunnelPool.cpp index 5e7e8ec4..7024e1a1 100644 --- a/TunnelPool.cpp +++ b/TunnelPool.cpp @@ -329,8 +329,9 @@ namespace tunnel bool TunnelPool::SelectPeers (std::vector >& peers, bool isInbound) { if (m_ExplicitPeers) return SelectExplicitPeers (peers, isInbound); - auto prevHop = i2p::context.GetSharedRouterInfo (); int numHops = isInbound ? m_NumInboundHops : m_NumOutboundHops; + if (numHops <= 0) return true; // peers is empty + auto prevHop = i2p::context.GetSharedRouterInfo (); if (i2p::transport::transports.GetNumPeers () > 25) { auto r = i2p::transport::transports.GetRandomPeer (); @@ -389,9 +390,16 @@ namespace tunnel std::vector > peers; if (SelectPeers (peers, true)) { - std::reverse (peers.begin (), peers.end ()); - auto tunnel = tunnels.CreateTunnel (std::make_shared (peers), outboundTunnel); + std::shared_ptr config; + if (m_NumInboundHops > 0) + { + std::reverse (peers.begin (), peers.end ()); + config = std::make_shared (peers); + } + auto tunnel = tunnels.CreateInboundTunnel (config, outboundTunnel); tunnel->SetTunnelPool (shared_from_this ()); + if (tunnel->IsEstablished ()) // zero hops + TunnelCreated (tunnel); } else LogPrint (eLogError, "Tunnels: Can't create inbound tunnel, no peers available"); @@ -403,8 +411,12 @@ namespace tunnel if (!outboundTunnel) outboundTunnel = tunnels.GetNextOutboundTunnel (); LogPrint (eLogDebug, "Tunnels: Re-creating destination inbound tunnel..."); - auto newTunnel = tunnels.CreateTunnel (std::make_shared(tunnel->GetPeers ()), outboundTunnel); + std::shared_ptr config; + if (m_NumInboundHops > 0) config = std::make_shared(tunnel->GetPeers ()); + auto newTunnel = tunnels.CreateInboundTunnel (config, outboundTunnel); newTunnel->SetTunnelPool (shared_from_this()); + if (newTunnel->IsEstablished ()) // zero hops + TunnelCreated (newTunnel); } void TunnelPool::CreateOutboundTunnel () @@ -418,9 +430,13 @@ namespace tunnel std::vector > peers; if (SelectPeers (peers, false)) { - auto tunnel = tunnels.CreateTunnel ( - std::make_shared (peers, inboundTunnel->GetNextTunnelID (), inboundTunnel->GetNextIdentHash ())); + std::shared_ptr config; + if (m_NumOutboundHops > 0) + config = std::make_shared(peers, inboundTunnel->GetNextTunnelID (), inboundTunnel->GetNextIdentHash ()); + auto tunnel = tunnels.CreateOutboundTunnel (config); tunnel->SetTunnelPool (shared_from_this ()); + if (tunnel->IsEstablished ()) // zero hops + TunnelCreated (tunnel); } else LogPrint (eLogError, "Tunnels: Can't create outbound tunnel, no peers available"); @@ -437,10 +453,13 @@ namespace tunnel if (inboundTunnel) { LogPrint (eLogDebug, "Tunnels: Re-creating destination outbound tunnel..."); - auto newTunnel = tunnels.CreateTunnel ( - std::make_shared (tunnel->GetPeers (), - inboundTunnel->GetNextTunnelID (), inboundTunnel->GetNextIdentHash ())); + std::shared_ptr config; + if (m_NumOutboundHops > 0) + config = std::make_shared(tunnel->GetPeers (), inboundTunnel->GetNextTunnelID (), inboundTunnel->GetNextIdentHash ()); + auto newTunnel = tunnels.CreateOutboundTunnel (config); newTunnel->SetTunnelPool (shared_from_this ()); + if (newTunnel->IsEstablished ()) // zero hops + TunnelCreated (newTunnel); } else LogPrint (eLogDebug, "Tunnels: Can't re-create outbound tunnel, no inbound tunnels found"); @@ -449,7 +468,7 @@ namespace tunnel void TunnelPool::CreatePairedInboundTunnel (std::shared_ptr outboundTunnel) { LogPrint (eLogDebug, "Tunnels: Creating paired inbound tunnel..."); - auto tunnel = tunnels.CreateTunnel (std::make_shared(outboundTunnel->GetInvertedPeers ()), outboundTunnel); + auto tunnel = tunnels.CreateInboundTunnel (std::make_shared(outboundTunnel->GetInvertedPeers ()), outboundTunnel); tunnel->SetTunnelPool (shared_from_this ()); } } diff --git a/UPnP.cpp b/UPnP.cpp index ea62998b..477342b3 100644 --- a/UPnP.cpp +++ b/UPnP.cpp @@ -6,13 +6,6 @@ #include #include -#ifdef _WIN32 -#include -#define dlsym GetProcAddress -#else -#include -#endif - #include "Log.h" #include "RouterContext.h" @@ -24,32 +17,11 @@ #include #include -// These are per-process and are safe to reuse for all threads -decltype(upnpDiscover) *upnpDiscoverFunc; -decltype(UPNP_AddPortMapping) *UPNP_AddPortMappingFunc; -decltype(UPNP_GetValidIGD) *UPNP_GetValidIGDFunc; -decltype(UPNP_GetExternalIPAddress) *UPNP_GetExternalIPAddressFunc; -decltype(UPNP_DeletePortMapping) *UPNP_DeletePortMappingFunc; -decltype(freeUPNPDevlist) *freeUPNPDevlistFunc; -decltype(FreeUPNPUrls) *FreeUPNPUrlsFunc; - -// Nice approach http://stackoverflow.com/a/21517513/673826 -template -F GetKnownProcAddressImpl(M hmod, const char *name, F) { - auto proc = reinterpret_cast(dlsym(hmod, name)); - if (!proc) { - LogPrint(eLogError, "UPnP: Error resolving ", name, " from library, version mismatch?"); - } - return proc; -} -#define GetKnownProcAddress(hmod, func) GetKnownProcAddressImpl(hmod, #func, func##Func); - - namespace i2p { namespace transport { - UPnP::UPnP () : m_Thread (nullptr) , m_IsModuleLoaded (false) + UPnP::UPnP () : m_Thread (nullptr) { } @@ -65,33 +37,6 @@ namespace transport void UPnP::Start() { - if (!m_IsModuleLoaded) { -#ifdef MAC_OSX - m_Module = dlopen ("libminiupnpc.dylib", RTLD_LAZY); -#elif _WIN32 - m_Module = LoadLibrary ("libminiupnpc.dll"); // from MSYS2 -#else - m_Module = dlopen ("libminiupnpc.so", RTLD_LAZY); -#endif - if (m_Module == NULL) - { - LogPrint (eLogError, "UPnP: Error loading UPNP library, version mismatch?"); - return; - } - else - { - upnpDiscoverFunc = GetKnownProcAddress (m_Module, upnpDiscover); - UPNP_GetValidIGDFunc = GetKnownProcAddress (m_Module, UPNP_GetValidIGD); - UPNP_GetExternalIPAddressFunc = GetKnownProcAddress (m_Module, UPNP_GetExternalIPAddress); - UPNP_AddPortMappingFunc = GetKnownProcAddress (m_Module, UPNP_AddPortMapping); - UPNP_DeletePortMappingFunc = GetKnownProcAddress (m_Module, UPNP_DeletePortMapping); - freeUPNPDevlistFunc = GetKnownProcAddress (m_Module, freeUPNPDevlist); - FreeUPNPUrlsFunc = GetKnownProcAddress (m_Module, FreeUPNPUrls); - if (upnpDiscoverFunc && UPNP_GetValidIGDFunc && UPNP_GetExternalIPAddressFunc && UPNP_AddPortMappingFunc && - UPNP_DeletePortMappingFunc && freeUPNPDevlistFunc && FreeUPNPUrlsFunc) - m_IsModuleLoaded = true; - } - } m_Thread = new std::thread (std::bind (&UPnP::Run, this)); } @@ -123,16 +68,16 @@ namespace transport { int nerror = 0; #if MINIUPNPC_API_VERSION >= 14 - m_Devlist = upnpDiscoverFunc (2000, m_MulticastIf, m_Minissdpdpath, 0, 0, 2, &nerror); + m_Devlist = upnpDiscover (2000, m_MulticastIf, m_Minissdpdpath, 0, 0, 2, &nerror); #else - m_Devlist = upnpDiscoverFunc (2000, m_MulticastIf, m_Minissdpdpath, 0, 0, &nerror); + m_Devlist = upnpDiscover (2000, m_MulticastIf, m_Minissdpdpath, 0, 0, &nerror); #endif int r; - r = UPNP_GetValidIGDFunc (m_Devlist, &m_upnpUrls, &m_upnpData, m_NetworkAddr, sizeof (m_NetworkAddr)); + r = UPNP_GetValidIGD (m_Devlist, &m_upnpUrls, &m_upnpData, m_NetworkAddr, sizeof (m_NetworkAddr)); if (r == 1) { - r = UPNP_GetExternalIPAddressFunc (m_upnpUrls.controlURL, m_upnpData.first.servicetype, m_externalIPAddress); + r = UPNP_GetExternalIPAddress (m_upnpUrls.controlURL, m_upnpData.first.servicetype, m_externalIPAddress); if(r != UPNPCOMMAND_SUCCESS) { LogPrint (eLogError, "UPnP: UPNP_GetExternalIPAddress () returned ", r); @@ -171,7 +116,7 @@ namespace transport std::string strDesc = "I2Pd"; try { for (;;) { - r = UPNP_AddPortMappingFunc (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strPort.c_str (), m_NetworkAddr, strDesc.c_str (), strType.c_str (), 0, "0"); + r = UPNP_AddPortMapping (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strPort.c_str (), m_NetworkAddr, strDesc.c_str (), strType.c_str (), 0, "0"); if (r!=UPNPCOMMAND_SUCCESS) { LogPrint (eLogError, "UPnP: AddPortMapping (", strPort.c_str () ,", ", strPort.c_str () ,", ", m_NetworkAddr, ") failed with code ", r); @@ -208,20 +153,15 @@ namespace transport strType = "UDP"; } int r = 0; - r = UPNP_DeletePortMappingFunc (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strType.c_str (), 0); + r = UPNP_DeletePortMapping (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strType.c_str (), 0); LogPrint (eLogError, "UPnP: DeletePortMapping() returned : ", r, "\n"); } void UPnP::Close () { - freeUPNPDevlistFunc (m_Devlist); + freeUPNPDevlist (m_Devlist); m_Devlist = 0; - FreeUPNPUrlsFunc (&m_upnpUrls); -#ifndef _WIN32 - dlclose (m_Module); -#else - FreeLibrary (m_Module); -#endif + FreeUPNPUrls (&m_upnpUrls); } } diff --git a/UPnP.h b/UPnP.h index 32c42118..0a000177 100644 --- a/UPnP.h +++ b/UPnP.h @@ -48,12 +48,6 @@ namespace transport struct UPNPDev * m_Devlist = 0; char m_NetworkAddr[64]; char m_externalIPAddress[40]; - bool m_IsModuleLoaded; -#ifndef _WIN32 - void *m_Module; -#else - HINSTANCE m_Module; -#endif }; } } diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt index 4a1bfe2b..3f5f599f 100644 --- a/build/CMakeLists.txt +++ b/build/CMakeLists.txt @@ -21,6 +21,7 @@ set (LIBI2PD_SRC "${CMAKE_SOURCE_DIR}/Config.cpp" "${CMAKE_SOURCE_DIR}/Crypto.cpp" "${CMAKE_SOURCE_DIR}/Garlic.cpp" + "${CMAKE_SOURCE_DIR}/Gzip.cpp" "${CMAKE_SOURCE_DIR}/I2NPProtocol.cpp" "${CMAKE_SOURCE_DIR}/Identity.cpp" "${CMAKE_SOURCE_DIR}/LeaseSet.cpp" @@ -338,6 +339,10 @@ if (WITH_BINARY) set_target_properties("${PROJECT_NAME}" PROPERTIES LINK_FLAGS "-z relro -z now" ) endif () + if (WITH_UPNP) + target_link_libraries("${PROJECT_NAME}" "miniupnpc") + endif () + # FindBoost pulls pthread for thread which is broken for static linking at least on Ubuntu 15.04 list(GET Boost_LIBRARIES -1 LAST_Boost_LIBRARIES) if(${LAST_Boost_LIBRARIES} MATCHES ".*pthread.*") diff --git a/debian/control b/debian/control index 78906ba4..ac6f5e28 100644 --- a/debian/control +++ b/debian/control @@ -8,6 +8,7 @@ Build-Depends: debhelper (>= 9.0.0), dpkg-dev (>= 1.16.1~), libboost-date-time-dev, libboost-filesystem-dev, libboost-program-options-dev, + libminiupnpc-dev, libssl-dev Standards-Version: 3.9.3 Homepage: https://github.com/PurpleI2P/i2pd diff --git a/debian/patches/0001-disable-aesni-by-default.patch b/debian/patches/01-tune-build-opts.patch similarity index 55% rename from debian/patches/0001-disable-aesni-by-default.patch rename to debian/patches/01-tune-build-opts.patch index eae44c8b..e0e24408 100644 --- a/debian/patches/0001-disable-aesni-by-default.patch +++ b/debian/patches/01-tune-build-opts.patch @@ -1,13 +1,16 @@ diff --git a/Makefile b/Makefile -index 2e86fd8..c1037af 100644 +index fe8ae7e..fc8abda 100644 --- a/Makefile +++ b/Makefile -@@ -9,7 +9,7 @@ DEPS := obj/make.dep +@@ -9,9 +9,9 @@ DEPS := obj/make.dep include filelist.mk -USE_AESNI := yes +USE_AESNI := no USE_STATIC := no +-USE_UPNP := no ++USE_UPNP := yes ifeq ($(UNAME),Darwin) + DAEMON_SRC += DaemonLinux.cpp diff --git a/debian/patches/series b/debian/patches/series index 1c9d0fbf..972d2a10 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -1 +1 @@ -0001-disable-aesni-by-default.patch +01-tune-build-opts.patch diff --git a/docs/build_notes_windows.md b/docs/build_notes_windows.md index 6290ccc2..8ba36131 100644 --- a/docs/build_notes_windows.md +++ b/docs/build_notes_windows.md @@ -172,8 +172,6 @@ pacman -S mingw-w64-i686-miniupnpc make USE_UPNP=1 ``` -It requires libminiupnpc.dll from /mingw32/bin - ### Creating Visual Studio project Start CMake GUI, navigate to i2pd directory, choose building directory, e.g. ./out, and configure options. diff --git a/docs/configuration.md b/docs/configuration.md index 9ab85b46..1b3e899b 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -41,7 +41,7 @@ All options below still possible in cmdline, but better write it in config file: * --http.pass= - Password for basic auth (default: random, see logs) * --httpproxy.address= - The address to listen on (HTTP Proxy) -* --httpproxy.port= - The port to listen on (HTTP Proxy) 4446 by default +* --httpproxy.port= - The port to listen on (HTTP Proxy) 4444 by default * --httpproxy.keys= - optional keys file for proxy local destination (both HTTP and SOCKS) * --httpproxy.enabled= - If HTTP proxy is enabled. true by default @@ -60,9 +60,9 @@ All options below still possible in cmdline, but better write it in config file: * --bob.port= - Port of BOB command channel. Usually 2827. BOB is off if not specified * --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 +* --i2cp.address= - The address to listen on or an abstract address for Android LocalSocket +* --i2cp.port= - Port of I2CP server. Usually 7654. Ignored for Andorid +* --i2cp.enabled= - If I2CP is enabled. false by default. Other services don't require 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/filelist.mk b/filelist.mk index 8abf0b4b..d8d74252 100644 --- a/filelist.mk +++ b/filelist.mk @@ -1,5 +1,5 @@ LIB_SRC = \ - Crypto.cpp Datagram.cpp Garlic.cpp I2NPProtocol.cpp LeaseSet.cpp \ + Gzip.cpp Crypto.cpp Datagram.cpp Garlic.cpp I2NPProtocol.cpp LeaseSet.cpp \ Log.cpp NTCPSession.cpp NetDb.cpp NetDbRequests.cpp Profiling.cpp \ Reseed.cpp RouterContext.cpp RouterInfo.cpp Signature.cpp SSU.cpp \ SSUSession.cpp SSUData.cpp Streaming.cpp Identity.cpp TransitTunnel.cpp \ diff --git a/qt/i2pd_qt/.gitignore b/qt/i2pd_qt/.gitignore new file mode 100644 index 00000000..35d7caf4 --- /dev/null +++ b/qt/i2pd_qt/.gitignore @@ -0,0 +1 @@ +i2pd_qt.pro.user* diff --git a/qt/i2pd_qt/android/AndroidManifest.xml b/qt/i2pd_qt/android/AndroidManifest.xml index 6ab763ff..37a736fd 100644 --- a/qt/i2pd_qt/android/AndroidManifest.xml +++ b/qt/i2pd_qt/android/AndroidManifest.xml @@ -1,5 +1,5 @@ - + diff --git a/qt/i2pd_qt/i2pd.qrc b/qt/i2pd_qt/i2pd.qrc new file mode 100644 index 00000000..2abdeb05 --- /dev/null +++ b/qt/i2pd_qt/i2pd.qrc @@ -0,0 +1,5 @@ + + + images/icon.png + + diff --git a/qt/i2pd_qt/i2pd_qt.pro b/qt/i2pd_qt/i2pd_qt.pro index c3b38a8c..db3f21c8 100644 --- a/qt/i2pd_qt/i2pd_qt.pro +++ b/qt/i2pd_qt/i2pd_qt.pro @@ -1,192 +1,128 @@ -#------------------------------------------------- -# -# Project created by QtCreator 2016-06-14T04:53:04 -# -#------------------------------------------------- - -QT += core gui +QT += core gui greaterThan(QT_MAJOR_VERSION, 4): QT += widgets TARGET = i2pd_qt TEMPLATE = app QMAKE_CXXFLAGS *= -std=c++11 +DEFINES += USE_UPNP + +# change to your own path, where you will store all needed libraries with 'git clone' commands below. +MAIN_PATH = /path/to/libraries # git clone https://github.com/PurpleI2P/Boost-for-Android-Prebuilt.git # git clone https://github.com/PurpleI2P/OpenSSL-for-Android-Prebuilt.git +# git clone https://github.com/PurpleI2P/MiniUPnP-for-Android-Prebuilt.git # git clone https://github.com/PurpleI2P/android-ifaddrs.git -# change to your own -BOOST_PATH = /mnt/media/android/Boost-for-Android-Prebuilt -OPENSSL_PATH = /mnt/media/android/OpenSSL-for-Android-Prebuilt -IFADDRS_PATH = /mnt/media/android/android-ifaddrs + +BOOST_PATH = $$MAIN_PATH/Boost-for-Android-Prebuilt +OPENSSL_PATH = $$MAIN_PATH/OpenSSL-for-Android-Prebuilt +MINIUPNP_PATH = $$MAIN_PATH/MiniUPnP-for-Android-Prebuilt +IFADDRS_PATH = $$MAIN_PATH/android-ifaddrs # Steps in Android SDK manager: # 1) Check Extras/Google Support Library https://developer.android.com/topic/libraries/support-library/setup.html # 2) Check API 11 # Finally, click Install. -SOURCES += DaemonQT.cpp\ - mainwindow.cpp \ - ../../HTTPServer.cpp ../../I2PControl.cpp ../../UPnP.cpp ../../Daemon.cpp ../../Config.cpp \ - ../../AddressBook.cpp \ - ../../api.cpp \ - ../../Base.cpp \ - ../../BOB.cpp \ - ../../ClientContext.cpp \ - ../../Crypto.cpp \ - ../../Datagram.cpp \ - ../../Destination.cpp \ - ../../Family.cpp \ - ../../FS.cpp \ - ../../Garlic.cpp \ - ../../HTTP.cpp \ - ../../HTTPProxy.cpp \ - ../../I2CP.cpp \ - ../../I2NPProtocol.cpp \ - ../../I2PEndian.cpp \ - ../../I2PService.cpp \ - ../../I2PTunnel.cpp \ - ../../Identity.cpp \ - ../../LeaseSet.cpp \ - ../../Log.cpp \ - ../../NetDb.cpp \ - ../../NetDbRequests.cpp \ - ../../NTCPSession.cpp \ - ../../Profiling.cpp \ - ../../Reseed.cpp \ - ../../RouterContext.cpp \ - ../../RouterInfo.cpp \ - ../../SAM.cpp \ - ../../Signature.cpp \ - ../../SOCKS.cpp \ - ../../SSU.cpp \ - ../../SSUData.cpp \ - ../../SSUSession.cpp \ - ../../Streaming.cpp \ - ../../TransitTunnel.cpp \ - ../../Transports.cpp \ - ../../Tunnel.cpp \ - ../../TunnelEndpoint.cpp \ - ../../TunnelGateway.cpp \ - ../../TunnelPool.cpp \ - ../../util.cpp \ - ../../i2pd.cpp \ - $$IFADDRS_PATH/ifaddrs.c +SOURCES += DaemonQT.cpp mainwindow.cpp \ + ../../HTTPServer.cpp ../../I2PControl.cpp ../../Daemon.cpp ../../Config.cpp \ + ../../AddressBook.cpp ../../api.cpp ../../Base.cpp ../../BOB.cpp ../../ClientContext.cpp \ + ../../Crypto.cpp ../../Datagram.cpp ../../Destination.cpp ../../Family.cpp ../../FS.cpp \ + ../../Garlic.cpp ../../HTTP.cpp ../../HTTPProxy.cpp ../../I2CP.cpp ../../I2NPProtocol.cpp \ + ../../I2PEndian.cpp ../../I2PService.cpp ../../I2PTunnel.cpp ../../Identity.cpp \ + ../../LeaseSet.cpp ../../Log.cpp ../../NetDb.cpp ../../NetDbRequests.cpp \ + ../../NTCPSession.cpp ../../Profiling.cpp ../../Reseed.cpp ../../RouterContext.cpp \ + ../../RouterInfo.cpp ../../SAM.cpp ../../Signature.cpp ../../SOCKS.cpp ../../SSU.cpp \ + ../../SSUData.cpp ../../SSUSession.cpp ../../Streaming.cpp ../../TransitTunnel.cpp \ + ../../Transports.cpp ../../Tunnel.cpp ../../TunnelEndpoint.cpp ../../TunnelGateway.cpp \ + ../../TunnelPool.cpp ../../UPnP.cpp ../../util.cpp ../../Gzip.cpp ../../i2pd.cpp HEADERS += DaemonQT.h mainwindow.h \ - ../../HTTPServer.h ../../I2PControl.h ../../UPnP.h ../../Daemon.h ../../Config.h \ - ../../AddressBook.h \ - ../../api.h \ - ../../Base.h \ - ../../BOB.h \ - ../../ClientContext.h \ - ../../Crypto.h \ - ../../Datagram.h \ - ../../Destination.h \ - ../../Family.h \ - ../../FS.h \ - ../../Garlic.h \ - ../../HTTP.h \ - ../../HTTPProxy.h \ - ../../I2CP.h \ - ../../I2NPProtocol.h \ - ../../I2PEndian.h \ - ../../I2PService.h \ - ../../I2PTunnel.h \ - ../../Identity.h \ - ../../LeaseSet.h \ - ../../LittleBigEndian.h \ - ../../Log.h \ - ../../NetDb.h \ - ../../NetDbRequests.h \ - ../../NTCPSession.h \ - ../../Profiling.h \ - ../../Queue.h \ - ../../Reseed.h \ - ../../RouterContext.h \ - ../../RouterInfo.h \ - ../../SAM.h \ - ../../Signature.h \ - ../../SOCKS.h \ - ../../SSU.h \ - ../../SSUData.h \ - ../../SSUSession.h \ - ../../Streaming.h \ - ../../Timestamp.h \ - ../../TransitTunnel.h \ - ../../Transports.h \ - ../../TransportSession.h \ - ../../Tunnel.h \ - ../../TunnelBase.h \ - ../../TunnelConfig.h \ - ../../TunnelEndpoint.h \ - ../../TunnelGateway.h \ - ../../TunnelPool.h \ - ../../util.h \ - ../../version.h \ - $$IFADDRS_PATH/ifaddrs.h + ../../HTTPServer.h ../../I2PControl.h ../../UPnP.h ../../Daemon.h ../../Config.h \ + ../../AddressBook.h ../../api.h ../../Base.h ../../BOB.h ../../ClientContext.h \ + ../../Crypto.h ../../Datagram.h ../../Destination.h ../../Family.h ../../FS.h \ + ../../Garlic.h ../../HTTP.h ../../HTTPProxy.h ../../I2CP.h ../../I2NPProtocol.h \ + ../../I2PEndian.h ../../I2PService.h ../../I2PTunnel.h ../../Identity.h ../../LeaseSet.h \ + ../../LittleBigEndian.h ../../Log.h ../../NetDb.h ../../NetDbRequests.h ../../NTCPSession.h \ + ../../Profiling.h ../../Queue.h ../../Reseed.h ../../RouterContext.h ../../RouterInfo.h \ + ../../SAM.h ../../Signature.h ../../SOCKS.h ../../SSU.h ../../SSUData.h ../../SSUSession.h \ + ../../Streaming.h ../../Timestamp.h ../../TransitTunnel.h ../../Transports.h \ + ../../TransportSession.h ../../Tunnel.h ../../TunnelBase.h ../../TunnelConfig.h \ + ../../TunnelEndpoint.h ../../TunnelGateway.h ../../TunnelPool.h ../../UPnP.h \ + ../../util.h ../../version.h ..//../Gzip.h ../../Tag.h -FORMS += mainwindow.ui +FORMS += mainwindow.ui CONFIG += mobility -MOBILITY = +MOBILITY = LIBS += -lz android { -message("Using Android settings") -DEFINES += ANDROID=1 -DEFINES += __ANDROID__ -INCLUDEPATH += $$BOOST_PATH/boost_1_53_0/include \ - $$OPENSSL_PATH/openssl-1.0.2/include \ - $$IFADDRS_PATH -DISTFILES += \ - android/AndroidManifest.xml + message("Using Android settings") + DEFINES += ANDROID=1 + DEFINES += __ANDROID__ -ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android + INCLUDEPATH += $$BOOST_PATH/boost_1_53_0/include \ + $$OPENSSL_PATH/openssl-1.0.2/include \ + $$MINIUPNP_PATH/miniupnp-2.0/include \ + $$IFADDRS_PATH + DISTFILES += android/AndroidManifest.xml -equals(ANDROID_TARGET_ARCH, armeabi-v7a){ + ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android -DEFINES += ANDROID_ARM7A + SOURCES += $$IFADDRS_PATH/ifaddrs.c + HEADERS += $$IFADDRS_PATH/ifaddrs.h -# http://stackoverflow.com/a/30235934/529442 -LIBS += -L$$BOOST_PATH/boost_1_53_0/armeabi-v7a/lib \ --lboost_system-gcc-mt-1_53 \ --lboost_date_time-gcc-mt-1_53 \ --lboost_filesystem-gcc-mt-1_53 \ --lboost_program_options-gcc-mt-1_53 \ --L$$OPENSSL_PATH/openssl-1.0.2/armeabi-v7a/lib/ -lcrypto -lssl + equals(ANDROID_TARGET_ARCH, armeabi-v7a){ + DEFINES += ANDROID_ARM7A + # http://stackoverflow.com/a/30235934/529442 + LIBS += -L$$BOOST_PATH/boost_1_53_0/armeabi-v7a/lib \ + -lboost_system-gcc-mt-1_53 -lboost_date_time-gcc-mt-1_53 \ + -lboost_filesystem-gcc-mt-1_53 -lboost_program_options-gcc-mt-1_53 \ + -L$$OPENSSL_PATH/openssl-1.0.2/armeabi-v7a/lib/ -lcrypto -lssl \ + -L$$MINIUPNP_PATH/miniupnp-2.0/armeabi-v7a/lib/ -lminiupnpc -PRE_TARGETDEPS += $$OPENSSL_PATH/openssl-1.0.2/armeabi-v7a/lib/libcrypto.a \ - $$OPENSSL_PATH/openssl-1.0.2/armeabi-v7a/lib/libssl.a + PRE_TARGETDEPS += $$OPENSSL_PATH/openssl-1.0.2/armeabi-v7a/lib/libcrypto.a \ + $$OPENSSL_PATH/openssl-1.0.2/armeabi-v7a/lib/libssl.a + DEPENDPATH += $$OPENSSL_PATH/openssl-1.0.2/include -DEPENDPATH += $$OPENSSL_PATH/openssl-1.0.2/include + ANDROID_EXTRA_LIBS += $$OPENSSL_PATH/openssl-1.0.2/armeabi-v7a/lib/libcrypto_1_0_0.so \ + $$OPENSSL_PATH/openssl-1.0.2/armeabi-v7a/lib/libssl_1_0_0.so \ + $$MINIUPNP_PATH/miniupnp-2.0/armeabi-v7a/lib/libminiupnpc.so + } -ANDROID_EXTRA_LIBS += $$OPENSSL_PATH/openssl-1.0.2/armeabi-v7a/lib/libcrypto_1_0_0.so \ - $$OPENSSL_PATH/openssl-1.0.2/armeabi-v7a/lib/libssl_1_0_0.so -} -equals(ANDROID_TARGET_ARCH, x86){ -# http://stackoverflow.com/a/30235934/529442 -LIBS += -L$$BOOST_PATH/boost_1_53_0/x86/lib \ --lboost_system-gcc-mt-1_53 \ --lboost_date_time-gcc-mt-1_53 \ --lboost_filesystem-gcc-mt-1_53 \ --lboost_program_options-gcc-mt-1_53 \ --L$$OPENSSL_PATH/openssl-1.0.2/x86/lib/ -lcrypto -lssl + equals(ANDROID_TARGET_ARCH, x86){ + # http://stackoverflow.com/a/30235934/529442 + LIBS += -L$$BOOST_PATH/boost_1_53_0/x86/lib \ + -lboost_system-gcc-mt-1_53 -lboost_date_time-gcc-mt-1_53 \ + -lboost_filesystem-gcc-mt-1_53 -lboost_program_options-gcc-mt-1_53 \ + -L$$OPENSSL_PATH/openssl-1.0.2/x86/lib/ -lcrypto -lssl \ + -L$$MINIUPNP_PATH/miniupnp-2.0/x86/lib/ -lminiupnpc -PRE_TARGETDEPS += $$OPENSSL_PATH/openssl-1.0.2/x86/lib/libcrypto.a \ - $$OPENSSL_PATH/openssl-1.0.2/x86/lib/libssl.a + PRE_TARGETDEPS += $$OPENSSL_PATH/openssl-1.0.2/x86/lib/libcrypto.a \ + $$OPENSSL_PATH/openssl-1.0.2/x86/lib/libssl.a -DEPENDPATH += $$OPENSSL_PATH/openssl-1.0.2/include + DEPENDPATH += $$OPENSSL_PATH/openssl-1.0.2/include -ANDROID_EXTRA_LIBS += $$OPENSSL_PATH/openssl-1.0.2/x86/lib/libcrypto_1_0_0.so \ - $$OPENSSL_PATH/openssl-1.0.2/x86/lib/libssl_1_0_0.so -} + ANDROID_EXTRA_LIBS += $$OPENSSL_PATH/openssl-1.0.2/x86/lib/libcrypto_1_0_0.so \ + $$OPENSSL_PATH/openssl-1.0.2/x86/lib/libssl_1_0_0.so \ + $$MINIUPNP_PATH/miniupnp-2.0/x86/lib/libminiupnpc.so + } } linux:!android { -message("Using Linux settings") -LIBS += -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread + message("Using Linux settings") + LIBS += -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread -lminiupnpc } +!android:!symbian:!maemo5:!simulator { + message("Build with a system tray icon") + # see also http://doc.qt.io/qt-4.8/qt-desktop-systray-systray-pro.html for example on wince* + #sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS i2pd_qt.pro resources images + RESOURCES = i2pd.qrc + QT += xml + #INSTALLS += sources +} diff --git a/qt/i2pd_qt/images/icon.png b/qt/i2pd_qt/images/icon.png new file mode 100644 index 00000000..a5dc7b68 Binary files /dev/null and b/qt/i2pd_qt/images/icon.png differ diff --git a/qt/i2pd_qt/mainwindow.cpp b/qt/i2pd_qt/mainwindow.cpp index c1654295..0e2ca01c 100644 --- a/qt/i2pd_qt/mainwindow.cpp +++ b/qt/i2pd_qt/mainwindow.cpp @@ -1,10 +1,19 @@ #include "mainwindow.h" //#include "ui_mainwindow.h" #include +#include +#include "../../RouterContext.h" +#ifndef ANDROID +#include +#endif MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)/*, ui(new Ui::MainWindow)*/ +#ifndef ANDROID + , + quitting(false) +#endif { //ui->setupUi(this); if (objectName().isEmpty()) @@ -22,30 +31,132 @@ MainWindow::MainWindow(QWidget *parent) : verticalLayout1->setContentsMargins(0, 0, 0, 0); quitButton = new QPushButton(verticalLayoutWidget); quitButton->setObjectName(QStringLiteral("quitButton")); - QSizePolicy sizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); - sizePolicy.setHorizontalStretch(0); - sizePolicy.setVerticalStretch(0); + QSizePolicy sizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); + sizePolicy.setHorizontalStretch(1); + //sizePolicy.setVerticalStretch(1); sizePolicy.setHeightForWidth(quitButton->sizePolicy().hasHeightForWidth()); quitButton->setSizePolicy(sizePolicy); - verticalLayout1->addWidget(quitButton); + gracefulQuitButton = new QPushButton(verticalLayoutWidget); + gracefulQuitButton->setObjectName(QStringLiteral("gracefulQuitButton")); + QSizePolicy sizePolicy2(QSizePolicy::Maximum, QSizePolicy::Maximum); + sizePolicy2.setHorizontalStretch(1); + //sizePolicy2.setVerticalStretch(1); + sizePolicy2.setHeightForWidth(gracefulQuitButton->sizePolicy().hasHeightForWidth()); + gracefulQuitButton->setSizePolicy(sizePolicy2); + verticalLayout1->addWidget(gracefulQuitButton); setCentralWidget(centralWidget); - setWindowTitle(QApplication::translate("MainWindow", "MainWindow", 0)); + setWindowTitle(QApplication::translate("MainWindow", "i2pd", 0)); quitButton->setText(QApplication::translate("MainWindow", "Quit", 0)); + gracefulQuitButton->setText(QApplication::translate("MainWindow", "Graceful Quit", 0)); + +#ifndef ANDROID + createActions(); + createTrayIcon(); +#endif QObject::connect(quitButton, SIGNAL(released()), this, SLOT(handleQuitButton())); + QObject::connect(gracefulQuitButton, SIGNAL(released()), this, SLOT(handleGracefulQuitButton())); + +#ifndef ANDROID + QObject::connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), + this, SLOT(iconActivated(QSystemTrayIcon::ActivationReason))); + + setIcon(); + trayIcon->show(); +#endif //QMetaObject::connectSlotsByName(this); } +#ifndef ANDROID +void MainWindow::createActions() { + toggleWindowVisibleAction = new QAction(tr("&Toggle the window"), this); + connect(toggleWindowVisibleAction, SIGNAL(triggered()), this, SLOT(toggleVisibilitySlot())); + + //quitAction = new QAction(tr("&Quit"), this); + //connect(quitAction, SIGNAL(triggered()), QApplication::instance(), SLOT(quit())); +} + +void MainWindow::toggleVisibilitySlot() { + setVisible(!isVisible()); +} + +void MainWindow::createTrayIcon() { + trayIconMenu = new QMenu(this); + trayIconMenu->addAction(toggleWindowVisibleAction); + //trayIconMenu->addSeparator(); + //trayIconMenu->addAction(quitAction); + + trayIcon = new QSystemTrayIcon(this); + trayIcon->setContextMenu(trayIconMenu); +} + +void MainWindow::setIcon() { + QIcon icon(":/images/icon.png"); + trayIcon->setIcon(icon); + setWindowIcon(icon); + + trayIcon->setToolTip(QApplication::translate("MainWindow", "i2pd", 0)); +} + +void MainWindow::iconActivated(QSystemTrayIcon::ActivationReason reason) { + switch (reason) { + case QSystemTrayIcon::Trigger: + case QSystemTrayIcon::DoubleClick: + case QSystemTrayIcon::MiddleClick: + setVisible(!isVisible()); + break; + default: + qDebug() << "MainWindow::iconActivated(): unknown reason: " << reason << endl; + break; + } +} + +void MainWindow::closeEvent(QCloseEvent *event) { + if(quitting){ QMainWindow::closeEvent(event); return; } + if (trayIcon->isVisible()) { + QMessageBox::information(this, tr("i2pd"), + tr("The program will keep running in the " + "system tray. To gracefully terminate the program, " + "choose Graceful Quit at the main i2pd window.")); + hide(); + event->ignore(); + } +} +#endif + void MainWindow::handleQuitButton() { qDebug("Quit pressed. Hiding the main window"); +#ifndef ANDROID + quitting=true; +#endif close(); QApplication::instance()->quit(); } +void MainWindow::handleGracefulQuitButton() { + qDebug("Graceful Quit pressed."); + gracefulQuitButton->setText(QApplication::translate("MainWindow", "Graceful quit is in progress", 0)); + gracefulQuitButton->setEnabled(false); + gracefulQuitButton->adjustSize(); + verticalLayoutWidget->adjustSize(); + i2p::context.SetAcceptsTunnels (false); // stop accpting tunnels + QTimer::singleShot(10*60*1000/*millis*/, this, SLOT(handleGracefulQuitTimerEvent())); +} + +void MainWindow::handleGracefulQuitTimerEvent() { + qDebug("Hiding the main window"); +#ifndef ANDROID + quitting=true; +#endif + close(); + qDebug("Performing quit"); + QApplication::instance()->quit(); +} + MainWindow::~MainWindow() { qDebug("Destroying main window"); diff --git a/qt/i2pd_qt/mainwindow.h b/qt/i2pd_qt/mainwindow.h index 3a172c25..349eadec 100644 --- a/qt/i2pd_qt/mainwindow.h +++ b/qt/i2pd_qt/mainwindow.h @@ -12,6 +12,11 @@ #include #include #include +#ifndef ANDROID +#include +#include +#include +#endif namespace Ui { class MainWindow; @@ -25,14 +30,43 @@ public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); +//#ifndef ANDROID +// void setVisible(bool visible); +//#endif + private slots: void handleQuitButton(); + void handleGracefulQuitButton(); + void handleGracefulQuitTimerEvent(); +#ifndef ANDROID + void setIcon(); + void iconActivated(QSystemTrayIcon::ActivationReason reason); + void toggleVisibilitySlot(); +#endif private: +#ifndef ANDROID + void createActions(); + void createTrayIcon(); +#endif + QWidget *centralWidget; QWidget *verticalLayoutWidget; QVBoxLayout *verticalLayout1; QPushButton *quitButton; + QPushButton *gracefulQuitButton; + +#ifndef ANDROID + bool quitting; + QAction *toggleWindowVisibleAction; + QSystemTrayIcon *trayIcon; + QMenu *trayIconMenu; +#endif + +protected: +#ifndef ANDROID + void closeEvent(QCloseEvent *event); +#endif }; #endif // MAINWINDOW_H diff --git a/qt/i2pd_qt/mainwindow.ui b/qt/i2pd_qt/mainwindow.ui index bdb57867..d73e7743 100644 --- a/qt/i2pd_qt/mainwindow.ui +++ b/qt/i2pd_qt/mainwindow.ui @@ -37,6 +37,13 @@ + + + + Graceful Quit + + + @@ -60,8 +67,25 @@ + + gracefulShutdownButton + released() + MainWindow + handleGracefulQuitButton() + + + 395 + 319 + + + 399 + 239 + + + handleQuitButton() + handleGracefulQuitButton() diff --git a/tests/Makefile b/tests/Makefile index 957d4632..ef30c631 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -1,11 +1,14 @@ 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 \ - test-http-merge_chunked + test-http-merge_chunked test-base-64 all: $(TESTS) run -test-http-%: test-http-%.cpp ../HTTP.cpp +test-http-%: ../HTTP.cpp test-http-%.cpp + $(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) -o $@ $^ + +test-base-%: ../Base.cpp test-base-%.cpp $(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) -o $@ $^ run: $(TESTS) diff --git a/tests/test-base-64.cpp b/tests/test-base-64.cpp new file mode 100644 index 00000000..d8d0ce2f --- /dev/null +++ b/tests/test-base-64.cpp @@ -0,0 +1,45 @@ +#include +#include + +#include "../Base.h" + +using namespace i2p::data; + +int main() { + const char *in = "test"; + size_t in_len = strlen(in); + char out[16]; + + /* bytes -> b64 */ + assert(ByteStreamToBase64(NULL, 0, NULL, 0) == 0); + assert(ByteStreamToBase64(NULL, 0, out, sizeof(out)) == 0); + + assert(Base64EncodingBufferSize(2) == 4); + assert(Base64EncodingBufferSize(4) == 8); + assert(Base64EncodingBufferSize(6) == 8); + assert(Base64EncodingBufferSize(7) == 12); + assert(Base64EncodingBufferSize(9) == 12); + assert(Base64EncodingBufferSize(10) == 16); + assert(Base64EncodingBufferSize(12) == 16); + assert(Base64EncodingBufferSize(13) == 20); + + assert(ByteStreamToBase64((uint8_t *) in, in_len, out, sizeof(out)) == 8); + assert(memcmp(out, "dGVzdA==", 8) == 0); + + /* b64 -> bytes */ + assert(Base64ToByteStream(NULL, 0, NULL, 0) == 0); + assert(Base64ToByteStream(NULL, 0, (uint8_t *) out, sizeof(out)) == 0); + + in = "dGVzdA=="; /* valid b64 */ + assert(Base64ToByteStream(in, strlen(in), (uint8_t *) out, sizeof(out)) == 4); + assert(memcmp(out, "test", 4) == 0); + + in = "dGVzdA="; /* invalid b64 : not padded */ + assert(Base64ToByteStream(in, strlen(in), (uint8_t *) out, sizeof(out)) == 0); + + in = "dG/z.A=="; /* invalid b64 : char not from alphabet */ +// assert(Base64ToByteStream(in, strlen(in), (uint8_t *) out, sizeof(out)) == 0); +// ^^^ fails, current implementation not checks acceptable symbols + + return 0; +} diff --git a/tests/test-http-req.cpp b/tests/test-http-req.cpp index d5362622..c857ca24 100644 --- a/tests/test-http-req.cpp +++ b/tests/test-http-req.cpp @@ -22,7 +22,6 @@ int main() { assert(req->version == "HTTP/1.0"); assert(req->method == "GET"); assert(req->uri == "/"); - assert(req->host == "inr.i2p"); assert(req->headers.size() == 3); assert(req->headers.count("Host") == 1); assert(req->headers.count("Accept") == 1); @@ -42,7 +41,6 @@ int main() { assert(req->version == "HTTP/1.0"); assert(req->method == "GET"); assert(req->uri == "/"); - assert(req->host == ""); assert(req->headers.size() == 0); delete req; @@ -52,7 +50,7 @@ int main() { "\r\n"; len = strlen(buf); req = new HTTPReq; - assert((ret = req->parse(buf, len)) == -1); /* no host header */ + assert((ret = req->parse(buf, len)) > 0); delete req; /* test: parsing incomplete request */ @@ -76,7 +74,6 @@ int main() { assert((ret = req->parse(buf, len)) == len); /* no host header */ assert(req->method == "GET"); assert(req->uri == "http://inr.i2p"); - assert(req->host == "stats.i2p"); assert(req->headers.size() == 3); assert(req->headers.count("Host") == 1); assert(req->headers.count("Accept") == 1); diff --git a/tests/test-http-res.cpp b/tests/test-http-res.cpp index 7dd74e1e..896a4403 100644 --- a/tests/test-http-res.cpp +++ b/tests/test-http-res.cpp @@ -29,7 +29,7 @@ int main() { assert(res->headers.find("Server")->second == "nginx/1.2.1"); assert(res->headers.find("Content-Length")->second == "536"); assert(res->is_chunked() == false); - assert(res->length() == 536); + assert(res->content_length() == 536); delete res; /* test: building request */