Browse Source

Merge pull request #493 from PurpleI2P/openssl

recent changes
pull/580/head
orignal 9 years ago
parent
commit
0291cc2ef4
  1. 26
      ClientContext.cpp
  2. 10
      ClientContext.h
  3. 3
      Config.cpp
  4. 2
      Crypto.cpp
  5. 2
      Crypto.h
  6. 73
      CryptoConst.cpp
  7. 38
      CryptoConst.h
  8. 5
      Daemon.cpp
  9. 3
      Datagram.cpp
  10. 29
      Destination.cpp
  11. 2
      Family.cpp
  12. 3
      Garlic.cpp
  13. 389
      HTTP.cpp
  14. 121
      HTTP.h
  15. 2
      HTTPProxy.cpp
  16. 876
      HTTPServer.cpp
  17. 120
      HTTPServer.h
  18. 112
      I2CP.cpp
  19. 68
      I2CP.h
  20. 2
      I2NPProtocol.cpp
  21. 2
      I2NPProtocol.h
  22. 1
      I2PControl.cpp
  23. 2
      I2PControl.h
  24. 3
      I2PService.h
  25. 3
      Identity.cpp
  26. 6
      Makefile
  27. 29
      Makefile.homebrew
  28. 2
      Makefile.linux
  29. 4
      NTCPSession.cpp
  30. 2
      NetDb.cpp
  31. 5
      Reseed.cpp
  32. 5
      RouterContext.cpp
  33. 1
      RouterContext.h
  34. 8
      RouterInfo.cpp
  35. 1
      RouterInfo.h
  36. 35
      SSUSession.cpp
  37. 2
      Signature.h
  38. 2
      Streaming.cpp
  39. 1
      Transports.cpp
  40. 2
      Tunnel.cpp
  41. 1
      TunnelConfig.h
  42. 2
      TunnelEndpoint.cpp
  43. 3
      TunnelGateway.cpp
  44. 1
      TunnelPool.cpp
  45. BIN
      Win32/Anke_2200px.jpg
  46. BIN
      Win32/Anke_700px.bmp
  47. 2
      Win32/Resource.rc
  48. 13
      Win32/Win32App.cpp
  49. BIN
      Win32/anke.ico
  50. 1
      Win32/resource.h
  51. 2
      build/CMakeLists.txt
  52. 12
      contrib/certificates/family/volatile.crt
  53. 15
      docs/build_notes_unix.md
  54. 3
      docs/configuration.md
  55. 2
      filelist.mk
  56. 14
      tests/Makefile
  57. 82
      tests/test-http-req.cpp
  58. 37
      tests/test-http-res.cpp
  59. 110
      tests/test-http-url.cpp
  60. 19
      tests/test-http-url_decode.cpp
  61. 14
      util.h

26
ClientContext.cpp

@ -16,7 +16,7 @@ namespace client @@ -16,7 +16,7 @@ namespace client
ClientContext::ClientContext (): m_SharedLocalDestination (nullptr),
m_HttpProxy (nullptr), m_SocksProxy (nullptr), m_SamBridge (nullptr),
m_BOBCommandChannel (nullptr)
m_BOBCommandChannel (nullptr), m_I2CPServer (nullptr)
{
}
@ -26,6 +26,7 @@ namespace client @@ -26,6 +26,7 @@ namespace client
delete m_SocksProxy;
delete m_SamBridge;
delete m_BOBCommandChannel;
delete m_I2CPServer;
}
void ClientContext::Start ()
@ -171,6 +172,11 @@ namespace client @@ -171,6 +172,11 @@ namespace client
m_Destinations.clear ();
m_SharedLocalDestination = nullptr;
}
void ClientContext::ReloadConfig ()
{
ReadTunnels (); // TODO: it reads new tunnels only, should be implemented better
}
void ClientContext::LoadPrivateKeys (i2p::data::PrivateKeys& keys, const std::string& filename, i2p::data::SigningKeyType sigType)
{
@ -328,11 +334,14 @@ namespace client @@ -328,11 +334,14 @@ namespace client
localDestination = CreateNewLocalDestination (k, false, &options);
}
auto clientTunnel = new I2PClientTunnel (name, dest, address, port, localDestination, destinationPort);
if (m_ClientTunnels.insert (std::make_pair (port, std::unique_ptr<I2PClientTunnel>(clientTunnel))).second)
if (m_ClientTunnels.insert (std::make_pair (clientTunnel->GetAcceptor ().local_endpoint (),
std::unique_ptr<I2PClientTunnel>(clientTunnel))).second)
{
clientTunnel->Start ();
numClientTunnels++;
}
else
LogPrint (eLogError, "Clients: I2P client tunnel with port ", port, " already exists");
numClientTunnels++;
LogPrint (eLogError, "Clients: I2P client tunnel for endpoint ", clientTunnel->GetAcceptor ().local_endpoint (), " already exists");
}
else if (type == I2P_TUNNELS_SECTION_TYPE_SERVER || type == I2P_TUNNELS_SECTION_TYPE_HTTP || type == I2P_TUNNELS_SECTION_TYPE_IRC)
{
@ -382,12 +391,15 @@ namespace client @@ -382,12 +391,15 @@ namespace client
serverTunnel->SetAccessList (idents);
}
if (m_ServerTunnels.insert (std::make_pair (
std::make_tuple (localDestination->GetIdentHash (), inPort),
std::make_pair (localDestination->GetIdentHash (), inPort),
std::unique_ptr<I2PServerTunnel>(serverTunnel))).second)
{
serverTunnel->Start ();
numServerTunnels++;
}
else
LogPrint (eLogError, "Clients: I2P server tunnel for destination ", m_AddressBook.ToAddress(localDestination->GetIdentHash ()), " already exists");
numServerTunnels++;
LogPrint (eLogError, "Clients: I2P server tunnel for destination/port ", m_AddressBook.ToAddress(localDestination->GetIdentHash ()), "/", inPort, " already exists");
}
else
LogPrint (eLogWarning, "Clients: Unknown section type=", type, " of ", name, " in ", tunConf);

10
ClientContext.h

@ -2,15 +2,16 @@ @@ -2,15 +2,16 @@
#define CLIENT_CONTEXT_H__
#include <map>
#include <tuple>
#include <mutex>
#include <memory>
#include <boost/asio.hpp>
#include "Destination.h"
#include "HTTPProxy.h"
#include "SOCKS.h"
#include "I2PTunnel.h"
#include "SAM.h"
#include "BOB.h"
#include "I2CP.h"
#include "AddressBook.h"
namespace i2p
@ -48,6 +49,8 @@ namespace client @@ -48,6 +49,8 @@ namespace client
void Start ();
void Stop ();
void ReloadConfig ();
std::shared_ptr<ClientDestination> GetSharedLocalDestination () const { return m_SharedLocalDestination; };
std::shared_ptr<ClientDestination> CreateNewLocalDestination (bool isPublic = false, i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_DSA_SHA1,
const std::map<std::string, std::string> * params = nullptr); // transient
@ -78,10 +81,11 @@ namespace client @@ -78,10 +81,11 @@ namespace client
i2p::proxy::HTTPProxy * m_HttpProxy;
i2p::proxy::SOCKSProxy * m_SocksProxy;
std::map<int, std::unique_ptr<I2PClientTunnel> > m_ClientTunnels; // port->tunnel
std::map<std::tuple<i2p::data::IdentHash, int>, std::unique_ptr<I2PServerTunnel> > m_ServerTunnels; // <destination,port>->tunnel
std::map<boost::asio::ip::tcp::endpoint, std::unique_ptr<I2PClientTunnel> > m_ClientTunnels; // local endpoint->tunnel
std::map<std::pair<i2p::data::IdentHash, int>, std::unique_ptr<I2PServerTunnel> > m_ServerTunnels; // <destination,port>->tunnel
SAMBridge * m_SamBridge;
BOBCommandChannel * m_BOBCommandChannel;
I2CPServer * m_I2CPServer;
public:
// for HTTP

3
Config.cpp

