Electroneum
socks.cpp
Go to the documentation of this file.
1 // Copyright (c) 2018-2019, The Monero Project
2 //
3 // All rights reserved.
4 //
5 // Redistribution and use in source and binary forms, with or without modification, are
6 // permitted provided that the following conditions are met:
7 //
8 // 1. Redistributions of source code must retain the above copyright notice, this list of
9 // conditions and the following disclaimer.
10 //
11 // 2. Redistributions in binary form must reproduce the above copyright notice, this list
12 // of conditions and the following disclaimer in the documentation and/or other
13 // materials provided with the distribution.
14 //
15 // 3. Neither the name of the copyright holder nor the names of its contributors may be
16 // used to endorse or promote products derived from this software without specific
17 // prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
20 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
21 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
22 // THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26 // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
27 // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 
29 #include "socks.h"
30 
31 #include <algorithm>
32 #include <boost/asio/buffer.hpp>
33 #include <boost/asio/read.hpp>
34 #include <boost/asio/write.hpp>
35 #include <boost/endian/arithmetic.hpp>
36 #include <boost/endian/conversion.hpp>
37 #include <cstring>
38 #include <limits>
39 #include <string>
40 
41 #include "net/net_utils_base.h"
42 #include "net/tor_address.h"
43 #include "net/i2p_address.h"
44 
45 namespace net
46 {
47 namespace socks
48 {
49  namespace
50  {
51  constexpr const unsigned v4_reply_size = 8;
52  constexpr const std::uint8_t v4_connect_command = 1;
53  constexpr const std::uint8_t v4tor_resolve_command = 0xf0;
54  constexpr const std::uint8_t v4_request_granted = 90;
55 
56  struct v4_header
57  {
60  boost::endian::big_uint16_t port;
61  boost::endian::big_uint32_t ip;
62  };
63 
64  std::size_t write_domain_header(epee::span<std::uint8_t> out, const std::uint8_t command, const std::uint16_t port, const boost::string_ref domain)
65  {
66  if (std::numeric_limits<std::size_t>::max() - sizeof(v4_header) - 2 < domain.size())
67  return 0;
68 
69  const std::size_t buf_size = sizeof(v4_header) + domain.size() + 2;
70  if (out.size() < buf_size)
71  return 0;
72 
73  // version 4, 1 indicates invalid ip for domain extension
74  const v4_header temp{4, command, port, std::uint32_t(1)};
75  std::memcpy(out.data(), std::addressof(temp), sizeof(temp));
76  out.remove_prefix(sizeof(temp));
77 
78  *(out.data()) = 0;
79  out.remove_prefix(1);
80 
81  std::memcpy(out.data(), domain.data(), domain.size());
82  out.remove_prefix(domain.size());
83 
84  *(out.data()) = 0;
85  return buf_size;
86  }
87 
88  struct socks_category : boost::system::error_category
89  {
90  explicit socks_category() noexcept
91  : boost::system::error_category()
92  {}
93 
94  const char* name() const noexcept override
95  {
96  return "net::socks::error_category";
97  }
98 
99  virtual std::string message(int value) const override
100  {
101  switch (socks::error(value))
102  {
104  return "Socks request rejected or failed";
106  return "Socks request rejected because server cannot connect to identd on the client";
108  return "Socks request rejected because the client program and identd report different user-ids";
109 
111  return "Socks boost::async_read read fewer bytes than expected";
113  return "Socks boost::async_write wrote fewer bytes than expected";
115  return "Socks server returned unexpected version in reply";
116 
117  default:
118  break;
119  }
120  return "Unknown net::socks::error";
121  }
122 
123  boost::system::error_condition default_error_condition(int value) const noexcept override
124  {
125  switch (socks::error(value))
126  {
129  return boost::system::errc::io_error;
131  return boost::system::errc::protocol_error;
132  default:
133  break;
134  };
135  if (1 <= value && value <= 256)
136  return boost::system::errc::protocol_error;
137 
138  return boost::system::error_condition{value, *this};
139  }
140  };
141  }
142 
144  {
145  static const socks_category instance{};
146  return instance;
147  }
148 
150  {
151  std::shared_ptr<client> self_;
152 
153  void operator()(const boost::system::error_code error, const std::size_t bytes) const
154  {
155  static_assert(1 < sizeof(self_->buffer_), "buffer too small for v4 response");
156 
157  if (self_)
158  {
159  client& self = *self_;
160  self.buffer_size_ = std::min(bytes, sizeof(self.buffer_));
161 
162  if (error)
163  self.done(error, std::move(self_));
164  else if (self.buffer().size() < sizeof(v4_header))
166  else if (self.buffer_[0] != 0) // response version
168  else if (self.buffer_[1] != v4_request_granted)
169  self.done(socks::error(int(self.buffer_[1]) + 1), std::move(self_));
170  else
171  self.done(boost::system::error_code{}, std::move(self_));
172  }
173  }
174  };
175 
177  {
178  std::shared_ptr<client> self_;
179 
180  static boost::asio::mutable_buffers_1 get_buffer(client& self) noexcept
181  {
182  static_assert(sizeof(v4_header) <= sizeof(self.buffer_), "buffer too small for v4 response");
183  return boost::asio::buffer(self.buffer_, sizeof(v4_header));
184  }
185 
186  void operator()(const boost::system::error_code error, const std::size_t bytes)
187  {
188  if (self_)
189  {
190  client& self = *self_;
191  if (error)
192  self.done(error, std::move(self_));
193  else if (bytes < self.buffer().size())
195  else
196  boost::asio::async_read(self.proxy_, get_buffer(self), self.strand_.wrap(completed{std::move(self_)}));
197  }
198  }
199  };
200 
202  {
203  std::shared_ptr<client> self_;
204 
205  static boost::asio::const_buffers_1 get_buffer(client const& self) noexcept
206  {
207  return boost::asio::buffer(self.buffer_, self.buffer_size_);
208  }
209 
210  void operator()(const boost::system::error_code error)
211  {
212  if (self_)
213  {
214  client& self = *self_;
215  if (error)
216  self.done(error, std::move(self_));
217  else
218  boost::asio::async_write(self.proxy_, get_buffer(self), self.strand_.wrap(read{std::move(self_)}));
219  }
220  }
221  };
222 
224  : proxy_(std::move(proxy)), strand_(GET_IO_SERVICE(proxy_)), buffer_size_(0), buffer_(), ver_(ver)
225  {}
226 
228 
230  {
231  switch (socks_version())
232  {
233  case version::v4:
234  case version::v4a:
235  case version::v4a_tor:
236  break;
237  default:
238  return false;
239  }
240 
241  static_assert(sizeof(v4_header) < sizeof(buffer_), "buffer size too small for request");
242  static_assert(0 < sizeof(buffer_), "buffer size too small for null termination");
243 
244  // version 4
245  const v4_header temp{4, v4_connect_command, address.port(), boost::endian::big_to_native(address.ip())};
246  std::memcpy(std::addressof(buffer_), std::addressof(temp), sizeof(temp));
247  buffer_[sizeof(temp)] = 0;
248  buffer_size_ = sizeof(temp) + 1;
249 
250  return true;
251  }
252 
253  bool client::set_connect_command(const boost::string_ref domain, std::uint16_t port)
254  {
255  switch (socks_version())
256  {
257  case version::v4a:
258  case version::v4a_tor:
259  break;
260 
261  default:
262  return false;
263  }
264 
265  const std::size_t buf_used = write_domain_header(buffer_, v4_connect_command, port, domain);
266  buffer_size_ = buf_used;
267  return buf_used != 0;
268  }
269 
271  {
272  if (!address.is_unknown())
273  return set_connect_command(address.host_str(), address.port());
274  return false;
275  }
276 
278  {
279  if (!address.is_unknown())
280  return set_connect_command(address.host_str(), address.port());
281  return false;
282  }
283 
284  bool client::set_resolve_command(boost::string_ref domain)
285  {
287  return false;
288 
289  const std::size_t buf_used = write_domain_header(buffer_, v4tor_resolve_command, 0, domain);
290  buffer_size_ = buf_used;
291  return buf_used != 0;
292  }
293 
294  bool client::connect_and_send(std::shared_ptr<client> self, const stream_type::endpoint& proxy_address)
295  {
296  if (self && !self->buffer().empty())
297  {
298  client& alias = *self;
299  alias.proxy_.async_connect(proxy_address, alias.strand_.wrap(write{std::move(self)}));
300  return true;
301  }
302  return false;
303  }
304 
305  bool client::send(std::shared_ptr<client> self)
306  {
307  if (self && !self->buffer().empty())
308  {
309  client& alias = *self;
310  boost::asio::async_write(alias.proxy_, write::get_buffer(alias), alias.strand_.wrap(read{std::move(self)}));
311  return true;
312  }
313  return false;
314  }
315 
316  void client::async_close::operator()(boost::system::error_code error)
317  {
318  if (self_ && error != boost::system::errc::operation_canceled)
319  {
320  const std::shared_ptr<client> self = std::move(self_);
321  self->strand_.dispatch([self] ()
322  {
323  if (self && self->proxy_.is_open())
324  {
325  self->proxy_.shutdown(boost::asio::ip::tcp::socket::shutdown_both);
326  self->proxy_.close();
327  }
328  });
329  }
330  }
331 } // socks
332 } // net
static boost::asio::const_buffers_1 get_buffer(client const &self) noexcept
Definition: socks.cpp:205
epee::span< const std::uint8_t > buffer() const noexcept
Definition: socks.h:143
socks::version socks_version() const noexcept
Definition: socks.h:140
static bool connect_and_send(std::shared_ptr< client > self, const stream_type::endpoint &proxy_address)
Definition: socks.cpp:294
void operator()(const boost::system::error_code error)
Definition: socks.cpp:210
b32 i2p address; internal format not condensed/decoded.
Definition: i2p_address.h:51
::std::string string
Definition: gtest-port.h:1097
bool set_resolve_command(boost::string_ref domain)
Try to set domain as remote DNS A record lookup request.
Definition: socks.cpp:284
std::unique_ptr< void, close > socket
Unique ZMQ socket handle, calls zmq_close on destruction.
Definition: zmq.h:101
STL namespace.
unsigned short uint16_t
Definition: stdint.h:125
Non-owning sequence of data. Does not deep copy.
Definition: span.h:56
unsigned char uint8_t
Definition: stdint.h:124
const char * name
static boost::asio::mutable_buffers_1 get_buffer(client &self) noexcept
Definition: socks.cpp:180
Extensions defined in Tor codebase.
void operator()(const boost::system::error_code error, const std::size_t bytes) const
Definition: socks.cpp:153
bool set_connect_command(const epee::net_utils::ipv4_network_address &address)
Try to set address as remote connection request.
Definition: socks.cpp:229
void operator()(boost::system::error_code error=boost::system::error_code{})
Definition: socks.cpp:316
unsigned int uint32_t
Definition: stdint.h:126
void operator()(const boost::system::error_code error, const std::size_t bytes)
Definition: socks.cpp:186
std::shared_ptr< client > self_
Definition: socks.h:201
virtual ~client()
Definition: socks.cpp:227
version
Supported socks variants.
Definition: socks.h:57
std::shared_ptr< client > self_
Definition: socks.cpp:151
std::uint8_t command_code
Definition: socks.cpp:59
std::string message("Message requiring signing")
boost::endian::big_uint32_t ip
Definition: socks.cpp:61
boost::endian::big_uint16_t port
Definition: socks.cpp:60
Client support for socks connect and resolve commands.
Definition: socks.h:93
#define GET_IO_SERVICE(s)
error
Possible errors with socks communication. Defined in https://www.openssh.com/txt/socks4.protocol.
Definition: socks.h:65
const T & move(const T &t)
Definition: gtest-port.h:1317
Tor onion address; internal format not condensed/decoded.
Definition: tor_address.h:51
client(stream_type::socket &&proxy, socks::version ver)
Definition: socks.cpp:223
const GenericPointer< typename T::ValueType > T2 value
Definition: pointer.h:1225
void * memcpy(void *a, const void *b, size_t c)
const boost::system::error_category & error_category() noexcept
Definition: socks.cpp:143
std::shared_ptr< client > self_
Definition: socks.cpp:178
const char * address
Definition: multisig.cpp:37
static bool send(std::shared_ptr< client > self)
Definition: socks.cpp:305
std::shared_ptr< client > self_
Definition: socks.cpp:203