Merge pull request #12 from mlt/wip/i2p

Few i2p fixes
This commit is contained in:
Arvid Norberg 2015-08-13 21:55:27 -04:00
commit 68bc4f2cf4
15 changed files with 205 additions and 69 deletions

View File

@ -39,6 +39,19 @@ POSSIBILITY OF SUCH DAMAGE.
namespace libtorrent
{
namespace string
{
enum flags_t
{
// use lower case alphabet used with i2p
lowercase = 0x1,
// don't insert padding
no_padding = 0x2,
// shortcut used for addresses as sha256 hashes
i2p = lowercase | no_padding
};
}
TORRENT_EXTRA_EXPORT std::string unescape_string(std::string const& s, error_code& ec);
// replaces all disallowed URL characters by their %-encoding
TORRENT_EXTRA_EXPORT std::string escape_string(const char* str, int len);
@ -58,7 +71,7 @@ namespace libtorrent
// encodes a string using the base64 scheme
TORRENT_EXTRA_EXPORT std::string base64encode(std::string const& s);
// encodes a string using the base32 scheme
TORRENT_EXTRA_EXPORT std::string base32encode(std::string const& s);
TORRENT_EXTRA_EXPORT std::string base32encode(std::string const& s, int flags=0);
TORRENT_EXTRA_EXPORT std::string base32decode(std::string const& s);
TORRENT_EXTRA_EXPORT std::string url_has_argument(

View File

@ -397,6 +397,8 @@ namespace libtorrent
// The URL specified an i2p address, but no i2p router is configured
no_i2p_router = 160,
// i2p acceptor is not available yet, can't announce without endpoint
no_i2p_endpoint = 161,

View File

@ -132,6 +132,7 @@ struct TORRENT_EXTRA_EXPORT http_connection
private:
#if TORRENT_USE_I2P
void connect_i2p_tracker(char const* destination);
void on_i2p_resolve(error_code const& e
, char const* destination);
#endif

View File

@ -100,7 +100,7 @@ namespace libtorrent
TORRENT_EXTRA_EXPORT tracker_response parse_tracker_response(
char const* data, int size, error_code& ec
, bool scrape_request, sha1_hash scrape_ih);
, int flags, sha1_hash scrape_ih);
TORRENT_EXTRA_EXPORT bool extract_peer_info(bdecode_node const& info
, peer_entry& ret, error_code& ec);

View File

@ -118,8 +118,12 @@ namespace libtorrent
enum kind_t
{
announce_request,
scrape_request
// do not compare against announce_request ! check if not scrape instead
announce_request = 0,
scrape_request = 1,
// affects interpretation of peers string in HTTP response
// see parse_tracker_response()
i2p = 2
};
std::string url;

View File

@ -2282,7 +2282,10 @@ namespace libtorrent
std::string remote_address;
std::back_insert_iterator<std::string> out(remote_address);
detail::write_address(remote().address(), out);
handshake["yourip"] = remote_address;
#if TORRENT_USE_I2P
if (!is_i2p(*get_socket()))
#endif
handshake["yourip"] = remote_address;
handshake["reqq"] = m_settings.get_int(settings_pack::max_allowed_in_request_queue);
boost::shared_ptr<torrent> t = associated_torrent().lock();
TORRENT_ASSERT(t);

View File

@ -321,15 +321,23 @@ namespace libtorrent
return ret;
}
std::string base32encode(std::string const& s)
std::string base32encode(std::string const& s, int flags)
{
static const char base32_table[] =
static const char base32_table_canonical[] =
{
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', '2', '3', '4', '5', '6', '7'
};
static const char base32_table_lowercase[] =
{
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
'y', 'z', '2', '3', '4', '5', '6', '7'
};
const char *base32_table = 0 != (flags & string::lowercase) ? base32_table_lowercase : base32_table_canonical;
int input_output_mapping[] = {0, 2, 4, 5, 7, 8};
@ -365,10 +373,13 @@ namespace libtorrent
ret += base32_table[outbuf[j]];
}
// write pad
for (int j = 0; j < 8 - num_out; ++j)
if (0 == (flags & string::no_padding))
{
ret += '=';
// write pad
for (int j = 0; j < 8 - num_out; ++j)
{
ret += '=';
}
}
}
return ret;