@ -141,6 +141,9 @@ namespace config { @@ -141,6 +141,9 @@ namespace config {
("http.enabled", value<bool>()->default_value(true), "Enable or disable webconsole")
("http.address", value<std::string>()->default_value("127.0.0.1"), "Webconsole listen address")
("http.port", value<uint16_t>()->default_value(7070), "Webconsole listen port")
("http.auth", value<bool>()->default_value(false), "Enable Basic HTTP auth for webconsole")
("http.user", value<std::string>()->default_value("i2pd"), "Username for basic auth")
("http.pass", value<std::string>()->default_value(""), "Password for basic auth (default: random, see logs)")
;
options_description httpproxy("HTTP Proxy options");

2
Crypto.cpp

@ -3,10 +3,8 @@ @@ -3,10 +3,8 @@
#include <vector>
#include <mutex>
#include <memory>
#include <openssl/sha.h>
#include <openssl/dh.h>
#include <openssl/md5.h>
#include <openssl/rand.h>
#include <openssl/crypto.h>
#include "TunnelBase.h"
#include <openssl/ssl.h>

2
Crypto.h

@ -7,6 +7,8 @@ @@ -7,6 +7,8 @@
#include <openssl/dh.h>
#include <openssl/aes.h>
#include <openssl/dsa.h>
#include <openssl/sha.h>
#include <openssl/rand.h>
#include "Base.h"
namespace i2p

73
CryptoConst.cpp

@ -1,73 +0,0 @@ @@ -1,73 +0,0 @@
#include <inttypes.h>
#include "CryptoConst.h"
namespace i2p
{
namespace crypto
{
const uint8_t elgp_[256]=
{
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F,
0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18,
0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
const uint8_t dsap_[128]=
{
0x9c, 0x05, 0xb2, 0xaa, 0x96, 0x0d, 0x9b, 0x97, 0xb8, 0x93, 0x19, 0x63, 0xc9, 0xcc, 0x9e, 0x8c,
0x30, 0x26, 0xe9, 0xb8, 0xed, 0x92, 0xfa, 0xd0, 0xa6, 0x9c, 0xc8, 0x86, 0xd5, 0xbf, 0x80, 0x15,
0xfc, 0xad, 0xae, 0x31, 0xa0, 0xad, 0x18, 0xfa, 0xb3, 0xf0, 0x1b, 0x00, 0xa3, 0x58, 0xde, 0x23,
0x76, 0x55, 0xc4, 0x96, 0x4a, 0xfa, 0xa2, 0xb3, 0x37, 0xe9, 0x6a, 0xd3, 0x16, 0xb9, 0xfb, 0x1c,
0xc5, 0x64, 0xb5, 0xae, 0xc5, 0xb6, 0x9a, 0x9f, 0xf6, 0xc3, 0xe4, 0x54, 0x87, 0x07, 0xfe, 0xf8,
0x50, 0x3d, 0x91, 0xdd, 0x86, 0x02, 0xe8, 0x67, 0xe6, 0xd3, 0x5d, 0x22, 0x35, 0xc1, 0x86, 0x9c,
0xe2, 0x47, 0x9c, 0x3b, 0x9d, 0x54, 0x01, 0xde, 0x04, 0xe0, 0x72, 0x7f, 0xb3, 0x3d, 0x65, 0x11,
0x28, 0x5d, 0x4c, 0xf2, 0x95, 0x38, 0xd9, 0xe3, 0xb6, 0x05, 0x1f, 0x5b, 0x22, 0xcc, 0x1c, 0x93
};
const uint8_t dsaq_[20]=
{
0xa5, 0xdf, 0xc2, 0x8f, 0xef, 0x4c, 0xa1, 0xe2, 0x86, 0x74, 0x4c, 0xd8, 0xee, 0xd9, 0xd2, 0x9d,
0x68, 0x40, 0x46, 0xb7
};
const uint8_t dsag_[128]=
{
0x0c, 0x1f, 0x4d, 0x27, 0xd4, 0x00, 0x93, 0xb4, 0x29, 0xe9, 0x62, 0xd7, 0x22, 0x38, 0x24, 0xe0,
0xbb, 0xc4, 0x7e, 0x7c, 0x83, 0x2a, 0x39, 0x23, 0x6f, 0xc6, 0x83, 0xaf, 0x84, 0x88, 0x95, 0x81,
0x07, 0x5f, 0xf9, 0x08, 0x2e, 0xd3, 0x23, 0x53, 0xd4, 0x37, 0x4d, 0x73, 0x01, 0xcd, 0xa1, 0xd2,
0x3c, 0x43, 0x1f, 0x46, 0x98, 0x59, 0x9d, 0xda, 0x02, 0x45, 0x18, 0x24, 0xff, 0x36, 0x97, 0x52,
0x59, 0x36, 0x47, 0xcc, 0x3d, 0xdc, 0x19, 0x7d, 0xe9, 0x85, 0xe4, 0x3d, 0x13, 0x6c, 0xdc, 0xfc,
0x6b, 0xd5, 0x40, 0x9c, 0xd2, 0xf4, 0x50, 0x82, 0x11, 0x42, 0xa5, 0xe6, 0xf8, 0xeb, 0x1c, 0x3a,
0xb5, 0xd0, 0x48, 0x4b, 0x81, 0x29, 0xfc, 0xf1, 0x7b, 0xce, 0x4f, 0x7f, 0x33, 0x32, 0x1c, 0x3c,
0xb3, 0xdb, 0xb1, 0x4a, 0x90, 0x5e, 0x7b, 0x2b, 0x3e, 0x93, 0xbe, 0x47, 0x08, 0xcb, 0xcc, 0x82
};
const CryptoConstants& GetCryptoConstants ()
{
static CryptoConstants cryptoConstants =
{
{elgp_, 256}, // elgp
{2}, // elgg
{dsap_, 128}, // dsap
{dsaq_, 20}, // dsaq
{dsag_, 128} // dsag
};
return cryptoConstants;
}
}
}

38
CryptoConst.h

@ -1,38 +0,0 @@ @@ -1,38 +0,0 @@
#ifndef CRYPTO_CONST_H__
#define CRYPTO_CONST_H__
#include <cryptopp/integer.h>
namespace i2p
{
namespace crypto
{
struct CryptoConstants
{
// DH/ElGamal
const CryptoPP::Integer elgp;
const CryptoPP::Integer elgg;
// DSA
const CryptoPP::Integer dsap;
const CryptoPP::Integer dsaq;
const CryptoPP::Integer dsag;
};
const CryptoConstants& GetCryptoConstants ();
// DH/ElGamal
#define elgp GetCryptoConstants ().elgp
#define elgg GetCryptoConstants ().elgg
// DSA
#define dsap GetCryptoConstants ().dsap
#define dsaq GetCryptoConstants ().dsaq
#define dsag GetCryptoConstants ().dsag
// RSA
const int rsae = 65537;
}
}
#endif

5
Daemon.cpp

@ -13,6 +13,7 @@ @@ -13,6 +13,7 @@
#include "RouterInfo.h"
#include "RouterContext.h"
#include "Tunnel.h"
#include "HTTP.h"
#include "NetDb.h"
#include "Garlic.h"
#include "Streaming.h"
@ -36,7 +37,7 @@ namespace i2p @@ -36,7 +37,7 @@ namespace i2p
Daemon_Singleton_Private() {};
~Daemon_Singleton_Private() {};
std::unique_ptr<i2p::util::HTTPServer> httpServer;
std::unique_ptr<i2p::http::HTTPServer> httpServer;
std::unique_ptr<i2p::client::I2PControlService> m_I2PControlService;
#ifdef USE_UPNP
@ -202,7 +203,7 @@ namespace i2p @@ -202,7 +203,7 @@ namespace i2p
std::string httpAddr; i2p::config::GetOption("http.address", httpAddr);
uint16_t httpPort; i2p::config::GetOption("http.port", httpPort);
LogPrint(eLogInfo, "Daemon: starting HTTP Server at ", httpAddr, ":", httpPort);
d.httpServer = std::unique_ptr<i2p::util::HTTPServer>(new i2p::util::HTTPServer(httpAddr, httpPort));
d.httpServer = std::unique_ptr<i2p::http::HTTPServer>(new i2p::http::HTTPServer(httpAddr, httpPort));
d.httpServer->Start();
}

3
Datagram.cpp

@ -1,7 +1,6 @@ @@ -1,7 +1,6 @@
#include <string.h>
#include <vector>
#include <openssl/sha.h>
#include <openssl/rand.h>
#include "Crypto.h"
#include "Log.h"
#include "TunnelBase.h"
#include "RouterContext.h"

29
Destination.cpp

@ -1,14 +1,13 @@ @@ -1,14 +1,13 @@
#include <algorithm>
#include <cassert>
#include <boost/lexical_cast.hpp>
#include <openssl/rand.h>
#include "Crypto.h"
#include "Log.h"
#include "FS.h"
#include "Crypto.h"
#include "Timestamp.h"
#include "NetDb.h"
#include "Destination.h"
#include "util.h"
namespace i2p
{
@ -35,28 +34,30 @@ namespace client @@ -35,28 +34,30 @@ namespace client
{
auto it = params->find (I2CP_PARAM_INBOUND_TUNNEL_LENGTH);
if (it != params->end ())
{
int len = boost::lexical_cast<int>(it->second);
{
int len = i2p::util::lexical_cast<int>(it->second, inboundTunnelLen);
if (len > 0)
{
inboundTunnelLen = len;
LogPrint (eLogInfo, "Destination: Inbound tunnel length set to ", len);
inboundTunnelLen = len;
}
LogPrint (eLogInfo, "Destination: Inbound tunnel length set to ", inboundTunnelLen);
}
it = params->find (I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH);
if (it != params->end ())
{
int len = boost::lexical_cast<int>(it->second);
int len = i2p::util::lexical_cast<int>(it->second, outboundTunnelLen);
if (len > 0)
{
outboundTunnelLen = len;
LogPrint (eLogInfo, "Destination: Outbound tunnel length set to ", len);
outboundTunnelLen = len;
}
LogPrint (eLogInfo, "Destination: Outbound tunnel length set to ", outboundTunnelLen);
}
it = params->find (I2CP_PARAM_INBOUND_TUNNELS_QUANTITY);
if (it != params->end ())
{
int quantity = boost::lexical_cast<int>(it->second);
int quantity = i2p::util::lexical_cast<int>(it->second, inboundTunnelsQuantity);
if (quantity > 0)
{
inboundTunnelsQuantity = quantity;
@ -66,7 +67,7 @@ namespace client @@ -66,7 +67,7 @@ namespace client
it = params->find (I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY);
if (it != params->end ())
{
int quantity = boost::lexical_cast<int>(it->second);
int quantity = i2p::util::lexical_cast<int>(it->second, outboundTunnelsQuantity);
if (quantity > 0)
{
outboundTunnelsQuantity = quantity;
@ -76,11 +77,11 @@ namespace client @@ -76,11 +77,11 @@ namespace client
it = params->find (I2CP_PARAM_TAGS_TO_SEND);
if (it != params->end ())
{
int tagsToSend = boost::lexical_cast<int>(it->second);
int tagsToSend = i2p::util::lexical_cast<int>(it->second, numTags);
if (tagsToSend > 0)
{
numTags = tagsToSend;
LogPrint (eLogInfo, "Destination: Tags to send set to ", tagsToSend);
LogPrint (eLogInfo, "Destination: Tags to send set to ", tagsToSend);
}
}
it = params->find (I2CP_PARAM_EXPLICIT_PEERS);

2
Family.cpp

@ -1,9 +1,9 @@ @@ -1,9 +1,9 @@
#include <string.h>
#include <openssl/evp.h>
#include <openssl/ssl.h>
#include "Crypto.h"
#include "FS.h"
#include "Log.h"
#include "Crypto.h"
#include "Family.h"
namespace i2p

3
Garlic.cpp

@ -2,8 +2,7 @@ @@ -2,8 +2,7 @@
#include "I2PEndian.h"
#include <map>
#include <string>
#include <openssl/rand.h>
#include <openssl/sha.h>
#include "Crypto.h"
#include "RouterContext.h"
#include "I2NPProtocol.h"
#include "Tunnel.h"

389
HTTP.cpp

@ -0,0 +1,389 @@ @@ -0,0 +1,389 @@
/*
* 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 "HTTP.h"
#include <algorithm>
namespace i2p {
namespace http {
const std::vector<std::string> HTTP_METHODS = {
"GET", "HEAD", "POST", "PUT", "PATCH",
"DELETE", "OPTIONS", "CONNECT"
};
const std::vector<std::string> HTTP_VERSIONS = {
"HTTP/1.0", "HTTP/1.1"
};
inline bool is_http_version(const std::string & str) {
return std::find(HTTP_VERSIONS.begin(), HTTP_VERSIONS.end(), str) != std::end(HTTP_VERSIONS);
}
inline bool is_http_method(const std::string & str) {
return std::find(HTTP_METHODS.begin(), HTTP_METHODS.end(), str) != std::end(HTTP_METHODS);
}
void strsplit(const std::string & line, std::vector<std::string> &tokens, char delim, std::size_t limit = 0) {
std::size_t count = 0;
std::stringstream ss(line);
std::string token;
while (1) {
count++;
if (limit > 0 && count >= limit)
delim = '\n'; /* reset delimiter */
if (!std::getline(ss, token, delim))
break;
tokens.push_back(token);
}
}
bool parse_header_line(const std::string & line, std::map<std::string, std::string> & headers) {
std::size_t pos = 0;
std::size_t len = 2; /* strlen(": ") */
if ((pos = line.find(": ", pos)) == std::string::npos)
return false;
while (isspace(line.at(pos + len)))
len++;
std::string name = line.substr(0, pos);
std::string value = line.substr(pos + len);
headers[name] = value;
return true;
}
bool URL::parse(const char *str, std::size_t len) {
std::string url(str, len ? len : strlen(str));
return parse(url);
}
bool URL::parse(const std::string& url) {
std::size_t pos_p = 0; /* < current parse position */
std::size_t pos_c = 0; /* < work position */
if (url.at(0) != '/') {
/* schema */
pos_c = url.find("://");
if (pos_c != std::string::npos) {
schema = url.substr(0, pos_c);
pos_p = pos_c + 3;
}
/* user[:pass] */
pos_c = url.find('@', pos_p);
if (pos_c != std::string::npos) {
std::size_t delim = url.find(':', pos_p);
if (delim != std::string::npos && delim < pos_c) {
user = url.substr(pos_p, delim - pos_p);
delim += 1;
pass = url.substr(delim, pos_c - delim);
} else {
user = url.substr(pos_p, pos_c - pos_p);
}
pos_p = pos_c + 1;
}
/* hostname[:port][/path] */
pos_c = url.find_first_of(":/", pos_p);
if (pos_c == std::string::npos) {
/* only hostname, without post and path */
host = url.substr(pos_p, std::string::npos);
return true;
} else if (url.at(pos_c) == ':') {
host = url.substr(pos_p, pos_c - pos_p);
/* port[/path] */
pos_p = pos_c + 1;
pos_c = url.find('/', pos_p);
std::string port_str = (pos_c == std::string::npos)
? url.substr(pos_p, std::string::npos)
: url.substr(pos_p, pos_c - pos_p);
/* stoi throws exception on failure, we don't need it */
for (char c : port_str) {
if (c < '0' || c > '9')
return false;
port *= 10;
port += c - '0';
}
if (pos_c == std::string::npos)
return true; /* no path part */
pos_p = pos_c;
} else {
/* start of path part found */
host = url.substr(pos_p, pos_c - pos_p);
pos_p = pos_c;
}
}
/* pos_p now at start of path part */
pos_c = url.find_first_of("?#", pos_p);
if (pos_c == std::string::npos) {
/* only path, without fragment and query */
path = url.substr(pos_p, std::string::npos);
return true;
} else if (url.at(pos_c) == '?') {
/* found query part */
path = url.substr(pos_p, pos_c - pos_p);
pos_p = pos_c + 1;
pos_c = url.find('#', pos_p);
if (pos_c == std::string::npos) {
/* no fragment */
query = url.substr(pos_p, std::string::npos);
return true;
} else {
query = url.substr(pos_p, pos_c - pos_p);
pos_p = pos_c + 1;
}
} else {
/* found fragment part */
path = url.substr(pos_p, pos_c - pos_p);
pos_p = pos_c + 1;
}
/* pos_p now at start of fragment part */
frag = url.substr(pos_p, std::string::npos);
return true;
}
bool URL::parse_query(std::map<std::string, std::string> & params) {
std::vector<std::string> tokens;
strsplit(query, tokens, '&');
params.clear();
for (auto it : tokens) {
std::size_t eq = it.find ('=');
if (eq != std::string::npos) {
auto e = std::pair<std::string, std::string>(it.substr(0, eq), it.substr(eq + 1));
params.insert(e);
} else {
auto e = std::pair<std::string, std::string>(it, "");
params.insert(e);
}
}
return true;
}
std::string URL::to_string() {
std::string out = "";
if (schema != "") {
out = schema + "://";
if (user != "" && pass != "") {
out += user + ":" + pass + "@";
} else if (user != "") {
out += user + "@";
}
if (port) {
out += host + ":" + std::to_string(port);
} else {
out += host;
}
}
out += path;
if (query != "")
out += "?" + query;
if (frag != "")
out += "#" + frag;
return out;
}
int HTTPReq::parse(const char *buf, size_t len) {
std::string str(buf, len);
return parse(str);
}
int HTTPReq::parse(const std::string& str) {
enum { REQ_LINE, HEADER_LINE } expect = REQ_LINE;
std::size_t eoh = str.find(HTTP_EOH); /* request head size */
std::size_t eol = 0, pos = 0;
URL url;
if (eoh == std::string::npos)
return 0; /* str not contains complete request */
while ((eol = str.find(CRLF, pos)) != std::string::npos) {
if (expect == REQ_LINE) {
std::string line = str.substr(pos, eol - pos);
std::vector<std::string> tokens;
strsplit(line, tokens, ' ');
if (tokens.size() != 3)
return -1;
if (!is_http_method(tokens[0]))
return -1;
if (!is_http_version(tokens[2]))
return -1;
if (!url.parse(tokens[1]))
return -1;
/* all ok */
method = tokens[0];
uri = tokens[1];
version = tokens[2];
expect = HEADER_LINE;
} else {
std::string line = str.substr(pos, eol - pos);
if (!parse_header_line(line, headers))
return -1;
}
pos = eol + strlen(CRLF);
if (pos >= eoh)
break;
}
auto it = headers.find("Host");
if (it != headers.end ()) {
host = it->second;
} else if (version == "HTTP/1.1") {
return -1; /* 'Host' header required for HTTP/1.1 */
} else if (url.host != "") {
host = url.host;
}
return eoh + strlen(HTTP_EOH);
}
std::string HTTPReq::to_string() {
std::stringstream ss;
ss << method << " " << uri << " " << version << CRLF;
ss << "Host: " << host << CRLF;
for (auto & h : headers) {
ss << h.first << ": " << h.second << CRLF;
}
ss << CRLF;
return ss.str();
}
bool HTTPRes::is_chunked() {
auto it = headers.find("Transfer-Encoding");
if (it == headers.end())
return false;
if (it->second.find("chunked") == std::string::npos)
return true;
return false;
}
long int HTTPRes::length() {
unsigned long int length = 0;
auto it = headers.find("Content-Length");
if (it == headers.end())
return -1;
errno = 0;
length = std::strtoul(it->second.c_str(), (char **) NULL, 10);
if (errno != 0)
return -1;
return length;
}
int HTTPRes::parse(const char *buf, size_t len) {
std::string str(buf, len);
return parse(str);
}
int HTTPRes::parse(const std::string& str) {
enum { RES_LINE, HEADER_LINE } expect = RES_LINE;
std::size_t eoh = str.find(HTTP_EOH); /* request head size */
std::size_t eol = 0, pos = 0;
if (eoh == std::string::npos)
return 0; /* str not contains complete request */
while ((eol = str.find(CRLF, pos)) != std::string::npos) {
if (expect == RES_LINE) {
std::string line = str.substr(pos, eol - pos);
std::vector<std::string> tokens;
strsplit(line, tokens, ' ', 3);
if (tokens.size() != 3)
return -1;
if (!is_http_version(tokens[0]))
return -1;
code = atoi(tokens[1].c_str());
if (code < 100 || code >= 600)
return -1;
/* all ok */
version = tokens[0];
status = tokens[2];
expect = HEADER_LINE;
} else {
std::string line = str.substr(pos, eol - pos);
if (!parse_header_line(line, headers))
return -1;
}
pos = eol + strlen(CRLF);
if (pos >= eoh)
break;
}
return eoh + strlen(HTTP_EOH);
}
std::string HTTPRes::to_string() {
std::stringstream ss;
ss << version << " " << code << " " << status << CRLF;
for (auto & h : headers) {
ss << h.first << ": " << h.second << CRLF;
}
ss << CRLF;
return ss.str();
}
const char * HTTPCodeToStatus(int code) {
const char *ptr;
switch (code) {
case 105: ptr = "Name Not Resolved"; break;
/* success */
case 200: ptr = "OK"; break;
case 206: ptr = "Partial Content"; break;
/* redirect */
case 301: ptr = "Moved Permanently"; break;
case 302: ptr = "Found"; break;
case 304: ptr = "Not Modified"; break;
case 307: ptr = "Temporary Redirect"; break;
/* client error */
case 400: ptr = "Bad Request"; break;
case 401: ptr = "Unauthorized"; break;
case 403: ptr = "Forbidden"; break;
case 404: ptr = "Not Found"; break;
case 407: ptr = "Proxy Authentication Required"; break;
case 408: ptr = "Request Timeout"; break;
/* server error */
case 500: ptr = "Internal Server Error"; break;
case 502: ptr = "Bad Gateway"; break;
case 503: ptr = "Not Implemented"; break;
case 504: ptr = "Gateway Timeout"; break;
default: ptr = "Unknown Status"; break;
}
return ptr;
}
std::string UrlDecode(const std::string& data, bool allow_null) {
std::string decoded(data);
size_t pos = 0;
while ((pos = decoded.find('%', pos)) != std::string::npos) {
char c = strtol(decoded.substr(pos + 1, 2).c_str(), NULL, 16);
if (c == '\0' && !allow_null) {
pos += 3;
continue;
}
decoded.replace(pos, 3, 1, c);
pos++;
}
return decoded;
}
bool MergeChunkedResponse (std::istream& in, std::ostream& out) {
std::string hexLen;
long int len;
while (!in.eof ()) {
std::getline (in, hexLen);
errno = 0;
len = strtoul(hexLen.c_str(), (char **) NULL, 16);
if (errno != 0)
return false; /* conversion error */
if (len == 0)
return true; /* end of stream */
if (len < 0 || len > 10 * 1024 * 1024) /* < 10Mb */
return false; /* too large chunk */
char * buf = new char[len];
in.read (buf, len);
out.write (buf, len);
delete[] buf;
std::getline (in, hexLen); // read \r\n after chunk
}
return true;
}
} // http
} // i2p

121
HTTP.h

@ -0,0 +1,121 @@ @@ -0,0 +1,121 @@
/*
* 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 HTTP_H__
#define HTTP_H__
#include <cstring>
#include <map>
#include <sstream>
#include <string>
#include <vector>
namespace i2p {
namespace http {
const char CRLF[] = "\r\n"; /**< HTTP line terminator */
const char HTTP_EOH[] = "\r\n\r\n"; /**< HTTP end-of-headers mark */
extern const std::vector<std::string> HTTP_METHODS; /**< list of valid HTTP methods */
extern const std::vector<std::string> HTTP_VERSIONS; /**< list of valid HTTP versions */
struct URL {
std::string schema;
std::string user;
std::string pass;
std::string host;
unsigned short int port;
std::string path;
std::string query;
std::string frag;
URL(): schema(""), user(""), pass(""), host(""), port(0), path(""), query(""), frag("") {};
/**
* @brief Tries to parse url from string
* @return true on success, false on invalid url
*/
bool parse (const char *str, size_t len = 0);
bool parse (const std::string& url);
/**
* @brief Parse query part of url to key/value map
* @note Honestly, this should be implemented with std::multimap
*/
bool parse_query(std::map<std::string, std::string> & params);
/**
* @brief Serialize URL structure to url
* @note Returns relative url if schema if empty, absolute url otherwise
*/
std::string to_string ();
};
struct HTTPReq {
std::map<std::string, std::string> headers;
std::string version;
std::string method;
std::string uri;
std::string host;
HTTPReq (): version("HTTP/1.0"), method("GET"), uri("/") {};
/**
* @brief Tries to parse HTTP request from string
* @return -1 on error, 0 on incomplete query, >0 on success
* @note Positive return value is a size of header
*/
int parse(const char *buf, size_t len);
int parse(const std::string& buf);
/** @brief Serialize HTTP request to string */
std::string to_string();
};
struct HTTPRes {
std::map<std::string, std::string> headers;
std::string version;
std::string status;
unsigned short int code;
HTTPRes (): version("HTTP/1.1"), status("OK"), code(200) {}
/**
* @brief Tries to parse HTTP response from string
* @return -1 on error, 0 on incomplete query, >0 on success
* @note Positive return value is a size of header
*/
int parse(const char *buf, size_t len);
int parse(const std::string& buf);
/** @brief Serialize HTTP response to string */
std::string to_string();
/** @brief Checks that response declared as chunked data */
bool is_chunked();
/** @brief Returns declared response length or -1 if unknown */
long int length();
};
/**
* @brief returns HTTP status string by integer code
* @param code HTTP code [100, 599]
* @return Immutable string with status
*/
const char * HTTPCodeToStatus(int code);
/**
* @brief Replaces %-encoded characters in string with their values
* @param data Source string
* @param null If set to true - decode also %00 sequence, otherwise - skip
* @return Decoded string
*/
std::string UrlDecode(const std::string& data, bool null = false);
} // http
} // i2p
#endif /* HTTP_H__ */

2
HTTPProxy.cpp

@ -104,7 +104,7 @@ namespace proxy @@ -104,7 +104,7 @@ namespace proxy
std::string httpAddr; i2p::config::GetOption("http.address", httpAddr);
uint16_t httpPort; i2p::config::GetOption("http.port", httpPort);
response << "HTTP/1.1 302 Found\r\nLocation: http://" << httpAddr << ":" << httpPort << "/?jumpservices=&address=" << m_address << "\r\n\r\n";
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));
}

