socks5_client_cp.hpp 34 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102
  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. * http proxy : https://blog.csdn.net/dolphin98629/article/details/54599850
  13. */
  14. #ifndef __ASIO2_SOCKS5_CLIENT_CP_HPP__
  15. #define __ASIO2_SOCKS5_CLIENT_CP_HPP__
  16. #if defined(_MSC_VER) && (_MSC_VER >= 1200)
  17. #pragma once
  18. #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
  19. #include <cstdint>
  20. #include <memory>
  21. #include <chrono>
  22. #include <functional>
  23. #include <atomic>
  24. #include <string>
  25. #include <string_view>
  26. #include <tuple>
  27. #include <type_traits>
  28. #include <asio2/base/error.hpp>
  29. #include <asio2/base/define.hpp>
  30. #include <asio2/base/detail/util.hpp>
  31. #include <asio2/base/detail/ecs.hpp>
  32. #include <asio2/component/socks/socks5_core.hpp>
  33. #include <asio2/component/socks/socks5_util.hpp>
  34. namespace asio2::detail
  35. {
  36. ASIO2_CLASS_FORWARD_DECLARE_BASE;
  37. ASIO2_CLASS_FORWARD_DECLARE_TCP_BASE;
  38. ASIO2_CLASS_FORWARD_DECLARE_TCP_SERVER;
  39. ASIO2_CLASS_FORWARD_DECLARE_TCP_SESSION;
  40. ASIO2_CLASS_FORWARD_DECLARE_TCP_CLIENT;
  41. template<class SocketT, class Sock5OptT>
  42. class socks5_client_handshake_op : public asio::coroutine
  43. {
  44. ASIO2_CLASS_FRIEND_DECLARE_BASE;
  45. ASIO2_CLASS_FRIEND_DECLARE_TCP_BASE;
  46. ASIO2_CLASS_FRIEND_DECLARE_TCP_SERVER;
  47. ASIO2_CLASS_FRIEND_DECLARE_TCP_SESSION;
  48. ASIO2_CLASS_FRIEND_DECLARE_TCP_CLIENT;
  49. public:
  50. std::string host_{}, port_{};
  51. SocketT& socket_;
  52. Sock5OptT socks5_;
  53. using socks5_value_type = typename detail::element_type_adapter<Sock5OptT>::type;
  54. inline socks5_value_type& socks5() noexcept
  55. {
  56. return detail::to_element_ref(socks5_);
  57. }
  58. template<class S5Opt>
  59. inline std::string socks5_opt_username(S5Opt& s5opt) noexcept
  60. {
  61. if constexpr (socks5::detail::has_member_username<S5Opt>::value)
  62. return s5opt.username();
  63. else
  64. return "";
  65. }
  66. template<class S5Opt>
  67. inline std::string socks5_opt_password(S5Opt& s5opt) noexcept
  68. {
  69. if constexpr (socks5::detail::has_member_password<S5Opt>::value)
  70. return s5opt.password();
  71. else
  72. return "";
  73. }
  74. std::unique_ptr<asio::streambuf> stream{ std::make_unique<asio::streambuf>() };
  75. asio::mutable_buffer buffer{};
  76. std::size_t bytes{};
  77. char* p{};
  78. asio::ip::tcp::endpoint endpoint{};
  79. std::string username{}, password{};
  80. std::uint8_t addr_type{}, addr_size{};
  81. socks5::method method{};
  82. std::string host{}, port{};
  83. template<class SKT, class S5Opt>
  84. socks5_client_handshake_op(std::string host, std::string port, SKT& skt, S5Opt s5)
  85. : host_ (std::move(host))
  86. , port_ (std::move(port))
  87. , socket_ (skt)
  88. , socks5_ (std::move(s5))
  89. {
  90. }
  91. template <typename Self>
  92. void operator()(Self& self, error_code ec = {}, std::size_t bytes_transferred = 0)
  93. {
  94. detail::ignore_unused(ec, bytes_transferred);
  95. asio::streambuf& strbuf = *stream;
  96. // There is no need to use a timeout timer because there is already has
  97. // connect_timeout_cp
  98. ASIO_CORO_REENTER(*this)
  99. {
  100. // The client connects to the server, and sends a version
  101. // identifier / method selection message :
  102. // +----+----------+----------+
  103. // |VER | NMETHODS | METHODS |
  104. // +----+----------+----------+
  105. // | 1 | 1 | 1 to 255 |
  106. // +----+----------+----------+
  107. stream->consume(stream->size());
  108. bytes = 1 + 1 + socks5().methods_count();
  109. buffer = stream->prepare(bytes);
  110. p = static_cast<char*>(buffer.data());
  111. write(p, std::uint8_t(0x05)); // SOCKS VERSION 5.
  112. write(p, std::uint8_t(socks5().methods_count())); // NMETHODS
  113. for (auto m : socks5().methods())
  114. {
  115. write(p, std::uint8_t(detail::to_underlying(m))); // METHODS
  116. }
  117. stream->commit(bytes);
  118. ASIO_CORO_YIELD
  119. asio::async_write(socket_, strbuf, asio::transfer_exactly(bytes), std::move(self));
  120. if (ec)
  121. goto end;
  122. // The server selects from one of the methods given in METHODS, and
  123. // sends a METHOD selection message :
  124. // +----+--------+
  125. // |VER | METHOD |
  126. // +----+--------+
  127. // | 1 | 1 |
  128. // +----+--------+
  129. stream->consume(stream->size());
  130. ASIO_CORO_YIELD
  131. asio::async_read(socket_, strbuf, asio::transfer_exactly(1 + 1), std::move(self));
  132. if (ec)
  133. goto end;
  134. p = const_cast<char*>(static_cast<const char*>(stream->data().data()));
  135. if (std::uint8_t version = read<std::uint8_t>(p); version != std::uint8_t(0x05))
  136. {
  137. ec = socks5::make_error_code(socks5::error::unsupported_version);
  138. goto end;
  139. }
  140. // If the selected METHOD is X'FF', none of the methods listed by the
  141. // client are acceptable, and the client MUST close the connection.
  142. //
  143. // The values currently defined for METHOD are:
  144. //
  145. // o X'00' NO AUTHENTICATION REQUIRED
  146. // o X'01' GSSAPI
  147. // o X'02' USERNAME/PASSWORD
  148. // o X'03' to X'7F' IANA ASSIGNED
  149. // o X'80' to X'FE' RESERVED FOR PRIVATE METHODS
  150. // o X'FF' NO ACCEPTABLE METHODS
  151. // Once the method-dependent subnegotiation has completed, the client
  152. // sends the request details. If the negotiated method includes
  153. // encapsulation for purposes of integrity checking and/or
  154. // confidentiality, these requests MUST be encapsulated in the method-
  155. // dependent encapsulation.
  156. //
  157. // The SOCKS request is formed as follows:
  158. //
  159. // +----+-----+-------+------+----------+----------+
  160. // |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
  161. // +----+-----+-------+------+----------+----------+
  162. // | 1 | 1 | X'00' | 1 | Variable | 2 |
  163. // +----+-----+-------+------+----------+----------+
  164. //
  165. // Where:
  166. //
  167. // o VER protocol version: X'05'
  168. // o CMD
  169. // o CONNECT X'01'
  170. // o BIND X'02'
  171. // o UDP ASSOCIATE X'03'
  172. // o RSV RESERVED
  173. // o ATYP address type of following address
  174. // o IP V4 address: X'01'
  175. // o DOMAINNAME: X'03'
  176. // o IP V6 address: X'04'
  177. // o DST.ADDR desired destination address
  178. // o DST.PORT desired destination port in network octet
  179. // order
  180. //
  181. // The SOCKS server will typically evaluate the request based on source
  182. // and destination addresses, and return one or more reply messages, as
  183. // appropriate for the request type.
  184. method = socks5::method(read<std::uint8_t>(p));
  185. // cannot use "switch", only "if else" can be used otherwise ASIO_CORO_YIELD will be exception.
  186. if /**/ (method == socks5::method::anonymous)
  187. {
  188. }
  189. else if (method == socks5::method::gssapi)
  190. {
  191. ec = socks5::make_error_code(socks5::error::unsupported_method);
  192. goto end;
  193. }
  194. else if (method == socks5::method::password)
  195. {
  196. // Once the SOCKS V5 server has started, and the client has selected the
  197. // Username/Password Authentication protocol, the Username/Password
  198. // subnegotiation begins. This begins with the client producing a
  199. // Username/Password request:
  200. //
  201. // +----+------+----------+------+----------+
  202. // |VER | ULEN | UNAME | PLEN | PASSWD |
  203. // +----+------+----------+------+----------+
  204. // | 1 | 1 | 1 to 255 | 1 | 1 to 255 |
  205. // +----+------+----------+------+----------+
  206. username = socks5_opt_username(socks5());
  207. password = socks5_opt_password(socks5());
  208. if (username.empty() || password.empty())
  209. {
  210. ASIO2_ASSERT(false);
  211. ec = socks5::make_error_code(socks5::error::username_required);
  212. goto end;
  213. }
  214. stream->consume(stream->size());
  215. bytes = 1 + 1 + username.size() + 1 + password.size();
  216. buffer = stream->prepare(bytes);
  217. p = static_cast<char*>(buffer.data());
  218. // The VER field contains the current version of the subnegotiation,
  219. // which is X'01'. The ULEN field contains the length of the UNAME field
  220. // that follows. The UNAME field contains the username as known to the
  221. // source operating system. The PLEN field contains the length of the
  222. // PASSWD field that follows. The PASSWD field contains the password
  223. // association with the given UNAME.
  224. // VER
  225. write(p, std::uint8_t(0x01));
  226. // ULEN
  227. write(p, std::uint8_t(username.size()));
  228. // UNAME
  229. std::copy(username.begin(), username.end(), p);
  230. p += username.size();
  231. // PLEN
  232. write(p, std::uint8_t(password.size()));
  233. // PASSWD
  234. std::copy(password.begin(), password.end(), p);
  235. p += password.size();
  236. stream->commit(bytes);
  237. // send username and password to server
  238. ASIO_CORO_YIELD
  239. asio::async_write(socket_, strbuf, asio::transfer_exactly(bytes), std::move(self));
  240. if (ec)
  241. goto end;
  242. // The server verifies the supplied UNAME and PASSWD, and sends the
  243. // following response:
  244. //
  245. // +----+--------+
  246. // |VER | STATUS |
  247. // +----+--------+
  248. // | 1 | 1 |
  249. // +----+--------+
  250. //
  251. // A STATUS field of X'00' indicates success. If the server returns a
  252. // `failure' (STATUS value other than X'00') status, it MUST close the
  253. // connection.
  254. // read reply
  255. stream->consume(stream->size());
  256. ASIO_CORO_YIELD
  257. asio::async_read(socket_, strbuf, asio::transfer_exactly(1 + 1), std::move(self));
  258. if (ec)
  259. goto end;
  260. // parse reply
  261. p = const_cast<char*>(static_cast<const char*>(stream->data().data()));
  262. if (std::uint8_t ver = read<std::uint8_t>(p); ver != std::uint8_t(0x01))
  263. {
  264. ec = socks5::make_error_code(socks5::error::unsupported_authentication_version);
  265. goto end;
  266. }
  267. if (std::uint8_t status = read<std::uint8_t>(p); status != std::uint8_t(0x00))
  268. {
  269. ec = socks5::make_error_code(socks5::error::authentication_failed);
  270. goto end;
  271. }
  272. }
  273. //else if (method == socks5::method::iana)
  274. //{
  275. // ec = socks5::make_error_code(socks5::error::unsupported_method);
  276. // goto end;
  277. //}
  278. //else if (method == socks5::method::reserved)
  279. //{
  280. // ec = socks5::make_error_code(socks5::error::unsupported_method);
  281. // goto end;
  282. //}
  283. else if (method == socks5::method::noacceptable)
  284. {
  285. ec = socks5::make_error_code(socks5::error::no_acceptable_methods);
  286. goto end;
  287. }
  288. else
  289. {
  290. ec = socks5::make_error_code(socks5::error::no_acceptable_methods);
  291. goto end;
  292. }
  293. stream->consume(stream->size());
  294. // the address field contains a fully-qualified domain name. The first
  295. // octet of the address field contains the number of octets of name that
  296. // follow, there is no terminating NUL octet.
  297. buffer = stream->prepare(1 + 1 + 1 + 1 + (std::max)(16, int(host_.size() + 1)) + 2);
  298. p = static_cast<char*>(buffer.data());
  299. write(p, std::uint8_t(0x05)); // VER 5.
  300. write(p, std::uint8_t(detail::to_underlying(socks5().command()))); // CMD CONNECT .
  301. write(p, std::uint8_t(0x00)); // RSV.
  302. // ATYP
  303. endpoint.address(asio::ip::make_address(host_, ec));
  304. if (ec)
  305. {
  306. ASIO2_ASSERT(host_.size() <= std::size_t(0xff));
  307. // real length
  308. bytes = 1 + 1 + 1 + 1 + 1 + host_.size() + 2;
  309. // type is domain
  310. write(p, std::uint8_t(0x03));
  311. // domain size
  312. write(p, std::uint8_t(host_.size()));
  313. // domain name
  314. std::copy(host_.begin(), host_.end(), p);
  315. p += host_.size();
  316. }
  317. else
  318. {
  319. if /**/ (endpoint.address().is_v4())
  320. {
  321. // real length
  322. bytes = 1 + 1 + 1 + 1 + 4 + 2;
  323. // type is ipv4
  324. write(p, std::uint8_t(0x01));
  325. write(p, std::uint32_t(endpoint.address().to_v4().to_uint()));
  326. }
  327. else if (endpoint.address().is_v6())
  328. {
  329. // real length
  330. bytes = 1 + 1 + 1 + 1 + 16 + 2;
  331. // type is ipv6
  332. write(p, std::uint8_t(0x04));
  333. auto addr_bytes = endpoint.address().to_v6().to_bytes();
  334. std::copy(addr_bytes.begin(), addr_bytes.end(), p);
  335. p += 16;
  336. }
  337. else
  338. {
  339. ASIO2_ASSERT(false);
  340. }
  341. }
  342. // port
  343. write(p, std::uint16_t(std::strtoul(port_.data(), nullptr, 10)));
  344. stream->commit(bytes);
  345. ASIO_CORO_YIELD
  346. asio::async_write(socket_, strbuf, asio::transfer_exactly(bytes), std::move(self));
  347. if (ec)
  348. goto end;
  349. // The SOCKS request information is sent by the client as soon as it has
  350. // established a connection to the SOCKS server, and completed the
  351. // authentication negotiations. The server evaluates the request, and
  352. // returns a reply formed as follows:
  353. //
  354. // +----+-----+-------+------+----------+----------+
  355. // |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
  356. // +----+-----+-------+------+----------+----------+
  357. // | 1 | 1 | X'00' | 1 | Variable | 2 |
  358. // +----+-----+-------+------+----------+----------+
  359. //
  360. // Where:
  361. //
  362. // o VER protocol version: X'05'
  363. // o REP Reply field:
  364. // o X'00' succeeded
  365. // o X'01' general SOCKS server failure
  366. // o X'02' connection not allowed by ruleset
  367. // o X'03' Network unreachable
  368. // o X'04' Host unreachable
  369. // o X'05' Connection refused
  370. // o X'06' TTL expired
  371. // o X'07' Command not supported
  372. // o X'08' Address type not supported
  373. // o X'09' to X'FF' unassigned
  374. // o RSV RESERVED
  375. // o ATYP address type of following address
  376. stream->consume(stream->size());
  377. // 1. read the first 5 bytes : VER REP RSV ATYP [LEN]
  378. ASIO_CORO_YIELD
  379. asio::async_read(socket_, strbuf, asio::transfer_exactly(5), std::move(self));
  380. if (ec)
  381. goto end;
  382. p = const_cast<char*>(static_cast<const char*>(stream->data().data()));
  383. // VER
  384. if (std::uint8_t ver = read<std::uint8_t>(p); ver != std::uint8_t(0x05))
  385. {
  386. ec = socks5::make_error_code(socks5::error::unsupported_version);
  387. goto end;
  388. }
  389. // REP
  390. switch (read<std::uint8_t>(p))
  391. {
  392. case std::uint8_t(0x00): ec = {} ; break;
  393. case std::uint8_t(0x01): ec = socks5::make_error_code(socks5::error::general_socks_server_failure ); break;
  394. case std::uint8_t(0x02): ec = socks5::make_error_code(socks5::error::connection_not_allowed_by_ruleset); break;
  395. case std::uint8_t(0x03): ec = socks5::make_error_code(socks5::error::network_unreachable ); break;
  396. case std::uint8_t(0x04): ec = socks5::make_error_code(socks5::error::host_unreachable ); break;
  397. case std::uint8_t(0x05): ec = socks5::make_error_code(socks5::error::connection_refused ); break;
  398. case std::uint8_t(0x06): ec = socks5::make_error_code(socks5::error::ttl_expired ); break;
  399. case std::uint8_t(0x07): ec = socks5::make_error_code(socks5::error::command_not_supported ); break;
  400. case std::uint8_t(0x08): ec = socks5::make_error_code(socks5::error::address_type_not_supported ); break;
  401. case std::uint8_t(0x09): ec = socks5::make_error_code(socks5::error::unassigned ); break;
  402. default: ec = socks5::make_error_code(socks5::error::unassigned ); break;
  403. }
  404. if (ec)
  405. goto end;
  406. // skip RSV.
  407. read<std::uint8_t>(p);
  408. addr_type = read<std::uint8_t>(p); // ATYP
  409. addr_size = read<std::uint8_t>(p); // [LEN]
  410. // ATYP
  411. switch (addr_type)
  412. {
  413. case std::uint8_t(0x01): bytes = 4 + 2 - 1; break; // IP V4 address: X'01'
  414. case std::uint8_t(0x03): bytes = addr_size + 2 - 0; break; // DOMAINNAME: X'03'
  415. case std::uint8_t(0x04): bytes = 16 + 2 - 1; break; // IP V6 address: X'04'
  416. default:
  417. {
  418. ec = socks5::make_error_code(socks5::error::address_type_not_supported);
  419. goto end;
  420. }
  421. }
  422. stream->consume(stream->size());
  423. ASIO_CORO_YIELD
  424. asio::async_read(socket_, strbuf, asio::transfer_exactly(bytes), std::move(self));
  425. if (ec)
  426. goto end;
  427. p = const_cast<char*>(static_cast<const char*>(stream->data().data()));
  428. switch (addr_type)
  429. {
  430. case std::uint8_t(0x01): // IP V4 address: X'01'
  431. {
  432. asio::ip::address_v4::bytes_type addr{};
  433. addr[0] = addr_size;
  434. addr[1] = read<std::uint8_t>(p);
  435. addr[2] = read<std::uint8_t>(p);
  436. addr[3] = read<std::uint8_t>(p);
  437. try
  438. {
  439. endpoint.address(asio::ip::address_v4(addr));
  440. host = endpoint.address().to_string();
  441. }
  442. catch (const system_error&)
  443. {
  444. }
  445. std::uint16_t uport = read<std::uint16_t>(p);
  446. endpoint.port(uport);
  447. port = std::to_string(uport);
  448. }
  449. break;
  450. case std::uint8_t(0x03): // DOMAINNAME: X'03'
  451. {
  452. std::string addr;
  453. addr.resize(addr_size);
  454. std::copy(p, p + addr_size, addr.data());
  455. p += addr_size;
  456. host = std::move(addr);
  457. std::uint16_t uport = read<std::uint16_t>(p);
  458. endpoint.port(uport);
  459. port = std::to_string(uport);
  460. }
  461. break;
  462. case std::uint8_t(0x04): // IP V6 address: X'04'
  463. {
  464. asio::ip::address_v6::bytes_type addr{};
  465. addr[0] = addr_size;
  466. for (int i = 1; i < 16; i++)
  467. {
  468. addr[i] = read<std::uint8_t>(p);
  469. }
  470. try
  471. {
  472. endpoint.address(asio::ip::address_v6(addr));
  473. host = endpoint.address().to_string();
  474. }
  475. catch (const system_error&)
  476. {
  477. }
  478. std::uint16_t uport = read<std::uint16_t>(p);
  479. endpoint.port(uport);
  480. port = std::to_string(uport);
  481. }
  482. break;
  483. }
  484. ec = {};
  485. end:
  486. self.complete(ec, std::move(host), std::move(port));
  487. }
  488. }
  489. };
  490. // C++17 class template argument deduction guides
  491. template<class SKT, class S5Opt>
  492. socks5_client_handshake_op(std::string, std::string, SKT&, S5Opt) -> socks5_client_handshake_op<SKT, S5Opt>;
  493. }
  494. namespace asio2
  495. {
  496. /**
  497. * @brief Perform the socks5 handshake asynchronously in the client role.
  498. * @param host - The target server ip. note: not the socks5 proxy server ip.
  499. * @param port - The target server port. note: not the socks5 proxy server port.
  500. * @param socket - The asio::ip::tcp::socket object reference.
  501. * @param socks5_opt - The socks5 option, must contains the socks5 proxy server ip and port.
  502. * @param token - The completion handler to invoke when the operation completes.
  503. * The implementation takes ownership of the handler by performing a decay-copy.
  504. * The equivalent function signature of the handler must be:
  505. * @code
  506. * void handler(error_code const& ec, std::string host, std::string port);
  507. */
  508. template <typename SocketT, typename Sock5OptT, typename CompletionToken>
  509. auto socks5_async_handshake(
  510. std::string host, std::string port, SocketT& socket, Sock5OptT socks5_opt, CompletionToken&& token)
  511. -> decltype(asio::async_compose<CompletionToken, void(asio::error_code, std::string, std::string)>(
  512. std::declval<detail::socks5_client_handshake_op<SocketT, Sock5OptT>>(), token, socket))
  513. {
  514. return asio::async_compose<CompletionToken, void(asio::error_code, std::string, std::string)>(
  515. detail::socks5_client_handshake_op<SocketT, Sock5OptT>{
  516. std::move(host), std::move(port), socket, std::move(socks5_opt)},
  517. token, socket);
  518. }
  519. }
  520. namespace asio2::detail
  521. {
  522. template<class derived_t, class args_t>
  523. class socks5_client_cp_impl
  524. {
  525. ASIO2_CLASS_FRIEND_DECLARE_BASE;
  526. ASIO2_CLASS_FRIEND_DECLARE_TCP_BASE;
  527. ASIO2_CLASS_FRIEND_DECLARE_TCP_SERVER;
  528. ASIO2_CLASS_FRIEND_DECLARE_TCP_SESSION;
  529. ASIO2_CLASS_FRIEND_DECLARE_TCP_CLIENT;
  530. public:
  531. /**
  532. * @brief constructor
  533. */
  534. socks5_client_cp_impl() {}
  535. /**
  536. * @brief destructor
  537. */
  538. ~socks5_client_cp_impl() = default;
  539. protected:
  540. template<typename C>
  541. inline void _check_socks5_command(std::shared_ptr<derived_t>&, std::shared_ptr<ecs_t<C>>& ecs)
  542. {
  543. if (!ecs)
  544. return;
  545. auto s5opt = ecs->get_component().socks5_option(std::in_place);
  546. if (s5opt && static_cast<int>(s5opt->command()) == 0)
  547. {
  548. if (std::is_base_of_v<detail::tcp_tag, derived_t>)
  549. {
  550. s5opt->command(socks5::command::connect);
  551. }
  552. else if (std::is_base_of_v<detail::udp_tag, derived_t>)
  553. {
  554. s5opt->command(socks5::command::udp_associate);
  555. }
  556. }
  557. }
  558. };
  559. template<class derived_t, class args_t, typename TagType = typename args_t::tl_tag_type>
  560. class socks5_client_cp_bridge {};
  561. template<class derived_t, class args_t>
  562. class socks5_client_cp_bridge<derived_t, args_t, detail::tcp_tag>
  563. : public socks5_client_cp_impl<derived_t, args_t>
  564. {
  565. template<class, class = void>
  566. struct has_member_set_bnd_addr : std::false_type {};
  567. template<class T>
  568. struct has_member_set_bnd_addr<T, std::void_t<decltype(
  569. std::declval<std::decay_t<T>&>().set_bnd_addr(std::string{}))>> : std::true_type {};
  570. template<class, class = void>
  571. struct has_member_set_bnd_port : std::false_type {};
  572. template<class T>
  573. struct has_member_set_bnd_port<T, std::void_t<decltype(
  574. std::declval<std::decay_t<T>&>().set_bnd_port(std::string{}))>> : std::true_type {};
  575. protected:
  576. // this function name can't be set_bnd_addr, it maybe cause recursive deadloop
  577. // when use udp client with socks5 option, the udp client will create a tcp client
  578. // to connect to the socks5 server, and the tcp client will use the same socks5 option
  579. // as the udp client's too, so the tcp client will derived from current class again,
  580. // so at here, the tparam D is the tcp client with socks5 option which is the internal
  581. // tcp connection of the udp client.
  582. template<class D = derived_t>
  583. inline void do_set_bnd_addr(std::string addr)
  584. {
  585. D& derive = static_cast<D&>(*this);
  586. if constexpr (has_member_set_bnd_addr<D>::value)
  587. derive.set_bnd_addr(std::move(addr));
  588. else
  589. detail::ignore_unused(addr);
  590. }
  591. // this function name can't be set_bnd_port, it maybe cause recursive deadloop
  592. template<class D = derived_t>
  593. inline void do_set_bnd_port(std::string port)
  594. {
  595. D& derive = static_cast<D&>(*this);
  596. if constexpr (has_member_set_bnd_port<D>::value)
  597. derive.set_bnd_port(std::move(port));
  598. else
  599. detail::ignore_unused(port);
  600. }
  601. protected:
  602. template<typename C>
  603. inline void _socks5_init(std::shared_ptr<ecs_t<C>>& ecs)
  604. {
  605. detail::ignore_unused(ecs);
  606. }
  607. template<typename C, typename DeferEvent>
  608. inline void _socks5_start(
  609. std::shared_ptr<derived_t> this_ptr, std::shared_ptr<ecs_t<C>> ecs, DeferEvent chain)
  610. {
  611. derived_t& derive = static_cast<derived_t&>(*this);
  612. if constexpr (ecs_helper::has_socks5<C>())
  613. {
  614. this->_check_socks5_command(this_ptr, ecs);
  615. socks5_async_handshake
  616. (
  617. derive.host_, derive.port_,
  618. derive.socks5_socket(),
  619. ecs->get_component().socks5_option(std::in_place),
  620. [this, this_ptr, ecs, chain = std::move(chain)]
  621. (error_code ec, std::string host, std::string port) mutable
  622. {
  623. derived_t& derive = static_cast<derived_t&>(*this);
  624. ASIO2_ASSERT(derive.running_in_this_thread());
  625. this->do_set_bnd_addr(std::move(host));
  626. this->do_set_bnd_port(std::move(port));
  627. derive._handle_proxy(ec, std::move(this_ptr), std::move(ecs), std::move(chain));
  628. }
  629. );
  630. }
  631. else
  632. {
  633. ASIO2_ASSERT(!get_last_error());
  634. derive._handle_proxy(error_code{}, std::move(this_ptr), std::move(ecs), std::move(chain));
  635. }
  636. }
  637. inline void _socks5_stop() noexcept
  638. {
  639. }
  640. public:
  641. inline auto& socks5_socket() noexcept
  642. {
  643. derived_t& derive = static_cast<derived_t&>(*this);
  644. return derive.socket();
  645. }
  646. inline const auto& socks5_socket() const noexcept
  647. {
  648. derived_t& derive = static_cast<derived_t&>(*this);
  649. return derive.socket();
  650. }
  651. };
  652. template<class derived_t, class args_t>
  653. class socks5_client_cp_bridge<derived_t, args_t, detail::udp_tag>
  654. : public socks5_client_cp_impl<derived_t, args_t>
  655. {
  656. protected:
  657. class internal_socks5_client_impl : public args_t::template socks5_client_t<internal_socks5_client_impl>
  658. {
  659. friend derived_t;
  660. template<class, class, typename> friend class socks5_client_cp_bridge;
  661. template<class, class> friend class connect_cp;
  662. public:
  663. using super = typename args_t::template socks5_client_t<internal_socks5_client_impl>;
  664. template<class... Args>
  665. internal_socks5_client_impl(Args&&... args)
  666. : super(std::forward<Args>(args)...)
  667. {
  668. }
  669. template<class T>
  670. inline auto data_filter_before_send(T&& data)
  671. {
  672. std::string_view sv = asio2::to_string_view(asio::buffer(data));
  673. // can't write the head at here, beacuse there maybe has multi thread call this function.
  674. std::vector<std::uint8_t>& head = this->udp_data_head_;
  675. std::uint16_t udatalen = detail::host_to_network(std::uint16_t(sv.size()));
  676. std::uint8_t* pdatalen = reinterpret_cast<std::uint8_t*>(std::addressof(udatalen));
  677. // the data is: stds::string, std::vector...
  678. if constexpr (detail::has_member_insert<T>::value && !std::is_const_v<std::remove_reference_t<T>>)
  679. {
  680. data.insert(data.begin(), head.begin(), head.end());
  681. std::memcpy((void*)(data.begin().operator->()), (const void*)pdatalen, sizeof(std::uint16_t));
  682. return std::forward<T>(data);
  683. }
  684. else
  685. {
  686. std::string str{sv};
  687. str.insert(str.begin(), head.begin(), head.end());
  688. str[0] = std::string::value_type(pdatalen[0]);
  689. str[1] = std::string::value_type(pdatalen[1]);
  690. return str;
  691. }
  692. }
  693. // recvd data from the socks5 server by tcp.
  694. // cant remove the socks5 protocol head, beacuse when this bind_recv is called,
  695. // it will call the udp client's _fire_recv, and the _fire_recv will remove the
  696. // socks5 protocol head by itself.
  697. //inline std::string_view data_filter_before_recv(std::string_view data)
  698. //{
  699. // auto [err, ep, domain, real_data] = asio2::socks5::parse_udp_packet(data, true);
  700. // detail::ignore_unused(err, ep, domain, real_data);
  701. // return real_data;
  702. //}
  703. void set_udp_data_head(const std::string& host, const std::string& port)
  704. {
  705. std::vector<std::uint8_t>& head = this->udp_data_head_;
  706. head.reserve(4 + 4 + 2);
  707. head.push_back(std::uint8_t(0x00)); // RSV
  708. head.push_back(std::uint8_t(0x00)); // RSV
  709. head.push_back(std::uint8_t(0x00)); // FRAG
  710. error_code ec{};
  711. asio::ip::address addr = asio::ip::address::from_string(host, ec);
  712. if (ec) // DOMAINNAME: X'03'
  713. {
  714. head.push_back(std::uint8_t(0x03));
  715. head.push_back(std::uint8_t(host.size()));
  716. head.insert(head.cend(), host.begin(), host.end());
  717. }
  718. else if (addr.is_v4()) // IP V4 address: X'01'
  719. {
  720. head.push_back(std::uint8_t(0x01));
  721. asio::ip::address_v4::bytes_type bytes = addr.to_v4().to_bytes();
  722. head.insert(head.cend(), bytes.begin(), bytes.end());
  723. }
  724. else if (addr.is_v6()) // IP V6 address: X'04'
  725. {
  726. head.push_back(std::uint8_t(0x04));
  727. asio::ip::address_v6::bytes_type bytes = addr.to_v6().to_bytes();
  728. head.insert(head.cend(), bytes.begin(), bytes.end());
  729. }
  730. std::uint16_t uport = detail::host_to_network(std::uint16_t(std::strtoul(port.data(), nullptr, 10)));
  731. std::uint8_t* pport = reinterpret_cast<std::uint8_t*>(std::addressof(uport));
  732. head.push_back(pport[0]);
  733. head.push_back(pport[1]);
  734. }
  735. inline void set_bnd_addr(std::string addr)
  736. {
  737. bnd_addr_ = std::move(addr);
  738. }
  739. inline void set_bnd_port(std::string port)
  740. {
  741. bnd_port_ = std::move(port);
  742. }
  743. protected:
  744. template<typename C, typename DeferEvent>
  745. inline void _post_proxy(
  746. const error_code& ec,
  747. std::shared_ptr<internal_socks5_client_impl> this_ptr, std::shared_ptr<ecs_t<C>> ecs, DeferEvent chain)
  748. {
  749. // after _post_proxy, the socks5_async_handshake will be called, and the socks5_async_handshake
  750. // will use the derive.host_ and derive.port_ to as the DST.ADDR and DST.PORT of the
  751. // socks5 protocol. and this is udp, so we need set the DST.PORT to the udp client's
  752. // local port.
  753. this->set_port(this->dst_port_);
  754. ASIO2_ASSERT(this->running_in_this_thread());
  755. super::_post_proxy(ec, std::move(this_ptr), std::move(ecs), std::move(chain));
  756. }
  757. protected:
  758. std::uint16_t dst_port_{};
  759. std::string bnd_addr_{};
  760. std::string bnd_port_{};
  761. std::vector<std::uint8_t> udp_data_head_{};
  762. };
  763. protected:
  764. template<typename C>
  765. inline void _socks5_init(std::shared_ptr<ecs_t<C>>& ecs)
  766. {
  767. detail::ignore_unused(ecs);
  768. derived_t& derive = static_cast<derived_t&>(*this);
  769. if constexpr (ecs_helper::has_socks5<C>())
  770. this->socks5_client_ = std::make_shared<internal_socks5_client_impl>(derive.io_->context());
  771. else
  772. std::ignore = true;
  773. }
  774. template<typename C, typename DeferEvent>
  775. inline void _socks5_start(
  776. std::shared_ptr<derived_t> this_ptr, std::shared_ptr<ecs_t<C>> ecs, DeferEvent chain)
  777. {
  778. derived_t& derive = static_cast<derived_t&>(*this);
  779. if constexpr (ecs_helper::has_socks5<C>())
  780. {
  781. this->_check_socks5_command(this_ptr, ecs);
  782. auto s5opt = ecs->get_component().socks5_option(std::in_place);
  783. this->socks5_client_->bind_connect([]() mutable
  784. {
  785. }).bind_disconnect([&derive, wptr = derive.weak_from_this()]() mutable
  786. {
  787. // socks5 connection is disconnected, so close the udp client too.
  788. ASIO2_ASSERT(derive.running_in_this_thread());
  789. derive._do_disconnect(asio2::get_last_error(), wptr.lock());
  790. }).bind_recv([&derive, wptr = derive.weak_from_this(), ecs](std::string_view data) mutable
  791. {
  792. // recvd data from the socks5 server by tcp, forward the data to the udp client.
  793. ASIO2_ASSERT(derive.running_in_this_thread());
  794. std::shared_ptr<derived_t> this_ptr = wptr.lock();
  795. derive._fire_recv(this_ptr, ecs, data);
  796. });
  797. // save this udp client's local bind port.
  798. this->socks5_client_->dst_port_ = derive.get_local_port();
  799. // disable the auto reconnect
  800. this->socks5_client_->set_auto_reconnect(false);
  801. this->socks5_client_->async_start(s5opt->host(), s5opt->port(),
  802. [this, &derive, this_ptr = std::move(this_ptr), ecs = std::move(ecs), chain = std::move(chain)]
  803. (error_code ec) mutable
  804. {
  805. ASIO2_ASSERT(derive.running_in_this_thread());
  806. if (ec)
  807. {
  808. derive._handle_proxy(ec,
  809. std::move(this_ptr), std::move(ecs), std::move(chain));
  810. return;
  811. }
  812. this->socks5_client_->set_udp_data_head(derive.host_, derive.port_);
  813. auto s5opt = ecs->get_component().socks5_option(std::in_place);
  814. asio::ip::address addr = asio::ip::address::from_string(this->socks5_client_->bnd_addr_, ec);
  815. if (ec)
  816. {
  817. this->socks5_client_->bnd_addr_ = s5opt->host();
  818. }
  819. else
  820. {
  821. if (addr.is_unspecified())
  822. this->socks5_client_->bnd_addr_ = s5opt->host();
  823. }
  824. derive._reconnect_to_socks5_server(std::move(this_ptr), std::move(ecs), std::move(chain));
  825. }, detail::socks5_udp_match_role, s5opt);
  826. }
  827. else
  828. {
  829. ASIO2_ASSERT(!get_last_error());
  830. derive._handle_proxy(error_code{}, std::move(this_ptr), std::move(ecs), std::move(chain));
  831. }
  832. }
  833. inline void _socks5_stop()
  834. {
  835. if (this->socks5_client_)
  836. {
  837. this->socks5_client_->stop();
  838. // if we destroy this shared_ptr at here, it maybe cause crash, beacuse the
  839. // data_filter_before_send of this class maybe called agagin after it destroyed,
  840. // and it will read the head variable which is in the socks5 client, so it
  841. // will cause crash.
  842. //this->socks5_client_.reset();
  843. }
  844. }
  845. template<typename C, typename DeferEvent>
  846. inline void _reconnect_to_socks5_server(
  847. std::shared_ptr<derived_t> this_ptr, std::shared_ptr<ecs_t<C>> ecs, DeferEvent chain)
  848. {
  849. derived_t& derive = static_cast<derived_t&>(*this);
  850. if constexpr (std::is_base_of_v<detail::cast_tag, derived_t>)
  851. {
  852. ASIO2_ASSERT(derive.running_in_this_thread());
  853. derive._handle_proxy(error_code{}, std::move(this_ptr), std::move(ecs), std::move(chain));
  854. }
  855. else
  856. {
  857. asio2::async_connect(
  858. this->socks5_client_->bnd_addr_,
  859. this->socks5_client_->bnd_port_,
  860. derive.socket(),
  861. [&derive, this_ptr = std::move(this_ptr), ecs = std::move(ecs), chain = std::move(chain)]
  862. (const error_code& ec) mutable
  863. {
  864. ASIO2_ASSERT(derive.running_in_this_thread());
  865. derive._handle_proxy(ec, std::move(this_ptr), std::move(ecs), std::move(chain));
  866. });
  867. }
  868. }
  869. // send data to the socks5 server by udp, we need add socks5 protocol head before the data.
  870. template<class T>
  871. inline auto data_filter_before_send(T&& data)
  872. {
  873. // the data is: stds::string, std::vector...
  874. if constexpr (detail::has_member_insert<T>::value && !std::is_const_v<std::remove_reference_t<T>>)
  875. {
  876. if (this->socks5_client_ == nullptr)
  877. return std::forward<T>(data);
  878. std::vector<std::uint8_t>& head = this->socks5_client_->udp_data_head_;
  879. data.insert(data.begin(), head.begin(), head.end());
  880. return std::forward<T>(data);
  881. }
  882. else
  883. {
  884. std::string str{asio2::to_string_view(asio::buffer(data))};
  885. if (this->socks5_client_ == nullptr)
  886. return str;
  887. std::vector<std::uint8_t>& head = this->socks5_client_->udp_data_head_;
  888. str.insert(str.begin(), head.begin(), head.end());
  889. return str;
  890. }
  891. }
  892. // recvd data from the socks5 server by udp, we need remove the socks5 protocol head.
  893. inline std::string_view data_filter_before_recv(std::string_view data)
  894. {
  895. if (this->socks5_client_ == nullptr)
  896. return data;
  897. auto [err, ep, domain, real_data] = asio2::socks5::parse_udp_packet(data, false);
  898. detail::ignore_unused(err, ep, domain, real_data);
  899. return real_data;
  900. }
  901. public:
  902. inline auto& socks5_socket() noexcept
  903. {
  904. return this->socks5_client_->socket();
  905. }
  906. inline const auto& socks5_socket() const noexcept
  907. {
  908. return this->socks5_client_->socket();
  909. }
  910. inline std::shared_ptr<internal_socks5_client_impl> get_socks5_connection() noexcept
  911. {
  912. return this->socks5_client_;
  913. }
  914. //template<class... Args>
  915. //inline void async_send_by_socks5_connection(Args&&... args) noexcept
  916. //{
  917. // ASIO2_ASSERT(this->socks5_client_ != nullptr);
  918. // if (this->socks5_client_)
  919. // {
  920. // this->socks5_client_->async_send(std::forward<Args>(args)...);
  921. // }
  922. //}
  923. protected:
  924. std::shared_ptr<internal_socks5_client_impl> socks5_client_;
  925. };
  926. template<class derived_t, class args_t>
  927. class socks5_client_cp : public socks5_client_cp_bridge<derived_t, args_t> {};
  928. }
  929. #endif // !__ASIO2_SOCKS5_CLIENT_CP_HPP__