From 19bb5553bb9064f3ef1dd3f647fc1e5df99f9ff5 Mon Sep 17 00:00:00 2001 From: Arvid Norberg Date: Wed, 25 Apr 2007 18:57:13 +0000 Subject: [PATCH] added missing files from previous check-in --- include/libtorrent/http_stream.hpp | 169 ++++++++++++++ include/libtorrent/socket_type.hpp | 46 ++++ include/libtorrent/socks5_stream.hpp | 171 +++++++++++++++ src/http_stream.cpp | 162 ++++++++++++++ src/instantiate_connection.cpp | 78 +++++++ src/socks5_stream.cpp | 315 +++++++++++++++++++++++++++ 6 files changed, 941 insertions(+) create mode 100644 include/libtorrent/http_stream.hpp create mode 100644 include/libtorrent/socket_type.hpp create mode 100644 include/libtorrent/socks5_stream.hpp create mode 100644 src/http_stream.cpp create mode 100644 src/instantiate_connection.cpp create mode 100644 src/socks5_stream.cpp diff --git a/include/libtorrent/http_stream.hpp b/include/libtorrent/http_stream.hpp new file mode 100644 index 000000000..a7139c4ee --- /dev/null +++ b/include/libtorrent/http_stream.hpp @@ -0,0 +1,169 @@ +#include "libtorrent/io.hpp" +#include "libtorrent/socket.hpp" +#include +#include +#include +#include +#include + + +namespace libtorrent { + +class http_stream : boost::noncopyable +{ +public: + + typedef stream_socket::lowest_layer_type lowest_layer_type; + typedef stream_socket::endpoint_type endpoint_type; + typedef stream_socket::protocol_type protocol_type; + + explicit http_stream(asio::io_service& io_service) + : m_sock(io_service) + , m_resolver(io_service) + , m_no_connect(false) + {} + + void set_no_connect(bool c) { m_no_connect = c; } + + void set_proxy(std::string hostname, int port) + { + m_hostname = hostname; + m_port = port; + } + + void set_username(std::string const& user + , std::string const& password) + { + m_user = user; + m_password = password; + } + + template + void async_read_some(Mutable_Buffers const& buffers, Handler const& handler) + { + m_sock.async_read_some(buffers, handler); + } + + template + void async_write_some(Const_Buffers const& buffers, Handler const& handler) + { + m_sock.async_write_some(buffers, handler); + } + + void bind(endpoint_type const& endpoint) + { + m_sock.bind(endpoint); + } + + template + void bind(endpoint_type const& endpoint, Error_Handler const& error_handler) + { + m_sock.bind(endpoint, error_handler); + } + + void open(protocol_type const& p) + { + m_sock.open(p); + } + + template + void open(protocol_type const& p, Error_Handler const& error_handler) + { + m_sock.open(p, error_handler); + } + + void close() + { + m_remote_endpoint = endpoint_type(); + m_sock.close(); + } + + template + void close(Error_Handler const& error_handler) + { + m_sock.close(error_handler); + } + + endpoint_type remote_endpoint() + { + return m_remote_endpoint; + } + + template + endpoint_type remote_endpoint(Error_Handler const& error_handler) + { + return m_remote_endpoint; + } + + endpoint_type local_endpoint() + { + return m_sock.local_endpoint(); + } + + template + endpoint_type local_endpoint(Error_Handler const& error_handler) + { + return m_sock.local_endpoint(error_handler); + } + + asio::io_service& io_service() + { + return m_sock.io_service(); + } + + lowest_layer_type& lowest_layer() + { + return m_sock.lowest_layer(); + } + + 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 HTTP CONNECT method and possibly username+password + // 4. read CONNECT response + + // 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( + &http_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); + + stream_socket m_sock; + // the http proxy + std::string m_hostname; + int m_port; + // send and receive buffer + std::vector m_buffer; + // proxy authentication + std::string m_user; + std::string m_password; + + endpoint_type m_remote_endpoint; + + tcp::resolver m_resolver; + // this is true if the connection is HTTP based and + // want to talk directly to the proxy + bool m_no_connect; +}; + +} + diff --git a/include/libtorrent/socket_type.hpp b/include/libtorrent/socket_type.hpp new file mode 100644 index 000000000..f81d12342 --- /dev/null +++ b/include/libtorrent/socket_type.hpp @@ -0,0 +1,46 @@ +/* + +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_SOCKET_TYPE +#define TORRENT_SOCKET_TYPE + +#include "libtorrent/socks5_stream.hpp" +#include "libtorrent/http_stream.hpp" +#include "libtorrent/variant_stream.hpp" + +namespace libtorrent +{ + typedef variant_stream socket_type; +} + +#endif + diff --git a/include/libtorrent/socks5_stream.hpp b/include/libtorrent/socks5_stream.hpp new file mode 100644 index 000000000..01cddab7e --- /dev/null +++ b/include/libtorrent/socks5_stream.hpp @@ -0,0 +1,171 @@ +#include "libtorrent/io.hpp" +#include "libtorrent/socket.hpp" +#include +#include +#include +#include +#include + + +namespace libtorrent { + +class socks5_stream : boost::noncopyable +{ +public: + + typedef stream_socket::lowest_layer_type lowest_layer_type; + typedef stream_socket::endpoint_type endpoint_type; + typedef stream_socket::protocol_type protocol_type; + + explicit socks5_stream(asio::io_service& io_service) + : m_sock(io_service) + , m_resolver(io_service) + {} + + void set_proxy(std::string hostname, int port) + { + m_hostname = hostname; + m_port = port; + } + + void set_username(std::string const& user + , std::string const& password) + { + m_user = user; + m_password = password; + } + + template + void async_read_some(Mutable_Buffers const& buffers, Handler const& handler) + { + m_sock.async_read_some(buffers, handler); + } + + template + void async_write_some(Const_Buffers const& buffers, Handler const& handler) + { + m_sock.async_write_some(buffers, handler); + } + + void bind(endpoint_type const& endpoint) + { + m_sock.bind(endpoint); + } + + template + void bind(endpoint_type const& endpoint, Error_Handler const& error_handler) + { + m_sock.bind(endpoint, error_handler); + } + + void open(protocol_type const& p) + { + m_sock.open(p); + } + + template + void open(protocol_type const& p, Error_Handler const& error_handler) + { + m_sock.open(p, error_handler); + } + + void close() + { + m_remote_endpoint = endpoint_type(); + m_sock.close(); + } + + template + void close(Error_Handler const& error_handler) + { + m_sock.close(error_handler); + } + + endpoint_type remote_endpoint() + { + return m_remote_endpoint; + } + + template + endpoint_type remote_endpoint(Error_Handler const& error_handler) + { + return m_remote_endpoint; + } + + endpoint_type local_endpoint() + { + return m_sock.local_endpoint(); + } + + template + endpoint_type local_endpoint(Error_Handler const& error_handler) + { + return m_sock.local_endpoint(error_handler); + } + + asio::io_service& io_service() + { + return m_sock.io_service(); + } + + lowest_layer_type& lowest_layer() + { + return m_sock.lowest_layer(); + } + + 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 SOCKS5 authentication method message + // 4. read SOCKS5 authentication response + // 5. send username+password + // 6. send SOCKS5 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( + &socks5_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); + void handshake3(asio::error_code const& e, boost::shared_ptr h); + void handshake4(asio::error_code const& e, boost::shared_ptr h); + void socks_connect(boost::shared_ptr h); + void connect1(asio::error_code const& e, boost::shared_ptr h); + void connect2(asio::error_code const& e, boost::shared_ptr h); + void connect3(asio::error_code const& e, boost::shared_ptr h); + + stream_socket m_sock; + // the socks5 proxy + std::string m_hostname; + int m_port; + // send and receive buffer + std::vector m_buffer; + // proxy authentication + std::string m_user; + std::string m_password; + + endpoint_type m_remote_endpoint; + + tcp::resolver m_resolver; +}; + +} + diff --git a/src/http_stream.cpp b/src/http_stream.cpp new file mode 100644 index 000000000..0973af798 --- /dev/null +++ b/src/http_stream.cpp @@ -0,0 +1,162 @@ +/* + +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/http_stream.hpp" +#include "libtorrent/tracker_manager.hpp" // for base64encode + +namespace libtorrent +{ + + void http_stream::name_lookup(asio::error_code const& e, tcp::resolver::iterator i + , boost::shared_ptr h) + { + if (e || i == tcp::resolver::iterator()) + { + (*h)(e); + close(); + return; + } + + m_sock.async_connect(i->endpoint(), boost::bind( + &http_stream::connected, this, _1, h)); + } + + void http_stream::connected(asio::error_code const& e, boost::shared_ptr h) + { + if (e) + { + (*h)(e); + close(); + return; + } + + using namespace libtorrent::detail; + + if (m_no_connect) + { + std::vector().swap(m_buffer); + (*h)(e); + return; + } + + // send CONNECT + std::back_insert_iterator > p(m_buffer); + write_string("CONNECT " + boost::lexical_cast(m_remote_endpoint) + + " HTTP/1.0\r\n", p); + if (!m_user.empty()) + { + write_string("Proxy-Authorization: Basic " + base64encode( + m_user + ":" + m_password) + "\r\n", p); + } + write_string("\r\n", p); + asio::async_write(m_sock, asio::buffer(m_buffer) + , boost::bind(&http_stream::handshake1, this, _1, h)); + } + + void http_stream::handshake1(asio::error_code const& e, boost::shared_ptr h) + { + if (e) + { + (*h)(e); + close(); + return; + } + + // read one byte from the socket + m_buffer.resize(1); + asio::async_read(m_sock, asio::buffer(m_buffer) + , boost::bind(&http_stream::handshake2, this, _1, h)); + } + + void http_stream::handshake2(asio::error_code const& e, boost::shared_ptr h) + { + if (e) + { + (*h)(e); + close(); + return; + } + + int read_pos = m_buffer.size(); + // look for \n\n and \r\n\r\n + // both of which means end of http response header + bool found_end = false; + if (m_buffer[read_pos - 1] == '\n' && read_pos > 2) + { + if (m_buffer[read_pos - 2] == '\n') + { + found_end = true; + } + else if (read_pos > 4 + && m_buffer[read_pos - 2] == '\r' + && m_buffer[read_pos - 3] == '\n' + && m_buffer[read_pos - 4] == '\r') + { + found_end = true; + } + } + + if (found_end) + { + m_buffer.push_back(0); + char* status = strchr(&m_buffer[0], ' '); + if (status == 0) + { + (*h)(asio::error::operation_not_supported); + close(); + return; + } + + status++; + int code = atoi(status); + if (code != 200) + { + (*h)(asio::error::operation_not_supported); + close(); + return; + } + + (*h)(e); + std::vector().swap(m_buffer); + return; + } + + // read another byte from the socket + m_buffer.resize(read_pos + 1); + asio::async_read(m_sock, asio::buffer(&m_buffer[0] + read_pos, 1) + , boost::bind(&http_stream::handshake2, this, _1, h)); + } + +} + diff --git a/src/instantiate_connection.cpp b/src/instantiate_connection.cpp new file mode 100644 index 000000000..ff4efbc59 --- /dev/null +++ b/src/instantiate_connection.cpp @@ -0,0 +1,78 @@ +/* + +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/socket.hpp" +#include "libtorrent/session_settings.hpp" +#include "libtorrent/socket_type.hpp" +#include +#include +#include + +namespace libtorrent +{ + + boost::shared_ptr instantiate_connection( + asio::io_service& ios, proxy_settings const& ps) + { + boost::shared_ptr s(new socket_type(ios)); + + if (ps.type == proxy_settings::none) + { + s->instantiate(); + } + else if (ps.type == proxy_settings::http + || ps.type == proxy_settings::http_pw) + { + s->instantiate(); + 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::socks5 + || ps.type == proxy_settings::socks5_pw) + { + s->instantiate(); + s->get().set_proxy(ps.hostname, ps.port); + if (ps.type == proxy_settings::socks5_pw) + s->get().set_username(ps.username, ps.password); + } + else + { + throw std::runtime_error("unsupported proxy type"); + } + return s; + } + +} + diff --git a/src/socks5_stream.cpp b/src/socks5_stream.cpp new file mode 100644 index 000000000..b1679c4ac --- /dev/null +++ b/src/socks5_stream.cpp @@ -0,0 +1,315 @@ +/* + +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/socks5_stream.hpp" + +namespace libtorrent +{ + + void socks5_stream::name_lookup(asio::error_code const& e, tcp::resolver::iterator i + , boost::shared_ptr h) + { + if (e || i == tcp::resolver::iterator()) + { + (*h)(e); + close(); + return; + } + + m_sock.async_connect(i->endpoint(), boost::bind( + &socks5_stream::connected, this, _1, h)); + } + + void socks5_stream::connected(asio::error_code const& e, boost::shared_ptr h) + { + if (e) + { + (*h)(e); + close(); + return; + } + + 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()) + { + 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 + } + asio::async_write(m_sock, asio::buffer(m_buffer) + , boost::bind(&socks5_stream::handshake1, this, _1, h)); + } + + void socks5_stream::handshake1(asio::error_code const& e, boost::shared_ptr h) + { + if (e) + { + (*h)(e); + close(); + return; + } + + m_buffer.resize(2); + asio::async_read(m_sock, asio::buffer(m_buffer) + , boost::bind(&socks5_stream::handshake2, this, _1, h)); + } + + void socks5_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 version = read_uint8(p); + int method = read_uint8(p); + + if (version < 5) + { + (*h)(asio::error::operation_not_supported); + close(); + return; + } + + if (method == 0) + { + socks_connect(h); + } + else if (method == 2) + { + if (m_user.empty()) + { + (*h)(asio::error::operation_not_supported); + close(); + return; + } + + // start sub-negotiation + m_buffer.resize(m_user.size() + m_password.size() + 3); + char* p = &m_buffer[0]; + write_uint8(1, p); + write_uint8(m_user.size(), p); + write_string(m_user, p); + write_uint8(m_password.size(), p); + write_string(m_password, p); + asio::async_write(m_sock, asio::buffer(m_buffer) + , boost::bind(&socks5_stream::handshake3, this, _1, h)); + } + else + { + (*h)(asio::error::operation_not_supported); + close(); + return; + } + } + + void socks5_stream::handshake3(asio::error_code const& e + , boost::shared_ptr h) + { + if (e) + { + (*h)(e); + close(); + return; + } + + m_buffer.resize(2); + asio::async_read(m_sock, asio::buffer(m_buffer) + , boost::bind(&socks5_stream::handshake4, this, _1, h)); + } + + void socks5_stream::handshake4(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 version = read_uint8(p); + int status = read_uint8(p); + + if (version != 1) + { + (*h)(asio::error::operation_not_supported); + close(); + return; + } + + if (status != 0) + { + (*h)(asio::error::operation_not_supported); + close(); + return; + } + + std::vector().swap(m_buffer); + (*h)(e); + } + + void socks5_stream::socks_connect(boost::shared_ptr h) + { + 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_address(m_remote_endpoint.address(), p); + write_uint16(m_remote_endpoint.port(), p); + assert(p - &m_buffer[0] == int(m_buffer.size())); + + asio::async_write(m_sock, asio::buffer(m_buffer) + , boost::bind(&socks5_stream::connect1, this, _1, h)); + } + + void socks5_stream::connect1(asio::error_code const& e, boost::shared_ptr h) + { + if (e) + { + (*h)(e); + close(); + return; + } + + m_buffer.resize(6 + 4); // assume an IPv4 address + asio::async_read(m_sock, asio::buffer(m_buffer) + , boost::bind(&socks5_stream::connect2, this, _1, h)); + } + + void socks5_stream::connect2(asio::error_code const& e, boost::shared_ptr h) + { + if (e) + { + (*h)(e); + close(); + return; + } + + using namespace libtorrent::detail; + + // send SOCKS5 connect command + char* p = &m_buffer[0]; + int version = read_uint8(p); + if (version < 5) + { + (*h)(asio::error::operation_not_supported); + close(); + return; + } + int response = read_uint8(p); + if (response != 0) + { + asio::error_code e = asio::error::fault; + 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; + } + (*h)(e); + close(); + 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::operation_not_supported); + close(); + return; + } + m_buffer.resize(skip_bytes); + + asio::async_read(m_sock, asio::buffer(m_buffer) + , boost::bind(&socks5_stream::connect3, this, _1, h)); + } + + void socks5_stream::connect3(asio::error_code const& e, boost::shared_ptr h) + { + if (e) + { + (*h)(e); + close(); + return; + } + + std::vector().swap(m_buffer); + (*h)(e); + } +} +