diff --git a/Jamfile b/Jamfile index 05163e142..44f3ea892 100755 --- a/Jamfile +++ b/Jamfile @@ -63,6 +63,7 @@ SOURCES = policy session session_impl + socks4_stream socks5_stream stat storage diff --git a/include/Makefile.am b/include/Makefile.am index 5ec4542f3..34095f020 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -43,6 +43,7 @@ libtorrent/session_status.hpp \ libtorrent/size_type.hpp \ libtorrent/socket.hpp \ libtorrent/socket_type.hpp \ +libtorrent/socks4_stream.hpp \ libtorrent/socks5_stream.hpp \ libtorrent/stat.hpp \ libtorrent/storage.hpp \ diff --git a/include/libtorrent/session_settings.hpp b/include/libtorrent/session_settings.hpp index b4b3fd791..20a2a48a8 100644 --- a/include/libtorrent/session_settings.hpp +++ b/include/libtorrent/session_settings.hpp @@ -54,6 +54,8 @@ namespace libtorrent // a plain tcp socket is used, and // the other settings are ignored. none, + // socks4 server, requires username. + socks4, // the hostname and port settings are // used to connect to the proxy. No // username or password is sent. diff --git a/include/libtorrent/socket_type.hpp b/include/libtorrent/socket_type.hpp index f81d12342..9ed8f9a4b 100644 --- a/include/libtorrent/socket_type.hpp +++ b/include/libtorrent/socket_type.hpp @@ -34,12 +34,17 @@ POSSIBILITY OF SUCH DAMAGE. #define TORRENT_SOCKET_TYPE #include "libtorrent/socks5_stream.hpp" +#include "libtorrent/socks4_stream.hpp" #include "libtorrent/http_stream.hpp" #include "libtorrent/variant_stream.hpp" namespace libtorrent { - typedef variant_stream socket_type; + typedef variant_stream< + stream_socket + , socks5_stream + , socks4_stream + , http_stream> socket_type; } #endif diff --git a/include/libtorrent/socks4_stream.hpp b/include/libtorrent/socks4_stream.hpp new file mode 100644 index 000000000..6110dbe4d --- /dev/null +++ b/include/libtorrent/socks4_stream.hpp @@ -0,0 +1,91 @@ +/* + +Copyright (c) 2007, 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_SOCKS4_STREAM_HPP_INCLUDED +#define TORRENT_SOCKS4_STREAM_HPP_INCLUDED + +#include "libtorrent/proxy_base.hpp" + +namespace libtorrent { + +class socks4_stream : public proxy_base +{ +public: + + explicit socks4_stream(asio::io_service& io_service) + : proxy_base(io_service) + {} + + void set_username(std::string const& user) + { + m_user = user; + } + + typedef boost::function handler_type; + + template + void async_connect(endpoint_type const& endpoint, Handler const& handler) + { + m_remote_endpoint = endpoint; + + // the connect is split up in the following steps: + // 1. resolve name of proxy server + // 2. connect to proxy server + // 3. send SOCKS4 CONNECT message + + // to avoid unnecessary copying of the handler, + // store it in a shaed_ptr + boost::shared_ptr h(new handler_type(handler)); + + tcp::resolver::query q(m_hostname + , boost::lexical_cast(m_port)); + m_resolver.async_resolve(q, boost::bind( + &socks4_stream::name_lookup, this, _1, _2, h)); + } + +private: + + void name_lookup(asio::error_code const& e, tcp::resolver::iterator i + , boost::shared_ptr h); + void connected(asio::error_code const& e, boost::shared_ptr h); + void handshake1(asio::error_code const& e, boost::shared_ptr h); + void handshake2(asio::error_code const& e, boost::shared_ptr h); + + // send and receive buffer + std::vector m_buffer; + // proxy authentication + std::string m_user; +}; + +} + +#endif diff --git a/src/Makefile.am b/src/Makefile.am index 5df13cde7..4be4e7862 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -21,7 +21,8 @@ torrent_info.cpp tracker_manager.cpp http_connection.cpp \ http_tracker_connection.cpp udp_tracker_connection.cpp \ 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 http_stream.cpp connection_queue.cpp $(kademlia_sources) +socks5_stream.cpp socks4_stream.cpp http_stream.cpp connection_queue.cpp \ +$(kademlia_sources) noinst_HEADERS = \ $(top_srcdir)/include/libtorrent/alert.hpp \ @@ -71,6 +72,7 @@ $(top_srcdir)/include/libtorrent/session.hpp \ $(top_srcdir)/include/libtorrent/size_type.hpp \ $(top_srcdir)/include/libtorrent/socket.hpp \ $(top_srcdir)/include/libtorrent/socket_type.hpp \ +$(top_srcdir)/include/libtorrent/socks4_stream.hpp \ $(top_srcdir)/include/libtorrent/socks5_stream.hpp \ $(top_srcdir)/include/libtorrent/stat.hpp \ $(top_srcdir)/include/libtorrent/storage.hpp \ diff --git a/src/instantiate_connection.cpp b/src/instantiate_connection.cpp index ff4efbc59..43b70f40d 100644 --- a/src/instantiate_connection.cpp +++ b/src/instantiate_connection.cpp @@ -67,6 +67,12 @@ namespace libtorrent if (ps.type == proxy_settings::socks5_pw) s->get().set_username(ps.username, ps.password); } + else if (ps.type == proxy_settings::socks4) + { + s->instantiate(); + s->get().set_proxy(ps.hostname, ps.port); + s->get().set_username(ps.username); + } else { throw std::runtime_error("unsupported proxy type"); diff --git a/src/socks4_stream.cpp b/src/socks4_stream.cpp new file mode 100644 index 000000000..3a31b2375 --- /dev/null +++ b/src/socks4_stream.cpp @@ -0,0 +1,147 @@ +/* + +Copyright (c) 2007, 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/pch.hpp" + +#include "libtorrent/socks4_stream.hpp" + +namespace libtorrent +{ + + void socks4_stream::name_lookup(asio::error_code const& e, tcp::resolver::iterator i + , boost::shared_ptr h) + { + if (e) + { + (*h)(e); + close(); + return; + } + + // SOCKS4 doesn't support IPv6 addresses + while (i != tcp::resolver::iterator() && i->endpoint().address().is_v6()) + ++i; + + if (i == tcp::resolver::iterator()) + { + asio::error_code ec = asio::error::operation_not_supported; + (*h)(e); + close(); + return; + } + + m_sock.async_connect(i->endpoint(), boost::bind( + &socks4_stream::connected, this, _1, h)); + } + + void socks4_stream::connected(asio::error_code const& e, boost::shared_ptr h) + { + if (e) + { + (*h)(e); + close(); + return; + } + + using namespace libtorrent::detail; + + m_buffer.resize(m_user.size() + 9); + char* p = &m_buffer[0]; + write_uint8(4, p); // SOCKS VERSION 4 + write_uint8(1, p); // SOCKS CONNECT + write_uint16(m_remote_endpoint.port(), p); + write_uint32(m_remote_endpoint.address().to_v4().to_ulong(), p); + std::copy(m_user.begin(), m_user.end(), p); + p += m_user.size(); + write_uint8(0, p); // NULL terminator + + asio::async_write(m_sock, asio::buffer(m_buffer) + , boost::bind(&socks4_stream::handshake1, this, _1, h)); + } + + void socks4_stream::handshake1(asio::error_code const& e, boost::shared_ptr h) + { + if (e) + { + (*h)(e); + close(); + return; + } + + m_buffer.resize(8); + asio::async_read(m_sock, asio::buffer(m_buffer) + , boost::bind(&socks4_stream::handshake2, this, _1, h)); + } + + void socks4_stream::handshake2(asio::error_code const& e, boost::shared_ptr h) + { + if (e) + { + (*h)(e); + close(); + return; + } + + using namespace libtorrent::detail; + + char* p = &m_buffer[0]; + int reply_version = read_uint8(p); + int status_code = read_uint8(p); + + if (reply_version != 0) + { + (*h)(asio::error::operation_not_supported); + close(); + return; + } + + // access granted + if (status_code == 90) + { + std::vector().swap(m_buffer); + (*h)(e); + return; + } + + asio::error_code ec = asio::error::fault; + switch (status_code) + { + case 91: ec = asio::error::connection_refused; break; + case 92: ec = asio::error::no_permission; break; + case 93: ec = asio::error::no_permission; break; + } + (*h)(ec); + close(); + } + +} +