1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
|
#include <atomic>
#include <boost/asio.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/asio/deadline_timer.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/core/noncopyable.hpp>
#include <boost/date_time/posix_time/posix_time_duration.hpp>
#include <boost/noncopyable.hpp>
#include <boost/system/detail/error_code.hpp>
#include <boost/system/error_code.hpp>
#include <boost/variant.hpp>
#include <cstdint>
#include <memory>
#include <string_view>
#include "boost-asio-fastbuffer/fastbuffer.hpp"
namespace SOCKS5 {
class DestinationSocketBase : private boost::noncopyable {
public:
virtual ~DestinationSocketBase() {}
template <typename Callback>
void
connect_tcp(boost::asio::ip::tcp::resolver::results_type::const_iterator &it,
Callback &&handler) {
do_connect_tcp(it, std::forward<Callback>(handler));
}
void tcp_bind() { throw std::runtime_error("TCP Bind not implemented"); }
void udp_bind() { throw std::runtime_error("UDP Bind not implemented"); }
template <typename Callback>
bool read(BufferBase &buffer, Callback &&handler) {
return do_read(buffer, std::forward<Callback>(handler));
}
template <typename Callback>
bool write(BufferBase &buffer, std::size_t length, Callback &&handler) {
return do_write(buffer, length, std::forward<Callback>(handler));
}
virtual bool cancel() = 0;
protected:
virtual void do_connect_tcp(
boost::asio::ip::tcp::resolver::results_type::const_iterator &it,
std::function<void(boost::system::error_code)>) = 0;
virtual bool
do_read(BufferBase &,
std::function<void(boost::system::error_code, std::size_t)>) = 0;
virtual bool
do_write(BufferBase &, std::size_t,
std::function<void(boost::system::error_code, std::size_t)>) = 0;
};
template <typename Executor>
class AsyncDestinationSocket : public DestinationSocketBase {
public:
AsyncDestinationSocket(const Executor &exec) : m_strand(exec) {}
~AsyncDestinationSocket() {}
protected:
void do_connect_tcp(
boost::asio::ip::tcp::resolver::results_type::const_iterator &it,
std::function<void(boost::system::error_code)> handler) override;
bool do_read(BufferBase &buffer,
std::function<void(boost::system::error_code, std::size_t)>
handler) override;
bool do_write(BufferBase &buffer, std::size_t length,
std::function<void(boost::system::error_code, std::size_t)>
handler) override;
bool cancel() override;
boost::asio::strand<Executor> m_strand;
boost::optional<boost::variant<boost::asio::ip::tcp::socket // TCP connect
>>
m_socket;
};
class ProxyBase : private boost::noncopyable {
public:
ProxyBase(std::uint32_t session_id,
boost::asio::ip::tcp::socket &&client_socket,
std::size_t buffer_size = BUFSIZ);
ProxyBase(std::uint32_t session_id,
boost::asio::ip::tcp::socket &&client_socket,
ContiguousStreamBuffer &&input_buffer,
ContiguousStreamBuffer &&output_buffer);
protected:
std::uint32_t m_sessionId;
ContiguousStreamBuffer m_inBuf;
ContiguousStreamBuffer m_outBuf;
boost::asio::ip::tcp::socket m_clientSocket;
};
class ProxyAuth : private ProxyBase,
public std::enable_shared_from_this<ProxyAuth> {
public:
ProxyAuth(std::uint32_t session_id,
boost::asio::ip::tcp::socket &&client_socket);
template <typename GetDsCallback> void start(GetDsCallback &&handler) {
m_getDestinationSocket = std::forward<GetDsCallback>(handler);
start_internal();
}
void set_session_buffer_size(std::size_t buffer_size) {
session_buffer_size = buffer_size;
}
private:
void start_internal();
void recv_client_greeting(const boost::system::error_code &ec,
std::size_t length);
void send_server_greeting(bool auth_supported);
void recv_connection_request(const boost::system::error_code &ec,
std::size_t length);
void process_connection_request();
void send_server_response(std::uint8_t proxy_cmd, std::uint8_t status_code);
void resolve_tcp_destination_host(std::uint8_t proxy_cmd,
const std::string_view &host,
std::uint16_t port);
void connect_to_destination(std::uint8_t proxy_cmd);
void handle_write(const boost::system::error_code &ec, std::size_t length);
void handle_response_write(std::uint8_t proxy_cmd, std::uint8_t status_code,
const boost::system::error_code &ec,
std::size_t length);
std::size_t session_buffer_size;
std::function<std::shared_ptr<DestinationSocketBase>(
boost::asio::any_io_executor)>
m_getDestinationSocket;
std::shared_ptr<DestinationSocketBase> m_destinationSocket;
boost::optional<boost::asio::ip::tcp::resolver> m_tcp_resolver;
boost::asio::ip::tcp::resolver::results_type m_tcp_resolver_results;
boost::asio::ip::tcp::resolver::results_type::const_iterator
m_tcp_resolver_iter;
};
class ProxySession : private ProxyBase,
public std::enable_shared_from_this<ProxySession> {
public:
ProxySession(std::uint32_t session_id,
boost::asio::ip::tcp::socket &&client_socket,
std::shared_ptr<DestinationSocketBase> &&dest_socket,
std::size_t buffer_size);
ProxySession(std::uint32_t session_id,
boost::asio::ip::tcp::socket &&client_socket,
std::shared_ptr<DestinationSocketBase> &&dest_socket,
ContiguousStreamBuffer &&input_buffer,
ContiguousStreamBuffer &&output_buffer);
void start();
private:
void recv_from_both();
void recv_from_destination(const boost::system::error_code &ec,
std::size_t length);
void recv_from_client(const boost::system::error_code &ec,
std::size_t length);
void handle_client_write(const boost::system::error_code &ec,
std::size_t length);
void handle_destination_write(const boost::system::error_code &ec,
std::size_t length);
std::shared_ptr<DestinationSocketBase> m_destinationSocket;
};
class ProxyServer : private boost::noncopyable {
public:
ProxyServer(boost::asio::io_context &ioc,
const boost::asio::ip::tcp::endpoint &local_endpoint);
ProxyServer(boost::asio::io_context &ioc, const std::string &listen_addr,
std::uint16_t listen_port);
void start();
void stop();
protected:
virtual void async_accept();
std::atomic<uint32_t> m_nextSessionId;
boost::asio::ip::tcp::acceptor m_acceptor;
};
// End Of Minimal Implementation
template <typename Executor>
class LoggingAsyncDestinationSocket : public AsyncDestinationSocket<Executor> {
public:
LoggingAsyncDestinationSocket(const Executor &exec)
: AsyncDestinationSocket<Executor>(exec), m_bytesRead{0},
m_bytesWritten{0} {}
~LoggingAsyncDestinationSocket() {}
std::size_t get_bytes_read() {
return m_bytesRead.exchange(0, std::memory_order_relaxed);
}
std::size_t get_bytes_written() {
return m_bytesWritten.exchange(0, std::memory_order_relaxed);
}
private:
void do_connect_tcp(
boost::asio::ip::tcp::resolver::results_type::const_iterator &it,
std::function<void(boost::system::error_code)> handler) override;
bool do_read(BufferBase &buffer,
std::function<void(boost::system::error_code, std::size_t)>
handler) override;
bool do_write(BufferBase &buffer, std::size_t length,
std::function<void(boost::system::error_code, std::size_t)>
handler) override;
std::atomic<std::size_t> m_bytesRead;
std::atomic<std::size_t> m_bytesWritten;
};
class LoggingProxyServer : public ProxyServer {
public:
LoggingProxyServer(boost::asio::io_context &ioc,
const std::string &listen_addr, std::uint16_t listen_port);
void start();
void stop();
private:
void async_timer();
void async_accept() override;
std::vector<std::weak_ptr<
LoggingAsyncDestinationSocket<boost::asio::any_io_executor>>>
m_weakDestinationSockets;
boost::asio::deadline_timer m_statusLogger;
std::atomic<std::size_t> m_bytesRead;
std::atomic<std::size_t> m_bytesWritten;
};
// The following is just an example if you want to use a custom proxy/tunnel
// protocol ;)
template <typename Executor>
class CustomProtocolAsyncDestinationSocket
: public AsyncDestinationSocket<Executor> {
public:
CustomProtocolAsyncDestinationSocket(const Executor &exec)
: AsyncDestinationSocket<Executor>(exec) {}
~CustomProtocolAsyncDestinationSocket() {}
private:
bool do_read(BufferBase &buffer,
std::function<void(boost::system::error_code, std::size_t)>
handler) override;
bool do_write(BufferBase &buffer, std::size_t length,
std::function<void(boost::system::error_code, std::size_t)>
handler) override;
};
class CustomProtocolProxyServer : public ProxyServer {
public:
CustomProtocolProxyServer(boost::asio::io_context &ioc,
const std::string &listen_addr,
std::uint16_t listen_port);
void start();
void stop();
private:
void async_accept() override;
};
}; // namespace SOCKS5
|