Electroneum
Loading...
Searching...
No Matches
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
45namespace net
46{
47namespace 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 {
58 std::uint8_t version;
59 std::uint8_t command_code;
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
143 const boost::system::error_category& error_category() noexcept
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))
165 self.done(socks::error::bad_read, std::move(self_));
166 else if (self.buffer_[0] != 0) // response version
167 self.done(socks::error::unexpected_version, std::move(self_));
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())
194 self.done(socks::error::bad_write, std::move(self_));
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
223 client::client(stream_type::socket&& proxy, socks::version ver)
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
constexpr std::size_t size() const noexcept
Definition span.h:111
b32 i2p address; internal format not condensed/decoded.
Definition i2p_address.h:52
virtual ~client()
Definition socks.cpp:227
bool set_resolve_command(boost::string_ref domain)
Try to set domain as remote DNS A record lookup request.
Definition socks.cpp:284
bool set_connect_command(const epee::net_utils::ipv4_network_address &address)
Try to set address as remote connection request.
Definition socks.cpp:229
epee::span< const std::uint8_t > buffer() const noexcept
Definition socks.h:143
static bool connect_and_send(std::shared_ptr< client > self, const stream_type::endpoint &proxy_address)
Definition socks.cpp:294
client(stream_type::socket &&proxy, socks::version ver)
Definition socks.cpp:223
static bool send(std::shared_ptr< client > self)
Definition socks.cpp:305
socks::version socks_version() const noexcept
Definition socks.h:140
Tor onion address; internal format not condensed/decoded.
Definition tor_address.h:52
std::string message("Message requiring signing")
const char * name
const boost::system::error_category & error_category() noexcept
Definition socks.cpp:143
error
Possible errors with socks communication. Defined in https://www.openssh.com/txt/socks4....
Definition socks.h:66
version
Supported socks variants.
Definition socks.h:58
@ v4a_tor
Extensions defined in Tor codebase.
Definition socks.h:61
STL namespace.
#define GET_IO_SERVICE(s)
const GenericPointer< typename T::ValueType > T2 value
Definition pointer.h:1225
void operator()(boost::system::error_code error=boost::system::error_code{})
Definition socks.cpp:316
std::shared_ptr< client > self_
Definition socks.h:201
void operator()(const boost::system::error_code error, const std::size_t bytes) const
Definition socks.cpp:153
std::shared_ptr< client > self_
Definition socks.cpp:151
std::shared_ptr< client > self_
Definition socks.cpp:178
static boost::asio::mutable_buffers_1 get_buffer(client &self) noexcept
Definition socks.cpp:180
void operator()(const boost::system::error_code error, const std::size_t bytes)
Definition socks.cpp:186
void operator()(const boost::system::error_code error)
Definition socks.cpp:210
std::shared_ptr< client > self_
Definition socks.cpp:203
static boost::asio::const_buffers_1 get_buffer(client const &self) noexcept
Definition socks.cpp:205
const char * address
Definition multisig.cpp:37