View File

@ -369,11 +369,16 @@ void http_connection::start(std::string const& hostname, int port
#if TORRENT_USE_I2P
if (is_i2p)
{
if (hostname.length() < 516) // Base64 encoded destination with optional .i2p
{
#if defined TORRENT_ASIO_DEBUGGING
add_outstanding_async("http_connection::on_i2p_resolve");
add_outstanding_async("http_connection::on_i2p_resolve");
#endif
i2p_conn->async_name_lookup(hostname.c_str(), boost::bind(&http_connection::on_i2p_resolve
, me, _1, _2));
i2p_conn->async_name_lookup(hostname.c_str(), boost::bind(&http_connection::on_i2p_resolve
, me, _1, _2));
}
else
connect_i2p_tracker(hostname.c_str());
}
else
#endif
@ -470,19 +475,8 @@ void http_connection::close(bool force)
}
#if TORRENT_USE_I2P
void http_connection::on_i2p_resolve(error_code const& e
, char const* destination)
void http_connection::connect_i2p_tracker(char const* destination)
{
#if defined TORRENT_ASIO_DEBUGGING
complete_async("http_connection::on_i2p_resolve");
#endif
if (e)
{
callback(e);
close();
return;
}
#ifdef TORRENT_USE_OPENSSL
TORRENT_ASSERT(m_ssl == false);
TORRENT_ASSERT(m_sock.get<socket_type>());
@ -501,6 +495,21 @@ void http_connection::on_i2p_resolve(error_code const& e
m_sock.async_connect(tcp::endpoint(), boost::bind(&http_connection::on_connect
, shared_from_this(), _1));
}
void http_connection::on_i2p_resolve(error_code const& e
, char const* destination)
{
#if defined TORRENT_ASIO_DEBUGGING
complete_async("http_connection::on_i2p_resolve");
#endif
if (e)
{
callback(e);
close();
return;
}
connect_i2p_tracker(destination);
}
#endif
void http_connection::on_resolve(error_code const& e

View File

@ -83,7 +83,7 @@ namespace libtorrent
{
std::string url = tracker_req().url;
if (tracker_req().kind == tracker_request::scrape_request)
if (0 != (tracker_req().kind & tracker_request::scrape_request))
{
// find and replace "announce" with "scrape"
// in request
@ -117,7 +117,7 @@ namespace libtorrent
url += "info_hash=";
url += escape_string((const char*)&tracker_req().info_hash[0], 20);
if (tracker_req().kind == tracker_request::announce_request)
if (0 == (tracker_req().kind & tracker_request::scrape_request))
{
const char* event_string[] = {"completed", "started", "stopped", "paused"};
@ -168,10 +168,10 @@ namespace libtorrent
#if TORRENT_USE_I2P
if (i2p && tracker_req().i2pconn)
{
url += "&ip=";
url += escape_string(tracker_req().i2pconn->local_endpoint().c_str()
, tracker_req().i2pconn->local_endpoint().size());
url += ".i2p";
if (tracker_req().i2pconn->local_endpoint().empty())
fail(error_code(errors::no_i2p_endpoint), -1, "Waiting for i2p acceptor from SAM bridge", 5);
else
url += "&ip=" + tracker_req ().i2pconn->local_endpoint () + ".i2p";
}
else
#endif
@ -332,8 +332,7 @@ namespace libtorrent
}
tracker_response resp = parse_tracker_response(data, size, ecode
, tracker_req().kind == tracker_request::scrape_request
, tracker_req().info_hash);
, tracker_req().kind, tracker_req().info_hash);
if (!resp.warning_message.empty())
cb->tracker_warning(tracker_req(), resp.warning_message);
@ -354,7 +353,7 @@ namespace libtorrent
}
// do slightly different things for scrape requests
if (tracker_req().kind == tracker_request::scrape_request)
if (0 != (tracker_req().kind & tracker_request::scrape_request))
{
cb->tracker_scrape_response(tracker_req(), resp.complete
, resp.incomplete, resp.downloaded, resp.downloaders);
@ -423,7 +422,7 @@ namespace libtorrent
}
tracker_response parse_tracker_response(char const* data, int size, error_code& ec
, bool scrape_request, sha1_hash scrape_ih)
, int flags, sha1_hash scrape_ih)
{
tracker_response resp;
@ -463,7 +462,7 @@ namespace libtorrent
if (warning)
resp.warning_message = warning.string_value();
if (scrape_request)
if (0 != (flags & tracker_request::scrape_request))
{
bdecode_node files = e.dict_find_dict("files");
if (!files)
@ -499,16 +498,34 @@ namespace libtorrent
{
char const* peers = peers_ent.string_ptr();
int len = peers_ent.string_length();
resp.peers4.reserve(len / 6);
for (int i = 0; i < len; i += 6)
#if TORRENT_USE_I2P
if (0 != (flags & tracker_request::i2p))
{
if (len - i < 6) break;
error_code parse_error;
for (int i = 0; i < len; i += 32)
{
if (len - i < 32) break;
peer_entry p;
p.hostname = base32encode(std::string(peers + i, 32), string::i2p);
p.hostname += ".b32.i2p";
p.port = 6881;
resp.peers.push_back (p);
}
}
else
#endif
{
resp.peers4.reserve(len / 6);
for (int i = 0; i < len; i += 6)
{
if (len - i < 6) break;
ipv4_peer_entry p;
error_code ec;
p.ip = detail::read_v4_address(peers).to_v4().to_bytes();
p.port = detail::read_uint16(peers);
resp.peers4.push_back(p);
ipv4_peer_entry p;
error_code ec;
p.ip = detail::read_v4_address(peers).to_v4().to_bytes();
p.port = detail::read_uint16(peers);
resp.peers4.push_back(p);
}
}
}
else if (peers_ent && peers_ent.type() == bdecode_node::list_t)

View File

@ -414,6 +414,7 @@ namespace libtorrent
}
}
error_code ec(result, get_i2p_category());
switch (result)
{
case i2p_error::no_error:
@ -421,7 +422,6 @@ namespace libtorrent
break;
default:
{
error_code ec (result, get_i2p_category ());
handle_error (ec, h);
return;
}
@ -449,7 +449,7 @@ namespace libtorrent
case read_connect_response:
case read_session_create_response:
case read_name_lookup_response:
(*h)(e);
(*h)(ec);
std::vector<char>().swap(m_buffer);
break;
case read_accept_response:

View File

@ -1266,6 +1266,7 @@ namespace aux {
#endif
#if TORRENT_USE_I2P
req.i2pconn = &m_i2p_conn;
req.kind |= tracker_request::i2p;
#endif
if (is_any(req.bind_ip)) req.bind_ip = m_listen_interface.address();

View File

@ -46,6 +46,9 @@ POSSIBILITY OF SUCH DAMAGE.
#include <boost/bind.hpp>
#include <boost/make_shared.hpp>
#if TORRENT_USE_I2P
# include <boost/algorithm/string/predicate.hpp>
#endif
#ifdef TORRENT_USE_OPENSSL
#include "libtorrent/ssl_stream.hpp"
@ -3244,7 +3247,7 @@ namespace libtorrent
req.filter = m_ip_filter;
req.info_hash = m_torrent_file->info_hash();
req.kind = tracker_request::scrape_request;
req.kind |= tracker_request::scrape_request;
req.url = m_trackers[i].url;
req.auth = tracker_login();
req.key = tracker_key();
@ -3267,7 +3270,7 @@ namespace libtorrent
TORRENT_ASSERT(is_single_thread());
INVARIANT_CHECK;
TORRENT_ASSERT(req.kind == tracker_request::scrape_request);
TORRENT_ASSERT(0 != (req.kind & tracker_request::scrape_request));
announce_entry* ae = find_tracker(req);
if (ae)
@ -3325,7 +3328,7 @@ namespace libtorrent
TORRENT_ASSERT(is_single_thread());
INVARIANT_CHECK;
TORRENT_ASSERT(r.kind == tracker_request::announce_request);
TORRENT_ASSERT(0 == (r.kind & tracker_request::scrape_request));
// TODO: 2 this looks suspicious. Figure out why it makes sense to use the
// first IP in this list and leave a comment here
@ -3418,20 +3421,22 @@ namespace libtorrent
{
// this is an i2p name, we need to use the sam connection
// to do the name lookup
/*
m_ses.m_i2p_conn.async_name_lookup(i->ip.c_str()
, boost::bind(&torrent::on_i2p_resolve
, shared_from_this(), _1));
*/
// it seems like you're not supposed to do a name lookup
// on the peers returned from the tracker, but just strip
// the .i2p and use it as a destination
std::string hostname = i->hostname.substr(i->hostname.size() - 4);
torrent_state st = get_peer_list_state();
need_peer_list();
if (m_peer_list->add_i2p_peer(hostname.c_str(), peer_info::tracker, 0, &st))
state_updated();
peers_erased(st.erased);
if (boost::algorithm::ends_with(i->hostname, ".b32.i2p"))
{
#if defined TORRENT_ASIO_DEBUGGING
add_outstanding_async("torrent::on_i2p_resolve");
#endif
r.i2pconn->async_name_lookup(i->hostname.c_str()
, boost::bind(&torrent::on_i2p_resolve
, shared_from_this(), _1, _2));
}
else {
torrent_state st = get_peer_list_state();
need_peer_list();
if (m_peer_list->add_i2p_peer (i->hostname.c_str (), peer_info::tracker, 0, &st))
state_updated ();
peers_erased (st.erased);
}
}
else
#endif
@ -3647,6 +3652,9 @@ namespace libtorrent
INVARIANT_CHECK;
#if defined TORRENT_ASIO_DEBUGGING
complete_async("torrent::on_i2p_resolve");
#endif
#ifndef TORRENT_DISABLE_LOGGING
if (ec)
debug_log("i2p_resolve error: %s", ec.message().c_str());
@ -7128,6 +7136,8 @@ namespace libtorrent
error_code ec;
torrent_peer const* p = *i;
address addr = p->address();
if (p->is_i2p_addr)
continue;
if (p->banned)
{
#if TORRENT_USE_IPV6
@ -11801,7 +11811,7 @@ namespace libtorrent
debug_log("*** tracker error: (%d) %s %s", ec.value()
, ec.message().c_str(), msg.c_str());
#endif
if (r.kind == tracker_request::announce_request)
if (0 == (r.kind & tracker_request::scrape_request))
{
announce_entry* ae = find_tracker(r);
if (ae)
@ -11824,7 +11834,7 @@ namespace libtorrent
, ae?ae->fails:0, response_code, r.url, ec, msg);
}
}
else if (r.kind == tracker_request::scrape_request)
else if (0 != (r.kind & tracker_request::scrape_request))
{
if (response_code == 410)
{

View File

@ -288,9 +288,9 @@ namespace libtorrent
// use if if it hasn't expired
if (aux::time_now() < cc->second.expires)
{
if (tracker_req().kind == tracker_request::announce_request)
if (0 == (tracker_req().kind & tracker_request::scrape_request))
send_udp_announce();
else if (tracker_req().kind == tracker_request::scrape_request)
else if (0 != (tracker_req().kind & tracker_request::scrape_request))
send_udp_scrape();
return;
}
@ -468,9 +468,9 @@ namespace libtorrent
cce.connection_id = connection_id;
cce.expires = aux::time_now() + seconds(m_man.settings().get_int(settings_pack::udp_tracker_token_expiry));
if (tracker_req().kind == tracker_request::announce_request)
if (0 == (tracker_req().kind & tracker_request::scrape_request))
send_udp_announce();
else if (tracker_req().kind == tracker_request::scrape_request)
else if (0 != (tracker_req().kind & tracker_request::scrape_request))
send_udp_scrape();
return true;
}

View File

@ -118,6 +118,11 @@ TORRENT_TEST(string)
TEST_CHECK(base32encode("fooba") == "MZXW6YTB");
TEST_CHECK(base32encode("foobar") == "MZXW6YTBOI======");
// base32 for i2p
TEST_CHECK(base32encode("fo", string::no_padding) == "MZXQ");
TEST_CHECK(base32encode("foob", string::i2p) == "mzxw6yq");
TEST_CHECK(base32encode("foobar", string::lowercase) == "mzxw6ytboi======");
TEST_CHECK(base32decode("") == "");
TEST_CHECK(base32decode("MY======") == "f");
TEST_CHECK(base32decode("MZXQ====") == "fo");

View File

@ -101,6 +101,66 @@ TORRENT_TEST(parse_peers4)
}
}
TORRENT_TEST(parse_i2p_peers)
{
// d8:completei8e10:incompletei4e8:intervali3600e5:peers352: ...
char const response[] = { 0x64, 0x38, 0x3a, 0x63, 0x6f, 0x6d,
0x70, 0x6c, 0x65, 0x74, 0x65, 0x69, 0x38, 0x65, 0x31, 0x30,
0x3a, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74,
0x65, 0x69, 0x34, 0x65, 0x38, 0x3a, 0x69, 0x6e, 0x74, 0x65,
0x72, 0x76, 0x61, 0x6c, 0x69, 0x33, 0x36, 0x30, 0x30, 0x65,
0x35, 0x3a, 0x70, 0x65, 0x65, 0x72, 0x73, 0x33, 0x35, 0x32,
0x3a, 0xb1, 0x84, 0xe0, 0x96, 0x1f, 0xdb, 0xf2, 0xc9, 0xb0,
0x53, 0x9a, 0x31, 0xa5, 0x35, 0xcd, 0xe8, 0x59, 0xa0, 0x7c,
0xcd, 0xf2, 0x7c, 0x81, 0x81, 0x02, 0x11, 0x7b, 0xb4, 0x2a,
0xd1, 0x20, 0x87, 0xd6, 0x1b, 0x06, 0x4c, 0xbb, 0x4c, 0x4e,
0x30, 0xf9, 0xa3, 0x5d, 0x58, 0xa0, 0xa5, 0x10, 0x48, 0xfa,
0x9b, 0x3b, 0x10, 0x86, 0x43, 0x5c, 0x2e, 0xa2, 0xa6, 0x22,
0x31, 0xd0, 0x63, 0x6a, 0xfb, 0x4f, 0x25, 0x5b, 0xe2, 0x29,
0xbc, 0xcc, 0xa0, 0x1a, 0x0a, 0x30, 0x45, 0x32, 0xa1, 0xc8,
0x49, 0xf7, 0x9e, 0x03, 0xfd, 0x34, 0x80, 0x9a, 0x5b, 0xe9,
0x78, 0x04, 0x48, 0x4e, 0xbd, 0xc0, 0x5c, 0xdd, 0x4f, 0xf8,
0xbd, 0xc8, 0x4c, 0x4b, 0xcc, 0xf6, 0x25, 0x1b, 0xb3, 0x4d,
0xc0, 0x91, 0xb1, 0x4b, 0xb6, 0xbd, 0x95, 0xb7, 0x8e, 0x88,
0x79, 0xa8, 0xaa, 0x83, 0xa5, 0x7e, 0xec, 0x17, 0x60, 0x8d,
0x1d, 0xe2, 0xbe, 0x16, 0x35, 0x83, 0x25, 0xee, 0xe4, 0xd5,
0xbe, 0x54, 0x7b, 0xc8, 0x00, 0xdc, 0x5d, 0x56, 0xc7, 0x29,
0xd2, 0x1e, 0x6d, 0x7a, 0xfb, 0xfc, 0xef, 0x36, 0x05, 0x8a,
0xd0, 0xa7, 0x05, 0x4c, 0x11, 0xd5, 0x50, 0xe6, 0x2d, 0x7b,
0xe0, 0x7d, 0x84, 0xda, 0x47, 0x48, 0x9d, 0xf9, 0x77, 0xa2,
0xc7, 0x78, 0x90, 0xa4, 0xb5, 0x05, 0xf4, 0x95, 0xea, 0x36,
0x7b, 0x92, 0x8c, 0x5b, 0xf7, 0x8b, 0x18, 0x94, 0x2c, 0x2f,
0x88, 0xcf, 0xf8, 0xec, 0x5c, 0x52, 0xa8, 0x98, 0x8f, 0xd1,
0xd3, 0xf0, 0xd8, 0x63, 0x19, 0x73, 0x33, 0xd7, 0xeb, 0x1f,
0x87, 0x1c, 0x9f, 0x5b, 0xce, 0xe4, 0xd0, 0x15, 0x4e, 0x38,
0xb7, 0xe3, 0xbd, 0x93, 0x64, 0xe2, 0x15, 0x3d, 0xfc, 0x56,
0x4f, 0xd4, 0x19, 0x62, 0xe0, 0xb7, 0x59, 0x24, 0xff, 0x7f,
0x32, 0xdf, 0x56, 0xa5, 0x62, 0x42, 0x87, 0xa3, 0x04, 0xec,
0x09, 0x0a, 0x5b, 0x90, 0x48, 0x57, 0xc3, 0x32, 0x5f, 0x87,
0xeb, 0xfb, 0x08, 0x69, 0x6f, 0xa9, 0x46, 0x46, 0xa9, 0x54,
0x67, 0xec, 0x7b, 0x15, 0xc9, 0x68, 0x6b, 0x01, 0xb8, 0x10,
0x59, 0x53, 0x9c, 0xe6, 0x1b, 0x2e, 0x70, 0x72, 0x6e, 0x82,
0x7b, 0x03, 0xbc, 0xf2, 0x26, 0x9b, 0xb3, 0x91, 0xaa, 0xf1,
0xba, 0x62, 0x12, 0xbb, 0x74, 0x4b, 0x70, 0x44, 0x74, 0x19,
0xb2, 0xa1, 0x68, 0xd2, 0x30, 0xd6, 0xa5, 0x1b, 0xd9, 0xea,
0x4d, 0xdb, 0x81, 0x8e, 0x66, 0xbf, 0x4d, 0x6c, 0x32, 0x66,
0xc2, 0x8a, 0x22, 0x6b, 0x47, 0xc1, 0xd1, 0x52, 0x61, 0x66,
0xa0, 0x75, 0xab, 0x65 };
error_code ec;
tracker_response resp = parse_tracker_response(response, sizeof(response)
, ec, tracker_request::i2p, sha1_hash());
TEST_EQUAL(ec, error_code());
TEST_EQUAL(resp.peers.size(), 11);
if (resp.peers.size() == 11)
{
TEST_EQUAL(resp.peers[0].hostname, "wgcobfq73pzmtmcttiy2knon5bm2a7gn6j6idaiccf53ikwrecdq.b32.i2p");
// TEST_EQUAL(resp.peers[9].hostname, "cbmvhhhgdmxha4toqj5qhphse2n3henk6g5geev3orfxardudgza.b32.i2p");
TEST_EQUAL(resp.peers[10].hostname, "ufunemgwuun5t2sn3oay4zv7jvwdezwcrirgwr6b2fjgczvaowvq.b32.i2p");
}
}
TORRENT_TEST(parse_interval)
{
char const response[] = "d8:intervali1042e12:min intervali10e5:peers0:e";