876
HTTPServer.cpp

File diff suppressed because it is too large Load Diff

120
HTTPServer.h

@ -1,108 +1,40 @@ @@ -1,108 +1,40 @@
#ifndef HTTP_SERVER_H__
#define HTTP_SERVER_H__
#include <sstream>
#include <thread>
#include <memory>
#include <boost/asio.hpp>
#include <boost/array.hpp>
#include "LeaseSet.h"
#include "Streaming.h"
namespace i2p
{
namespace util
{
namespace i2p {
namespace http {
extern const char *itoopieImage;
extern const char *itoopieFavicon;
const size_t HTTP_CONNECTION_BUFFER_SIZE = 8192;
const int HTTP_DESTINATION_REQUEST_TIMEOUT = 10; // in seconds
class HTTPConnection: public std::enable_shared_from_this<HTTPConnection>
{
protected:
struct header
{
std::string name;
std::string value;
};
struct request
{
std::string method;
std::string uri;
std::string host;
int port;
int http_version_major;
int http_version_minor;
std::vector<header> headers;
};
struct reply
{
std::vector<header> headers;
std::string status_string, content;
std::vector<boost::asio::const_buffer> to_buffers (int status);
};
public:
HTTPConnection (std::shared_ptr<boost::asio::ip::tcp::socket> socket):
m_Socket (socket), m_Timer (socket->get_io_service ()),
m_Stream (nullptr), m_BufferLen (0) {};
HTTPConnection (std::shared_ptr<boost::asio::ip::tcp::socket> socket);
void Receive ();
private:
void Terminate ();
void HandleReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void AsyncStreamReceive ();
void HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void HandleWriteReply(const boost::system::error_code& ecode);
void HandleWrite (const boost::system::error_code& ecode);
void SendReply (const std::string& content, int status = 200);
void SendError (const std::string& message);
void Terminate (const boost::system::error_code& ecode);
void HandleRequest (const std::string& address);
void HandleCommand (const std::string& command, std::stringstream& s);
void ShowJumpServices (const std::string& address, std::stringstream& s);
void ShowTransports (std::stringstream& s);
void ShowTunnels (std::stringstream& s);
void ShowTransitTunnels (std::stringstream& s);
void ShowLocalDestinations (std::stringstream& s);
void ShowLocalDestination (const std::string& b32, std::stringstream& s);
void ShowSAMSessions (std::stringstream& s);
void ShowSAMSession (const std::string& id, std::stringstream& s);
void ShowI2PTunnels (std::stringstream& s);
void StartAcceptingTunnels (std::stringstream& s);
void StopAcceptingTunnels (std::stringstream& s);
void RunPeerTest (std::stringstream& s);
void FillContent (std::stringstream& s);
std::string ExtractAddress ();
void ExtractParams (const std::string& str, std::map<std::string, std::string>& params);
protected:
void RunRequest ();
bool CheckAuth (const HTTPReq & req);
void HandleRequest (const HTTPReq & req);
void HandlePage (const HTTPReq & req, HTTPRes & res, std::stringstream& data);
void HandleCommand (const HTTPReq & req, HTTPRes & res, std::stringstream& data);
void SendReply (HTTPRes & res, std::string & content);
private:
std::shared_ptr<boost::asio::ip::tcp::socket> m_Socket;
boost::asio::deadline_timer m_Timer;
std::shared_ptr<i2p::stream::Stream> m_Stream;
char m_Buffer[HTTP_CONNECTION_BUFFER_SIZE + 1], m_StreamBuffer[HTTP_CONNECTION_BUFFER_SIZE + 1];
char m_Buffer[HTTP_CONNECTION_BUFFER_SIZE + 1];
size_t m_BufferLen;
request m_Request;
reply m_Reply;
protected:
virtual void RunRequest ();
void HandleDestinationRequest(const std::string& address, const std::string& uri);
void SendToAddress (const std::string& address, int port, const char * buf, size_t len);
void HandleDestinationRequestTimeout (const boost::system::error_code& ecode,
i2p::data::IdentHash destination, int port, const char * buf, size_t len);
void SendToDestination (std::shared_ptr<const i2p::data::LeaseSet> remote, int port, const char * buf, size_t len);
public:
static const std::string itoopieImage;
static const std::string itoopieFavicon;
bool needAuth;
std::string user;
std::string pass;
};
class HTTPServer
@ -110,7 +42,7 @@ namespace util @@ -110,7 +42,7 @@ namespace util
public:
HTTPServer (const std::string& address, int port);
virtual ~HTTPServer ();
~HTTPServer ();
void Start ();
void Stop ();
@ -121,6 +53,7 @@ namespace util @@ -121,6 +53,7 @@ namespace util
void Accept ();
void HandleAccept(const boost::system::error_code& ecode,
std::shared_ptr<boost::asio::ip::tcp::socket> newSocket);
void CreateConnection(std::shared_ptr<boost::asio::ip::tcp::socket> newSocket);
private:
@ -128,13 +61,8 @@ namespace util @@ -128,13 +61,8 @@ namespace util
boost::asio::io_service m_Service;
boost::asio::io_service::work m_Work;
boost::asio::ip::tcp::acceptor m_Acceptor;
protected:
virtual void CreateConnection(std::shared_ptr<boost::asio::ip::tcp::socket> newSocket);
};
}
}
#endif
} // http
} // i2p
#endif /* HTTP_SERVER_H__ */

