From a956265be4b81272db46710633f8d297f082524a Mon Sep 17 00:00:00 2001 From: Arvid Norberg Date: Wed, 8 Apr 2009 04:18:45 +0000 Subject: [PATCH] merged socks4 and socks5 code and improved SOCKS error messages --- CMakeLists.txt | 1 - Jamfile | 1 - include/libtorrent/socket_type.hpp | 2 - include/libtorrent/socks4_stream.hpp | 92 ---------- include/libtorrent/socks5_stream.hpp | 46 ++++- src/instantiate_connection.cpp | 11 +- src/socks4_stream.cpp | 152 ---------------- src/socks5_stream.cpp | 251 +++++++++++++++++++-------- 8 files changed, 220 insertions(+), 336 deletions(-) delete mode 100644 include/libtorrent/socks4_stream.hpp delete mode 100644 src/socks4_stream.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index d69ba834b..6451b6dde 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,7 +30,6 @@ set(sources policy session session_impl - socks4_stream socks5_stream stat storage diff --git a/Jamfile b/Jamfile index 3bf4843ef..9eec8d22b 100755 --- a/Jamfile +++ b/Jamfile @@ -364,7 +364,6 @@ SOURCES = policy session session_impl - socks4_stream socks5_stream stat storage diff --git a/include/libtorrent/socket_type.hpp b/include/libtorrent/socket_type.hpp index 9ed8f9a4b..01fb3f9be 100644 --- a/include/libtorrent/socket_type.hpp +++ b/include/libtorrent/socket_type.hpp @@ -34,7 +34,6 @@ 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" @@ -43,7 +42,6 @@ namespace libtorrent typedef variant_stream< stream_socket , socks5_stream - , socks4_stream , http_stream> socket_type; } diff --git a/include/libtorrent/socks4_stream.hpp b/include/libtorrent/socks4_stream.hpp deleted file mode 100644 index e7054595e..000000000 --- a/include/libtorrent/socks4_stream.hpp +++ /dev/null @@ -1,92 +0,0 @@ -/* - -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(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(error_code const& e, tcp::resolver::iterator i - , boost::shared_ptr h); - void connected(error_code const& e, boost::shared_ptr h); - void handshake1(error_code const& e, boost::shared_ptr h); - void handshake2(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/include/libtorrent/socks5_stream.hpp b/include/libtorrent/socks5_stream.hpp index 24b27da85..9819fd763 100644 --- a/include/libtorrent/socks5_stream.hpp +++ b/include/libtorrent/socks5_stream.hpp @@ -37,14 +37,47 @@ POSSIBILITY OF SUCH DAMAGE. namespace libtorrent { + namespace socks_error { + + enum socks_error_code + { + no_error = 0, + unsupported_version, + unsupported_authentication_method, + unsupported_authentication_version, + authentication_error, + username_required, + general_failure, + command_not_supported, + no_identd, + identd_error, + + num_errors + }; + } + +struct TORRENT_EXPORT socks_error_category : boost::system::error_category +{ + virtual const char* name() const; + virtual std::string message(int ev) const; + virtual boost::system::error_condition default_error_condition(int ev) const + { return boost::system::error_condition(ev, *this); } +}; + +extern socks_error_category socks_category; + class socks5_stream : public proxy_base { public: explicit socks5_stream(io_service& io_service) : proxy_base(io_service) + , m_version(5) + , m_command(1) {} + void set_version(int v) { m_version = v; } + void set_username(std::string const& user , std::string const& password) { @@ -54,6 +87,8 @@ public: typedef boost::function handler_type; +//#error fix error messages to use custom error_code category +//#error add async_connect() that takes a hostname and port as well template void async_connect(endpoint_type const& endpoint, Handler const& handler) { @@ -62,10 +97,11 @@ public: // the connect is split up in the following steps: // 1. resolve name of proxy server // 2. connect to proxy server - // 3. send SOCKS5 authentication method message - // 4. read SOCKS5 authentication response - // 5. send username+password - // 6. send SOCKS5 CONNECT message + // 3. if version == 5: + // 3.1 send SOCKS5 authentication method message + // 3.2 read SOCKS5 authentication response + // 3.3 send username+password + // 4 send SOCKS command message // to avoid unnecessary copying of the handler, // store it in a shaed_ptr @@ -96,6 +132,8 @@ private: // proxy authentication std::string m_user; std::string m_password; + int m_version; + int m_command; }; } diff --git a/src/instantiate_connection.cpp b/src/instantiate_connection.cpp index 1c69ecd35..d744071fd 100644 --- a/src/instantiate_connection.cpp +++ b/src/instantiate_connection.cpp @@ -57,18 +57,15 @@ namespace libtorrent s.get()->set_username(ps.username, ps.password); } else if (ps.type == proxy_settings::socks5 - || ps.type == proxy_settings::socks5_pw) + || ps.type == proxy_settings::socks5_pw + || ps.type == proxy_settings::socks4) { s.instantiate(ios); s.get()->set_proxy(ps.hostname, ps.port); if (ps.type == proxy_settings::socks5_pw) s.get()->set_username(ps.username, ps.password); - } - else if (ps.type == proxy_settings::socks4) - { - s.instantiate(ios); - s.get()->set_proxy(ps.hostname, ps.port); - s.get()->set_username(ps.username); + if (ps.type == proxy_settings::socks4) + s.get()->set_version(4); } else { diff --git a/src/socks4_stream.cpp b/src/socks4_stream.cpp deleted file mode 100644 index e4add5d4f..000000000 --- a/src/socks4_stream.cpp +++ /dev/null @@ -1,152 +0,0 @@ -/* - -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(error_code const& e, tcp::resolver::iterator i - , boost::shared_ptr h) - { - if (e) - { - (*h)(e); - error_code ec; - close(ec); - return; - } - - // SOCKS4 doesn't support IPv6 addresses - while (i != tcp::resolver::iterator() && i->endpoint().address().is_v6()) - ++i; - - if (i == tcp::resolver::iterator()) - { - error_code ec = asio::error::operation_not_supported; - (*h)(ec); - close(ec); - return; - } - - m_sock.async_connect(i->endpoint(), boost::bind( - &socks4_stream::connected, this, _1, h)); - } - - void socks4_stream::connected(error_code const& e, boost::shared_ptr h) - { - if (e) - { - (*h)(e); - error_code ec; - close(ec); - 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 - - async_write(m_sock, asio::buffer(m_buffer) - , boost::bind(&socks4_stream::handshake1, this, _1, h)); - } - - void socks4_stream::handshake1(error_code const& e, boost::shared_ptr h) - { - if (e) - { - (*h)(e); - error_code ec; - close(ec); - return; - } - - m_buffer.resize(8); - async_read(m_sock, asio::buffer(m_buffer) - , boost::bind(&socks4_stream::handshake2, this, _1, h)); - } - - void socks4_stream::handshake2(error_code const& e, boost::shared_ptr h) - { - if (e) - { - (*h)(e); - error_code ec; - close(ec); - 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) - { - error_code ec = asio::error::operation_not_supported; - (*h)(ec); - close(ec); - return; - } - - // access granted - if (status_code == 90) - { - std::vector().swap(m_buffer); - (*h)(e); - return; - } - - 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(ec); - } - -} - diff --git a/src/socks5_stream.cpp b/src/socks5_stream.cpp index 3fb9ec666..361c944ca 100644 --- a/src/socks5_stream.cpp +++ b/src/socks5_stream.cpp @@ -38,6 +38,33 @@ POSSIBILITY OF SUCH DAMAGE. namespace libtorrent { + socks_error_category socks_category; + + const char* socks_error_category::name() const + { + return "socks error"; + } + + std::string socks_error_category::message(int ev) const + { + static char const* messages[] = + { + "no error", + "unsupported version", + "unsupported authentication method", + "unsupported authentication version", + "authentication error", + "username required", + "general failure", + "command not supported", + "no identd running", + "identd could not identify username" + }; + + if (ev < 0 || ev > socks_error::num_errors) return "unknown error"; + return messages[ev]; + } + void socks5_stream::name_lookup(error_code const& e, tcp::resolver::iterator i , boost::shared_ptr h) { @@ -64,23 +91,36 @@ namespace libtorrent } using namespace libtorrent::detail; - // send SOCKS5 authentication methods - m_buffer.resize(m_user.empty()?3:4); - char* p = &m_buffer[0]; - write_uint8(5, p); // SOCKS VERSION 5 - if (m_user.empty()) + if (m_version == 5) { - write_uint8(1, p); // 1 authentication method (no auth) - write_uint8(0, p); // no authentication + // send SOCKS5 authentication methods + m_buffer.resize(m_user.empty()?3:4); + char* p = &m_buffer[0]; + write_uint8(5, p); // SOCKS VERSION 5 + if (m_user.empty()) + { + write_uint8(1, p); // 1 authentication method (no auth) + write_uint8(0, p); // no authentication + } + else + { + write_uint8(2, p); // 2 authentication methods + write_uint8(0, p); // no authentication + write_uint8(2, p); // username/password + } + async_write(m_sock, asio::buffer(m_buffer) + , boost::bind(&socks5_stream::handshake1, this, _1, h)); + } + else if (m_version == 4) + { + socks_connect(h); } else { - write_uint8(2, p); // 2 authentication methods - write_uint8(0, p); // no authentication - write_uint8(2, p); // username/password + (*h)(error_code(socks_error::unsupported_version, socks_category)); + error_code ec; + close(ec); } - async_write(m_sock, asio::buffer(m_buffer) - , boost::bind(&socks5_stream::handshake1, this, _1, h)); } void socks5_stream::handshake1(error_code const& e, boost::shared_ptr h) @@ -114,9 +154,9 @@ namespace libtorrent int version = read_uint8(p); int method = read_uint8(p); - if (version < 5) + if (version < m_version) { - (*h)(asio::error::operation_not_supported); + (*h)(error_code(socks_error::unsupported_version, socks_category)); error_code ec; close(ec); return; @@ -130,7 +170,7 @@ namespace libtorrent { if (m_user.empty()) { - (*h)(asio::error::operation_not_supported); + (*h)(error_code(socks_error::username_required, socks_category)); error_code ec; close(ec); return; @@ -149,7 +189,7 @@ namespace libtorrent } else { - (*h)(asio::error::operation_not_supported); + (*h)(error_code(socks_error::unsupported_authentication_method, socks_category)); error_code ec; close(ec); return; @@ -191,7 +231,7 @@ namespace libtorrent if (version != 1) { - (*h)(asio::error::operation_not_supported); + (*h)(error_code(socks_error::unsupported_authentication_version, socks_category)); error_code ec; close(ec); return; @@ -199,7 +239,7 @@ namespace libtorrent if (status != 0) { - (*h)(asio::error::operation_not_supported); + (*h)(error_code(socks_error::authentication_error, socks_category)); error_code ec; close(ec); return; @@ -213,15 +253,37 @@ namespace libtorrent { using namespace libtorrent::detail; - // send SOCKS5 connect command - m_buffer.resize(6 + (m_remote_endpoint.address().is_v4()?4:16)); - char* p = &m_buffer[0]; - write_uint8(5, p); // SOCKS VERSION 5 - write_uint8(1, p); // CONNECT command - write_uint8(0, p); // reserved - write_uint8(m_remote_endpoint.address().is_v4()?1:4, p); // address type - write_endpoint(m_remote_endpoint, p); - TORRENT_ASSERT(p - &m_buffer[0] == int(m_buffer.size())); + if (m_version == 5) + { + // send SOCKS5 connect command + m_buffer.resize(6 + (m_remote_endpoint.address().is_v4()?4:16)); + char* p = &m_buffer[0]; + write_uint8(5, p); // SOCKS VERSION 5 + write_uint8(m_command, p); // CONNECT/BIND command + write_uint8(0, p); // reserved + write_uint8(m_remote_endpoint.address().is_v4()?1:4, p); // address type + write_endpoint(m_remote_endpoint, p); + TORRENT_ASSERT(p - &m_buffer[0] == int(m_buffer.size())); + } + else if (m_version == 4) + { + m_buffer.resize(m_user.size() + 9); + char* p = &m_buffer[0]; + write_uint8(4, p); // SOCKS VERSION 4 + write_uint8(m_command, p); // CONNECT/BIND command + 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 + } + else + { + (*h)(error_code(socks_error::unsupported_version, socks_category)); + error_code ec; + close(ec); + return; + } async_write(m_sock, asio::buffer(m_buffer) , boost::bind(&socks5_stream::connect1, this, _1, h)); @@ -237,7 +299,10 @@ namespace libtorrent return; } - m_buffer.resize(6 + 4); // assume an IPv4 address + if (m_version == 5) + m_buffer.resize(6 + 4); // assume an IPv4 address + else if (m_version == 4) + m_buffer.resize(8); async_read(m_sock, asio::buffer(m_buffer) , boost::bind(&socks5_stream::connect2, this, _1, h)); } @@ -257,62 +322,94 @@ namespace libtorrent // send SOCKS5 connect command char* p = &m_buffer[0]; int version = read_uint8(p); - if (version < 5) - { - (*h)(asio::error::operation_not_supported); - error_code ec; - close(ec); - return; - } int response = read_uint8(p); - if (response != 0) + + if (m_version == 5) { - error_code e = asio::error::fault; + if (version < m_version) + { + (*h)(error_code(socks_error::unsupported_version, socks_category)); + error_code ec; + close(ec); + return; + } + if (response != 0) + { + error_code e(socks_error::general_failure, socks_category); + switch (response) + { + case 2: e = asio::error::no_permission; break; + case 3: e = asio::error::network_unreachable; break; + case 4: e = asio::error::host_unreachable; break; + case 5: e = asio::error::connection_refused; break; + case 6: e = asio::error::timed_out; break; + case 7: e = error_code(socks_error::command_not_supported, socks_category); break; + case 8: e = asio::error::address_family_not_supported; break; + } + (*h)(e); + error_code ec; + close(ec); + return; + } + p += 1; // reserved + int atyp = read_uint8(p); + // we ignore the proxy IP it was bound to + if (atyp == 1) + { + std::vector().swap(m_buffer); + (*h)(e); + return; + } + int skip_bytes = 0; + if (atyp == 4) + { + skip_bytes = 12; + } + else if (atyp == 3) + { + skip_bytes = read_uint8(p) - 3; + } + else + { + (*h)(asio::error::address_family_not_supported); + error_code ec; + close(ec); + return; + } + m_buffer.resize(skip_bytes); + + async_read(m_sock, asio::buffer(m_buffer) + , boost::bind(&socks5_stream::connect3, this, _1, h)); + } + else if (m_version == 4) + { + if (version != 0) + { + (*h)(error_code(socks_error::general_failure, socks_category)); + error_code ec; + close(ec); + return; + } + + // access granted + if (response == 90) + { + std::vector().swap(m_buffer); + (*h)(e); + return; + } + + int code = socks_error::general_failure; switch (response) { - case 1: e = asio::error::fault; break; - case 2: e = asio::error::no_permission; break; - case 3: e = asio::error::network_unreachable; break; - case 4: e = asio::error::host_unreachable; break; - case 5: e = asio::error::connection_refused; break; - case 6: e = asio::error::timed_out; break; - case 7: e = asio::error::operation_not_supported; break; - case 8: e = asio::error::address_family_not_supported; break; + case 91: code = socks_error::authentication_error; break; + case 92: code = socks_error::no_identd; break; + case 93: code = socks_error::identd_error; break; } + error_code e(code, socks_category); (*h)(e); - error_code ec; - close(ec); - return; + close(e); } - p += 1; // reserved - int atyp = read_uint8(p); - // we ignore the proxy IP it was bound to - if (atyp == 1) - { - std::vector().swap(m_buffer); - (*h)(e); - return; - } - int skip_bytes = 0; - if (atyp == 4) - { - skip_bytes = 12; - } - else if (atyp == 3) - { - skip_bytes = read_uint8(p) - 3; - } - else - { - (*h)(asio::error::operation_not_supported); - error_code ec; - close(ec); - return; - } - m_buffer.resize(skip_bytes); - - async_read(m_sock, asio::buffer(m_buffer) - , boost::bind(&socks5_stream::connect3, this, _1, h)); } void socks5_stream::connect3(error_code const& e, boost::shared_ptr h)