src/detail/socks5_tunnel.cpp

100.0% Lines (2/2) 100.0% List of functions (1/1) 100.0% Branches (1/1)
socks5_tunnel.cpp
f(x) Functions (1)
Line Branch TLA Hits Source Code
1 //
2 // Copyright (c) 2026 Mohammad Nejati
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 // Official repository: https://github.com/cppalliance/burl
8 //
9
10 #include "socks5_tunnel.hpp"
11
12 #include <boost/burl/error.hpp>
13
14 #include <boost/capy/buffers/make_buffer.hpp>
15 #include <boost/capy/read.hpp>
16 #include <boost/capy/write.hpp>
17 #include <boost/url/grammar/string_token.hpp>
18
19 #include <cstddef>
20 #include <cstdint>
21 #include <string>
22
23 namespace boost
24 {
25 namespace burl
26 {
27 namespace detail
28 {
29
30 capy::io_task<>
31
1/1
✓ Branch 1 taken 21 times.
21x open_socks5_tunnel(
32 capy::any_stream stream,
33 std::string_view target_host,
34 std::string_view target_port,
35 urls::url_view proxy)
36 {
37 // Greeting: offer username/password auth only when credentials are present.
38 if(proxy.has_userinfo())
39 {
40 std::uint8_t greeting[4] = { 0x05, 0x02, 0x00, 0x02 };
41 auto [ec, n] =
42 co_await capy::write(stream, capy::make_buffer(greeting));
43 if(ec)
44 co_return ec;
45 }
46 else
47 {
48 std::uint8_t greeting[3] = { 0x05, 0x01, 0x00 };
49 auto [ec, n] =
50 co_await capy::write(stream, capy::make_buffer(greeting));
51 if(ec)
52 co_return ec;
53 }
54
55 std::uint8_t greeting_resp[2];
56 if(auto [ec, n] =
57 co_await capy::read(stream, capy::make_buffer(greeting_resp));
58 ec)
59 co_return ec;
60
61 if(greeting_resp[0] != 0x05)
62 co_return { error::proxy_unsupported_version };
63
64 switch(greeting_resp[1])
65 {
66 case 0x00: // no authentication required
67 break;
68 case 0x02: // username/password (RFC 1929)
69 {
70 std::string auth_req;
71 auth_req.push_back(0x01); // sub-negotiation version
72
73 auto user = proxy.encoded_user();
74 auth_req.push_back(static_cast<char>(user.decoded_size()));
75 user.decode({}, urls::string_token::append_to(auth_req));
76
77 auto pass = proxy.encoded_password();
78 auth_req.push_back(static_cast<char>(pass.decoded_size()));
79 pass.decode({}, urls::string_token::append_to(auth_req));
80
81 if(auto [ec, n] =
82 co_await capy::write(stream, capy::make_buffer(auth_req));
83 ec)
84 co_return ec;
85
86 std::uint8_t auth_resp[2];
87 if(auto [ec, n] =
88 co_await capy::read(stream, capy::make_buffer(auth_resp));
89 ec)
90 co_return ec;
91
92 if(auth_resp[1] != 0x00)
93 co_return { error::proxy_auth_failed };
94 break;
95 }
96 default: // no acceptable method (0xFF) or anything unexpected
97 co_return { error::proxy_auth_failed };
98 }
99
100 // connection request
101 std::string conn_req = { 0x05, 0x01, 0x00, 0x03 };
102
103 conn_req.push_back(static_cast<char>(target_host.size()));
104 conn_req.append(target_host);
105
106 auto port =
107 static_cast<std::uint16_t>(std::stoul(std::string(target_port)));
108 conn_req.push_back(static_cast<char>((port >> 8) & 0xFF));
109 conn_req.push_back(static_cast<char>(port & 0xFF));
110
111 if(auto [ec, n] = co_await capy::write(stream, capy::make_buffer(conn_req));
112 ec)
113 co_return ec;
114
115 // connection response
116 std::uint8_t reply_head[5];
117 if(auto [ec, n] =
118 co_await capy::read(stream, capy::make_buffer(reply_head));
119 ec)
120 co_return ec;
121
122 if(reply_head[1] != 0x00)
123 co_return { error::proxy_connect_failed };
124
125 std::size_t tail = 0;
126 switch(reply_head[3])
127 {
128 case 0x01:
129 tail = 4 + 2 - 1; // ipv4 + port
130 break;
131 case 0x03:
132 tail = reply_head[4] + 2u; // domain name + port
133 break;
134 case 0x04:
135 tail = 16 + 2 - 1; // ipv6 + port
136 break;
137 default:
138 co_return { error::proxy_connect_failed };
139 }
140
141 std::string reply_tail;
142 reply_tail.resize(tail);
143 if(auto [ec, n] =
144 co_await capy::read(stream, capy::make_buffer(reply_tail));
145 ec)
146 co_return ec;
147
148 co_return {};
149 42x }
150
151 } // namespace detail
152 } // namespace burl
153 } // namespace boost
154