diff --git a/Jamfile b/Jamfile index a4212f02f..184f3bc48 100755 --- a/Jamfile +++ b/Jamfile @@ -300,6 +300,7 @@ SOURCES = enum_net broadcast_socket magnet_uri + parse_url # -- extensions -- metadata_transfer diff --git a/include/Makefile.am b/include/Makefile.am index 9ebcf2723..7613bf864 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -40,6 +40,7 @@ libtorrent/peer.hpp \ libtorrent/peer_connection.hpp \ libtorrent/bt_peer_connection.hpp \ libtorrent/web_peer_connection.hpp \ +libtorrent/parse_url.hpp \ libtorrent/pe_crypto.hpp \ libtorrent/magnet_uri.hpp \ libtorrent/natpmp.hpp \ diff --git a/include/libtorrent/parse_url.hpp b/include/libtorrent/parse_url.hpp new file mode 100644 index 000000000..18a6ddf90 --- /dev/null +++ b/include/libtorrent/parse_url.hpp @@ -0,0 +1,59 @@ +/* + +Copyright (c) 2008, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_PARSE_URL_HPP_INCLUDED +#define TORRENT_PARSE_URL_HPP_INCLUDED + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include +#include "libtorrent/config.hpp" + +namespace libtorrent +{ + + TORRENT_EXPORT boost::tuple + parse_url_components(std::string url); + +} + +#endif + diff --git a/include/libtorrent/tracker_manager.hpp b/include/libtorrent/tracker_manager.hpp index 56d7b7bec..c9d1f52c6 100755 --- a/include/libtorrent/tracker_manager.hpp +++ b/include/libtorrent/tracker_manager.hpp @@ -74,9 +74,6 @@ 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 - parse_url_components(std::string url); - struct TORRENT_EXPORT tracker_request { tracker_request() diff --git a/src/Makefile.am b/src/Makefile.am index 568198fc2..e81811d58 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -23,7 +23,8 @@ alert.cpp identify_client.cpp ip_filter.cpp file.cpp metadata_transfer.cpp \ logger.cpp file_pool.cpp ut_pex.cpp lsd.cpp upnp.cpp instantiate_connection.cpp \ socks5_stream.cpp socks4_stream.cpp http_stream.cpp connection_queue.cpp \ disk_io_thread.cpp ut_metadata.cpp magnet_uri.cpp udp_socket.cpp smart_ban.cpp \ -http_parser.cpp gzip.cpp disk_buffer_holder.cpp create_torrent.cpp GeoIP.c $(kademlia_sources) +http_parser.cpp gzip.cpp disk_buffer_holder.cpp create_torrent.cpp GeoIP.c \ +parse_url.cpp $(kademlia_sources) noinst_HEADERS = \ $(top_srcdir)/include/libtorrent/alert.hpp \ @@ -69,6 +70,7 @@ $(top_srcdir)/include/libtorrent/peer.hpp \ $(top_srcdir)/include/libtorrent/peer_connection.hpp \ $(top_srcdir)/include/libtorrent/bt_peer_connection.hpp \ $(top_srcdir)/include/libtorrent/web_peer_connection.hpp \ +$(top_srcdir)/include/libtorrent/pares_url.hpp \ $(top_srcdir)/include/libtorrent/pe_crypto.hpp \ $(top_srcdir)/include/libtorrent/natpmp.hpp \ $(top_srcdir)/include/libtorrent/pch.hpp \ diff --git a/src/http_connection.cpp b/src/http_connection.cpp index 12fb2195f..d2449fcbb 100644 --- a/src/http_connection.cpp +++ b/src/http_connection.cpp @@ -34,8 +34,9 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/escape_string.hpp" #include "libtorrent/instantiate_connection.hpp" #include "libtorrent/gzip.hpp" -#include "libtorrent/tracker_manager.hpp" +#include "libtorrent/parse_url.hpp" #include "libtorrent/socket.hpp" +#include "libtorrent/connection_queue.hpp" #include #include @@ -56,8 +57,17 @@ void http_connection::get(std::string const& url, time_duration timeout, int pri std::string auth; std::string hostname; std::string path; + char const* error; int port; - boost::tie(protocol, auth, hostname, port, path) = parse_url_components(url); + + boost::tie(protocol, auth, hostname, port, path, error) + = parse_url_components(url); + + if (error) + { + callback(asio::error::socket_type_not_supported); + return; + } TORRENT_ASSERT(prio >= 0 && prio < 2); diff --git a/src/parse_url.cpp b/src/parse_url.cpp new file mode 100644 index 000000000..699375433 --- /dev/null +++ b/src/parse_url.cpp @@ -0,0 +1,131 @@ +/* + +Copyright (c) 2008, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/parse_url.hpp" + +namespace libtorrent +{ + + // returns protocol, auth, hostname, port, path, error + boost::tuple + parse_url_components(std::string url) + { + std::string hostname; // hostname only + std::string auth; // user:pass + std::string protocol; // http or https for instance + char const* error = 0; + int port = 80; + + std::string::iterator at; + std::string::iterator colon; + std::string::iterator port_pos; + + // PARSE URL + std::string::iterator start = url.begin(); + // remove white spaces in front of the url + while (start != url.end() && (*start == ' ' || *start == '\t')) + ++start; + std::string::iterator end + = std::find(url.begin(), url.end(), ':'); + protocol.assign(start, end); + + if (protocol == "https") port = 443; + + if (end == url.end()) + { + error = "no protocol in url"; + goto exit; + } + ++end; + if (end == url.end() || *end != '/') + { + error = "incomplete protocol"; + goto exit; + } + ++end; + if (end == url.end() || *end != '/') + { + error = "incomplete protocol"; + goto exit; + } + ++end; + start = end; + + at = std::find(start, url.end(), '@'); + 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; + } + + // this is for IPv6 addresses + if (start != url.end() && *start == '[') + { + port_pos = std::find(start, url.end(), ']'); + if (port_pos == url.end()) + { + error = "expected closing ']' for address"; + goto exit; + } + port_pos = std::find(port_pos, url.end(), ':'); + } + else + { + port_pos = std::find(start, url.end(), ':'); + } + + if (port_pos < end) + { + hostname.assign(start, port_pos); + ++port_pos; + port = atoi(std::string(port_pos, end).c_str()); + } + else + { + hostname.assign(start, end); + } + + start = end; +exit: + return boost::make_tuple(protocol, auth, hostname, port + , std::string(start, url.end()), error); + } + +} + diff --git a/src/torrent.cpp b/src/torrent.cpp index 233b81a0e..b52108196 100755 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -59,6 +59,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/session.hpp" #include "libtorrent/torrent_info.hpp" #include "libtorrent/tracker_manager.hpp" +#include "libtorrent/parse_url.hpp" #include "libtorrent/bencode.hpp" #include "libtorrent/hasher.hpp" #include "libtorrent/entry.hpp" @@ -1922,9 +1923,20 @@ namespace libtorrent std::string hostname; int port; std::string path; - boost::tie(protocol, auth, hostname, port, path) + char const* error; + boost::tie(protocol, auth, hostname, port, path, error) = parse_url_components(url); + if (error) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + (*m_ses.m_logger) << time_now_string() << " failed to parse web seed url: " << error << "\n"; +#endif + // never try it again + remove_url_seed(url); + return; + } + #ifdef TORRENT_USE_OPENSSL if (protocol != "http" && protocol != "https") #else @@ -2013,10 +2025,8 @@ namespace libtorrent { if (m_ses.m_alerts.should_post(alert::warning)) { - std::stringstream msg; - msg << "HTTP seed proxy hostname lookup failed: " << e.message(); m_ses.m_alerts.post_alert( - url_seed_alert(get_handle(), url, msg.str())); + url_seed_alert(get_handle(), url, e.message())); } // the name lookup failed for the http host. Don't try @@ -2032,9 +2042,21 @@ namespace libtorrent using boost::tuples::ignore; std::string hostname; int port; - boost::tie(ignore, ignore, hostname, port, ignore) + char const* error; + boost::tie(ignore, ignore, hostname, port, ignore, error) = parse_url_components(url); + if (error) + { + if (m_ses.m_alerts.should_post(alert::warning)) + { + m_ses.m_alerts.post_alert( + url_seed_alert(get_handle(), url, error)); + } + remove_url_seed(url); + return; + } + if (m_ses.m_ip_filter.access(a.address()) & ip_filter::blocked) { if (m_ses.m_alerts.should_post(alert::info)) diff --git a/src/tracker_manager.cpp b/src/tracker_manager.cpp index a0b89dd0f..6a0b28d60 100755 --- a/src/tracker_manager.cpp +++ b/src/tracker_manager.cpp @@ -180,83 +180,6 @@ namespace libtorrent m_connections.erase(i); } - // 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; // http or https for instance - int port = 80; - - std::string::iterator at; - std::string::iterator colon; - std::string::iterator port_pos; - - // PARSE URL - std::string::iterator start = url.begin(); - // remove white spaces in front of the url - while (start != url.end() && (*start == ' ' || *start == '\t')) - ++start; - std::string::iterator end - = std::find(url.begin(), url.end(), ':'); - protocol.assign(start, end); - - if (protocol == "https") port = 443; - - if (end == url.end()) goto exit; - ++end; - if (end == url.end()) goto exit; - if (*end != '/') goto exit; - ++end; - if (end == url.end()) goto exit; - if (*end != '/') goto exit; - ++end; - start = end; - - at = std::find(start, url.end(), '@'); - 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; - } - - // this is for IPv6 addresses - if (start != url.end() && *start == '[') - { - port_pos = std::find(start, url.end(), ']'); - if (port_pos == url.end()) goto exit; - port_pos = std::find(port_pos, url.end(), ':'); - } - else - { - port_pos = std::find(start, url.end(), ':'); - } - - if (port_pos < end) - { - hostname.assign(start, port_pos); - ++port_pos; - port = atoi(std::string(port_pos, end).c_str()); - } - else - { - hostname.assign(start, end); - } - - start = end; -exit: - return make_tuple(protocol, auth, hostname, port - , std::string(start, url.end())); - } - void tracker_manager::queue_request( io_service& ios , connection_queue& cc diff --git a/src/udp_tracker_connection.cpp b/src/udp_tracker_connection.cpp index 766af88fa..e19d5696d 100755 --- a/src/udp_tracker_connection.cpp +++ b/src/udp_tracker_connection.cpp @@ -52,6 +52,7 @@ POSSIBILITY OF SUCH DAMAGE. #endif #include "libtorrent/tracker_manager.hpp" +#include "libtorrent/parse_url.hpp" #include "libtorrent/udp_tracker_connection.hpp" #include "libtorrent/io.hpp" @@ -95,10 +96,18 @@ namespace libtorrent std::string hostname; int port; + char const* error; using boost::tuples::ignore; - boost::tie(ignore, ignore, hostname, port, ignore) = parse_url_components(req.url); + boost::tie(ignore, ignore, hostname, port, ignore, error) + = parse_url_components(req.url); + if (error) + { + fail(-1, error); + return; + } + udp::resolver::query q(hostname, boost::lexical_cast(port)); m_name_lookup.async_resolve(q , boost::bind( diff --git a/src/upnp.cpp b/src/upnp.cpp index 055605cf4..05e667fc6 100644 --- a/src/upnp.cpp +++ b/src/upnp.cpp @@ -35,7 +35,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/socket.hpp" #include "libtorrent/upnp.hpp" #include "libtorrent/io.hpp" -#include "libtorrent/http_tracker_connection.hpp" +#include "libtorrent/parse_url.hpp" #include "libtorrent/xml_parse.hpp" #include "libtorrent/connection_queue.hpp" #include "libtorrent/enum_net.hpp" @@ -412,10 +412,21 @@ void upnp::on_reply(udp::endpoint const& from, char* buffer std::string protocol; std::string auth; + char const* error; // we don't have this device in our list. Add it - boost::tie(protocol, auth, d.hostname, d.port, d.path) + boost::tie(protocol, auth, d.hostname, d.port, d.path, error) = parse_url_components(d.url); + if (error) + { +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() + << " <== (" << from << ") Rootdevice advertized an invalid url: '" << d.url + << "'. " << error << ". Ignoring device" << std::endl; +#endif + return; + } + // ignore the auth here. It will be re-parsed // by the http connection later diff --git a/src/web_peer_connection.cpp b/src/web_peer_connection.cpp index 3b9a19a7d..a23cc7cbc 100755 --- a/src/web_peer_connection.cpp +++ b/src/web_peer_connection.cpp @@ -49,6 +49,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/io.hpp" #include "libtorrent/version.hpp" #include "libtorrent/aux_/session_impl.hpp" +#include "libtorrent/parse_url.hpp" using boost::bind; using boost::shared_ptr; @@ -95,8 +96,10 @@ namespace libtorrent #endif std::string protocol; - boost::tie(protocol, m_auth, m_host, m_port, m_path) + char const* error; + boost::tie(protocol, m_auth, m_host, m_port, m_path, error) = parse_url_components(url); + TORRENT_ASSERT(error == 0); if (!m_auth.empty()) m_auth = base64encode(m_auth); diff --git a/test/test_primitives.cpp b/test/test_primitives.cpp index ad2cecf5c..1a6b62166 100644 --- a/test/test_primitives.cpp +++ b/test/test_primitives.cpp @@ -1,4 +1,4 @@ -#include "libtorrent/tracker_manager.hpp" +#include "libtorrent/parse_url.hpp" #include "libtorrent/http_tracker_connection.hpp" #include "libtorrent/buffer.hpp" #include "libtorrent/xml_parse.hpp" @@ -83,22 +83,22 @@ 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")); + == make_tuple("http", "foo:bar", "host.com", 80, "/path/to/file", (char const*)0)); TEST_CHECK(parse_url_components("http://host.com/path/to/file") - == make_tuple("http", "", "host.com", 80, "/path/to/file")); + == make_tuple("http", "", "host.com", 80, "/path/to/file", (char const*)0)); TEST_CHECK(parse_url_components("ftp://host.com:21/path/to/file") - == make_tuple("ftp", "", "host.com", 21, "/path/to/file")); + == make_tuple("ftp", "", "host.com", 21, "/path/to/file", (char const*)0)); TEST_CHECK(parse_url_components("http://host.com/path?foo:bar@foo:") - == make_tuple("http", "", "host.com", 80, "/path?foo:bar@foo:")); + == make_tuple("http", "", "host.com", 80, "/path?foo:bar@foo:", (char const*)0)); TEST_CHECK(parse_url_components("http://192.168.0.1/path/to/file") - == make_tuple("http", "", "192.168.0.1", 80, "/path/to/file")); + == make_tuple("http", "", "192.168.0.1", 80, "/path/to/file", (char const*)0)); TEST_CHECK(parse_url_components("http://[::1]/path/to/file") - == make_tuple("http", "", "[::1]", 80, "/path/to/file")); + == make_tuple("http", "", "[::1]", 80, "/path/to/file", (char const*)0)); // base64 test vectors from http://www.faqs.org/rfcs/rfc4648.html