112
I2CP.cpp

@ -0,0 +1,112 @@ @@ -0,0 +1,112 @@
#include <string.h>
#include "I2PEndian.h"
#include "Log.h"
#include "I2CP.h"
namespace i2p
{
namespace client
{
I2CPSession::I2CPSession (I2CPServer& owner, std::shared_ptr<boost::asio::ip::tcp::socket> socket):
m_Owner (owner), m_Socket (socket),
m_NextMessage (nullptr), m_NextMessageLen (0), m_NextMessageOffset (0)
{
ReadProtocolByte ();
}
I2CPSession::~I2CPSession ()
{
delete[] m_NextMessage;
}
void I2CPSession::ReadProtocolByte ()
{
if (m_Socket)
{
auto s = shared_from_this ();
m_Socket->async_read_some (boost::asio::buffer (m_Buffer, 1),
[s](const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
if (!ecode && bytes_transferred > 0 && s->m_Buffer[0] == I2CP_PRTOCOL_BYTE)
s->Receive ();
else
s->Terminate ();
});
}
}
void I2CPSession::Receive ()
{
m_Socket->async_read_some (boost::asio::buffer (m_Buffer, I2CP_SESSION_BUFFER_SIZE),
std::bind (&I2CPSession::HandleReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2));
}
void I2CPSession::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
if (ecode)
Terminate ();
else
{
size_t offset = 0;
if (m_NextMessage)
{
if (m_NextMessageOffset + bytes_transferred <= m_NextMessageLen)
{
memcpy (m_NextMessage + m_NextMessageOffset, m_Buffer, bytes_transferred);
m_NextMessageOffset += bytes_transferred;
}
else
{
offset = m_NextMessageLen - m_NextMessageOffset;
memcpy (m_NextMessage + m_NextMessageOffset, m_Buffer, offset);
HandleNextMessage (m_NextMessage);
delete[] m_NextMessage;
}
}
while (offset < bytes_transferred)
{
auto msgLen = bufbe32toh (m_Buffer + offset + I2CP_HEADER_LENGTH_OFFSET) + I2CP_HEADER_SIZE;
if (msgLen <= bytes_transferred - offset)
{
HandleNextMessage (m_Buffer + offset);
offset += msgLen;
}
else
{
m_NextMessageLen = msgLen;
m_NextMessageOffset = bytes_transferred - offset;
m_NextMessage = new uint8_t[m_NextMessageLen];
memcpy (m_NextMessage, m_Buffer + offset, m_NextMessageOffset);
offset = bytes_transferred;
}
}
Receive ();
}
}
void I2CPSession::HandleNextMessage (const uint8_t * buf)
{
auto handler = m_Owner.GetMessagesHandlers ()[buf[I2CP_HEADER_TYPE_OFFSET]];
if (handler)
(this->*handler)(buf + I2CP_HEADER_SIZE, bufbe32toh (buf + I2CP_HEADER_LENGTH_OFFSET));
else
LogPrint (eLogError, "I2CP: Unknown I2CP messsage ", (int)buf[I2CP_HEADER_TYPE_OFFSET]);
}
void I2CPSession::Terminate ()
{
}
void I2CPSession::GetDateMessageHandler (const uint8_t * buf, size_t len)
{
}
I2CPServer::I2CPServer (const std::string& interface, int port)
{
memset (m_MessagesHandlers, 0, sizeof (m_MessagesHandlers));
m_MessagesHandlers[I2CP_GET_DATE_MESSAGE] = &I2CPSession::GetDateMessageHandler;
}
}
}

