mirror of
https://github.com/PurpleI2P/i2pd
synced 2024-11-10 08:00:38 +03:00
Merge branch 'openssl'
This commit is contained in:
commit
0a83d8e6a0
@ -328,7 +328,8 @@ 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 ();
|
||||
else
|
||||
LogPrint (eLogError, "Clients: I2P client tunnel with port ", port, " already exists");
|
||||
@ -382,7 +383,7 @@ 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 ();
|
||||
else
|
||||
|
@ -2,9 +2,9 @@
|
||||
#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"
|
||||
@ -78,8 +78,8 @@ 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;
|
||||
|
||||
|
21
Config.cpp
21
Config.cpp
@ -131,11 +131,19 @@ namespace config {
|
||||
#endif
|
||||
;
|
||||
|
||||
options_description limits("Limits options");
|
||||
limits.add_options()
|
||||
("limits.transittunnels", value<uint16_t>()->default_value(2500), "Maximum active transit sessions (default:2500)")
|
||||
;
|
||||
|
||||
options_description httpserver("HTTP Server options");
|
||||
httpserver.add_options()
|
||||
("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");
|
||||
@ -180,14 +188,27 @@ namespace config {
|
||||
("i2pcontrol.key", value<std::string>()->default_value("i2pcontrol.key.pem"), "I2PCP connection cerificate key")
|
||||
;
|
||||
|
||||
options_description precomputation("Precomputation options");
|
||||
precomputation.add_options()
|
||||
("precomputation.elgamal",
|
||||
#if defined(__x86_64__)
|
||||
value<bool>()->default_value(false),
|
||||
#else
|
||||
value<bool>()->default_value(true),
|
||||
#endif
|
||||
"Enable or disable elgamal precomputation table")
|
||||
;
|
||||
|
||||
m_OptionsDesc
|
||||
.add(general)
|
||||
.add(limits)
|
||||
.add(httpserver)
|
||||
.add(httpproxy)
|
||||
.add(socksproxy)
|
||||
.add(sam)
|
||||
.add(bob)
|
||||
.add(i2pcontrol)
|
||||
.add(precomputation)
|
||||
;
|
||||
}
|
||||
|
||||
|
143
Crypto.cpp
143
Crypto.cpp
@ -146,9 +146,87 @@ namespace crypto
|
||||
}
|
||||
|
||||
// DH/ElGamal
|
||||
|
||||
const int ELGAMAL_SHORT_EXPONENT_NUM_BITS = 226;
|
||||
const int ELGAMAL_SHORT_EXPONENT_NUM_BYTES = ELGAMAL_SHORT_EXPONENT_NUM_BITS/8+1;
|
||||
const int ELGAMAL_FULL_EXPONENT_NUM_BITS = 2048;
|
||||
const int ELGAMAL_FULL_EXPONENT_NUM_BYTES = ELGAMAL_FULL_EXPONENT_NUM_BITS/8;
|
||||
|
||||
#define elgp GetCryptoConstants ().elgp
|
||||
#define elgg GetCryptoConstants ().elgg
|
||||
|
||||
static BN_MONT_CTX * g_MontCtx = nullptr;
|
||||
static void PrecalculateElggTable (BIGNUM * table[][255], int len) // table is len's array of array of 255 bignums
|
||||
{
|
||||
if (len <= 0) return;
|
||||
BN_CTX * ctx = BN_CTX_new ();
|
||||
g_MontCtx = BN_MONT_CTX_new ();
|
||||
BN_MONT_CTX_set (g_MontCtx, elgp, ctx);
|
||||
auto montCtx = BN_MONT_CTX_new ();
|
||||
BN_MONT_CTX_copy (montCtx, g_MontCtx);
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
table[i][0] = BN_new ();
|
||||
if (!i)
|
||||
BN_to_montgomery (table[0][0], elgg, montCtx, ctx);
|
||||
else
|
||||
BN_mod_mul_montgomery (table[i][0], table[i-1][254], table[i-1][0], montCtx, ctx);
|
||||
for (int j = 1; j < 255; j++)
|
||||
{
|
||||
table[i][j] = BN_new ();
|
||||
BN_mod_mul_montgomery (table[i][j], table[i][j-1], table[i][0], montCtx, ctx);
|
||||
}
|
||||
}
|
||||
BN_MONT_CTX_free (montCtx);
|
||||
BN_CTX_free (ctx);
|
||||
}
|
||||
|
||||
static void DestroyElggTable (BIGNUM * table[][255], int len)
|
||||
{
|
||||
for (int i = 0; i < len; i++)
|
||||
for (int j = 0; j < 255; j++)
|
||||
{
|
||||
BN_free (table[i][j]);
|
||||
table[i][j] = nullptr;
|
||||
}
|
||||
BN_MONT_CTX_free (g_MontCtx);
|
||||
}
|
||||
|
||||
static BIGNUM * ElggPow (const uint8_t * exp, int len, BIGNUM * table[][255], BN_CTX * ctx)
|
||||
// exp is in Big Endian
|
||||
{
|
||||
if (len <= 0) return nullptr;
|
||||
auto montCtx = BN_MONT_CTX_new ();
|
||||
BN_MONT_CTX_copy (montCtx, g_MontCtx);
|
||||
BIGNUM * res = nullptr;
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
if (res)
|
||||
{
|
||||
if (exp[i])
|
||||
BN_mod_mul_montgomery (res, res, table[len-1-i][exp[i]-1], montCtx, ctx);
|
||||
}
|
||||
else if (exp[i])
|
||||
res = BN_dup (table[len-i-1][exp[i]-1]);
|
||||
}
|
||||
if (res)
|
||||
BN_from_montgomery (res, res, montCtx, ctx);
|
||||
BN_MONT_CTX_free (montCtx);
|
||||
return res;
|
||||
}
|
||||
|
||||
static BIGNUM * ElggPow (const BIGNUM * exp, BIGNUM * table[][255], BN_CTX * ctx)
|
||||
{
|
||||
auto len = BN_num_bytes (exp);
|
||||
uint8_t * buf = new uint8_t[len];
|
||||
BN_bn2bin (exp, buf);
|
||||
auto ret = ElggPow (buf, len, table, ctx);
|
||||
delete[] buf;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static BIGNUM * (* g_ElggTable)[255] = nullptr;
|
||||
|
||||
// DH
|
||||
|
||||
DHKeys::DHKeys (): m_IsUpdated (true)
|
||||
@ -169,7 +247,23 @@ namespace crypto
|
||||
{
|
||||
if (m_DH->priv_key) { BN_free (m_DH->priv_key); m_DH->priv_key = NULL; };
|
||||
if (m_DH->pub_key) { BN_free (m_DH->pub_key); m_DH->pub_key = NULL; };
|
||||
#if !defined(__x86_64__) // use short exponent for non x64
|
||||
m_DH->priv_key = BN_new ();
|
||||
BN_rand (m_DH->priv_key, ELGAMAL_SHORT_EXPONENT_NUM_BITS, 0, 1);
|
||||
#endif
|
||||
if (g_ElggTable)
|
||||
{
|
||||
#if defined(__x86_64__)
|
||||
m_DH->priv_key = BN_new ();
|
||||
BN_rand (m_DH->priv_key, ELGAMAL_FULL_EXPONENT_NUM_BITS, 0, 1);
|
||||
#endif
|
||||
auto ctx = BN_CTX_new ();
|
||||
m_DH->pub_key = ElggPow (m_DH->priv_key, g_ElggTable, ctx);
|
||||
BN_CTX_free (ctx);
|
||||
}
|
||||
else
|
||||
DH_generate_key (m_DH);
|
||||
|
||||
if (priv) bn2buf (m_DH->priv_key, priv, 256);
|
||||
if (pub) bn2buf (m_DH->pub_key, pub, 256);
|
||||
m_IsUpdated = true;
|
||||
@ -200,11 +294,18 @@ namespace crypto
|
||||
ctx = BN_CTX_new ();
|
||||
// select random k
|
||||
BIGNUM * k = BN_new ();
|
||||
BN_rand_range (k, elgp);
|
||||
if (BN_is_zero (k)) BN_one (k);
|
||||
// caulculate a
|
||||
#if defined(__x86_64__)
|
||||
BN_rand (k, ELGAMAL_FULL_EXPONENT_NUM_BITS, -1, 1); // full exponent for x64
|
||||
#else
|
||||
BN_rand (k, ELGAMAL_SHORT_EXPONENT_NUM_BITS, -1, 1); // short exponent of 226 bits
|
||||
#endif
|
||||
// calculate a
|
||||
a = BN_new ();
|
||||
if (g_ElggTable)
|
||||
a = ElggPow (k, g_ElggTable, ctx);
|
||||
else
|
||||
BN_mod_exp (a, elgg, k, elgp, ctx);
|
||||
|
||||
BIGNUM * y = BN_new ();
|
||||
BN_bin2bn (key, 256, y);
|
||||
// calculate b1
|
||||
@ -279,6 +380,14 @@ namespace crypto
|
||||
{
|
||||
#if defined(__x86_64__) || defined(__i386__) || defined(_MSC_VER)
|
||||
RAND_bytes (priv, 256);
|
||||
#else
|
||||
// lower 226 bits (28 bytes and 2 bits) only. short exponent
|
||||
auto numBytes = (ELGAMAL_SHORT_EXPONENT_NUM_BITS)/8 + 1; // 29
|
||||
auto numZeroBytes = 256 - numBytes;
|
||||
RAND_bytes (priv + numZeroBytes, numBytes);
|
||||
memset (priv, 0, numZeroBytes);
|
||||
priv[numZeroBytes] &= 0x03;
|
||||
#endif
|
||||
BN_CTX * ctx = BN_CTX_new ();
|
||||
BIGNUM * p = BN_new ();
|
||||
BN_bin2bn (priv, 256, p);
|
||||
@ -286,11 +395,6 @@ namespace crypto
|
||||
bn2buf (p, pub, 256);
|
||||
BN_free (p);
|
||||
BN_CTX_free (ctx);
|
||||
#else
|
||||
DHKeys dh;
|
||||
dh.GenerateKeys (priv, pub);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
// HMAC
|
||||
@ -695,17 +799,38 @@ namespace crypto
|
||||
}
|
||||
}*/
|
||||
|
||||
void InitCrypto ()
|
||||
void InitCrypto (bool precomputation)
|
||||
{
|
||||
SSL_library_init ();
|
||||
/* auto numLocks = CRYPTO_num_locks();
|
||||
for (int i = 0; i < numLocks; i++)
|
||||
m_OpenSSLMutexes.emplace_back (new std::mutex);
|
||||
CRYPTO_set_locking_callback (OpensslLockingCallback);*/
|
||||
if (precomputation)
|
||||
{
|
||||
#if defined(__x86_64__)
|
||||
g_ElggTable = new BIGNUM * [ELGAMAL_FULL_EXPONENT_NUM_BYTES][255];
|
||||
PrecalculateElggTable (g_ElggTable, ELGAMAL_FULL_EXPONENT_NUM_BYTES);
|
||||
#else
|
||||
g_ElggTable = new BIGNUM * [ELGAMAL_SHORT_EXPONENT_NUM_BYTES][255];
|
||||
PrecalculateElggTable (g_ElggTable, ELGAMAL_SHORT_EXPONENT_NUM_BYTES);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void TerminateCrypto ()
|
||||
{
|
||||
if (g_ElggTable)
|
||||
{
|
||||
DestroyElggTable (g_ElggTable,
|
||||
#if defined(__x86_64__)
|
||||
ELGAMAL_FULL_EXPONENT_NUM_BYTES
|
||||
#else
|
||||
ELGAMAL_SHORT_EXPONENT_NUM_BYTES
|
||||
#endif
|
||||
);
|
||||
delete[] g_ElggTable; g_ElggTable = nullptr;
|
||||
}
|
||||
/* CRYPTO_set_locking_callback (nullptr);
|
||||
m_OpenSSLMutexes.clear ();*/
|
||||
}
|
||||
|
2
Crypto.h
2
Crypto.h
@ -273,7 +273,7 @@ namespace crypto
|
||||
#endif
|
||||
};
|
||||
|
||||
void InitCrypto ();
|
||||
void InitCrypto (bool precomputation);
|
||||
void TerminateCrypto ();
|
||||
}
|
||||
}
|
||||
|
10
Daemon.cpp
10
Daemon.cpp
@ -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
|
||||
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
|
||||
@ -117,7 +118,8 @@ namespace i2p
|
||||
LogPrint(eLogDebug, "FS: main config file: ", config);
|
||||
LogPrint(eLogDebug, "FS: data directory: ", datadir);
|
||||
|
||||
i2p::crypto::InitCrypto ();
|
||||
bool precomputation; i2p::config::GetOption("precomputation.elgamal", precomputation);
|
||||
i2p::crypto::InitCrypto (precomputation);
|
||||
i2p::context.Init ();
|
||||
|
||||
uint16_t port; i2p::config::GetOption("port", port);
|
||||
@ -140,6 +142,8 @@ namespace i2p
|
||||
i2p::context.SetSupportsV6 (ipv6);
|
||||
i2p::context.SetSupportsV4 (ipv4);
|
||||
i2p::context.SetAcceptsTunnels (!transit);
|
||||
uint16_t transitTunnels; i2p::config::GetOption("limits.transittunnels", transitTunnels);
|
||||
SetMaxNumTransitTunnels (transitTunnels);
|
||||
|
||||
bool isFloodfill; i2p::config::GetOption("floodfill", isFloodfill);
|
||||
if (isFloodfill) {
|
||||
@ -199,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();
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "Timestamp.h"
|
||||
#include "NetDb.h"
|
||||
#include "Destination.h"
|
||||
#include "util.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
@ -36,27 +37,29 @@ 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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
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 +69,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,7 +79,7 @@ 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;
|
||||
@ -780,5 +783,19 @@ namespace client
|
||||
}
|
||||
LogPrint(eLogError, "Destinations: Can't save keys to ", path);
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<const i2p::stream::Stream> > ClientDestination::GetAllStreams () const
|
||||
{
|
||||
std::vector<std::shared_ptr<const i2p::stream::Stream> > ret;
|
||||
if (m_StreamingDestination)
|
||||
{
|
||||
for (auto& it: m_StreamingDestination->GetStreams ())
|
||||
ret.push_back (it.second);
|
||||
}
|
||||
for (auto& it: m_StreamingDestinationsByPorts)
|
||||
for (auto& it1: it.second->GetStreams ())
|
||||
ret.push_back (it1.second);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -159,6 +159,7 @@ namespace client
|
||||
|
||||
// for HTTP only
|
||||
int GetNumRemoteLeaseSets () const { return m_RemoteLeaseSets.size (); };
|
||||
std::vector<std::shared_ptr<const i2p::stream::Stream> > GetAllStreams () const;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
389
HTTP.cpp
Normal file
389
HTTP.cpp
Normal file
@ -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"
|
||||
|
||||
namespace i2p {
|
||||
namespace http {
|
||||
const char *HTTP_METHODS[] = {
|
||||
"GET", "HEAD", "POST", "PUT", "PATCH",
|
||||
"DELETE", "OPTIONS", "CONNECT",
|
||||
NULL
|
||||
};
|
||||
const char *HTTP_VERSIONS[] = {
|
||||
"HTTP/1.0", "HTTP/1.1", NULL
|
||||
};
|
||||
|
||||
bool in_cstr_array(const char **haystack, const char *needle) {
|
||||
for (const char *p = haystack[0]; p != NULL; p++) {
|
||||
if (strcmp(p, needle) == 0)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
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 (!in_cstr_array(HTTP_METHODS, tokens[0].c_str()))
|
||||
return -1;
|
||||
if (!in_cstr_array(HTTP_VERSIONS, tokens[2].c_str()))
|
||||
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 (!in_cstr_array(HTTP_VERSIONS, tokens[0].c_str()))
|
||||
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
Normal file
121
HTTP.h
Normal file
@ -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 char *HTTP_METHODS[]; /**< list of valid HTTP methods */
|
||||
extern const char *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__ */
|
845
HTTPServer.cpp
845
HTTPServer.cpp
File diff suppressed because it is too large
Load Diff
117
HTTPServer.h
117
HTTPServer.h
@ -1,107 +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 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);
|
||||
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);
|
||||
|
||||
|
||||
protected:
|
||||
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
|
||||
@ -109,7 +42,7 @@ namespace util
|
||||
public:
|
||||
|
||||
HTTPServer (const std::string& address, int port);
|
||||
virtual ~HTTPServer ();
|
||||
~HTTPServer ();
|
||||
|
||||
void Start ();
|
||||
void Stop ();
|
||||
@ -120,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:
|
||||
|
||||
@ -127,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__ */
|
||||
|
@ -286,6 +286,16 @@ namespace i2p
|
||||
return !msg->GetPayload ()[DATABASE_STORE_TYPE_OFFSET]; // 0- RouterInfo
|
||||
}
|
||||
|
||||
static uint16_t g_MaxNumTransitTunnels = DEFAULT_MAX_NUM_TRANSIT_TUNNELS; // TODO:
|
||||
void SetMaxNumTransitTunnels (uint16_t maxNumTransitTunnels)
|
||||
{
|
||||
if (maxNumTransitTunnels > 0 && maxNumTransitTunnels <= 10000 && g_MaxNumTransitTunnels != maxNumTransitTunnels)
|
||||
{
|
||||
LogPrint (eLogDebug, "I2NP: Max number of transit tunnels set to ", maxNumTransitTunnels);
|
||||
g_MaxNumTransitTunnels = maxNumTransitTunnels;
|
||||
}
|
||||
}
|
||||
|
||||
bool HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * clearText)
|
||||
{
|
||||
for (int i = 0; i < num; i++)
|
||||
@ -298,7 +308,7 @@ namespace i2p
|
||||
i2p::crypto::ElGamalDecrypt (i2p::context.GetEncryptionPrivateKey (), record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText);
|
||||
// replace record to reply
|
||||
if (i2p::context.AcceptsTunnels () &&
|
||||
i2p::tunnel::tunnels.GetTransitTunnels ().size () <= MAX_NUM_TRANSIT_TUNNELS &&
|
||||
i2p::tunnel::tunnels.GetTransitTunnels ().size () <= g_MaxNumTransitTunnels &&
|
||||
!i2p::transport::transports.IsBandwidthExceeded ())
|
||||
{
|
||||
auto transitTunnel = i2p::tunnel::CreateTransitTunnel (
|
||||
|
@ -97,8 +97,6 @@ namespace i2p
|
||||
const uint8_t DATABASE_LOOKUP_TYPE_ROUTERINFO_LOOKUP = 0x08; // 1000
|
||||
const uint8_t DATABASE_LOOKUP_TYPE_EXPLORATORY_LOOKUP = 0x0C; // 1100
|
||||
|
||||
const unsigned int MAX_NUM_TRANSIT_TUNNELS = 2500;
|
||||
|
||||
namespace tunnel
|
||||
{
|
||||
class InboundTunnel;
|
||||
@ -259,6 +257,9 @@ namespace tunnel
|
||||
|
||||
std::vector<std::shared_ptr<I2NPMessage> > m_TunnelMsgs, m_TunnelGatewayMsgs;
|
||||
};
|
||||
|
||||
const uint16_t DEFAULT_MAX_NUM_TRANSIT_TUNNELS = 2500;
|
||||
void SetMaxNumTransitTunnels (uint16_t maxNumTransitTunnels);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -83,6 +83,10 @@ namespace client
|
||||
m_RouterManagerHandlers["Reseed"] = &I2PControlService::ReseedHandler;
|
||||
m_RouterManagerHandlers["Shutdown"] = &I2PControlService::ShutdownHandler;
|
||||
m_RouterManagerHandlers["ShutdownGraceful"] = &I2PControlService::ShutdownGracefulHandler;
|
||||
|
||||
// NetworkSetting
|
||||
m_NetworkSettingHandlers["i2p.router.net.bw.in"] = &I2PControlService::InboundBandwidthLimit;
|
||||
m_NetworkSettingHandlers["i2p.router.net.bw.out"] = &I2PControlService::OutboundBandwidthLimit;
|
||||
}
|
||||
|
||||
I2PControlService::~I2PControlService ()
|
||||
@ -496,6 +500,22 @@ namespace client
|
||||
}
|
||||
}
|
||||
|
||||
void I2PControlService::InboundBandwidthLimit (const std::string& value, std::ostringstream& results)
|
||||
{
|
||||
if (value != "null")
|
||||
i2p::context.SetBandwidth (std::atoi(value.c_str()));
|
||||
int bw = i2p::context.GetBandwidthLimit();
|
||||
InsertParam (results, "i2p.router.net.bw.in", bw);
|
||||
}
|
||||
|
||||
void I2PControlService::OutboundBandwidthLimit (const std::string& value, std::ostringstream& results)
|
||||
{
|
||||
if (value != "null")
|
||||
i2p::context.SetBandwidth (std::atoi(value.c_str()));
|
||||
int bw = i2p::context.GetBandwidthLimit();
|
||||
InsertParam (results, "i2p.router.net.bw.out", bw);
|
||||
}
|
||||
|
||||
// certificate
|
||||
void I2PControlService::CreateCertificate (const char *crt_path, const char *key_path)
|
||||
{
|
||||
|
@ -94,6 +94,8 @@ namespace client
|
||||
|
||||
// NetworkSetting
|
||||
typedef void (I2PControlService::*NetworkSettingRequestHandler)(const std::string& value, std::ostringstream& results);
|
||||
void InboundBandwidthLimit (const std::string& value, std::ostringstream& results);
|
||||
void OutboundBandwidthLimit (const std::string& value, std::ostringstream& results);
|
||||
|
||||
private:
|
||||
|
||||
|
@ -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"; }
|
||||
|
24
Identity.cpp
24
Identity.cpp
@ -311,18 +311,18 @@ namespace data
|
||||
switch (keyType)
|
||||
{
|
||||
case SIGNING_KEY_TYPE_DSA_SHA1:
|
||||
m_Verifier.reset (new i2p::crypto::DSAVerifier (m_StandardIdentity.signingKey));
|
||||
UpdateVerifier (new i2p::crypto::DSAVerifier (m_StandardIdentity.signingKey));
|
||||
break;
|
||||
case SIGNING_KEY_TYPE_ECDSA_SHA256_P256:
|
||||
{
|
||||
size_t padding = 128 - i2p::crypto::ECDSAP256_KEY_LENGTH; // 64 = 128 - 64
|
||||
m_Verifier.reset (new i2p::crypto::ECDSAP256Verifier (m_StandardIdentity.signingKey + padding));
|
||||
UpdateVerifier (new i2p::crypto::ECDSAP256Verifier (m_StandardIdentity.signingKey + padding));
|
||||
break;
|
||||
}
|
||||
case SIGNING_KEY_TYPE_ECDSA_SHA384_P384:
|
||||
{
|
||||
size_t padding = 128 - i2p::crypto::ECDSAP384_KEY_LENGTH; // 32 = 128 - 96
|
||||
m_Verifier.reset (new i2p::crypto::ECDSAP384Verifier (m_StandardIdentity.signingKey + padding));
|
||||
UpdateVerifier (new i2p::crypto::ECDSAP384Verifier (m_StandardIdentity.signingKey + padding));
|
||||
break;
|
||||
}
|
||||
case SIGNING_KEY_TYPE_ECDSA_SHA512_P521:
|
||||
@ -331,7 +331,7 @@ namespace data
|
||||
memcpy (signingKey, m_StandardIdentity.signingKey, 128);
|
||||
size_t excessLen = i2p::crypto::ECDSAP521_KEY_LENGTH - 128; // 4 = 132- 128
|
||||
memcpy (signingKey + 128, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types
|
||||
m_Verifier.reset (new i2p::crypto::ECDSAP521Verifier (signingKey));
|
||||
UpdateVerifier (new i2p::crypto::ECDSAP521Verifier (signingKey));
|
||||
break;
|
||||
}
|
||||
case SIGNING_KEY_TYPE_RSA_SHA256_2048:
|
||||
@ -340,7 +340,7 @@ namespace data
|
||||
memcpy (signingKey, m_StandardIdentity.signingKey, 128);
|
||||
size_t excessLen = i2p::crypto::RSASHA2562048_KEY_LENGTH - 128; // 128 = 256- 128
|
||||
memcpy (signingKey + 128, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types
|
||||
m_Verifier.reset (new i2p::crypto:: RSASHA2562048Verifier (signingKey));
|
||||
UpdateVerifier (new i2p::crypto:: RSASHA2562048Verifier (signingKey));
|
||||
break;
|
||||
}
|
||||
case SIGNING_KEY_TYPE_RSA_SHA384_3072:
|
||||
@ -349,7 +349,7 @@ namespace data
|
||||
memcpy (signingKey, m_StandardIdentity.signingKey, 128);
|
||||
size_t excessLen = i2p::crypto::RSASHA3843072_KEY_LENGTH - 128; // 256 = 384- 128
|
||||
memcpy (signingKey + 128, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types
|
||||
m_Verifier.reset (new i2p::crypto:: RSASHA3843072Verifier (signingKey));
|
||||
UpdateVerifier (new i2p::crypto:: RSASHA3843072Verifier (signingKey));
|
||||
break;
|
||||
}
|
||||
case SIGNING_KEY_TYPE_RSA_SHA512_4096:
|
||||
@ -358,13 +358,13 @@ namespace data
|
||||
memcpy (signingKey, m_StandardIdentity.signingKey, 128);
|
||||
size_t excessLen = i2p::crypto::RSASHA5124096_KEY_LENGTH - 128; // 384 = 512- 128
|
||||
memcpy (signingKey + 128, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types
|
||||
m_Verifier.reset (new i2p::crypto:: RSASHA5124096Verifier (signingKey));
|
||||
UpdateVerifier (new i2p::crypto:: RSASHA5124096Verifier (signingKey));
|
||||
break;
|
||||
}
|
||||
case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519:
|
||||
{
|
||||
size_t padding = 128 - i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH; // 96 = 128 - 32
|
||||
m_Verifier.reset (new i2p::crypto::EDDSA25519Verifier (m_StandardIdentity.signingKey + padding));
|
||||
UpdateVerifier (new i2p::crypto::EDDSA25519Verifier (m_StandardIdentity.signingKey + padding));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@ -372,6 +372,14 @@ namespace data
|
||||
}
|
||||
}
|
||||
|
||||
void IdentityEx::UpdateVerifier (i2p::crypto::Verifier * verifier) const
|
||||
{
|
||||
if (!m_Verifier || !verifier)
|
||||
m_Verifier.reset (verifier);
|
||||
else
|
||||
delete verifier;
|
||||
}
|
||||
|
||||
void IdentityEx::DropVerifier () const
|
||||
{
|
||||
// TODO: potential race condition with Verify
|
||||
|
@ -95,6 +95,7 @@ namespace data
|
||||
private:
|
||||
|
||||
void CreateVerifier () const;
|
||||
void UpdateVerifier (i2p::crypto::Verifier * verifier) const;
|
||||
|
||||
private:
|
||||
|
||||
|
4
Makefile
4
Makefile
@ -14,7 +14,11 @@ USE_STATIC := no
|
||||
|
||||
ifeq ($(UNAME),Darwin)
|
||||
DAEMON_SRC += DaemonLinux.cpp
|
||||
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
Normal file
29
Makefile.homebrew
Normal file
@ -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}/
|
11
README.md
11
README.md
@ -15,14 +15,11 @@ Donations
|
||||
BTC: 1K7Ds6KUeR8ya287UC4rYTjvC96vXyZbDY
|
||||
LTC: LKQirrYrDeTuAPnpYq5y7LVKtywfkkHi59
|
||||
ANC: AQJYweYYUqM1nVfLqfoSMpUMfzxvS4Xd7z
|
||||
DOGE: DNXLQKziRPAsD9H3DFNjk4fLQrdaSX893Y
|
||||
|
||||
Downloads
|
||||
------------
|
||||
|
||||
Official binary releases could be found at:
|
||||
http://i2pd.website/releases/
|
||||
older releases
|
||||
http://download.i2p.io/purplei2p/i2pd/releases/
|
||||
Documentation:
|
||||
--------------
|
||||
http://i2pd.readthedocs.org
|
||||
|
||||
Supported OS
|
||||
------------
|
||||
|
@ -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;
|
||||
@ -356,8 +361,10 @@ namespace i2p
|
||||
delete[] buf;
|
||||
}
|
||||
|
||||
i2p::data::RouterInfo routerInfo(i2p::fs::DataDirPath (ROUTER_INFO)); // TODO
|
||||
m_RouterInfo.SetRouterIdentity (GetIdentity ());
|
||||
i2p::data::RouterInfo routerInfo(i2p::fs::DataDirPath (ROUTER_INFO));
|
||||
if (!routerInfo.IsUnreachable ()) // router.info looks good
|
||||
{
|
||||
m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ());
|
||||
m_RouterInfo.SetProperty ("coreVersion", I2P_VERSION);
|
||||
m_RouterInfo.SetProperty ("router.version", I2P_VERSION);
|
||||
@ -365,6 +372,12 @@ namespace i2p
|
||||
// Migration to 0.9.24. TODO: remove later
|
||||
m_RouterInfo.DeleteProperty ("coreVersion");
|
||||
m_RouterInfo.DeleteProperty ("stat_uptime");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, ROUTER_INFO, " is malformed. Creating new");
|
||||
NewRouterInfo ();
|
||||
}
|
||||
|
||||
if (IsUnreachable ())
|
||||
SetReachable (); // we assume reachable until we discover firewall through peer tests
|
||||
|
@ -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; };
|
||||
|
@ -104,6 +104,8 @@ namespace data
|
||||
{
|
||||
if (LoadFile ())
|
||||
ReadFromBuffer (false);
|
||||
else
|
||||
m_IsUnreachable = true;
|
||||
}
|
||||
|
||||
void RouterInfo::ReadFromBuffer (bool verifySignature)
|
||||
@ -514,19 +516,20 @@ namespace data
|
||||
m_BufferLen += privateKeys.GetPublic ()->GetSignatureLen ();
|
||||
}
|
||||
|
||||
void RouterInfo::SaveToFile (const std::string& fullPath)
|
||||
bool RouterInfo::SaveToFile (const std::string& fullPath)
|
||||
{
|
||||
m_FullPath = fullPath;
|
||||
if (m_Buffer)
|
||||
{
|
||||
std::ofstream f (fullPath, std::ofstream::binary | std::ofstream::out);
|
||||
if (f.is_open ())
|
||||
f.write ((char *)m_Buffer, m_BufferLen);
|
||||
else
|
||||
LogPrint(eLogError, "RouterInfo: Can't save to ", fullPath);
|
||||
}
|
||||
else
|
||||
if (!m_Buffer) {
|
||||
LogPrint (eLogError, "RouterInfo: Can't save, m_Buffer == NULL");
|
||||
return false;
|
||||
}
|
||||
std::ofstream f (fullPath, std::ofstream::binary | std::ofstream::out);
|
||||
if (!f.is_open ()) {
|
||||
LogPrint(eLogError, "RouterInfo: Can't save to ", fullPath);
|
||||
return false;
|
||||
}
|
||||
f.write ((char *)m_Buffer, m_BufferLen);
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t RouterInfo::ReadString (char * str, std::istream& s)
|
||||
@ -633,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)
|
||||
|
@ -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; };
|
||||
@ -161,7 +162,7 @@ namespace data
|
||||
|
||||
bool IsUpdated () const { return m_IsUpdated; };
|
||||
void SetUpdated (bool updated) { m_IsUpdated = updated; };
|
||||
void SaveToFile (const std::string& fullPath);
|
||||
bool SaveToFile (const std::string& fullPath);
|
||||
|
||||
std::shared_ptr<RouterProfile> GetProfile () const;
|
||||
void SaveProfile () { if (m_Profile) m_Profile->Save (); };
|
||||
|
24
SAM.cpp
24
SAM.cpp
@ -47,9 +47,8 @@ namespace client
|
||||
break;
|
||||
case eSAMSocketTypeStream:
|
||||
{
|
||||
if (m_Session) {
|
||||
if (m_Session)
|
||||
m_Session->DelSocket (shared_from_this ());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case eSAMSocketTypeAcceptor:
|
||||
@ -67,6 +66,7 @@ namespace client
|
||||
}
|
||||
m_SocketType = eSAMSocketTypeTerminated;
|
||||
if (m_Socket.is_open()) m_Socket.close ();
|
||||
m_Session = nullptr;
|
||||
}
|
||||
|
||||
void SAMSocket::ReceiveHandshake ()
|
||||
@ -720,7 +720,7 @@ namespace client
|
||||
m_IsRunning = false;
|
||||
m_Acceptor.cancel ();
|
||||
for (auto it: m_Sessions)
|
||||
delete it.second;
|
||||
it.second->CloseStreams ();
|
||||
m_Sessions.clear ();
|
||||
m_Service.stop ();
|
||||
if (m_Thread)
|
||||
@ -774,7 +774,7 @@ namespace client
|
||||
Accept ();
|
||||
}
|
||||
|
||||
SAMSession * SAMBridge::CreateSession (const std::string& id, const std::string& destination,
|
||||
std::shared_ptr<SAMSession> SAMBridge::CreateSession (const std::string& id, const std::string& destination,
|
||||
const std::map<std::string, std::string> * params)
|
||||
{
|
||||
std::shared_ptr<ClientDestination> localDestination = nullptr;
|
||||
@ -799,8 +799,9 @@ namespace client
|
||||
}
|
||||
if (localDestination)
|
||||
{
|
||||
auto session = std::make_shared<SAMSession>(localDestination);
|
||||
std::unique_lock<std::mutex> l(m_SessionsMutex);
|
||||
auto ret = m_Sessions.insert (std::pair<std::string, SAMSession *>(id, new SAMSession (localDestination)));
|
||||
auto ret = m_Sessions.insert (std::make_pair(id, session));
|
||||
if (!ret.second)
|
||||
LogPrint (eLogWarning, "SAM: Session ", id, " already exists");
|
||||
return ret.first->second;
|
||||
@ -809,20 +810,25 @@ namespace client
|
||||
}
|
||||
|
||||
void SAMBridge::CloseSession (const std::string& id)
|
||||
{
|
||||
std::shared_ptr<SAMSession> session;
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_SessionsMutex);
|
||||
auto it = m_Sessions.find (id);
|
||||
if (it != m_Sessions.end ())
|
||||
{
|
||||
auto session = it->second;
|
||||
session = it->second;
|
||||
m_Sessions.erase (it);
|
||||
}
|
||||
}
|
||||
if (session)
|
||||
{
|
||||
session->localDestination->StopAcceptingStreams ();
|
||||
session->CloseStreams ();
|
||||
m_Sessions.erase (it);
|
||||
delete session;
|
||||
}
|
||||
}
|
||||
|
||||
SAMSession * SAMBridge::FindSession (const std::string& id) const
|
||||
std::shared_ptr<SAMSession> SAMBridge::FindSession (const std::string& id) const
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_SessionsMutex);
|
||||
auto it = m_Sessions.find (id);
|
||||
|
8
SAM.h
8
SAM.h
@ -128,7 +128,7 @@ namespace client
|
||||
std::string m_ID; // nickname
|
||||
bool m_IsSilent;
|
||||
std::shared_ptr<i2p::stream::Stream> m_Stream;
|
||||
SAMSession * m_Session;
|
||||
std::shared_ptr<SAMSession> m_Session;
|
||||
};
|
||||
|
||||
struct SAMSession
|
||||
@ -176,10 +176,10 @@ namespace client
|
||||
void Stop ();
|
||||
|
||||
boost::asio::io_service& GetService () { return m_Service; };
|
||||
SAMSession * CreateSession (const std::string& id, const std::string& destination, // empty string means transient
|
||||
std::shared_ptr<SAMSession> CreateSession (const std::string& id, const std::string& destination, // empty string means transient
|
||||
const std::map<std::string, std::string> * params);
|
||||
void CloseSession (const std::string& id);
|
||||
SAMSession * FindSession (const std::string& id) const;
|
||||
std::shared_ptr<SAMSession> FindSession (const std::string& id) const;
|
||||
|
||||
private:
|
||||
|
||||
@ -200,7 +200,7 @@ namespace client
|
||||
boost::asio::ip::udp::endpoint m_DatagramEndpoint, m_SenderEndpoint;
|
||||
boost::asio::ip::udp::socket m_DatagramSocket;
|
||||
mutable std::mutex m_SessionsMutex;
|
||||
std::map<std::string, SAMSession *> m_Sessions;
|
||||
std::map<std::string, std::shared_ptr<SAMSession> > m_Sessions;
|
||||
uint8_t m_DatagramReceiveBuffer[i2p::datagram::MAX_DATAGRAM_SIZE+1];
|
||||
|
||||
public:
|
||||
|
@ -265,8 +265,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,12 +281,19 @@ 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))
|
||||
LogPrint (eLogError, "SSU: message 'created' signature verification failed");
|
||||
|
||||
// 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");
|
||||
Failed ();
|
||||
}
|
||||
}
|
||||
|
||||
void SSUSession::ProcessSessionConfirmed (const uint8_t * buf, size_t len)
|
||||
{
|
||||
@ -313,12 +318,18 @@ namespace transport
|
||||
paddingSize &= 0x0F; // %16
|
||||
if (paddingSize > 0) paddingSize = 16 - paddingSize;
|
||||
payload += paddingSize;
|
||||
// verify
|
||||
if (m_SignedData && !m_SignedData->Verify (m_RemoteIdentity, payload))
|
||||
LogPrint (eLogError, "SSU message 'confirmed' signature verification failed");
|
||||
// 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");
|
||||
Failed ();
|
||||
}
|
||||
}
|
||||
|
||||
void SSUSession::SendSessionRequest ()
|
||||
{
|
||||
|
@ -119,7 +119,7 @@ namespace tunnel
|
||||
if (ret.second)
|
||||
HandleOutOfSequenceFragment (msgID, ret.first->second);
|
||||
else
|
||||
LogPrint (eLogError, "TunnelMessage: Incomplete message ", msgID, "already exists");
|
||||
LogPrint (eLogError, "TunnelMessage: Incomplete message ", msgID, " already exists");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -52,8 +52,8 @@ END
|
||||
|
||||
// Icon with lowest ID value placed first to ensure application icon
|
||||
// remains consistent on all systems.
|
||||
//MAINICON ICON "ictoopie.ico"
|
||||
MAINICON ICON "anke.ico"
|
||||
MAINICON ICON "ictoopie.ico"
|
||||
//MAINICON ICON "anke.ico"
|
||||
|
||||
MASCOT BITMAP "Anke_700px.bmp"
|
||||
|
||||
|
6
api.cpp
6
api.cpp
@ -28,7 +28,11 @@ namespace api
|
||||
i2p::fs::DetectDataDir(datadir, false);
|
||||
i2p::fs::Init();
|
||||
|
||||
i2p::crypto::InitCrypto ();
|
||||
#if defined(__x86_64__)
|
||||
i2p::crypto::InitCrypto (false);
|
||||
#else
|
||||
i2p::crypto::InitCrypto (true);
|
||||
#endif
|
||||
i2p::context.Init ();
|
||||
}
|
||||
|
||||
|
@ -74,6 +74,7 @@ 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"
|
||||
)
|
||||
|
||||
|
12
contrib/certificates/family/volatile.crt
Normal file
12
contrib/certificates/family/volatile.crt
Normal file
@ -0,0 +1,12 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIBxDCCAWmgAwIBAgIJAJnJIdKHYwWcMAoGCCqGSM49BAMCMGcxCzAJBgNVBAYT
|
||||
AkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRn
|
||||
aXRzIFB0eSBMdGQxIDAeBgNVBAMMF3ZvbGF0aWxlLmZhbWlseS5pMnAubmV0MB4X
|
||||
DTE2MDQyNjE1MjAyNloXDTI2MDQyNDE1MjAyNlowZzELMAkGA1UEBhMCQVUxEzAR
|
||||
BgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5
|
||||
IEx0ZDEgMB4GA1UEAwwXdm9sYXRpbGUuZmFtaWx5LmkycC5uZXQwWTATBgcqhkjO
|
||||
PQIBBggqhkjOPQMBBwNCAARf6LBfbbfL6HInvC/4wAGaN3rj0eeLE/OdBpA93R3L
|
||||
s8EUp0YTEJHWPo9APiKMmAwQSsMJfjhNrbp+UWEnnx2LMAoGCCqGSM49BAMCA0kA
|
||||
MEYCIQDpQu2KPV5G1JOFLoZvdj+rcvEnjxM/FxkaqikwkVx8FAIhANP7DkUal+GT
|
||||
SuiCtcqM4QyIBsfsCJBWEMzovft164Bo
|
||||
-----END CERTIFICATE-----
|
6
debian/copyright
vendored
6
debian/copyright
vendored
@ -3,9 +3,9 @@ Upstream-Name: i2pd
|
||||
Source: https://github.com/PurpleI2P
|
||||
|
||||
Files: *
|
||||
Copyright: 2013-2015 PurpleI2P
|
||||
Copyright: 2013-2016 PurpleI2P
|
||||
License: BSD-3-clause
|
||||
Copyright (c) 2013-2015, The PurpleI2P Project
|
||||
Copyright (c) 2013-2016, The PurpleI2P Project
|
||||
.
|
||||
All rights reserved.
|
||||
.
|
||||
@ -34,7 +34,7 @@ License: BSD-3-clause
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Files: debian/*
|
||||
Copyright: 2014-2015 hagen <hagen@i2pmail.org>
|
||||
Copyright: 2014-2016 hagen <hagen@i2pmail.org>
|
||||
2013-2015 Kill Your TV <killyourtv@i2pmail.org>
|
||||
License: GPL-2.0+
|
||||
This package is free software; you can redistribute it and/or modify
|
||||
|
107
debian/i2pd.1
vendored
107
debian/i2pd.1
vendored
@ -5,7 +5,7 @@ i2pd \- Load-balanced unspoofable packet switching network
|
||||
|
||||
.SH SYNOPSIS
|
||||
.B i2pd
|
||||
[\fIOPTION1\fR) [\fIOPTION2\fR]...
|
||||
[\fIOPTION1\fR] [\fIOPTION2\fR]...
|
||||
|
||||
.SH DESCRIPTION
|
||||
i2pd
|
||||
@ -18,59 +18,58 @@ network is both distributed and dynamic, with no trusted parties.
|
||||
Any of the configuration options below can be used in the \fBDAEMON_ARGS\fR variable in \fI/etc/default/i2pd\fR.
|
||||
.BR
|
||||
.TP
|
||||
\fB\-\-host=\fR
|
||||
The external IP (deprecated)
|
||||
.TP
|
||||
\fB\-\-port=\fR
|
||||
The external port to listen on
|
||||
.TP
|
||||
\fB\-\-httpport=\fR
|
||||
The HTTP port to listen on
|
||||
.TP
|
||||
\fB\-\-log=\fR[\fI1\fR|\fI0\fR]
|
||||
.br
|
||||
Enable of disable logging to a file. \fI1\fR for yes, \fI0\fR for no. (default: \fI0\fR, off)
|
||||
.TP
|
||||
\fB\-\-daemon=\fR[\fI1\fR|\fI0\fR]
|
||||
Enable or disable daemon mode. Daemon mode is enabled with \fI1\fR and disabled with \fI0\fR. (default: \fI0\fR, off)
|
||||
.TP
|
||||
\fB\-\-service=\fR[\fI1\fR|\fI0\fR]
|
||||
If enabled, system folders (\fB/var/run/i2pd.pid\fR, \fB/var/log/i2pd.log\fR, \fB/var/lib/i2pd\fR) will be used. If off, \fB$HOME/.i2pd\fR will be used instead. (default: \fI0\fR, off).
|
||||
.TP
|
||||
\fB\-\-unreachable=\fR[\fI1\fR|\fI0\fR]
|
||||
\fI1\fR if router is declared as unreachable and works through introducers. (default: \fI0\fR, off)
|
||||
.TP
|
||||
\fB\-\-v6=\fR[\fI1\fR|\fI0\fR]
|
||||
\fI1\fR if \fBi2pd\fR should communicate via IPv6. (default: \fI0\fR, off)
|
||||
.TP
|
||||
\fB\-\-floodfill=\fR[\fI1\fR|\fI0\fR]
|
||||
\fI1\fR if \fBi2pd\fR should become a floodfill. (default: \fI0\fR, off)
|
||||
.TP
|
||||
\fB\-\-bandwidth=\fR[\fI1\fR|\fI0\fR]
|
||||
\fIL\fR if \fBi2pd\fR should be limited to 32KiB/s. Enabling floodfill will automatically set this to \fI0\fR (default: \fI0\fR, no limit)
|
||||
.TP
|
||||
\fB\-\-httpproxyport=\fR
|
||||
The local port for the HTTP Proxy to listen on (default: \fI4446\fR)
|
||||
.TP
|
||||
\fB\-\-socksproxyport=\fR
|
||||
The local port for the SOCKS proxy to listen on (default: \fI4447\fR)
|
||||
.TP
|
||||
\fB\-\-proxykeys=\fR
|
||||
An optional keys file for tunnel local destination (both HTTP and SOCKS)
|
||||
.TP
|
||||
\fB\-\-samport=\fR
|
||||
Port of SAM bridge. Usually \fI7656\fR. SAM will not be enabled if this is not set. (default: unset)
|
||||
.TP
|
||||
\fB\-\-bobport=\fR
|
||||
Port of BOB command channel. Usually \fI2827\fR. BOB will not be enabled if this is not set. (default: unset)
|
||||
.TP
|
||||
\fB\-\-i2pcontrolport=\fR
|
||||
Port of I2P control service. Usually \fI7650\fR. I2PControl will not be enabled if this is not set. (default: unset)
|
||||
\fB\-\-help\fR
|
||||
Show available options.
|
||||
.TP
|
||||
\fB\-\-conf=\fR
|
||||
Config file (default: \fI~/.i2pd/i2pd.conf\fR or \fI/var/lib/i2pd/i2pd.conf\fR)
|
||||
.BR
|
||||
This parameter will be silently ignored if the specified config file does not exist.
|
||||
Options specified on the command line take precedence over those in the config file.
|
||||
.TP
|
||||
\fB\-\-tunconf=\fR
|
||||
Tunnels config file (default: \fI~/.i2pd/tunnels.conf\fR or \fI/var/lib/i2pd/tunnels.conf\fR)
|
||||
.TP
|
||||
\fB\-\-pidfile=\fR
|
||||
Where to write pidfile (don\'t write by default)
|
||||
.TP
|
||||
\fB\-\-log=\fR
|
||||
Logs destination: \fIstdout\fR, \fIfile\fR, \fIsyslog\fR (\fIstdout\fR if not set, \fIfile\fR - otherwise, for compatibility)
|
||||
.TP
|
||||
\fB\-\-loglevel=\fR
|
||||
Log messages above this level (\fIdebug\fR, \fBinfo\fR, \fIwarn\fR, \fIerror\fR)
|
||||
.TP
|
||||
\fB\-\-datadir=\fR
|
||||
Path to storage of i2pd data (RI, keys, peer profiles, ...)
|
||||
.TP
|
||||
\fB\-\-host=\fR
|
||||
The external IP address
|
||||
.TP
|
||||
\fB\-\-port=\fR
|
||||
The port to listen on for incoming connections
|
||||
.TP
|
||||
\fB\-\-daemon\fR
|
||||
Router will go to background after start
|
||||
.TP
|
||||
\fB\-\-service\fR
|
||||
Router will use system folders like \fI/var/lib/i2pd\fR
|
||||
.TP
|
||||
\fB\-\-ipv6\fR
|
||||
Enable communication through ipv6. false by default
|
||||
.TP
|
||||
\fB\-\-notransit\fR
|
||||
Router will not accept transit tunnels at startup
|
||||
.TP
|
||||
\fB\-\-floodfill\fR
|
||||
Router will be floodfill
|
||||
.TP
|
||||
\fB\-\-bandwidth=\fR
|
||||
Bandwidth limit: integer in KBps or letter aliases: \fIL (32KBps)\fR, O (256), P (2048), X (>9000)
|
||||
.TP
|
||||
\fB\-\-family=\fR
|
||||
Name of a family, router belongs to.
|
||||
.PP
|
||||
See service-specific parameters in page \fIdocs/configuration.md\fR or in example config file \fIdocs/i2pd.conf\fR
|
||||
|
||||
.SH FILES
|
||||
.PP
|
||||
@ -82,10 +81,10 @@ i2pd configuration files (when running as a system service)
|
||||
.PP
|
||||
/var/lib/i2pd/
|
||||
.RS 4
|
||||
i2pd profile directory (when running as a system service, see \fB\-\-service=\fR above)
|
||||
i2pd profile directory (when running as a system service, see \fB\-\-service\fR above)
|
||||
.RE
|
||||
.PP
|
||||
$HOME/.i2pd
|
||||
$HOME/.i2pd/
|
||||
.RS 4
|
||||
i2pd profile directory (when running as a normal user)
|
||||
.RE
|
||||
@ -95,7 +94,9 @@ i2pd profile directory (when running as a normal user)
|
||||
default I2P hosts file
|
||||
.SH AUTHOR
|
||||
This manual page was written by kytv <killyourtv@i2pmail.org> for the Debian system (but may be used by others).
|
||||
.BR
|
||||
.PP
|
||||
Updated by hagen <hagen@i2pmail.org> in 2016.
|
||||
.PP
|
||||
Permission is granted to copy, distribute and/or modify this document under the terms of the GNU General Public License, Version 2 or any later version published by the Free Software Foundation
|
||||
.BR
|
||||
On Debian systems, the complete text of the GNU General Public License can be found in /usr/share/common-licenses/GPL
|
||||
On Debian systems, the complete text of the GNU General Public License can be found in \fI/usr/share/common-licenses/GPL\fR
|
||||
|
19
debian/i2pd.conf
vendored
19
debian/i2pd.conf
vendored
@ -1,19 +0,0 @@
|
||||
ipv6
|
||||
|
||||
[httpproxy]
|
||||
address = 127.0.0.1
|
||||
port = 4444
|
||||
|
||||
# other services (disabled by default)
|
||||
#
|
||||
#[sam]
|
||||
#address = 127.0.0.1
|
||||
#port = 7656
|
||||
#
|
||||
#[bob]
|
||||
#address = 127.0.0.1
|
||||
#port = 2827
|
||||
#
|
||||
#[i2pcontrol]
|
||||
#address = 127.0.0.1
|
||||
#port = 7650
|
2
debian/i2pd.install
vendored
2
debian/i2pd.install
vendored
@ -1,5 +1,5 @@
|
||||
i2pd usr/sbin/
|
||||
debian/i2pd.conf etc/i2pd/
|
||||
docs/i2pd.conf etc/i2pd/
|
||||
debian/tunnels.conf etc/i2pd/
|
||||
debian/subscriptions.txt etc/i2pd/
|
||||
contrib/certificates/ usr/share/i2pd/
|
||||
|
@ -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
|
||||
-------
|
||||
|
||||
|
@ -16,8 +16,8 @@ If you are upgrading your very old router (< 2.3.0) see also [this](config_opts_
|
||||
* --logfile= - Path to logfile (default - autodetect)
|
||||
* --loglevel= - Log messages above this level (debug, *info, warn, error)
|
||||
* --datadir= - Path to storage of i2pd data (RI, keys, peer profiles, ...)
|
||||
* --host= - The external IP
|
||||
* --port= - The port to listen on
|
||||
* --host= - Router external IP for incoming connections
|
||||
* --port= - Port to listen for incoming connections (default: auto)
|
||||
* --daemon - Router will go to background after start
|
||||
* --service - Router will use system folders like '/var/lib/i2pd'
|
||||
* --ipv6 - Enable communication through ipv6. false by default
|
||||
@ -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
|
||||
@ -61,6 +64,10 @@ All options below still possible in cmdline, but better write it in config file:
|
||||
* --i2pcontrol.port= - Port of I2P control service. Usually 7650. I2PControl is off if not specified
|
||||
* --i2pcontrol.enabled= - If I2P control is enabled. false by default
|
||||
|
||||
* --precomputation.elgamal= - Use ElGamal precomputated tables. false for x64 and true for other platforms by default
|
||||
|
||||
* --limits.transittunnels= - Override maximum number of transit tunnels. 2500 by default
|
||||
|
||||
Config files
|
||||
------------
|
||||
|
||||
|
@ -69,8 +69,8 @@ port = 7070
|
||||
## Uncomment and set to 'false' to disable HTTP Proxy
|
||||
# enabled = true
|
||||
## Address and port service will listen on
|
||||
# address = 127.0.0.1
|
||||
# port = 4444
|
||||
address = 127.0.0.1
|
||||
port = 4444
|
||||
## Optional keys file for proxy local destination
|
||||
# keys = http-proxy-keys.dat
|
||||
|
||||
|
@ -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
|
||||
|
||||
# also: Daemon{Linux,Win32}.cpp will be added later
|
||||
DAEMON_SRC = \
|
||||
|
14
tests/Makefile
Normal file
14
tests/Makefile
Normal file
@ -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
Normal file
82
tests/test-http-req.cpp
Normal file
@ -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
Normal file
37
tests/test-http-res.cpp
Normal file
@ -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
Normal file
110
tests/test-http-url.cpp
Normal file
@ -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
Normal file
19
tests/test-http-url_decode.cpp
Normal file
@ -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
14
util.h
@ -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…
Reference in New Issue
Block a user