1 #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
2 #include <doctest/doctest.h>
3 
4 #include <tests/connection_handler_simulator/pub.hpp>
5 
6 #include <asio.hpp>
7 
8 using namespace std::string_view_literals;
9 using namespace std::chrono_literals;
10 
11 namespace chs = connection_handler_simulator;
12 
13 TEST_CASE("no auth PDU") {
14 	asio::ip::tcp::endpoint proxy_endpoint{
15 			asio::ip::make_address_v4( "127.0.0.1" ),
16 			2444
17 		};
18 
19 	chs::simulator_t simulator{
20 			proxy_endpoint,
21 			chs::handler_config_values_t{}
22 	};
23 
24 	asio::io_context ctx;
25 
26 	asio::ip::tcp::socket connection{ ctx };
27 	REQUIRE_NOTHROW( connection.connect( proxy_endpoint ) );
28 
29 	{
30 		std::array< std::uint8_t, 3 > first_pdu{ 0x5u, 0x1u, 0x2u };
31 		std::size_t written;
32 		REQUIRE_NOTHROW( written = connection.write_some( asio::buffer(first_pdu) ) );
33 		REQUIRE( first_pdu.size() == written );
34 	}
35 
36 	{
37 		std::array< std::uint8_t, 20 > response;
38 		std::size_t read;
39 		REQUIRE_NOTHROW( read = connection.read_some( asio::buffer(response) ) );
40 		REQUIRE( 2u == read );
41 		REQUIRE( 0x5u == response[ 0 ] );
42 		REQUIRE( 0x2u == response[ 1 ] );
43 	}
44 
45 	// The connection has to be closed on the other side.
46 	{
47 		std::array< std::uint8_t, 20 > data;
48 		asio::error_code ec;
49 		REQUIRE_NOTHROW( connection.read_some( asio::buffer(data), ec ) );
50 		REQUIRE( asio::error::eof == ec );
51 	}
52 
53 	chs::dump_trace( (std::cout << "-----\n"), simulator.get_trace() );
54 }
55 
56 TEST_CASE("no auth PDU (one byte per second)") {
57 	asio::ip::tcp::endpoint proxy_endpoint{
58 			asio::ip::make_address_v4( "127.0.0.1" ),
59 			2444
60 		};
61 
62 	chs::handler_config_values_t config_values;
63 	config_values.m_socks_handshake_phase_timeout = 3s;
64 	chs::simulator_t simulator{
65 			proxy_endpoint,
66 			config_values
67 	};
68 
69 	asio::io_context ctx;
70 
71 	asio::ip::tcp::socket connection{ ctx };
72 	REQUIRE_NOTHROW( connection.connect( proxy_endpoint ) );
73 
74 	{
75 		std::array< std::uint8_t, 1 > first_pdu{ 0x5u };
76 		std::size_t written;
77 		REQUIRE_NOTHROW( written = connection.write_some( asio::buffer(first_pdu) ) );
78 		REQUIRE( first_pdu.size() == written );
79 
80 		std::this_thread::sleep_for( 1s );
81 	}
82 
83 	{
84 		std::array< std::uint8_t, 1 > first_pdu{ 0x1u };
85 		std::size_t written;
86 		REQUIRE_NOTHROW( written = connection.write_some( asio::buffer(first_pdu) ) );
87 		REQUIRE( first_pdu.size() == written );
88 
89 		std::this_thread::sleep_for( 1s );
90 	}
91 
92 	{
93 		std::array< std::uint8_t, 1 > first_pdu{ 0x2u };
94 		std::size_t written;
95 		REQUIRE_NOTHROW( written = connection.write_some( asio::buffer(first_pdu) ) );
96 		REQUIRE( first_pdu.size() == written );
97 	}
98 
99 	{
100 		std::array< std::uint8_t, 20 > response;
101 		std::size_t read;
102 		REQUIRE_NOTHROW( read = connection.read_some( asio::buffer(response) ) );
103 		REQUIRE( 2u == read );
104 		REQUIRE( 0x5u == response[ 0 ] );
105 		REQUIRE( 0x2u == response[ 1 ] );
106 	}
107 
108 	// The connection has to be closed on the other side.
109 	{
110 		std::array< std::uint8_t, 20 > data;
111 		asio::error_code ec;
112 		REQUIRE_NOTHROW( connection.read_some( asio::buffer(data), ec ) );
113 		REQUIRE( asio::error::eof == ec );
114 	}
115 
116 	chs::dump_trace( (std::cout << "-----\n"), simulator.get_trace() );
117 }
118 
119 TEST_CASE("wrong auth PDU version") {
120 	asio::ip::tcp::endpoint proxy_endpoint{
121 			asio::ip::make_address_v4( "127.0.0.1" ),
122 			2444
123 		};
124 
125 	chs::simulator_t simulator{
126 			proxy_endpoint,
127 			chs::handler_config_values_t{}
128 	};
129 
130 	asio::io_context ctx;
131 
132 	asio::ip::tcp::socket connection{ ctx };
133 	REQUIRE_NOTHROW( connection.connect( proxy_endpoint ) );
134 
135 	{
136 		std::array< std::uint8_t, 3 > first_pdu{ 0x5u, 0x1u, 0x2u };
137 		std::size_t written;
138 		REQUIRE_NOTHROW( written = connection.write_some( asio::buffer(first_pdu) ) );
139 		REQUIRE( first_pdu.size() == written );
140 	}
141 
142 	{
143 		std::array< std::uint8_t, 20 > response;
144 		std::size_t read;
145 		REQUIRE_NOTHROW( read = connection.read_some( asio::buffer(response) ) );
146 		REQUIRE( 2u == read );
147 		REQUIRE( 0x5u == response[ 0 ] );
148 		REQUIRE( 0x2u == response[ 1 ] );
149 	}
150 
151 	{
152 		std::array< std::uint8_t, 12 > data{
153 			0x2,
154 			0x4, 'u', 's', 'e', 'r',
155 			0x5, '1', '2', '3', '4', '5'
156 		};
157 		std::size_t written;
158 		REQUIRE_NOTHROW( written = connection.write_some( asio::buffer(data) ) );
159 		REQUIRE( data.size() == written );
160 	}
161 
162 	// The connection has to be closed on the other side.
163 	{
164 		std::array< std::uint8_t, 20 > data;
165 		asio::error_code ec;
166 		REQUIRE_NOTHROW( connection.read_some( asio::buffer(data), ec ) );
167 		REQUIRE( asio::error::eof == ec );
168 	}
169 
170 	chs::dump_trace( (std::cout << "-----\n"), simulator.get_trace() );
171 }
172 
173 TEST_CASE("partial auth PDU then close connection") {
174 	asio::ip::tcp::endpoint proxy_endpoint{
175 			asio::ip::make_address_v4( "127.0.0.1" ),
176 			2444
177 		};
178 
179 	chs::simulator_t simulator{
180 			proxy_endpoint,
181 			chs::handler_config_values_t{}
182 	};
183 
184 	asio::io_context ctx;
185 
186 	asio::ip::tcp::socket connection{ ctx };
187 	REQUIRE_NOTHROW( connection.connect( proxy_endpoint ) );
188 
189 	{
190 		std::array< std::uint8_t, 3 > first_pdu{ 0x5u, 0x1u, 0x2u };
191 		std::size_t written;
192 		REQUIRE_NOTHROW( written = connection.write_some( asio::buffer(first_pdu) ) );
193 		REQUIRE( first_pdu.size() == written );
194 	}
195 
196 	{
197 		std::array< std::uint8_t, 20 > response;
198 		std::size_t read;
199 		REQUIRE_NOTHROW( read = connection.read_some( asio::buffer(response) ) );
200 		REQUIRE( 2u == read );
201 		REQUIRE( 0x5u == response[ 0 ] );
202 		REQUIRE( 0x2u == response[ 1 ] );
203 	}
204 
205 	{
206 		std::array< std::uint8_t, 5 > data{
207 			0x1,
208 			0x4, 'u', 's', 'e'
209 		};
210 		std::size_t written;
211 		REQUIRE_NOTHROW( written = connection.write_some( asio::buffer(data) ) );
212 		REQUIRE( data.size() == written );
213 	}
214 
215 	connection.close();
216 
217 	std::this_thread::sleep_for( 1s );
218 
219 	chs::dump_trace( (std::cout << "-----\n"), simulator.get_trace() );
220 }
221 
222 TEST_CASE("partial auth PDU") {
223 	asio::ip::tcp::endpoint proxy_endpoint{
224 			asio::ip::make_address_v4( "127.0.0.1" ),
225 			2444
226 		};
227 
228 	chs::simulator_t simulator{
229 			proxy_endpoint,
230 			chs::handler_config_values_t{}
231 	};
232 
233 	asio::io_context ctx;
234 
235 	asio::ip::tcp::socket connection{ ctx };
236 	REQUIRE_NOTHROW( connection.connect( proxy_endpoint ) );
237 
238 	{
239 		std::array< std::uint8_t, 3 > first_pdu{ 0x5u, 0x1u, 0x2u };
240 		std::size_t written;
241 		REQUIRE_NOTHROW( written = connection.write_some( asio::buffer(first_pdu) ) );
242 		REQUIRE( first_pdu.size() == written );
243 	}
244 
245 	{
246 		std::array< std::uint8_t, 20 > response;
247 		std::size_t read;
248 		REQUIRE_NOTHROW( read = connection.read_some( asio::buffer(response) ) );
249 		REQUIRE( 2u == read );
250 		REQUIRE( 0x5u == response[ 0 ] );
251 		REQUIRE( 0x2u == response[ 1 ] );
252 	}
253 
254 	{
255 		std::array< std::uint8_t, 5 > data{
256 			0x1,
257 			0x4, 'u', 's', 'e'
258 		};
259 		std::size_t written;
260 		REQUIRE_NOTHROW( written = connection.write_some( asio::buffer(data) ) );
261 		REQUIRE( data.size() == written );
262 	}
263 
264 	// The connection has to be closed on the other side.
265 	{
266 		std::array< std::uint8_t, 20 > data;
267 		asio::error_code ec;
268 		REQUIRE_NOTHROW( connection.read_some( asio::buffer(data), ec ) );
269 		REQUIRE( asio::error::eof == ec );
270 	}
271 
272 	chs::dump_trace( (std::cout << "-----\n"), simulator.get_trace() );
273 }
274 
275 TEST_CASE("garbage after auth PDU") {
276 	asio::ip::tcp::endpoint proxy_endpoint{
277 			asio::ip::make_address_v4( "127.0.0.1" ),
278 			2444
279 		};
280 
281 	chs::simulator_t simulator{
282 			proxy_endpoint,
283 			chs::handler_config_values_t{}
284 	};
285 
286 	asio::io_context ctx;
287 
288 	asio::ip::tcp::socket connection{ ctx };
289 	REQUIRE_NOTHROW( connection.connect( proxy_endpoint ) );
290 
291 	{
292 		std::array< std::uint8_t, 3 > first_pdu{ 0x5u, 0x1u, 0x2u };
293 		std::size_t written;
294 		REQUIRE_NOTHROW( written = connection.write_some( asio::buffer(first_pdu) ) );
295 		REQUIRE( first_pdu.size() == written );
296 	}
297 
298 	{
299 		std::array< std::uint8_t, 20 > response;
300 		std::size_t read;
301 		REQUIRE_NOTHROW( read = connection.read_some( asio::buffer(response) ) );
302 		REQUIRE( 2u == read );
303 		REQUIRE( 0x5u == response[ 0 ] );
304 		REQUIRE( 0x2u == response[ 1 ] );
305 	}
306 
307 	{
308 		std::array< std::uint8_t, 28 > data{
309 			0x1,
310 			0x4, 'u', 's', 'e', 'r',
311 			0x5, '1', '2', '3', '4', '5', 'a', 'b', 'c',
312 			0x5, 0x1, 0x0,
313 			0x3, 0x6, 'y', 'a', '.', 'c', 'o', 'm',
314 			0x01, 0x00
315 		};
316 		std::size_t written;
317 		REQUIRE_NOTHROW( written = connection.write_some( asio::buffer(data) ) );
318 		REQUIRE( data.size() == written );
319 	}
320 
321 	// Since v.0.5.0 the size of auth PDU isn't checked.
322 	{
323 		std::array< std::uint8_t, 20 > data;
324 		std::size_t bytes_read{};
325 		REQUIRE_NOTHROW( bytes_read = connection.read_some( asio::buffer(data) ) );
326 		REQUIRE( 2u == bytes_read );
327 	}
328 
329 	chs::dump_trace( (std::cout << "-----\n"), simulator.get_trace() );
330 }
331 
332 TEST_CASE("zero-length username/password") {
333 	asio::ip::tcp::endpoint proxy_endpoint{
334 			asio::ip::make_address_v4( "127.0.0.1" ),
335 			2444
336 		};
337 
338 	chs::simulator_t simulator{
339 			proxy_endpoint,
340 			chs::handler_config_values_t{}
341 	};
342 
343 	asio::io_context ctx;
344 
345 	asio::ip::tcp::socket connection{ ctx };
346 	REQUIRE_NOTHROW( connection.connect( proxy_endpoint ) );
347 
348 	{
349 		std::array< std::uint8_t, 3 > first_pdu{ 0x5u, 0x1u, 0x2u };
350 		std::size_t written;
351 		REQUIRE_NOTHROW( written = connection.write_some( asio::buffer(first_pdu) ) );
352 		REQUIRE( first_pdu.size() == written );
353 	}
354 
355 	{
356 		std::array< std::uint8_t, 20 > response;
357 		std::size_t read;
358 		REQUIRE_NOTHROW( read = connection.read_some( asio::buffer(response) ) );
359 		REQUIRE( 2u == read );
360 		REQUIRE( 0x5u == response[ 0 ] );
361 		REQUIRE( 0x2u == response[ 1 ] );
362 	}
363 
364 	{
365 		std::array< std::uint8_t, 3 > data{
366 			0x1,
367 			0x0,
368 			0x0
369 		};
370 		std::size_t written;
371 		REQUIRE_NOTHROW( written = connection.write_some( asio::buffer(data) ) );
372 		REQUIRE( data.size() == written );
373 	}
374 
375 	{
376 		std::array< std::uint8_t, 20 > response;
377 		std::size_t read;
378 		REQUIRE_NOTHROW( read = connection.read_some( asio::buffer(response) ) );
379 		REQUIRE( 2u == read );
380 		REQUIRE( 0x1u == response[ 0 ] );
381 		REQUIRE( 0x0u == response[ 1 ] );
382 	}
383 
384 	chs::dump_trace( (std::cout << "-----\n"), simulator.get_trace() );
385 }
386 
387 TEST_CASE("valid auth PDU") {
388 	asio::ip::tcp::endpoint proxy_endpoint{
389 			asio::ip::make_address_v4( "127.0.0.1" ),
390 			2444
391 		};
392 
393 	chs::simulator_t simulator{
394 			proxy_endpoint,
395 			chs::handler_config_values_t{}
396 	};
397 
398 	asio::io_context ctx;
399 
400 	asio::ip::tcp::socket connection{ ctx };
401 	REQUIRE_NOTHROW( connection.connect( proxy_endpoint ) );
402 
403 	{
404 		std::array< std::uint8_t, 3 > first_pdu{ 0x5u, 0x1u, 0x2u };
405 		std::size_t written;
406 		REQUIRE_NOTHROW( written = connection.write_some( asio::buffer(first_pdu) ) );
407 		REQUIRE( first_pdu.size() == written );
408 	}
409 
410 	{
411 		std::array< std::uint8_t, 20 > response;
412 		std::size_t read;
413 		REQUIRE_NOTHROW( read = connection.read_some( asio::buffer(response) ) );
414 		REQUIRE( 2u == read );
415 		REQUIRE( 0x5u == response[ 0 ] );
416 		REQUIRE( 0x2u == response[ 1 ] );
417 	}
418 
419 	{
420 		std::array< std::uint8_t, 12 > data{
421 			0x1,
422 			0x4, 'u', 's', 'e', 'r',
423 			0x5, '1', '2', '3', '4', '5'
424 		};
425 		std::size_t written;
426 		REQUIRE_NOTHROW( written = connection.write_some( asio::buffer(data) ) );
427 		REQUIRE( data.size() == written );
428 	}
429 
430 	{
431 		std::array< std::uint8_t, 20 > response;
432 		std::size_t read;
433 		REQUIRE_NOTHROW( read = connection.read_some( asio::buffer(response) ) );
434 		REQUIRE( 2u == read );
435 		REQUIRE( 0x1u == response[ 0 ] );
436 		REQUIRE( 0x0u == response[ 1 ] );
437 	}
438 
439 	chs::dump_trace( (std::cout << "-----\n"), simulator.get_trace() );
440 }
441 
442 TEST_CASE("auth method with auth PDU as one package") {
443 	asio::ip::tcp::endpoint proxy_endpoint{
444 			asio::ip::make_address_v4( "127.0.0.1" ),
445 			2444
446 		};
447 
448 	chs::simulator_t simulator{
449 			proxy_endpoint,
450 			chs::handler_config_values_t{}
451 	};
452 
453 	asio::io_context ctx;
454 
455 	asio::ip::tcp::socket connection{ ctx };
456 	REQUIRE_NOTHROW( connection.connect( proxy_endpoint ) );
457 
458 	{
459 		std::array< std::uint8_t, 15 > first_pdu{
460 			0x5u, 0x1u, 0x2u, // Auth method selection.
461 			0x1, // Auth PDU.
462 			0x4, 'u', 's', 'e', 'r',
463 			0x5, '1', '2', '3', '4', '5'
464 		};
465 		std::size_t written;
466 		REQUIRE_NOTHROW( written = connection.write_some( asio::buffer(first_pdu) ) );
467 		REQUIRE( first_pdu.size() == written );
468 	}
469 
470 	{
471 		std::array< std::uint8_t, 2 > response;
472 		std::size_t read;
473 		REQUIRE_NOTHROW( read = connection.read_some( asio::buffer(response) ) );
474 		REQUIRE( 2u == read );
475 		REQUIRE( 0x5u == response[ 0 ] );
476 		REQUIRE( 0x2u == response[ 1 ] );
477 	}
478 
479 	{
480 		std::array< std::uint8_t, 20 > response;
481 		std::size_t read;
482 		REQUIRE_NOTHROW( read = connection.read_some( asio::buffer(response) ) );
483 		REQUIRE( 2u == read );
484 		REQUIRE( 0x1u == response[ 0 ] );
485 		REQUIRE( 0x0u == response[ 1 ] );
486 	}
487 
488 	chs::dump_trace( (std::cout << "-----\n"), simulator.get_trace() );
489 }
490 
491