68
I2CP.h

@ -0,0 +1,68 @@ @@ -0,0 +1,68 @@
#ifndef I2CP_H__
#define I2CP_H__
#include <inttypes.h>
#include <string>
#include <memory>
#include <boost/asio.hpp>
namespace i2p
{
namespace client
{
const uint8_t I2CP_PRTOCOL_BYTE = 0x2A;
const size_t I2CP_SESSION_BUFFER_SIZE = 4096;
const size_t I2CP_HEADER_LENGTH_OFFSET = 0;
const size_t I2CP_HEADER_TYPE_OFFSET = I2CP_HEADER_LENGTH_OFFSET + 4;
const size_t I2CP_HEADER_SIZE = I2CP_HEADER_TYPE_OFFSET + 1;
const uint8_t I2CP_GET_DATE_MESSAGE = 32;
class I2CPServer;
class I2CPSession: public std::enable_shared_from_this<I2CPSession>
{
public:
I2CPSession (I2CPServer& owner, std::shared_ptr<boost::asio::ip::tcp::socket> socket);
~I2CPSession ();
// message handlers
void GetDateMessageHandler (const uint8_t * buf, size_t len);
private:
void ReadProtocolByte ();
void Receive ();
void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void HandleNextMessage (const uint8_t * buf);
void Terminate ();
private:
I2CPServer& m_Owner;
std::shared_ptr<boost::asio::ip::tcp::socket> m_Socket;
uint8_t m_Buffer[I2CP_SESSION_BUFFER_SIZE], * m_NextMessage;
size_t m_NextMessageLen, m_NextMessageOffset;
};
typedef void (I2CPSession::*I2CPMessageHandler)(const uint8_t * buf, size_t len);
class I2CPServer
{
public:
I2CPServer (const std::string& interface, int port);
private:
I2CPMessageHandler m_MessagesHandlers[256];
public:
const decltype(m_MessagesHandlers)& GetMessagesHandlers () const { return m_MessagesHandlers; };
};
}
}
#endif

