Asio Extensions
Additional functionality built on top of (Boost.)Asio
detail/client.hpp
1 /// @copyright Copyright (c) 2018 Tim Niederhausen (tim@rnc-ag.de)
2 /// Distributed under the Boost Software License, Version 1.0.
3 /// (See accompanying file LICENSE_1_0.txt or copy at
4 /// http://www.boost.org/LICENSE_1_0.txt)
5 
6 #ifndef ASIOEXT_SOCKS_DETAIL_CLIENT_HPP
7 #define ASIOEXT_SOCKS_DETAIL_CLIENT_HPP
8 
9 #include "asioext/detail/config.hpp"
10 
11 #if ASIOEXT_HAS_PRAGMA_ONCE
12 # pragma once
13 #endif
14 
15 #include "asioext/socks/error.hpp"
16 #include "asioext/composed_operation.hpp"
17 #include "asioext/bind_handler.hpp"
18 
19 #include "asioext/socks/detail/protocol.hpp"
20 #include "asioext/detail/coroutine.hpp"
21 #include "asioext/detail/move_support.hpp"
22 
23 #if defined(ASIOEXT_USE_BOOST_ASIO)
24 # include <boost/asio/read.hpp>
25 # include <boost/asio/write.hpp>
26 #else
27 # include <asio/read.hpp>
28 # include <asio/write.hpp>
29 #endif
30 
31 ASIOEXT_NS_BEGIN
32 
33 namespace socks {
34 namespace detail {
35 
36 template <typename Socket, typename DynamicBuffer, typename Handler>
38  : public composed_operation<Handler>
39  , asio::coroutine
40 {
41 public:
42  socks_sgreet_op(Handler& handler, Socket& socket,
43  const auth_method* auth_methods,
44  std::size_t num_auth_methods,
45  DynamicBuffer& buffer)
46  : composed_operation<Handler>(ASIOEXT_MOVE_CAST(Handler)(handler))
47  , socket_(socket)
48  , buffer_(ASIOEXT_MOVE_CAST(DynamicBuffer)(buffer))
49  {
50  const std::size_t size =
51  get_sgreet_packet_size(auth_methods, num_auth_methods);
52 
53  if (0 == size) {
54  socket_.get_io_service().post(::asioext::bind_handler(
55  this->handler_, asio::error::invalid_argument,
56  auth_method::no_acceptable));
57  return;
58  }
59 
60  asio::mutable_buffer buf = buffer_.prepare(size);
61  encode_sgreet_packet(auth_methods, num_auth_methods,
62  asio::buffer_cast<uint8_t*>(buf));
63  buffer_.commit(size);
64 
65  asio::async_write(socket, buffer_.data(),
66  ASIOEXT_MOVE_CAST(socks_sgreet_op)(*this));
67  }
68 
69  void operator()(error_code ec, std::size_t bytes_transferred = 0);
70 
71 private:
72  Socket& socket_;
73  DynamicBuffer buffer_;
74 };
75 
76 template <typename Socket, typename DynamicBuffer, typename Handler>
78  error_code ec, std::size_t bytes_transferred)
79 {
80  if (ec) {
81  this->handler_(ec, auth_method::no_acceptable);
82  return;
83  }
84 
85  ASIOEXT_CORO_REENTER (this) {
86  buffer_.consume(bytes_transferred);
87  ASIOEXT_CORO_YIELD asio::async_read(
88  socket_, buffer_.prepare(2),
89  ASIOEXT_MOVE_CAST(socks_sgreet_op)(*this));
90 
91  if (ec) {
92  this->handler_(ec, auth_method::no_acceptable);
93  return;
94  }
95 
96  buffer_.commit(2);
97 
98  const uint8_t* data = asio::buffer_cast<const uint8_t*>(buffer_.data());
99 
100  const uint8_t version = data[0];
101  const auth_method chosen_method = static_cast<auth_method>(data[1]);
102 
103  buffer_.consume(2);
104 
105  if (version != 5) {
106  this->handler_(error::invalid_version,
107  auth_method::no_acceptable);
108  return;
109  }
110 
111  if (chosen_method == auth_method::no_acceptable) {
112  this->handler_(error::no_acceptable_auth_method,
113  auth_method::no_acceptable);
114  return;
115  }
116 
117  this->handler_(ec, chosen_method);
118  }
119 }
120 
121 template <typename Socket, typename DynamicBuffer, typename Handler>
123  : public composed_operation<Handler>
124  , asio::coroutine
125 {
126 public:
127  socks_slogin_op(Handler& handler, Socket& socket,
128  const std::string& username,
129  const std::string& password,
130  DynamicBuffer& buffer)
131  : composed_operation<Handler>(ASIOEXT_MOVE_CAST(Handler)(handler))
132  , socket_(socket)
133  , buffer_(ASIOEXT_MOVE_CAST(DynamicBuffer)(buffer))
134  {
135  const std::size_t size =
136  get_slogin_packet_size(username, password);
137 
138  if (0 == size) {
139  socket_.get_io_service().post(::asioext::bind_handler(
140  this->handler_, asio::error::invalid_argument));
141  return;
142  }
143 
144  asio::mutable_buffer buf = buffer_.prepare(size);
145  encode_slogin_packet(username, password,
146  asio::buffer_cast<uint8_t*>(buf));
147  buffer_.commit(size);
148 
149  asio::async_write(socket, buffer_.data(),
150  ASIOEXT_MOVE_CAST(socks_slogin_op)(*this));
151  }
152 
153  void operator()(error_code ec, std::size_t bytes_transferred = 0);
154 
155 private:
156  Socket& socket_;
157  DynamicBuffer buffer_;
158 };
159 
160 template <typename Socket, typename DynamicBuffer, typename Handler>
162  error_code ec, std::size_t bytes_transferred)
163 {
164  if (ec) {
165  this->handler_(ec);
166  return;
167  }
168 
169  ASIOEXT_CORO_REENTER (this) {
170  buffer_.consume(bytes_transferred);
171  ASIOEXT_CORO_YIELD asio::async_read(
172  socket_, buffer_.prepare(2),
173  ASIOEXT_MOVE_CAST(socks_slogin_op)(*this));
174 
175  if (ec) {
176  this->handler_(ec);
177  return;
178  }
179 
180  buffer_.commit(2);
181 
182  const uint8_t* data = asio::buffer_cast<const uint8_t*>(buffer_.data());
183 
184  const uint8_t version = data[0];
185  const uint8_t status_code = data[1];
186 
187  buffer_.consume(2);
188 
189  if (version != 1) {
190  this->handler_(error::invalid_auth_version);
191  return;
192  }
193 
194  if (status_code != 0) {
195  this->handler_(error::login_failed);
196  return;
197  }
198 
199  this->handler_(ec);
200  }
201 }
202 
203 template <typename Socket, typename DynamicBuffer, typename Handler>
205  : public composed_operation<Handler>
206  , asio::coroutine
207 {
208 public:
209  socks_sexec_op(Handler& handler, Socket& socket,
210  command cmd,
211  const asio::ip::tcp::endpoint& remote,
212  const std::string& remote_host,
213  uint16_t port,
214  DynamicBuffer& buffer)
215  : composed_operation<Handler>(ASIOEXT_MOVE_CAST(Handler)(handler))
216  , socket_(socket)
217  , buffer_(ASIOEXT_MOVE_CAST(DynamicBuffer)(buffer))
218  {
219  const std::size_t size =
220  get_sexec_packet_size(cmd, remote, remote_host, port);
221 
222  if (0 == size) {
223  socket_.get_io_service().post(::asioext::bind_handler(
224  this->handler_, asio::error::invalid_argument));
225  return;
226  }
227 
228  asio::mutable_buffer buf = buffer_.prepare(size);
229  encode_sexec_packet(cmd, remote, remote_host, port,
230  asio::buffer_cast<uint8_t*>(buf));
231  buffer_.commit(size);
232 
233  asio::async_write(socket, buffer_.data(),
234  ASIOEXT_MOVE_CAST(socks_sexec_op)(*this));
235  }
236 
237  void operator()(error_code ec, std::size_t bytes_transferred = 0);
238 
239 private:
240  Socket& socket_;
241  DynamicBuffer buffer_;
242  uint8_t address_type_;
243  uint8_t first_address_byte_;
244 };
245 
246 template <typename Socket, typename DynamicBuffer, typename Handler>
248  error_code ec, std::size_t bytes_transferred)
249 {
250  if (ec) {
251  this->handler_(ec);
252  return;
253  }
254 
255  std::size_t size;
256  ASIOEXT_CORO_REENTER (this) {
257  buffer_.consume(bytes_transferred);
258  ASIOEXT_CORO_YIELD asio::async_read(
259  socket_, buffer_.prepare(5),
260  ASIOEXT_MOVE_CAST(socks_sexec_op)(*this));
261 
262  if (ec) {
263  this->handler_(ec);
264  return;
265  }
266 
267  {
268  buffer_.commit(5);
269 
270  const uint8_t* data = asio::buffer_cast<const uint8_t*>(buffer_.data());
271 
272  const uint8_t version = data[0];
273  const uint8_t status_code = data[1];
274 
275  address_type_ = data[3];
276  first_address_byte_ = data[4];
277 
278  buffer_.consume(5);
279 
280  if (version != 5) {
281  this->handler_(error::invalid_version);
282  return;
283  }
284 
285  if (status_code != 0) {
286  switch (status_code) {
287  case 2: ec = asio::error::no_permission; break;
288  case 3: ec = asio::error::network_unreachable; break;
289  case 4: ec = asio::error::host_unreachable; break;
290  case 5: ec = asio::error::connection_refused; break;
291  case 6: ec = asio::error::timed_out; break;
292  case 7: ec = error::command_not_supported; break;
293  case 8: ec = asio::error::address_family_not_supported; break;
294  }
295  this->handler_(ec);
296  return;
297  }
298  }
299 
300  switch (address_type_) {
301  // IPv4
302  case 1: {
303  size = 4 - 1;
304  break;
305  }
306  // Hostname
307  case 3: {
308  size = first_address_byte_;
309  break;
310  }
311  // IPv6
312  case 4: {
313  size = 16 - 1;
314  break;
315  }
316  }
317 
318  ASIOEXT_CORO_YIELD asio::async_read(
319  socket_, buffer_.prepare(size + 2),
320  ASIOEXT_MOVE_CAST(socks_sexec_op)(*this));
321 
322  if (ec) {
323  this->handler_(ec);
324  return;
325  }
326 
327  buffer_.commit(bytes_transferred);
328  buffer_.consume(bytes_transferred);
329 
330  this->handler_(ec);
331  }
332 }
333 
334 }
335 }
336 
337 ASIOEXT_NS_END
338 
339 #endif
command
SOCKS commands.
Definition: constants.hpp:32
Definition: detail/client.hpp:204
auth_method
SOCKS login authentication methods.
Definition: constants.hpp:58
void encode_sgreet_packet(const auth_method *auth_methods, std::size_t num_auth_methods, uint8_t *out)
void encode_sexec_packet(command cmd, const asio::ip::tcp::endpoint &remote, const std::string &remote_host, uint16_t port, uint8_t *out)
socks_slogin_op(Handler &handler, Socket &socket, const std::string &username, const std::string &password, DynamicBuffer &buffer)
Definition: detail/client.hpp:127
implementation_defined bind_handler(Handler &&handler, Args &&... args)
Bind values to a Handler&#39;s arguments to create a CompletionHandler.
asio::const_buffers_1 buffer(const basic_linear_buffer< Allocator > &b) noexcept
Definition: linear_buffer.hpp:385
std::size_t get_sexec_packet_size(command cmd, const asio::ip::tcp::endpoint &remote, const std::string &remote_host, uint16_t port)
std::size_t get_slogin_packet_size(const std::string &username, const std::string &password)
STL class.
void encode_slogin_packet(const std::string &username, const std::string &password, uint8_t *out)
Base class for composed operations.
Definition: composed_operation.hpp:111
socks_sgreet_op(Handler &handler, Socket &socket, const auth_method *auth_methods, std::size_t num_auth_methods, DynamicBuffer &buffer)
Definition: detail/client.hpp:42
version
SOCKS versions.
Definition: constants.hpp:48
socks_sexec_op(Handler &handler, Socket &socket, command cmd, const asio::ip::tcp::endpoint &remote, const std::string &remote_host, uint16_t port, DynamicBuffer &buffer)
Definition: detail/client.hpp:209
automatically_chosen error_code
Typedef for the error_code class used by this library.
Definition: error_code.hpp:37
std::size_t get_sgreet_packet_size(const auth_method *auth_methods, std::size_t num_auth_methods)
Definition: detail/client.hpp:37
Definition: detail/client.hpp:122