1 #include "libtorrent/udp_socket.hpp"
2 #include "libtorrent/connection_queue.hpp"
4 #include <boost/bind.hpp>
5 #include <boost/lexical_cast.hpp>
6 #include <boost/array.hpp>
7 #if BOOST_VERSION < 103500
8 #include <asio/read.hpp>
10 #include <boost/asio/read.hpp>
13 using namespace libtorrent
;
15 udp_socket::udp_socket(asio::io_service
& ios
, udp_socket::callback_t
const& c
16 , connection_queue
& cc
)
22 , m_connection_ticket(-1)
25 , m_tunnel_packets(false)
29 void udp_socket::send(udp::endpoint
const& ep
, char const* p
, int len
, error_code
& ec
)
31 if (ec
== asio::error::operation_aborted
) return;
35 // send udp packets through SOCKS5 server
40 if (ep
.address().is_v4() && m_ipv4_sock
.is_open())
41 m_ipv4_sock
.send_to(asio::buffer(p
, len
), ep
, 0, ec
);
43 m_ipv6_sock
.send_to(asio::buffer(p
, len
), ep
, 0, ec
);
46 void udp_socket::on_read(udp::socket
* s
, error_code
const& e
, std::size_t bytes_transferred
)
48 if (e
== asio::error::operation_aborted
) return;
50 if (!m_callback
) return;
54 #ifndef BOOST_NO_EXCEPTIONS
57 if (s
== &m_ipv4_sock
)
58 m_callback(e
, m_v4_ep
, 0, 0);
60 m_callback(e
, m_v6_ep
, 0, 0);
61 #ifndef BOOST_NO_EXCEPTIONS
62 } catch(std::exception
&) {}
65 // don't stop listening on recoverable errors
66 if (e
!= asio::error::host_unreachable
67 && e
!= asio::error::fault
68 && e
!= asio::error::connection_reset
69 && e
!= asio::error::connection_refused
70 && e
!= asio::error::connection_aborted
71 && e
!= asio::error::message_size
)
74 if (s
== &m_ipv4_sock
)
75 s
->async_receive_from(asio::buffer(m_v4_buf
, sizeof(m_v4_buf
))
76 , m_v4_ep
, boost::bind(&udp_socket::on_read
, this, s
, _1
, _2
));
78 s
->async_receive_from(asio::buffer(m_v6_buf
, sizeof(m_v6_buf
))
79 , m_v6_ep
, boost::bind(&udp_socket::on_read
, this, s
, _1
, _2
));
84 if (s
== &m_ipv4_sock
)
86 #ifndef BOOST_NO_EXCEPTIONS
90 if (m_tunnel_packets
&& m_v4_ep
== m_proxy_addr
)
91 unwrap(e
, m_v4_buf
, bytes_transferred
);
93 m_callback(e
, m_v4_ep
, m_v4_buf
, bytes_transferred
);
95 #ifndef BOOST_NO_EXCEPTIONS
96 } catch(std::exception
&) {}
98 s
->async_receive_from(asio::buffer(m_v4_buf
, sizeof(m_v4_buf
))
99 , m_v4_ep
, boost::bind(&udp_socket::on_read
, this, s
, _1
, _2
));
103 #ifndef BOOST_NO_EXCEPTIONS
107 if (m_tunnel_packets
&& m_v6_ep
== m_proxy_addr
)
108 unwrap(e
, m_v6_buf
, bytes_transferred
);
110 m_callback(e
, m_v6_ep
, m_v6_buf
, bytes_transferred
);
112 #ifndef BOOST_NO_EXCEPTIONS
113 } catch(std::exception
&) {}
115 s
->async_receive_from(asio::buffer(m_v6_buf
, sizeof(m_v6_buf
))
116 , m_v6_ep
, boost::bind(&udp_socket::on_read
, this, s
, _1
, _2
));
120 void udp_socket::wrap(udp::endpoint
const& ep
, char const* p
, int len
, error_code
& ec
)
122 using namespace libtorrent::detail
;
127 write_uint16(0, h
); // reserved
128 write_uint8(0, h
); // fragment
129 write_uint8(ep
.address().is_v4()?1:4, h
); // atyp
130 write_address(ep
.address(), h
);
131 write_uint16(ep
.port(), h
);
133 boost::array
<asio::const_buffer
, 2> iovec
;
134 iovec
[0] = asio::const_buffer(header
, h
- header
);
135 iovec
[1] = asio::const_buffer(p
, len
);
137 if (m_proxy_addr
.address().is_v4() && m_ipv4_sock
.is_open())
138 m_ipv4_sock
.send_to(iovec
, m_proxy_addr
, 0, ec
);
140 m_ipv6_sock
.send_to(iovec
, m_proxy_addr
, 0, ec
);
143 // unwrap the UDP packet from the SOCKS5 header
144 void udp_socket::unwrap(error_code
const& e
, char const* buf
, int size
)
146 using namespace libtorrent::detail
;
148 // the minimum socks5 header size
149 if (size
<= 10) return;
153 int frag
= read_uint8(p
);
154 // fragmentation is not supported
155 if (frag
!= 0) return;
157 udp::endpoint sender
;
159 int atyp
= read_uint8(p
);
163 sender
= read_v4_endpoint
<udp::endpoint
>(p
);
168 sender
= read_v6_endpoint
<udp::endpoint
>(p
);
172 // domain name not supported
176 m_callback(e
, sender
, p
, size
- (p
- buf
));
179 void udp_socket::close()
182 m_ipv4_sock
.close(ec
);
183 m_ipv6_sock
.close(ec
);
184 m_socks5_sock
.close(ec
);
186 if (m_connection_ticket
>= 0)
188 m_cc
.done(m_connection_ticket
);
189 m_connection_ticket
= -1;
193 void udp_socket::bind(udp::endpoint
const& ep
, error_code
& ec
)
195 if (m_ipv4_sock
.is_open()) m_ipv4_sock
.close(ec
);
196 if (m_ipv6_sock
.is_open()) m_ipv6_sock
.close(ec
);
198 if (ep
.address().is_v4())
200 m_ipv4_sock
.open(udp::v4(), ec
);
202 m_ipv4_sock
.bind(ep
, ec
);
204 m_ipv4_sock
.async_receive_from(asio::buffer(m_v4_buf
, sizeof(m_v4_buf
))
205 , m_v4_ep
, boost::bind(&udp_socket::on_read
, this, &m_ipv4_sock
, _1
, _2
));
209 m_ipv6_sock
.set_option(v6only(true), ec
);
211 m_ipv6_sock
.bind(ep
, ec
);
213 m_ipv6_sock
.async_receive_from(asio::buffer(m_v6_buf
, sizeof(m_v6_buf
))
214 , m_v6_ep
, boost::bind(&udp_socket::on_read
, this, &m_ipv6_sock
, _1
, _2
));
216 m_bind_port
= ep
.port();
219 void udp_socket::bind(int port
)
223 if (m_ipv4_sock
.is_open()) m_ipv4_sock
.close(ec
);
224 if (m_ipv6_sock
.is_open()) m_ipv6_sock
.close(ec
);
226 m_ipv4_sock
.open(udp::v4(), ec
);
229 m_ipv4_sock
.bind(udp::endpoint(address_v4::any(), port
), ec
);
230 m_ipv4_sock
.async_receive_from(asio::buffer(m_v4_buf
, sizeof(m_v4_buf
))
231 , m_v4_ep
, boost::bind(&udp_socket::on_read
, this, &m_ipv4_sock
, _1
, _2
));
233 m_ipv6_sock
.open(udp::v6(), ec
);
236 m_ipv6_sock
.set_option(v6only(true), ec
);
237 m_ipv6_sock
.bind(udp::endpoint(address_v6::any(), port
), ec
);
238 m_ipv6_sock
.async_receive_from(asio::buffer(m_v6_buf
, sizeof(m_v6_buf
))
239 , m_v6_ep
, boost::bind(&udp_socket::on_read
, this, &m_ipv6_sock
, _1
, _2
));
244 void udp_socket::set_proxy_settings(proxy_settings
const& ps
)
247 m_socks5_sock
.close(ec
);
248 m_tunnel_packets
= false;
250 m_proxy_settings
= ps
;
252 if (ps
.type
== proxy_settings::socks5
253 || ps
.type
== proxy_settings::socks5_pw
)
255 // connect to socks5 server and open up the UDP tunnel
256 tcp::resolver::query
q(ps
.hostname
257 , boost::lexical_cast
<std::string
>(ps
.port
));
258 m_resolver
.async_resolve(q
, boost::bind(
259 &udp_socket::on_name_lookup
, this, _1
, _2
));
263 void udp_socket::on_name_lookup(error_code
const& e
, tcp::resolver::iterator i
)
266 m_proxy_addr
.address(i
->endpoint().address());
267 m_proxy_addr
.port(i
->endpoint().port());
268 m_cc
.enqueue(boost::bind(&udp_socket::on_connect
, this, _1
)
269 , boost::bind(&udp_socket::on_timeout
, this), seconds(10));
272 void udp_socket::on_timeout()
275 m_socks5_sock
.close(ec
);
276 m_connection_ticket
= -1;
279 void udp_socket::on_connect(int ticket
)
281 m_connection_ticket
= ticket
;
283 m_socks5_sock
.open(m_proxy_addr
.address().is_v4()?tcp::v4():tcp::v6(), ec
);
284 m_socks5_sock
.async_connect(tcp::endpoint(m_proxy_addr
.address(), m_proxy_addr
.port())
285 , boost::bind(&udp_socket::on_connected
, this, _1
));
288 void udp_socket::on_connected(error_code
const& e
)
290 m_cc
.done(m_connection_ticket
);
291 m_connection_ticket
= -1;
294 using namespace libtorrent::detail
;
296 // send SOCKS5 authentication methods
297 char* p
= &m_tmp_buf
[0];
298 write_uint8(5, p
); // SOCKS VERSION 5
299 if (m_proxy_settings
.username
.empty()
300 || m_proxy_settings
.type
== proxy_settings::socks5
)
302 write_uint8(1, p
); // 1 authentication method (no auth)
303 write_uint8(0, p
); // no authentication
307 write_uint8(2, p
); // 2 authentication methods
308 write_uint8(0, p
); // no authentication
309 write_uint8(2, p
); // username/password
311 asio::async_write(m_socks5_sock
, asio::buffer(m_tmp_buf
, p
- m_tmp_buf
)
312 , boost::bind(&udp_socket::handshake1
, this, _1
));
315 void udp_socket::handshake1(error_code
const& e
)
319 asio::async_read(m_socks5_sock
, asio::buffer(m_tmp_buf
, 2)
320 , boost::bind(&udp_socket::handshake2
, this, _1
));
323 void udp_socket::handshake2(error_code
const& e
)
327 using namespace libtorrent::detail
;
329 char* p
= &m_tmp_buf
[0];
330 int version
= read_uint8(p
);
331 int method
= read_uint8(p
);
333 if (version
< 5) return;
339 else if (method
== 2)
341 if (m_proxy_settings
.username
.empty())
344 m_socks5_sock
.close(ec
);
348 // start sub-negotiation
349 char* p
= &m_tmp_buf
[0];
351 write_uint8(m_proxy_settings
.username
.size(), p
);
352 write_string(m_proxy_settings
.username
, p
);
353 write_uint8(m_proxy_settings
.password
.size(), p
);
354 write_string(m_proxy_settings
.password
, p
);
355 asio::async_write(m_socks5_sock
, asio::buffer(m_tmp_buf
, p
- m_tmp_buf
)
356 , boost::bind(&udp_socket::handshake3
, this, _1
));
361 m_socks5_sock
.close(ec
);
366 void udp_socket::handshake3(error_code
const& e
)
370 asio::async_read(m_socks5_sock
, asio::buffer(m_tmp_buf
, 2)
371 , boost::bind(&udp_socket::handshake4
, this, _1
));
374 void udp_socket::handshake4(error_code
const& e
)
378 using namespace libtorrent::detail
;
380 char* p
= &m_tmp_buf
[0];
381 int version
= read_uint8(p
);
382 int status
= read_uint8(p
);
384 if (version
!= 1) return;
385 if (status
!= 0) return;
390 void udp_socket::socks_forward_udp()
392 using namespace libtorrent::detail
;
394 // send SOCKS5 UDP command
395 char* p
= &m_tmp_buf
[0];
396 write_uint8(5, p
); // SOCKS VERSION 5
397 write_uint8(3, p
); // UDP ASSOCIATE command
398 write_uint8(0, p
); // reserved
399 write_uint8(0, p
); // ATYP IPv4
400 write_uint32(0, p
); // IP any
401 write_uint16(m_bind_port
, p
);
403 asio::async_write(m_socks5_sock
, asio::buffer(m_tmp_buf
, p
- m_tmp_buf
)
404 , boost::bind(&udp_socket::connect1
, this, _1
));
407 void udp_socket::connect1(error_code
const& e
)
411 asio::async_read(m_socks5_sock
, asio::buffer(m_tmp_buf
, 10)
412 , boost::bind(&udp_socket::connect2
, this, _1
));
415 void udp_socket::connect2(error_code
const& e
)
419 using namespace libtorrent::detail
;
421 char* p
= &m_tmp_buf
[0];
422 int version
= read_uint8(p
); // VERSION
423 int status
= read_uint8(p
); // STATUS
424 read_uint8(p
); // RESERVED
425 int atyp
= read_uint8(p
); // address type
427 if (version
!= 5) return;
428 if (status
!= 0) return;
432 m_proxy_addr
.address(address_v4(read_uint32(p
)));
433 m_proxy_addr
.port(read_uint16(p
));
437 // in this case we need to read more data from the socket
438 TORRENT_ASSERT(false && "not implemented yet!");
441 m_tunnel_packets
= true;