2
I2NPProtocol.cpp

@ -1,7 +1,5 @@ @@ -1,7 +1,5 @@
#include <string.h>
#include <atomic>
#include <openssl/rand.h>
#include <openssl/sha.h>
#include "Base.h"
#include "Log.h"
#include "Crypto.h"

2
I2NPProtocol.h

@ -5,7 +5,7 @@ @@ -5,7 +5,7 @@
#include <string.h>
#include <set>
#include <memory>
#include <openssl/sha.h>
#include "Crypto.h"
#include "I2PEndian.h"
#include "Identity.h"
#include "RouterInfo.h"

1
I2PControl.cpp

@ -13,6 +13,7 @@ @@ -13,6 +13,7 @@
#include <boost/property_tree/json_parser.hpp>
#endif
#include "Crypto.h"
#include "FS.h"
#include "Log.h"
#include "Config.h"

2
I2PControl.h

@ -10,7 +10,7 @@ @@ -10,7 +10,7 @@
#include <map>
#include <set>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/property_tree/ptree.hpp>
namespace i2p

3
I2PService.h

@ -118,6 +118,9 @@ namespace client @@ -118,6 +118,9 @@ namespace client
void Start ();
//If you override this make sure you call it from the children
void Stop ();
const boost::asio::ip::tcp::acceptor& GetAcceptor () const { return m_Acceptor; };
protected:
virtual std::shared_ptr<I2PServiceHandler> CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket) = 0;
virtual const char* GetName() { return "Generic TCP/IP accepting daemon"; }

3
Identity.cpp

@ -1,8 +1,5 @@ @@ -1,8 +1,5 @@
#include <time.h>
#include <stdio.h>
#include <openssl/sha.h>
#include <openssl/dh.h>
#include <openssl/rand.h>
#include "Crypto.h"
#include "I2PEndian.h"
#include "Log.h"

6
Makefile

@ -14,7 +14,11 @@ USE_STATIC := no @@ -14,7 +14,11 @@ USE_STATIC := no
ifeq ($(UNAME),Darwin)
DAEMON_SRC += DaemonLinux.cpp
include Makefile.osx
ifeq ($(HOMEBREW),1)
include Makefile.homebrew
else
include Makefile.osx
endif
else ifeq ($(shell echo $(UNAME) | $(GREP) -c FreeBSD),1)
DAEMON_SRC += DaemonLinux.cpp
include Makefile.bsd

29
Makefile.homebrew

@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
# root directory holding homebrew
BREWROOT = /usr/local/
BOOSTROOT = ${BREWROOT}/opt/boost
SSLROOT = ${BREWROOT}/opt/libressl
CXX = clang++
CXXFLAGS = -g -Wall -std=c++11 -DMAC_OSX
INCFLAGS = -I${SSLROOT}/include -I${BOOSTROOT}/include
LDFLAGS = -L${SSLROOT}/lib -L${BOOSTROOT}/lib
LDLIBS = -lz -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_regex -lboost_program_options -lpthread
ifeq ($(USE_UPNP),1)
LDFLAGS += -ldl
CXXFLAGS += -DUSE_UPNP
endif
# OSX Notes
# http://www.hutsby.net/2011/08/macs-with-aes-ni.html
# Seems like all recent Mac's have AES-NI, after firmware upgrade 2.2
# Found no good way to detect it from command line. TODO: Might be some osx sysinfo magic
# note from psi: 2009 macbook does not have aesni
#ifeq ($(USE_AESNI),yes)
# CXXFLAGS += -maes -DAESNI
#endif
# Disabled, since it will be the default make rule. I think its better
# to define the default rule in Makefile and not Makefile.<ostype> - torkel
#install: all
# test -d ${PREFIX} || mkdir -p ${PREFIX}/
# cp -r i2p ${PREFIX}/

2
Makefile.linux

@ -19,7 +19,7 @@ else ifeq ($(shell expr match ${CXXVER} "4\.[7-9]"),3) # >= 4.7 @@ -19,7 +19,7 @@ else ifeq ($(shell expr match ${CXXVER} "4\.[7-9]"),3) # >= 4.7
NEEDED_CXXFLAGS += -std=c++11 -D_GLIBCXX_USE_NANOSLEEP=1
else ifeq ($(shell expr match ${CXXVER} "4\.6"),3) # = 4.6
NEEDED_CXXFLAGS += -std=c++0x
else ifeq ($(shell expr match ${CXXVER} "5\.[0-9]"),3) # gcc >= 5.0
else ifeq ($(shell expr match ${CXXVER} "[5-6]\.[0-9]"),3) # gcc >= 5.0
NEEDED_CXXFLAGS += -std=c++11
else # not supported
$(error Compiler too old)

4
NTCPSession.cpp

@ -1,13 +1,11 @@ @@ -1,13 +1,11 @@
#include <string.h>
#include <stdlib.h>
#include <openssl/dh.h>
#include <openssl/sha.h>
#include <zlib.h>
#include "I2PEndian.h"
#include "Base.h"
#include "Crypto.h"
#include "Log.h"
#include "Timestamp.h"
#include "Crypto.h"
#include "I2NPProtocol.h"
#include "RouterContext.h"
#include "Transports.h"

2
NetDb.cpp

@ -2,10 +2,10 @@ @@ -2,10 +2,10 @@
#include <fstream>
#include <vector>
#include <boost/asio.hpp>
#include <openssl/rand.h>
#include <zlib.h>
#include "I2PEndian.h"
#include "Base.h"
#include "Crypto.h"
#include "Log.h"
#include "Timestamp.h"
#include "I2NPProtocol.h"

5
Reseed.cpp

@ -3,18 +3,17 @@ @@ -3,18 +3,17 @@
#include <sstream>
#include <boost/regex.hpp>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <openssl/bn.h>
#include <boost/asio/ssl.hpp>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <zlib.h>
#include "Crypto.h"
#include "I2PEndian.h"
#include "Reseed.h"
#include "FS.h"
#include "Log.h"
#include "Identity.h"
#include "Crypto.h"
#include "NetDb.h"
#include "util.h"

5
RouterContext.cpp

