added missing file error_code.cpp
[libtorrent.git] / src / udp_socket.cpp
blob78091116a768b04bdefdc50a5f7459cfe3e0bcd5
1 #include "libtorrent/udp_socket.hpp"
2 #include "libtorrent/connection_queue.hpp"
3 #include <stdlib.h>
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>
9 #else
10 #include <boost/asio/read.hpp>
11 #endif
13 using namespace libtorrent;
15 udp_socket::udp_socket(asio::io_service& ios, udp_socket::callback_t const& c
16 , connection_queue& cc)
17 : m_callback(c)
18 , m_ipv4_sock(ios)
19 , m_ipv6_sock(ios)
20 , m_bind_port(0)
21 , m_socks5_sock(ios)
22 , m_connection_ticket(-1)
23 , m_cc(cc)
24 , m_resolver(ios)
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;
33 if (m_tunnel_packets)
35 // send udp packets through SOCKS5 server
36 wrap(ep, p, len, ec);
37 return;
40 if (ep.address().is_v4() && m_ipv4_sock.is_open())
41 m_ipv4_sock.send_to(asio::buffer(p, len), ep, 0, ec);
42 else
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;
52 if (e)
54 #ifndef BOOST_NO_EXCEPTIONS
55 try {
56 #endif
57 if (s == &m_ipv4_sock)
58 m_callback(e, m_v4_ep, 0, 0);
59 else
60 m_callback(e, m_v6_ep, 0, 0);
61 #ifndef BOOST_NO_EXCEPTIONS
62 } catch(std::exception&) {}
63 #endif
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)
72 return;
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));
77 else
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));
81 return;
84 if (s == &m_ipv4_sock)
86 #ifndef BOOST_NO_EXCEPTIONS
87 try {
88 #endif
90 if (m_tunnel_packets && m_v4_ep == m_proxy_addr)
91 unwrap(e, m_v4_buf, bytes_transferred);
92 else
93 m_callback(e, m_v4_ep, m_v4_buf, bytes_transferred);
95 #ifndef BOOST_NO_EXCEPTIONS
96 } catch(std::exception&) {}
97 #endif
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));
101 else
103 #ifndef BOOST_NO_EXCEPTIONS
104 try {
105 #endif
107 if (m_tunnel_packets && m_v6_ep == m_proxy_addr)
108 unwrap(e, m_v6_buf, bytes_transferred);
109 else
110 m_callback(e, m_v6_ep, m_v6_buf, bytes_transferred);
112 #ifndef BOOST_NO_EXCEPTIONS
113 } catch(std::exception&) {}
114 #endif
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;
124 char header[20];
125 char* h = header;
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);
139 else
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;
151 char const* p = buf;
152 p += 2; // reserved
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);
160 if (atyp == 1)
162 // IPv4
163 sender = read_v4_endpoint<udp::endpoint>(p);
165 else if (atyp == 4)
167 // IPv6
168 sender = read_v6_endpoint<udp::endpoint>(p);
170 else
172 // domain name not supported
173 return;
176 m_callback(e, sender, p, size - (p - buf));
179 void udp_socket::close()
181 error_code ec;
182 m_ipv4_sock.close(ec);
183 m_ipv6_sock.close(ec);
184 m_socks5_sock.close(ec);
185 m_callback.clear();
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);
201 if (ec) return;
202 m_ipv4_sock.bind(ep, ec);
203 if (ec) return;
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));
207 else
209 m_ipv6_sock.set_option(v6only(true), ec);
210 if (ec) return;
211 m_ipv6_sock.bind(ep, ec);
212 if (ec) return;
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)
221 error_code ec;
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);
227 if (!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);
234 if (!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));
241 m_bind_port = port;
244 void udp_socket::set_proxy_settings(proxy_settings const& ps)
246 error_code ec;
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)
265 if (e) return;
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()
274 error_code ec;
275 m_socks5_sock.close(ec);
276 m_connection_ticket = -1;
279 void udp_socket::on_connect(int ticket)
281 m_connection_ticket = ticket;
282 error_code ec;
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;
292 if (e) return;
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
305 else
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)
317 if (e) return;
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)
325 if (e) return;
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;
335 if (method == 0)
337 socks_forward_udp();
339 else if (method == 2)
341 if (m_proxy_settings.username.empty())
343 error_code ec;
344 m_socks5_sock.close(ec);
345 return;
348 // start sub-negotiation
349 char* p = &m_tmp_buf[0];
350 write_uint8(1, p);
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));
358 else
360 error_code ec;
361 m_socks5_sock.close(ec);
362 return;
366 void udp_socket::handshake3(error_code const& e)
368 if (e) return;
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)
376 if (e) return;
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;
387 socks_forward_udp();
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)
409 if (e) return;
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)
417 if (e) return;
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;
430 if (atyp == 1)
432 m_proxy_addr.address(address_v4(read_uint32(p)));
433 m_proxy_addr.port(read_uint16(p));
435 else
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;