Merge branch 'openssl'

This commit is contained in:
Jeff 2016-05-11 08:19:51 -04:00
commit 0a83d8e6a0
48 changed files with 1729 additions and 763 deletions

View File

@ -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

View File

@ -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;

View File

@ -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)
;
}

View File

@ -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 ();*/
}

View File

@ -273,7 +273,7 @@ namespace crypto
#endif
};
void InitCrypto ();
void InitCrypto (bool precomputation);
void TerminateCrypto ();
}
}

View File

@ -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();
}

View File

@ -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;
}
}
}

View File

@ -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
View 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
View 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__ */

File diff suppressed because it is too large Load Diff

View File

@ -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__ */

View File

@ -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 (

View File

@ -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

View File

@ -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)
{

View File

@ -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:

View File

@ -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"; }

View File

@ -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

View File

@ -95,6 +95,7 @@ namespace data
private:
void CreateVerifier () const;
void UpdateVerifier (i2p::crypto::Verifier * verifier) const;
private:

View File

@ -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
View 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}/

View File

@ -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
------------

View File

@ -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

View File

@ -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; };

View File

@ -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)

View File

@ -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
View File

@ -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
View File

@ -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:

View File

@ -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 ()
{

View File

@ -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
{

View File

@ -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"

View File

@ -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 ();
}

View File

@ -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"
)

View 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
View File

@ -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
View File

@ -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
View File

@ -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
View File

@ -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/

View File

@ -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
-------

View File

@ -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
------------

View File

@ -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

View File

@ -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
View 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
View 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
View 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
View 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 */

View 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
View File

@ -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)