@ -149,6 +149,11 @@ namespace i2p @@ -149,6 +149,11 @@ namespace i2p
UpdateRouterInfo ();
}
std::string RouterContext::GetFamily () const
{
return m_RouterInfo.GetProperty (i2p::data::ROUTER_INFO_PROPERTY_FAMILY);
}
void RouterContext::SetFamily (const std::string& family)
{
std::string signature;

1
RouterContext.h

@ -59,6 +59,7 @@ namespace i2p @@ -59,6 +59,7 @@ namespace i2p
bool IsFloodfill () const { return m_IsFloodfill; };
void SetFloodfill (bool floodfill);
void SetFamily (const std::string& family);
std::string GetFamily () const;
void SetBandwidth (int limit); /* in kilobytes */
void SetBandwidth (char L); /* by letter */
bool AcceptsTunnels () const { return m_AcceptsTunnels; };

8
RouterInfo.cpp

@ -636,6 +636,14 @@ namespace data @@ -636,6 +636,14 @@ namespace data
m_Properties.erase (key);
}
std::string RouterInfo::GetProperty (const std::string& key) const
{
auto it = m_Properties.find (key);
if (it != m_Properties.end ())
return it->second;
return "";
}
bool RouterInfo::IsNTCP (bool v4only) const
{
if (v4only)

1
RouterInfo.h

@ -128,6 +128,7 @@ namespace data @@ -128,6 +128,7 @@ namespace data
bool RemoveIntroducer (const boost::asio::ip::udp::endpoint& e);
void SetProperty (const std::string& key, const std::string& value); // called from RouterContext only
void DeleteProperty (const std::string& key); // called from RouterContext only
std::string GetProperty (const std::string& key) const; // called from RouterContext only
void ClearProperties () { m_Properties.clear (); };
bool IsFloodfill () const { return m_Caps & Caps::eFloodfill; };
bool IsReachable () const { return m_Caps & Caps::eReachable; };

35
SSUSession.cpp

@ -1,7 +1,5 @@ @@ -1,7 +1,5 @@
#include <boost/bind.hpp>
#include <openssl/dh.h>
#include <openssl/sha.h>
#include <openssl/rand.h>
#include "Crypto.h"
#include "Log.h"
#include "Timestamp.h"
#include "RouterContext.h"
@ -265,8 +263,6 @@ namespace transport @@ -265,8 +263,6 @@ namespace transport
uint16_t ourPort = bufbe16toh (payload);
s.Insert (payload, 2); // our port
payload += 2; // port
LogPrint (eLogInfo, "SSU: Our external address is ", ourIP.to_string (), ":", ourPort);
i2p::context.UpdateAddress (ourIP);
if (m_RemoteEndpoint.address ().is_v4 ())
s.Insert (m_RemoteEndpoint.address ().to_v4 ().to_bytes ().data (), 4); // remote IP v4
else
@ -283,11 +279,18 @@ namespace transport @@ -283,11 +279,18 @@ namespace transport
//TODO: since we are accessing a uint8_t this is unlikely to crash due to alignment but should be improved
m_SessionKeyDecryption.SetIV (((SSUHeader *)buf)->iv);
m_SessionKeyDecryption.Decrypt (payload, signatureLen, payload); // TODO: non-const payload
// verify
if (!s.Verify (m_RemoteIdentity, payload))
// verify signature
if (s.Verify (m_RemoteIdentity, payload))
{
LogPrint (eLogInfo, "SSU: Our external address is ", ourIP.to_string (), ":", ourPort);
i2p::context.UpdateAddress (ourIP);
SendSessionConfirmed (y, ourAddress, addressSize + 2);
}
else
{
LogPrint (eLogError, "SSU: message 'created' signature verification failed");
SendSessionConfirmed (y, ourAddress, addressSize + 2);
Failed ();
}
}
void SSUSession::ProcessSessionConfirmed (const uint8_t * buf, size_t len)
@ -313,11 +316,17 @@ namespace transport @@ -313,11 +316,17 @@ namespace transport
paddingSize &= 0x0F; // %16
if (paddingSize > 0) paddingSize = 16 - paddingSize;
payload += paddingSize;
// verify
if (m_SignedData && !m_SignedData->Verify (m_RemoteIdentity, payload))
// verify signature
if (m_SignedData && m_SignedData->Verify (m_RemoteIdentity, payload))
{
m_Data.Send (CreateDeliveryStatusMsg (0));
Established ();
}
else
{
LogPrint (eLogError, "SSU message 'confirmed' signature verification failed");
m_Data.Send (CreateDeliveryStatusMsg (0));
Established ();
Failed ();
}
}
void SSUSession::SendSessionRequest ()

2
Signature.h

@ -3,12 +3,10 @@ @@ -3,12 +3,10 @@
#include <inttypes.h>
#include <string.h>
#include <openssl/sha.h>
#include <openssl/dsa.h>
#include <openssl/ec.h>
#include <openssl/ecdsa.h>
#include <openssl/rsa.h>
#include <openssl/rand.h>
#include <openssl/evp.h>
#include "Crypto.h"

2
Streaming.cpp

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
#include <openssl/rand.h>
#include "Crypto.h"
#include "Log.h"
#include "RouterInfo.h"
#include "RouterContext.h"

1
Transports.cpp

@ -1,4 +1,3 @@ @@ -1,4 +1,3 @@
#include <openssl/dh.h>
#include "Log.h"
#include "Crypto.h"
#include "RouterContext.h"

2
Tunnel.cpp

@ -3,7 +3,7 @@ @@ -3,7 +3,7 @@
#include <thread>
#include <algorithm>
#include <vector>
#include <openssl/rand.h>
#include "Crypto.h"
#include "RouterContext.h"
#include "Log.h"
#include "Timestamp.h"

1
TunnelConfig.h

@ -5,7 +5,6 @@ @@ -5,7 +5,6 @@
#include <sstream>
#include <vector>
#include <memory>
#include <openssl/rand.h>
#include "Crypto.h"
#include "Identity.h"
#include "RouterContext.h"

2
TunnelEndpoint.cpp

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
#include "I2PEndian.h"
#include <string.h>
#include <openssl/sha.h>
#include "Crypto.h"
#include "Log.h"
#include "NetDb.h"
#include "I2NPProtocol.h"

3
TunnelGateway.cpp

@ -1,6 +1,5 @@ @@ -1,6 +1,5 @@
#include <string.h>
#include <openssl/rand.h>
#include <openssl/sha.h>
#include "Crypto.h"
#include "I2PEndian.h"
#include "Log.h"
#include "RouterContext.h"

1
TunnelPool.cpp

@ -1,5 +1,4 @@ @@ -1,5 +1,4 @@
#include <algorithm>
#include <openssl/rand.h>
#include "I2PEndian.h"
#include "Crypto.h"
#include "Tunnel.h"

BIN
Win32/Anke_2200px.jpg

Binary file not shown.

Before

Width:  |  Height:  |  Size: 146 KiB

BIN
Win32/Anke_700px.bmp

Binary file not shown.

Before

Width:  |  Height:  |  Size: 730 KiB

2
Win32/Resource.rc

@ -55,8 +55,6 @@ END @@ -55,8 +55,6 @@ END
MAINICON ICON "ictoopie.ico"
//MAINICON ICON "anke.ico"
MASCOT BITMAP "Anke_700px.bmp"
#endif // English (United States) resources
/////////////////////////////////////////////////////////////////////////////

13
Win32/Win32App.cpp

@ -167,19 +167,6 @@ namespace win32 @@ -167,19 +167,6 @@ namespace win32
}
break;
}
case WM_PAINT:
{
PAINTSTRUCT ps;
auto hDC = BeginPaint (hWnd, &ps);
auto mascot = LoadBitmap (GetModuleHandle(NULL), MAKEINTRESOURCE (MASCOT));
auto mascotDC = CreateCompatibleDC (hDC);
SelectObject (mascotDC, mascot);
BitBlt (hDC, 0,0, 533, 700, mascotDC, 0, 0, SRCCOPY);
DeleteDC (mascotDC);
DeleteObject (mascot);
EndPaint (hWnd, &ps);
break;
}
}
return DefWindowProc( hWnd, uMsg, wParam, lParam);
}

BIN
Win32/anke.ico

Binary file not shown.

Before

Width:  |  Height:  |  Size: 119 KiB

1
Win32/resource.h

@ -3,7 +3,6 @@ @@ -3,7 +3,6 @@
// Used by Resource.rc
//
#define MAINICON 101
#define MASCOT 201
// Next default values for new objects
//

2
build/CMakeLists.txt

