From cbd67ed729e8da2bc66626dd7a57d6a908949453 Mon Sep 17 00:00:00 2001 From: Arvid Norberg Date: Tue, 22 May 2007 20:44:18 +0000 Subject: [PATCH] added support for authentication in the url_parser and added a test for it --- include/libtorrent/tracker_manager.hpp | 2 +- include/libtorrent/upnp.hpp | 2 +- include/libtorrent/web_peer_connection.hpp | 1 + src/http_connection.cpp | 9 +- src/peer_connection.cpp | 2 +- src/torrent.cpp | 11 ++- src/tracker_manager.cpp | 27 ++++-- src/upnp.cpp | 6 +- src/web_peer_connection.cpp | 17 +++- test/Jamfile | 1 + test/test_primitives.cpp | 95 ++++++++++++++++++++++ 11 files changed, 155 insertions(+), 18 deletions(-) create mode 100644 test/test_primitives.cpp diff --git a/include/libtorrent/tracker_manager.hpp b/include/libtorrent/tracker_manager.hpp index a75e15693..fc1a9f6dc 100755 --- a/include/libtorrent/tracker_manager.hpp +++ b/include/libtorrent/tracker_manager.hpp @@ -76,7 +76,7 @@ namespace libtorrent // returns -1 if gzip header is invalid or the header size in bytes TORRENT_EXPORT int gzip_header(const char* buf, int size); - TORRENT_EXPORT boost::tuple + TORRENT_EXPORT boost::tuple parse_url_components(std::string url); struct TORRENT_EXPORT tracker_request diff --git a/include/libtorrent/upnp.hpp b/include/libtorrent/upnp.hpp index ce125458f..d4b701aad 100644 --- a/include/libtorrent/upnp.hpp +++ b/include/libtorrent/upnp.hpp @@ -45,7 +45,7 @@ POSSIBILITY OF SUCH DAMAGE. #include -#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) +#if (defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)) && !defined (TORRENT_UPNP_LOGGING) #define TORRENT_UPNP_LOGGING #endif diff --git a/include/libtorrent/web_peer_connection.hpp b/include/libtorrent/web_peer_connection.hpp index 569ea0b61..690066fb2 100755 --- a/include/libtorrent/web_peer_connection.hpp +++ b/include/libtorrent/web_peer_connection.hpp @@ -148,6 +148,7 @@ namespace libtorrent std::string m_server_string; http_parser m_parser; + std::string m_auth; std::string m_host; int m_port; std::string m_path; diff --git a/src/http_connection.cpp b/src/http_connection.cpp index 197b3029c..ba1592898 100644 --- a/src/http_connection.cpp +++ b/src/http_connection.cpp @@ -47,15 +47,18 @@ void http_connection::get(std::string const& url, time_duration timeout { m_redirect = handle_redirect; std::string protocol; + std::string auth; std::string hostname; std::string path; int port; - boost::tie(protocol, hostname, port, path) = parse_url_components(url); + boost::tie(protocol, auth, hostname, port, path) = parse_url_components(url); std::stringstream headers; headers << "GET " << path << " HTTP/1.0\r\n" "Host:" << hostname << - "\r\nConnection: close\r\n" - "\r\n"; + "\r\nConnection: close\r\n"; + if (!auth.empty()) + headers << "Authorization: Basic " << base64encode(auth) << "\r\n"; + headers << "\r\n"; sendbuffer = headers.str(); start(hostname, boost::lexical_cast(port), timeout); } diff --git a/src/peer_connection.cpp b/src/peer_connection.cpp index 58f1ddc4e..c1f925749 100755 --- a/src/peer_connection.cpp +++ b/src/peer_connection.cpp @@ -2426,7 +2426,7 @@ namespace libtorrent } else { - assert(i->info[j].peer != m_remote || i->finished_blocks[j]); + assert(i->info[j].peer != m_remote || i->info[j].finished); } } } diff --git a/src/torrent.cpp b/src/torrent.cpp index 3c9802fe7..1de10dd43 100755 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -1383,12 +1383,15 @@ namespace libtorrent else { std::string protocol; + std::string auth; std::string hostname; int port; std::string path; - boost::tie(protocol, hostname, port, path) + boost::tie(protocol, auth, hostname, port, path) = parse_url_components(url); + // TODO: should auth be used here? + tcp::resolver::query q(hostname, boost::lexical_cast(port)); m_host_resolver.async_resolve(q, m_ses.m_strand.wrap( bind(&torrent::on_name_lookup, shared_from_this(), _1, _2, url @@ -1427,11 +1430,11 @@ namespace libtorrent if (m_ses.is_aborted()) return; tcp::endpoint a(host->endpoint()); - std::string protocol; + + using boost::tuples::ignore; std::string hostname; int port; - std::string path; - boost::tie(protocol, hostname, port, path) + boost::tie(ignore, ignore, hostname, port, ignore) = parse_url_components(url); if (m_ses.m_ip_filter.access(a.address()) & ip_filter::blocked) diff --git a/src/tracker_manager.cpp b/src/tracker_manager.cpp index df69d9098..993d27497 100755 --- a/src/tracker_manager.cpp +++ b/src/tracker_manager.cpp @@ -424,11 +424,13 @@ namespace libtorrent m_connections.erase(i); } - - tuple + + // returns protocol, auth, hostname, port, path + tuple parse_url_components(std::string url) { std::string hostname; // hostname only + std::string auth; // user:pass std::string protocol; // should be http int port = 80; @@ -439,7 +441,7 @@ namespace libtorrent ++start; std::string::iterator end = std::find(url.begin(), url.end(), ':'); - protocol = std::string(start, end); + protocol.assign(start, end); if (end == url.end()) throw std::runtime_error("invalid url"); ++end; @@ -451,7 +453,20 @@ namespace libtorrent ++end; start = end; + std::string::iterator at = std::find(start, url.end(), '@'); + std::string::iterator colon = std::find(start, url.end(), ':'); end = std::find(start, url.end(), '/'); + + if (at != url.end() + && colon != url.end() + && colon < at + && at < end) + { + auth.assign(start, at); + start = at; + ++start; + } + std::string::iterator port_pos = std::find(start, url.end(), ':'); @@ -475,7 +490,7 @@ namespace libtorrent } start = end; - return make_tuple(protocol, hostname, port + return make_tuple(protocol, auth, hostname, port , std::string(start, url.end())); } @@ -499,7 +514,9 @@ namespace libtorrent int port; std::string request_string; - boost::tie(protocol, hostname, port, request_string) + using boost::tuples::ignore; + // TODO: should auth be used here? + boost::tie(protocol, ignore, hostname, port, request_string) = parse_url_components(req.url); boost::intrusive_ptr con; diff --git a/src/upnp.cpp b/src/upnp.cpp index 5232cc075..41c0c390a 100644 --- a/src/upnp.cpp +++ b/src/upnp.cpp @@ -391,10 +391,14 @@ try { std::string protocol; + std::string auth; // we don't have this device in our list. Add it - boost::tie(protocol, d.hostname, d.port, d.path) + boost::tie(protocol, auth, d.hostname, d.port, d.path) = parse_url_components(d.url); + // ignore the auth here. It will be re-parsed + // by the http connection later + if (protocol != "http") { #ifdef TORRENT_UPNP_LOGGING diff --git a/src/web_peer_connection.cpp b/src/web_peer_connection.cpp index cd0c709de..e71af6a0d 100755 --- a/src/web_peer_connection.cpp +++ b/src/web_peer_connection.cpp @@ -94,9 +94,12 @@ namespace libtorrent #endif std::string protocol; - boost::tie(protocol, m_host, m_port, m_path) + boost::tie(protocol, m_auth, m_host, m_port, m_path) = parse_url_components(url); - + + if (!m_auth.empty()) + m_auth = base64encode(m_auth); + m_server_string = "URL seed @ "; m_server_string += m_host; } @@ -201,6 +204,11 @@ namespace libtorrent request += "\r\nUser-Agent: "; request += m_ses.settings().user_agent; } + if (!m_auth.empty()) + { + request += "\r\nAuthorization: Basic "; + request += m_auth; + } if (ps.type == proxy_settings::http_pw) { request += "\r\nProxy-Authorization: Basic "; @@ -253,6 +261,11 @@ namespace libtorrent request += "\r\nUser-Agent: "; request += m_ses.settings().user_agent; } + if (!m_auth.empty()) + { + request += "\r\nAuthorization: Basic "; + request += m_auth; + } if (ps.type == proxy_settings::http_pw) { request += "\r\nProxy-Authorization: Basic "; diff --git a/test/Jamfile b/test/Jamfile index 17ca9cdbf..548ea76e2 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -16,6 +16,7 @@ test-suite libtorrent : [ run test_piece_picker.cpp ] # [ run test_entry.cpp ] [ run test_bencoding.cpp ] + [ run test_primitives.cpp ] [ run test_ip_filter.cpp ] [ run test_hasher.cpp ] [ run test_metadata_extension.cpp ] diff --git a/test/test_primitives.cpp b/test/test_primitives.cpp new file mode 100644 index 000000000..0d64e8fb2 --- /dev/null +++ b/test/test_primitives.cpp @@ -0,0 +1,95 @@ +#include "libtorrent/tracker_manager.hpp" +#include "libtorrent/http_tracker_connection.hpp" +#include "libtorrent/buffer.hpp" +#include +#include + +#include "test.hpp" + +using namespace libtorrent; +using namespace boost::tuples; + +tuple feed_bytes(http_parser& parser, char const* str) +{ + tuple ret(0, 0); + buffer::const_interval recv_buf(str, str + 1); + for (; *str; ++str) + { + recv_buf.end = str + 1; + int payload, protocol; + tie(payload, protocol) = parser.incoming(recv_buf); + ret.get<0>() += payload; + ret.get<1>() += protocol; + } + return ret; +} + +int test_main() +{ + using namespace libtorrent; + + TEST_CHECK(parse_url_components("http://foo:bar@host.com:80/path/to/file") + == make_tuple("http", "foo:bar", "host.com", 80, "/path/to/file")); + + TEST_CHECK(parse_url_components("http://host.com/path/to/file") + == make_tuple("http", "", "host.com", 80, "/path/to/file")); + + TEST_CHECK(parse_url_components("ftp://host.com:21/path/to/file") + == make_tuple("ftp", "", "host.com", 21, "/path/to/file")); + + TEST_CHECK(parse_url_components("http://host.com/path?foo:bar@foo:") + == make_tuple("http", "", "host.com", 80, "/path?foo:bar@foo:")); + + // base64 test vectors from http://www.faqs.org/rfcs/rfc4648.html + + TEST_CHECK(base64encode("") == ""); + TEST_CHECK(base64encode("f") == "Zg=="); + TEST_CHECK(base64encode("fo") == "Zm8="); + TEST_CHECK(base64encode("foo") == "Zm9v"); + TEST_CHECK(base64encode("foob") == "Zm9vYg=="); + TEST_CHECK(base64encode("fooba") == "Zm9vYmE="); + TEST_CHECK(base64encode("foobar") == "Zm9vYmFy"); + + // HTTP request parser + + http_parser parser; + boost::tuple received = feed_bytes(parser + , "HTTP/1.1 200 OK\r\n" + "Content-Length: 4\r\n" + "Content-Type: text/plain\r\n" + "\r\n" + "test"); + + TEST_CHECK(received == make_tuple(4, 64)); + TEST_CHECK(parser.finished()); + TEST_CHECK(std::equal(parser.get_body().begin, parser.get_body().end, "test")); + TEST_CHECK(parser.header("content-type") == "text/plain"); + TEST_CHECK(parser.header("content-length") == 4); + + parser.reset(); + + TEST_CHECK(!parser.finished()); + + char const* upnp_response = + "HTTP/1.1 200 OK\r\n" + "ST:upnp:rootdevice\r\n" + "USN:uuid:000f-66d6-7296000099dc::upnp:rootdevice\r\n" + "Location: http://192.168.1.1:5431/dyndev/uuid:000f-66d6-7296000099dc\r\n" + "Server: Custom/1.0 UPnP/1.0 Proc/Ver\r\n" + "EXT:\r\n" + "Cache-Control:max-age=180\r\n" + "DATE: Fri, 02 Jan 1970 08:10:38 GMT\r\n\r\n"; + + received = feed_bytes(parser, upnp_response); + + TEST_CHECK(received == make_tuple(0, int(strlen(upnp_response)))); + TEST_CHECK(parser.get_body().left() == 0); + TEST_CHECK(parser.header("st") == "upnp:rootdevice"); + TEST_CHECK(parser.header("location") + == "http://192.168.1.1:5431/dyndev/uuid:000f-66d6-7296000099dc"); + TEST_CHECK(parser.header("ext") == ""); + TEST_CHECK(parser.header("date") == "Fri, 02 Jan 1970 08:10:38 GMT"); + + return 0; +} +