socks5_server_cp.hpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601
  1. /*
  2. * Copyright (c) 2017-2023 zhllxt
  3. *
  4. * author : zhllxt
  5. * email : 37792738@qq.com
  6. *
  7. * Distributed under the Boost Software License, Version 1.0. (See accompanying
  8. * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  9. *
  10. * socks5 protocol : https://www.ddhigh.com/2019/08/24/socks5-protocol.html
  11. * UDP Associate : https://blog.csdn.net/whatday/article/details/40183555
  12. */
  13. #ifndef __ASIO2_SOCKS5_SERVER_CP_HPP__
  14. #define __ASIO2_SOCKS5_SERVER_CP_HPP__
  15. #if defined(_MSC_VER) && (_MSC_VER >= 1200)
  16. #pragma once
  17. #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
  18. #include <cstdint>
  19. #include <memory>
  20. #include <chrono>
  21. #include <functional>
  22. #include <atomic>
  23. #include <string>
  24. #include <string_view>
  25. #include <tuple>
  26. #include <type_traits>
  27. #include <asio2/base/error.hpp>
  28. #include <asio2/base/define.hpp>
  29. #include <asio2/base/detail/util.hpp>
  30. #include <asio2/base/detail/ecs.hpp>
  31. #include <asio2/component/socks/socks5_core.hpp>
  32. #include <asio2/component/socks/socks5_util.hpp>
  33. namespace asio2::detail
  34. {
  35. ASIO2_CLASS_FORWARD_DECLARE_BASE;
  36. ASIO2_CLASS_FORWARD_DECLARE_TCP_BASE;
  37. ASIO2_CLASS_FORWARD_DECLARE_TCP_SERVER;
  38. ASIO2_CLASS_FORWARD_DECLARE_TCP_SESSION;
  39. ASIO2_CLASS_FORWARD_DECLARE_TCP_CLIENT;
  40. template<class SocketT, class Sock5OptT, class CommandCallback>
  41. class socks5_server_handshake_op : public asio::coroutine
  42. {
  43. ASIO2_CLASS_FRIEND_DECLARE_BASE;
  44. ASIO2_CLASS_FRIEND_DECLARE_TCP_BASE;
  45. ASIO2_CLASS_FRIEND_DECLARE_TCP_SERVER;
  46. ASIO2_CLASS_FRIEND_DECLARE_TCP_SESSION;
  47. ASIO2_CLASS_FRIEND_DECLARE_TCP_CLIENT;
  48. public:
  49. SocketT& socket_;
  50. Sock5OptT socks5_;
  51. CommandCallback cmd_cb_;
  52. using socks5_value_type = typename detail::element_type_adapter<Sock5OptT>::type;
  53. inline socks5_value_type& socks5() noexcept
  54. {
  55. return detail::to_element_ref(socks5_);
  56. }
  57. template<class S5Opt>
  58. inline std::string socks5_opt_username(S5Opt& s5opt) noexcept
  59. {
  60. if constexpr (socks5::detail::has_member_username<S5Opt>::value)
  61. return s5opt.username();
  62. else
  63. return "";
  64. }
  65. template<class S5Opt>
  66. inline std::string socks5_opt_password(S5Opt& s5opt) noexcept
  67. {
  68. if constexpr (socks5::detail::has_member_password<S5Opt>::value)
  69. return s5opt.password();
  70. else
  71. return "";
  72. }
  73. std::unique_ptr<asio::streambuf> stream{ std::make_unique<asio::streambuf>() };
  74. asio::mutable_buffer buffer{};
  75. std::size_t bytes{};
  76. char* p{};
  77. asio::ip::tcp::endpoint endpoint{};
  78. std::uint8_t addr_type{}, addr_size{};
  79. socks5::method method{};
  80. std::uint8_t nmethods{};
  81. std::uint8_t ulen{};
  82. std::uint8_t plen{};
  83. std::string username{}, password{};
  84. socks5::command cmd{};
  85. std::string host{}, port{};
  86. asio::ip::address bnd_addr{};
  87. std::uint16_t bnd_port{};
  88. asio::steady_timer* connect_finish_timer = nullptr;
  89. inline bool check_auth()
  90. {
  91. std::string username_in_opt = socks5_opt_username(socks5());
  92. std::string password_in_opt = socks5_opt_password(socks5());
  93. bool f = false;
  94. if (!username_in_opt.empty() && !password_in_opt.empty())
  95. {
  96. f = (username == username_in_opt && password == password_in_opt);
  97. }
  98. if (!f)
  99. {
  100. if (auto& authcb = socks5().get_auth_callback(); authcb)
  101. {
  102. f = authcb(username, password);
  103. }
  104. }
  105. return f;
  106. }
  107. template<class Sock, class S5Opt, class CmdCB>
  108. socks5_server_handshake_op(Sock& sock, S5Opt&& s5opt, CmdCB&& cmdcb)
  109. : socket_(sock)
  110. , socks5_(std::forward<S5Opt>(s5opt))
  111. , cmd_cb_(std::forward<CmdCB>(cmdcb))
  112. {
  113. }
  114. template <typename Self>
  115. void operator()(Self& self, error_code ec = {}, std::size_t bytes_transferred = 0)
  116. {
  117. detail::ignore_unused(ec, bytes_transferred);
  118. asio::streambuf& strbuf = *stream;
  119. // There is no need to use a timeout timer because there is already has
  120. // connect_timeout_cp
  121. ASIO_CORO_REENTER(*this)
  122. {
  123. // The client connects to the server, and sends a version
  124. // identifier / method selection message :
  125. // +----+----------+----------+
  126. // |VER | NMETHODS | METHODS |
  127. // +----+----------+----------+
  128. // | 1 | 1 | 1 to 255 |
  129. // +----+----------+----------+
  130. stream->consume(stream->size());
  131. ASIO_CORO_YIELD
  132. asio::async_read(socket_, strbuf, asio::transfer_exactly(1 + 1), std::move(self));
  133. if (ec)
  134. goto end;
  135. p = const_cast<char*>(static_cast<const char*>(stream->data().data()));
  136. if (std::uint8_t version = read<std::uint8_t>(p); version != std::uint8_t(0x05))
  137. {
  138. ec = socks5::make_error_code(socks5::error::unsupported_version);
  139. goto end;
  140. }
  141. if (nmethods = read<std::uint8_t>(p); nmethods == std::uint8_t(0))
  142. {
  143. ec = socks5::make_error_code(socks5::error::no_acceptable_methods);
  144. goto end;
  145. }
  146. stream->consume(stream->size());
  147. ASIO_CORO_YIELD
  148. asio::async_read(socket_, strbuf, asio::transfer_exactly(nmethods), std::move(self));
  149. if (ec)
  150. goto end;
  151. p = const_cast<char*>(static_cast<const char*>(stream->data().data()));
  152. method = socks5::method::noacceptable;
  153. for (std::uint8_t i = 0; method == socks5::method::noacceptable && i < nmethods; ++i)
  154. {
  155. socks5::method m1 = static_cast<socks5::method>(read<std::uint8_t>(p));
  156. for(socks5::method m2 : socks5().methods())
  157. {
  158. if (m1 == m2)
  159. {
  160. method = m1;
  161. break;
  162. }
  163. }
  164. }
  165. // +----+--------+
  166. // |VER | METHOD |
  167. // +----+--------+
  168. // | 1 | 1 |
  169. // +----+--------+
  170. stream->consume(stream->size());
  171. bytes = 2;
  172. buffer = stream->prepare(bytes);
  173. p = static_cast<char*>(buffer.data());
  174. write(p, std::uint8_t(0x05)); // VER
  175. write(p, std::uint8_t(detail::to_underlying(method))); // METHOD
  176. stream->commit(bytes);
  177. ASIO_CORO_YIELD
  178. asio::async_write(socket_, strbuf, asio::transfer_exactly(bytes), std::move(self));
  179. if (ec)
  180. goto end;
  181. if (method == socks5::method::noacceptable)
  182. {
  183. ec = socks5::make_error_code(socks5::error::no_acceptable_methods);
  184. goto end;
  185. }
  186. if (method == socks5::method::password)
  187. {
  188. // +----+------+----------+------+----------+
  189. // |VER | ULEN | UNAME | PLEN | PASSWD |
  190. // +----+------+----------+------+----------+
  191. // | 1 | 1 | 1 to 255 | 1 | 1 to 255 |
  192. // +----+------+----------+------+----------+
  193. stream->consume(stream->size());
  194. ASIO_CORO_YIELD
  195. asio::async_read(socket_, strbuf, asio::transfer_exactly(1 + 1), std::move(self));
  196. if (ec)
  197. goto end;
  198. p = const_cast<char*>(static_cast<const char*>(stream->data().data()));
  199. // The VER field contains the current version of the subnegotiation, which is X'01'.
  200. if (std::uint8_t version = read<std::uint8_t>(p); version != std::uint8_t(0x01))
  201. {
  202. ec = socks5::make_error_code(socks5::error::unsupported_authentication_version);
  203. goto end;
  204. }
  205. if (ulen = read<std::uint8_t>(p); ulen == std::uint8_t(0))
  206. {
  207. ec = socks5::make_error_code(socks5::error::authentication_failed);
  208. goto end;
  209. }
  210. // read username
  211. stream->consume(stream->size());
  212. ASIO_CORO_YIELD
  213. asio::async_read(socket_, strbuf, asio::transfer_exactly(ulen), std::move(self));
  214. if (ec)
  215. goto end;
  216. p = const_cast<char*>(static_cast<const char*>(stream->data().data()));
  217. username.assign(p, ulen);
  218. // read password len
  219. stream->consume(stream->size());
  220. ASIO_CORO_YIELD
  221. asio::async_read(socket_, strbuf, asio::transfer_exactly(1), std::move(self));
  222. if (ec)
  223. goto end;
  224. p = const_cast<char*>(static_cast<const char*>(stream->data().data()));
  225. if (plen = read<std::uint8_t>(p); plen == std::uint8_t(0))
  226. {
  227. ec = socks5::make_error_code(socks5::error::authentication_failed);
  228. goto end;
  229. }
  230. // read password
  231. stream->consume(stream->size());
  232. ASIO_CORO_YIELD
  233. asio::async_read(socket_, strbuf, asio::transfer_exactly(plen), std::move(self));
  234. if (ec)
  235. goto end;
  236. p = const_cast<char*>(static_cast<const char*>(stream->data().data()));
  237. password.assign(p, plen);
  238. // compare username and password
  239. if (!check_auth())
  240. {
  241. stream->consume(stream->size());
  242. bytes = 2;
  243. buffer = stream->prepare(bytes);
  244. p = static_cast<char*>(buffer.data());
  245. write(p, std::uint8_t(0x01)); // VER
  246. write(p, std::uint8_t(detail::to_underlying(socks5::error::authentication_failed))); // STATUS
  247. stream->commit(bytes);
  248. ASIO_CORO_YIELD
  249. asio::async_write(socket_, strbuf, asio::transfer_exactly(bytes), std::move(self));
  250. ec = socks5::make_error_code(socks5::error::authentication_failed);
  251. goto end;
  252. }
  253. else
  254. {
  255. stream->consume(stream->size());
  256. bytes = 2;
  257. buffer = stream->prepare(bytes);
  258. p = static_cast<char*>(buffer.data());
  259. write(p, std::uint8_t(0x01)); // VER
  260. write(p, std::uint8_t(0x00)); // STATUS
  261. stream->commit(bytes);
  262. ASIO_CORO_YIELD
  263. asio::async_write(socket_, strbuf, asio::transfer_exactly(bytes), std::move(self));
  264. if (ec)
  265. goto end;
  266. }
  267. }
  268. // +----+-----+-------+------+----------+----------+
  269. // |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
  270. // +----+-----+-------+------+----------+----------+
  271. // | 1 | 1 | X'00' | 1 | Variable | 2 |
  272. // +----+-----+-------+------+----------+----------+
  273. stream->consume(stream->size());
  274. // 1. read the first 5 bytes : VER REP RSV ATYP [LEN]
  275. ASIO_CORO_YIELD
  276. asio::async_read(socket_, strbuf, asio::transfer_exactly(5), std::move(self));
  277. if (ec)
  278. goto end;
  279. p = const_cast<char*>(static_cast<const char*>(stream->data().data()));
  280. // VER
  281. if (std::uint8_t ver = read<std::uint8_t>(p); ver != std::uint8_t(0x05))
  282. {
  283. ec = socks5::make_error_code(socks5::error::unsupported_version);
  284. goto end;
  285. }
  286. // CMD
  287. cmd = static_cast<socks5::command>(read<std::uint8_t>(p));
  288. // skip RSV.
  289. read<std::uint8_t>(p);
  290. addr_type = read<std::uint8_t>(p); // ATYP
  291. addr_size = read<std::uint8_t>(p); // [LEN]
  292. // ATYP
  293. switch (addr_type)
  294. {
  295. case std::uint8_t(0x01): bytes = 4 + 2 - 1; break; // IP V4 address: X'01'
  296. case std::uint8_t(0x03): bytes = addr_size + 2 - 0; break; // DOMAINNAME: X'03'
  297. case std::uint8_t(0x04): bytes = 16 + 2 - 1; break; // IP V6 address: X'04'
  298. default:
  299. {
  300. ec = socks5::make_error_code(socks5::error::address_type_not_supported);
  301. goto end;
  302. }
  303. }
  304. stream->consume(stream->size());
  305. ASIO_CORO_YIELD
  306. asio::async_read(socket_, strbuf, asio::transfer_exactly(bytes), std::move(self));
  307. if (ec)
  308. goto end;
  309. p = const_cast<char*>(static_cast<const char*>(stream->data().data()));
  310. switch (addr_type)
  311. {
  312. case std::uint8_t(0x01): // IP V4 address: X'01'
  313. {
  314. asio::ip::address_v4::bytes_type addr{};
  315. addr[0] = addr_size;
  316. addr[1] = read<std::uint8_t>(p);
  317. addr[2] = read<std::uint8_t>(p);
  318. addr[3] = read<std::uint8_t>(p);
  319. try
  320. {
  321. endpoint.address(asio::ip::address_v4(addr));
  322. host = endpoint.address().to_string();
  323. }
  324. catch (const system_error&)
  325. {
  326. }
  327. std::uint16_t uport = read<std::uint16_t>(p);
  328. endpoint.port(uport);
  329. port = std::to_string(uport);
  330. }
  331. break;
  332. case std::uint8_t(0x03): // DOMAINNAME: X'03'
  333. {
  334. std::string addr;
  335. addr.resize(addr_size);
  336. std::copy(p, p + addr_size, addr.data());
  337. p += addr_size;
  338. host = std::move(addr);
  339. std::uint16_t uport = read<std::uint16_t>(p);
  340. endpoint.port(uport);
  341. port = std::to_string(uport);
  342. }
  343. break;
  344. case std::uint8_t(0x04): // IP V6 address: X'04'
  345. {
  346. asio::ip::address_v6::bytes_type addr{};
  347. addr[0] = addr_size;
  348. for (int i = 1; i < 16; i++)
  349. {
  350. addr[i] = read<std::uint8_t>(p);
  351. }
  352. try
  353. {
  354. endpoint.address(asio::ip::address_v6(addr));
  355. host = endpoint.address().to_string();
  356. }
  357. catch (const system_error&)
  358. {
  359. }
  360. std::uint16_t uport = read<std::uint16_t>(p);
  361. endpoint.port(uport);
  362. port = std::to_string(uport);
  363. }
  364. break;
  365. }
  366. bnd_addr = socket_.local_endpoint(ec).address();
  367. bnd_port = socket_.local_endpoint(ec).port();
  368. if (host.empty() || port.empty() || std::strtoull(port.data(), nullptr, 10) == 0)
  369. {
  370. ec = socks5::make_error_code(socks5::error::host_unreachable);
  371. goto end;
  372. }
  373. stream->consume(stream->size());
  374. // the address field contains a fully-qualified domain name. The first
  375. // octet of the address field contains the number of octets of name that
  376. // follow, there is no terminating NUL octet.
  377. buffer = stream->prepare(1 + 1 + 1 + 1 + (std::max)(16, int(host.size() + 1)) + 2);
  378. p = static_cast<char*>(buffer.data());
  379. write(p, std::uint8_t(0x05)); // VER 5.
  380. write(p, std::uint8_t(0x00)); // REP
  381. write(p, std::uint8_t(0x00)); // RSV.
  382. if (bnd_addr.is_v4())
  383. {
  384. write(p, std::uint8_t(0x01)); // ATYP
  385. // real length
  386. bytes = 1 + 1 + 1 + 1 + 4 + 2;
  387. write(p, bnd_addr.to_v4().to_uint());
  388. }
  389. else
  390. {
  391. write(p, std::uint8_t(0x04)); // ATYP
  392. // real length
  393. bytes = 1 + 1 + 1 + 1 + 16 + 2;
  394. auto addr_bytes = bnd_addr.to_v6().to_bytes();
  395. std::copy(addr_bytes.begin(), addr_bytes.end(), p);
  396. p += 16;
  397. }
  398. // port
  399. write(p, endpoint.port());
  400. stream->commit(bytes);
  401. p = const_cast<char*>(static_cast<const char*>(stream->data().data()));
  402. // o REP Reply field:
  403. // o X'00' succeeded
  404. // o X'01' general SOCKS server failure
  405. // o X'02' connection not allowed by ruleset
  406. // o X'03' Network unreachable
  407. // o X'04' Host unreachable
  408. // o X'05' Connection refused
  409. // o X'06' TTL expired
  410. // o X'07' Command not supported
  411. // o X'08' Address type not supported
  412. // o X'09' to X'FF' unassigned
  413. if (cmd == socks5::command::connect || cmd == socks5::command::udp_associate)
  414. {
  415. connect_finish_timer = cmd_cb_(*this);
  416. if (!connect_finish_timer)
  417. {
  418. if (!ec)
  419. ec = socks5::make_error_code(socks5::error::host_unreachable);
  420. }
  421. else
  422. {
  423. ASIO_CORO_YIELD
  424. connect_finish_timer->async_wait(std::move(self));
  425. ec = get_last_error();
  426. }
  427. if (!ec)
  428. p[1] = char(0x00);
  429. else if (ec == asio::error::network_unreachable)
  430. p[1] = char(0x03);
  431. else if (ec == asio::error::host_unreachable || ec == asio::error::host_not_found)
  432. p[1] = char(0x04);
  433. else if (ec == asio::error::connection_refused)
  434. p[1] = char(0x05);
  435. else if (ec)
  436. p[1] = char(0x01);
  437. ASIO_CORO_YIELD
  438. asio::async_write(socket_, strbuf, asio::transfer_exactly(bytes), std::move(self));
  439. if (ec)
  440. goto end;
  441. }
  442. else/* if (cmd == socks5::command::bind)*/
  443. {
  444. p[1] = char(0x07);
  445. ASIO_CORO_YIELD
  446. asio::async_write(socket_, strbuf, asio::transfer_exactly(bytes), std::move(self));
  447. ec = socks5::make_error_code(socks5::error::command_not_supported);
  448. goto end;
  449. }
  450. ec = {};
  451. end:
  452. self.complete(ec);
  453. }
  454. }
  455. };
  456. // C++17 class template argument deduction guides
  457. template<class SKT, class S5Opt, class CommandCallback>
  458. socks5_server_handshake_op(SKT&, S5Opt, CommandCallback) ->
  459. socks5_server_handshake_op<SKT, S5Opt, CommandCallback>;
  460. }
  461. namespace asio2
  462. {
  463. /**
  464. * @brief Perform the socks5 handshake asynchronously in the client role.
  465. * @param socket - The asio::ip::tcp::socket object reference.
  466. * @param socks5_opt - The socks5 option, must contains the socks5 proxy server ip and port.
  467. * @param cmd_cb - command callback. Signature: asio::steady_timer*(auto& s5_server_handshake_op){}
  468. * @param token - The completion handler to invoke when the operation completes.
  469. * The implementation takes ownership of the handler by performing a decay-copy.
  470. * The equivalent function signature of the handler must be:
  471. * @code
  472. * void handler(
  473. * error_code const& ec // Result of operation
  474. * );
  475. */
  476. template <typename SocketT, typename Sock5OptT, typename CommandCallback, typename CompletionToken>
  477. auto socks5_async_handshake(
  478. SocketT& socket, Sock5OptT&& socks5_opt, CommandCallback&& cmd_cb, CompletionToken&& token)
  479. -> decltype(asio::async_compose<CompletionToken, void(asio::error_code)>(
  480. std::declval<detail::socks5_server_handshake_op<SocketT, Sock5OptT, CommandCallback>>(), token, socket))
  481. {
  482. return asio::async_compose<CompletionToken, void(asio::error_code)>(
  483. detail::socks5_server_handshake_op<SocketT, Sock5OptT, CommandCallback>{
  484. socket, std::forward<Sock5OptT>(socks5_opt), std::forward<CommandCallback>(cmd_cb)},
  485. token, socket);
  486. }
  487. }
  488. #endif // !__ASIO2_SOCKS5_SERVER_CP_HPP__