1 #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
2 #include <doctest/doctest.h>
3 
4 #include <arataga/acl_handler/buffers.hpp>
5 
6 #include <tests/connection_handler_simulator/pub.hpp>
7 
8 #include <asio.hpp>
9 
10 using namespace std::string_view_literals;
11 using namespace std::chrono_literals;
12 
13 namespace chs = connection_handler_simulator;
14 
15 void
write_auth_pdu(asio::ip::tcp::socket & connection,std::string username="user",std::string password="1234")16 write_auth_pdu(
17 	asio::ip::tcp::socket & connection,
18 	std::string username = "user",
19 	std::string password = "1234" )
20 {
21 	{
22 		std::array< std::uint8_t, 3 > first_pdu{ 0x5u, 0x1u, 0x2u };
23 		REQUIRE_NOTHROW( asio::write( connection, asio::buffer(first_pdu) ) );
24 	}
25 
26 	{
27 		std::array< std::uint8_t, 20 > response;
28 		std::size_t read;
29 		REQUIRE_NOTHROW( read = connection.read_some( asio::buffer(response) ) );
30 		REQUIRE( 2u == read );
31 		REQUIRE( 0x5u == response[ 0 ] );
32 		REQUIRE( 0x2u == response[ 1 ] );
33 	}
34 
35 	{
36 		arataga::acl_handler::out_buffer_fixed_t< 1u + 1u + 255u + 1u + 255u >
37 				data;
38 
39 		data.write_byte( std::byte{0x1u} );
40 		data.write_byte( std::byte{ static_cast<std::uint8_t>(username.size()) } );
41 		data.write_string( username );
42 		data.write_byte( std::byte{ static_cast<std::uint8_t>(password.size()) } );
43 		data.write_string( password );
44 
45 		REQUIRE_NOTHROW( asio::write( connection, data.asio_buffer() ) );
46 	}
47 
48 	{
49 		std::array< std::uint8_t, 20 > response;
50 		std::size_t read;
51 		REQUIRE_NOTHROW( read = connection.read_some( asio::buffer(response) ) );
52 		REQUIRE( 2u == read );
53 		REQUIRE( 0x1u == response[ 0 ] );
54 		REQUIRE( 0x0u == response[ 1 ] );
55 	}
56 }
57 
58 void
write_bind_pdu(asio::ip::tcp::socket & connection,std::string_view host_name,std::uint16_t port)59 write_bind_pdu(
60 	asio::ip::tcp::socket & connection,
61 	std::string_view host_name,
62 	std::uint16_t port )
63 {
64 	{
65 		arataga::acl_handler::out_buffer_fixed_t<
66 				1 // VER
67 				+ 1 // CMD
68 				+ 1 // RESERVED
69 				+ 1 // ATYP
70 				+ 256 // DST.ADDR (it's the max possible length).
71 				+ 2 // DST.PORT
72 			> data;
73 
74 		data.write_byte( std::byte{0x5u} );
75 		data.write_byte( std::byte{0x2u} );
76 		data.write_byte( std::byte{0u} );
77 		data.write_byte( std::byte{0x3u} ); // ATYP
78 
79 		// domain name length.
80 		data.write_byte( std::byte{ static_cast<std::uint8_t>(host_name.size()) } );
81 		// domain name.
82 		data.write_string( host_name );
83 
84 		// DST.PORT
85 		data.write_byte(
86 				std::byte{ static_cast<std::uint8_t>(port >> 8) } );
87 		data.write_byte(
88 				std::byte{ static_cast<std::uint8_t>(port & 0xff) } );
89 
90 		REQUIRE_NOTHROW( asio::write( connection, data.asio_buffer() ) );
91 	}
92 }
93 
94 TEST_CASE("no connection from target-end") {
95 	asio::ip::tcp::endpoint proxy_endpoint{
96 			asio::ip::make_address_v4( "127.0.0.1" ),
97 			2444
98 		};
99 
100 	chs::simulator_t simulator{
101 			proxy_endpoint,
102 			chs::handler_config_values_t{}
103 	};
104 
105 	asio::io_context ctx;
106 
107 	asio::ip::tcp::socket connection{ ctx };
108 	REQUIRE_NOTHROW( connection.connect( proxy_endpoint ) );
109 
110 	write_auth_pdu( connection, "user", "12345" );
111 	write_bind_pdu( connection, "localhost", 3333 );
112 
113 	// A positive response is expected.
114 	{
115 		std::array< std::uint8_t,
116 				1 // VER
117 				+ 1 // REP
118 				+ 1 // RESERVED
119 				+ 1 // ATYP
120 				+ 4 // DST.ADDR (IPv4).
121 				+ 2 // DST.PORT
122 			> data;
123 
124 		std::size_t read;
125 		REQUIRE_NOTHROW( read = connection.read_some( asio::buffer(data) ) );
126 		REQUIRE( 10u == read );
127 		REQUIRE( 0x5u == data[ 0 ] );
128 		REQUIRE( 0x0u == data[ 1 ] );
129 		REQUIRE( 0x0u == data[ 2 ] );
130 		REQUIRE( 0x1u == data[ 3 ] );
131 		REQUIRE( 0x7fu == data[ 4 ] );
132 		REQUIRE( 0x0u == data[ 5 ] );
133 		REQUIRE( 0x0u == data[ 6 ] );
134 		REQUIRE( 0x1u == data[ 7 ] );
135 	}
136 
137 	chs::dump_trace( (std::cout << "***\n"), simulator.get_trace() );
138 
139 	// A negative response is expected.
140 	{
141 		std::array< std::uint8_t,
142 				1 // VER
143 				+ 1 // REP
144 				+ 1 // RESERVED
145 				+ 1 // ATYP
146 				+ 4 // DST.ADDR (IPv4).
147 				+ 2 // DST.PORT
148 			> data;
149 
150 		std::size_t read;
151 		REQUIRE_NOTHROW( read = connection.read_some( asio::buffer(data) ) );
152 		REQUIRE( 4u == read );
153 		REQUIRE( 0x5u == data[ 0 ] );
154 		REQUIRE( 0x4u == data[ 1 ] );
155 		REQUIRE( 0x0u == data[ 2 ] );
156 	}
157 
158 	chs::dump_trace( (std::cout << "-----\n"), simulator.get_trace() );
159 }
160 
161 TEST_CASE("connection from target-end") {
162 	asio::ip::tcp::endpoint proxy_endpoint{
163 			asio::ip::make_address_v4( "127.0.0.1" ),
164 			2444
165 		};
166 
167 	chs::simulator_t simulator{
168 			proxy_endpoint,
169 			chs::handler_config_values_t{}
170 	};
171 
172 	asio::io_context ctx;
173 
174 	asio::ip::tcp::socket connection{ ctx };
175 	REQUIRE_NOTHROW( connection.connect( proxy_endpoint ) );
176 
177 	write_auth_pdu( connection, "user", "12345" );
178 	write_bind_pdu( connection, "localhost", 3333 );
179 
180 	chs::dump_trace( (std::cout << "***\n"), simulator.get_trace() );
181 
182 	// A positive response is expected.
183 	std::uint16_t listening_port{ 0u };
184 	{
185 		std::array< std::uint8_t,
186 				1 // VER
187 				+ 1 // REP
188 				+ 1 // RESERVED
189 				+ 1 // ATYP
190 				+ 4 // DST.ADDR (IPv4).
191 				+ 2 // DST.PORT
192 			> data;
193 
194 		std::size_t read;
195 		REQUIRE_NOTHROW( read = connection.read_some( asio::buffer(data) ) );
196 		REQUIRE( 10u == read );
197 		REQUIRE( 0x5u == data[ 0 ] );
198 		REQUIRE( 0x0u == data[ 1 ] );
199 		REQUIRE( 0x0u == data[ 2 ] );
200 		REQUIRE( 0x1u == data[ 3 ] );
201 		REQUIRE( 0x7fu == data[ 4 ] );
202 		REQUIRE( 0x0u == data[ 5 ] );
203 		REQUIRE( 0x0u == data[ 6 ] );
204 		REQUIRE( 0x1u == data[ 7 ] );
205 
206 		listening_port = (static_cast<std::uint16_t>(data[8]) << 8) |
207 				static_cast<std::uint16_t>(data[9]);
208 
209 		std::cout << "=====\n => listening port: " << listening_port << std::endl;
210 	}
211 
212 	chs::dump_trace( (std::cout << "***\n"), simulator.get_trace() );
213 
214 	asio::ip::tcp::socket incoming{ ctx };
215 
216 	REQUIRE_NOTHROW( incoming.connect(
217 			asio::ip::tcp::endpoint{
218 				asio::ip::make_address( "127.0.0.1" ),
219 				listening_port
220 			} )
221 	);
222 
223 	// A negative response is expected.
224 	{
225 		std::array< std::uint8_t,
226 				1 // VER
227 				+ 1 // REP
228 				+ 1 // RESERVED
229 				+ 1 // ATYP
230 				+ 4 // DST.ADDR (IPv4).
231 				+ 2 // DST.PORT
232 			> data;
233 
234 		std::size_t read;
235 		REQUIRE_NOTHROW( read = connection.read_some( asio::buffer(data) ) );
236 		REQUIRE( 10u == read );
237 		REQUIRE( 0x5u == data[ 0 ] );
238 		REQUIRE( 0x0u == data[ 1 ] );
239 		REQUIRE( 0x0u == data[ 2 ] );
240 	}
241 
242 	chs::dump_trace( (std::cout << "***\n"), simulator.get_trace() );
243 
244 	{
245 		std::array< char, 6 > data{ 'H', 'e', 'l', 'l', 'o', '?' };
246 		REQUIRE_NOTHROW( asio::write( connection, asio::buffer(data) ) );
247 	}
248 
249 	{
250 		std::array< char, 6 > data;
251 		const std::array< char, 6 > expected{ 'H', 'e', 'l', 'l', 'o', '?' };
252 		REQUIRE_NOTHROW( asio::read( incoming, asio::buffer(data) ) );
253 		REQUIRE( data == expected );
254 	}
255 
256 	{
257 		std::array< char, 6 > data{ 'W', 'o', 'r', 'l', 'd', '!' };
258 		REQUIRE_NOTHROW( asio::write( incoming, asio::buffer(data) ) );
259 	}
260 
261 	{
262 		std::array< char, 6 > data;
263 		const std::array< char, 6 > expected{ 'W', 'o', 'r', 'l', 'd', '!' };
264 		REQUIRE_NOTHROW( asio::read( connection, asio::buffer(data) ) );
265 		REQUIRE( data == expected );
266 	}
267 
268 	chs::dump_trace( (std::cout << "-----\n"), simulator.get_trace() );
269 }
270 
271