@ -74,7 +74,9 @@ set (CLIENT_SRC @@ -74,7 +74,9 @@ set (CLIENT_SRC
"${CMAKE_SOURCE_DIR}/I2PService.cpp"
"${CMAKE_SOURCE_DIR}/SAM.cpp"
"${CMAKE_SOURCE_DIR}/SOCKS.cpp"
"${CMAKE_SOURCE_DIR}/HTTP.cpp"
"${CMAKE_SOURCE_DIR}/HTTPProxy.cpp"
"${CMAKE_SOURCE_DIR}/I2CP.cpp"
)
add_library(i2pdclient ${CLIENT_SRC})

12
contrib/certificates/family/volatile.crt

@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
-----BEGIN CERTIFICATE-----
MIIBxDCCAWmgAwIBAgIJAJnJIdKHYwWcMAoGCCqGSM49BAMCMGcxCzAJBgNVBAYT
AkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRn
aXRzIFB0eSBMdGQxIDAeBgNVBAMMF3ZvbGF0aWxlLmZhbWlseS5pMnAubmV0MB4X
DTE2MDQyNjE1MjAyNloXDTI2MDQyNDE1MjAyNlowZzELMAkGA1UEBhMCQVUxEzAR
BgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5
IEx0ZDEgMB4GA1UEAwwXdm9sYXRpbGUuZmFtaWx5LmkycC5uZXQwWTATBgcqhkjO
PQIBBggqhkjOPQMBBwNCAARf6LBfbbfL6HInvC/4wAGaN3rj0eeLE/OdBpA93R3L
s8EUp0YTEJHWPo9APiKMmAwQSsMJfjhNrbp+UWEnnx2LMAoGCCqGSM49BAMCA0kA
MEYCIQDpQu2KPV5G1JOFLoZvdj+rcvEnjxM/FxkaqikwkVx8FAIhANP7DkUal+GT
SuiCtcqM4QyIBsfsCJBWEMzovft164Bo
-----END CERTIFICATE-----

15
docs/build_notes_unix.md

@ -93,6 +93,21 @@ If you need UPnP support (don't forget to run CMake with `WITH_UPNP=ON`) miniupn @@ -93,6 +93,21 @@ If you need UPnP support (don't forget to run CMake with `WITH_UPNP=ON`) miniupn
miniupnpc-devel
```
MAC OS X
--------
Requires homebrew
```bash
brew install libressl boost
```
Then build:
```bash
make HOMEBREW=1
```
FreeBSD
-------

3
docs/configuration.md

@ -36,6 +36,9 @@ All options below still possible in cmdline, but better write it in config file: @@ -36,6 +36,9 @@ All options below still possible in cmdline, but better write it in config file:
* --http.address= - The address to listen on (HTTP server)
* --http.port= - The port to listen on (HTTP server)
* --http.auth - Enable basic HTTP auth for webconsole
* --http.user= - Username for basic auth (default: i2pd)
* --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

2
filelist.mk

@ -9,7 +9,7 @@ LIB_SRC = \ @@ -9,7 +9,7 @@ LIB_SRC = \
LIB_CLIENT_SRC = \
AddressBook.cpp BOB.cpp ClientContext.cpp I2PTunnel.cpp I2PService.cpp \
SAM.cpp SOCKS.cpp HTTPProxy.cpp
SAM.cpp SOCKS.cpp HTTP.cpp HTTPProxy.cpp I2CP.cpp
# also: Daemon{Linux,Win32}.cpp will be added later
DAEMON_SRC = \

14
tests/Makefile

@ -0,0 +1,14 @@ @@ -0,0 +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
all: $(TESTS) run
test-http-%: test-http-%.cpp ../HTTP.cpp
$(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) -o $@ $^
run: $(TESTS)
@for TEST in $(TESTS); do ./$$TEST ; done
clean:
rm -f $(TESTS)

82
tests/test-http-req.cpp

@ -0,0 +1,82 @@ @@ -0,0 +1,82 @@
#include <cassert>
#include "../HTTP.h"
using namespace i2p::http;
int main(int argc, char *argv[]) {
HTTPReq *req;
int ret = 0, len = 0;
const char *buf;
buf =
"GET / HTTP/1.0\r\n"
"User-Agent: curl/7.26.0\r\n"
"Host: inr.i2p\r\n"
"Accept: */*\r\n"
"\r\n"
"test";
len = strlen(buf);
req = new HTTPReq;
assert((ret = req->parse(buf, len)) == len - 4);
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);
assert(req->headers.count("User-Agent") == 1);
assert(req->headers.find("Host")->second == "inr.i2p");
assert(req->headers.find("Accept")->second == "*/*");
assert(req->headers.find("User-Agent")->second == "curl/7.26.0");
delete req;
buf =
"GET / HTTP/1.0\r\n"
"\r\n";
len = strlen(buf);
req = new HTTPReq;
assert((ret = req->parse(buf, len)) == len);
assert(req->version == "HTTP/1.0");
assert(req->method == "GET");
assert(req->uri == "/");
assert(req->host == "");
assert(req->headers.size() == 0);
delete req;
buf =
"GET / HTTP/1.1\r\n"
"\r\n";
len = strlen(buf);
req = new HTTPReq;
assert((ret = req->parse(buf, len)) == -1); /* no host header */
delete req;
buf =
"GET / HTTP/1.0\r\n"
"";
len = strlen(buf);
req = new HTTPReq;
assert((ret = req->parse(buf, len)) == 0); /* request not completed */
delete req;
buf =
"GET http://inr.i2p HTTP/1.1\r\n"
"Host: stats.i2p\r\n"
"Accept: */*\r\n"
"\r\n";
len = strlen(buf);
req = new HTTPReq;
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() == 2);
assert(req->headers.count("Host") == 1);
assert(req->headers.count("Accept") == 1);
delete req;
return 0;
}
/* vim: expandtab:ts=2 */

37
tests/test-http-res.cpp

@ -0,0 +1,37 @@ @@ -0,0 +1,37 @@
#include <cassert>
#include "../HTTP.h"
using namespace i2p::http;
int main(int argc, char *argv[]) {
HTTPRes *res;
int ret = 0, len = 0;
const char *buf;
buf =
"HTTP/1.1 304 Not Modified\r\n"
"Date: Thu, 14 Apr 2016 00:00:00 GMT\r\n"
"Server: nginx/1.2.1\r\n"
"Content-Length: 536\r\n"
"\r\n";
len = strlen(buf);
res = new HTTPRes;
assert((ret = res->parse(buf, len)) == len);
assert(res->version == "HTTP/1.1");
assert(res->status == "Not Modified");
assert(res->code == 304);
assert(res->headers.size() == 3);
assert(res->headers.count("Date") == 1);
assert(res->headers.count("Server") == 1);
assert(res->headers.count("Content-Length") == 1);
assert(res->headers.find("Date")->second == "Thu, 14 Apr 2016 00:00:00 GMT");
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);
delete res;
return 0;
}
/* vim: expandtab:ts=2 */

110
tests/test-http-url.cpp

@ -0,0 +1,110 @@ @@ -0,0 +1,110 @@
#include <cassert>
#include "../HTTP.h"
using namespace i2p::http;
int main(int argc, char *argv[]) {
std::map<std::string, std::string> params;
URL *url;
url = new URL;
assert(url->parse("https://127.0.0.1:7070/asdasd?12345") == true);
assert(url->schema == "https");
assert(url->user == "");
assert(url->pass == "");
assert(url->host == "127.0.0.1");
assert(url->port == 7070);
assert(url->path == "/asdasd");
assert(url->query == "12345");
assert(url->to_string() == "https://127.0.0.1:7070/asdasd?12345");
delete url;
url = new URL;
assert(url->parse("http://user:password@site.com:8080/asdasd?123456") == true);
assert(url->schema == "http");
assert(url->user == "user");
assert(url->pass == "password");
assert(url->host == "site.com");
assert(url->port == 8080);
assert(url->path == "/asdasd");
assert(url->query == "123456");
delete url;
url = new URL;
assert(url->parse("http://user:password@site.com/asdasd?name=value") == true);
assert(url->schema == "http");
assert(url->user == "user");
assert(url->pass == "password");
assert(url->host == "site.com");
assert(url->port == 0);
assert(url->path == "/asdasd");
assert(url->query == "name=value");
delete url;
url = new URL;
assert(url->parse("http://user:@site.com/asdasd?name=value1&name=value2") == true);
assert(url->schema == "http");
assert(url->user == "user");
assert(url->pass == "");
assert(url->host == "site.com");
assert(url->port == 0);
assert(url->path == "/asdasd");
assert(url->query == "name=value1&name=value2");
delete url;
url = new URL;
assert(url->parse("http://user@site.com/asdasd?name1=value1&name2&name3=value2") == true);
assert(url->schema == "http");
assert(url->user == "user");
assert(url->pass == "");
assert(url->host == "site.com");
assert(url->port == 0);
assert(url->path == "/asdasd");
assert(url->query == "name1=value1&name2&name3=value2");
assert(url->parse_query(params));
assert(params.size() == 3);
assert(params.count("name1") == 1);
assert(params.count("name2") == 1);
assert(params.count("name3") == 1);
assert(params.find("name1")->second == "value1");
assert(params.find("name2")->second == "");
assert(params.find("name3")->second == "value2");
delete url;
url = new URL;
assert(url->parse("http://@site.com:800/asdasd?") == true);
assert(url->schema == "http");
assert(url->user == "");
assert(url->pass == "");
assert(url->host == "site.com");
assert(url->port == 800);
assert(url->path == "/asdasd");
assert(url->query == "");
delete url;
url = new URL;
assert(url->parse("http://@site.com:17") == true);
assert(url->schema == "http");
assert(url->user == "");
assert(url->pass == "");
assert(url->host == "site.com");
assert(url->port == 17);
assert(url->path == "");
assert(url->query == "");
delete url;
url = new URL;
assert(url->parse("http://user:password@site.com:err_port/asdasd") == false);
assert(url->schema == "http");
assert(url->user == "user");
assert(url->pass == "password");
assert(url->host == "site.com");
assert(url->port == 0);
assert(url->path == "");
assert(url->query == "");
delete url;
return 0;
}
/* vim: expandtab:ts=2 */

19
tests/test-http-url_decode.cpp

@ -0,0 +1,19 @@ @@ -0,0 +1,19 @@
#include <cassert>
#include "../HTTP.h"
using namespace i2p::http;
int main(int argc, char *argv[]) {
std::string in("/%D1%81%D1%82%D1%80%D0%B0%D0%BD%D0%B8%D1%86%D0%B0/");
std::string out = UrlDecode(in);
assert(strcmp(out.c_str(), "/страница/") == 0);
in = "/%00/";
out = UrlDecode(in, false);
assert(strcmp(out.c_str(), "/%00/") == 0);
out = UrlDecode(in, true);
assert(strcmp(out.c_str(), "/\0/") == 0);
return 0;
}

14
util.h

@ -5,11 +5,25 @@ @@ -5,11 +5,25 @@
#include <string>
#include <iostream>
#include <boost/asio.hpp>
#include <boost/lexical_cast.hpp>
namespace i2p
{
namespace util
{
/**
wrapper arround boost::lexical_cast that "never" fails
*/
template <typename T>
T lexical_cast(const std::string & str, const T fallback) {
try {
return boost::lexical_cast<T>(str);
} catch ( ... ) {
return fallback;
}
}
namespace http
{
// in (lower case)

Loading…
Cancel
Save