diff --git a/Base.cpp b/Base.cpp index 766eaab9..35aae437 100644 --- a/Base.cpp +++ b/Base.cpp @@ -1,4 +1,6 @@ #include +#include + #include "Base.h" namespace i2p @@ -283,99 +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; - } - 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) - { - inflateEnd (&m_Inflator); - s.setstate(std::ios_base::failbit); - break; - } - 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 */ - 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/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/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/HTTPServer.cpp b/HTTPServer.cpp index be3b9b58..9eed80d9 100644 --- a/HTTPServer.cpp +++ b/HTTPServer.cpp @@ -58,6 +58,7 @@ 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" "\r\n"; const char HTTP_PAGE_TUNNELS[] = "tunnels"; @@ -205,6 +206,7 @@ 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 << "Router Ident: " << i2p::context.GetRouterInfo().GetIdentHashBase64()<< "
\r\n"; s << "Our external address:" << "
\r\n" ; for (auto address : i2p::context.GetRouterInfo().GetAddresses()) { 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/NTCPSession.cpp b/NTCPSession.cpp index 8dc200b1..953c0707 100644 --- a/NTCPSession.cpp +++ b/NTCPSession.cpp @@ -1,6 +1,6 @@ #include #include -#include + #include "I2PEndian.h" #include "Base.h" #include "Crypto.h" diff --git a/NetDb.cpp b/NetDb.cpp index e94cc97d..1ecf646e 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; } 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/Streaming.cpp b/Streaming.cpp index e5260556..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; @@ -451,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; } @@ -492,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 (); @@ -514,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); }; } @@ -546,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) @@ -600,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; } } @@ -625,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; } @@ -649,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); } @@ -668,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; @@ -703,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: ; } @@ -725,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; @@ -828,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; } } @@ -844,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); @@ -863,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/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/build/CMakeLists.txt b/build/CMakeLists.txt index 61e05e83..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" 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/i2pd_qt.pro b/qt/i2pd_qt/i2pd_qt.pro index 07e71839..db3f21c8 100644 --- a/qt/i2pd_qt/i2pd_qt.pro +++ b/qt/i2pd_qt/i2pd_qt.pro @@ -36,7 +36,7 @@ SOURCES += DaemonQT.cpp mainwindow.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 ../../i2pd.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 \ @@ -50,7 +50,7 @@ HEADERS += DaemonQT.h mainwindow.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 + ../../util.h ../../version.h ..//../Gzip.h ../../Tag.h FORMS += mainwindow.ui 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-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 */