1 /**
2  * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3  * SPDX-License-Identifier: Apache-2.0.
4  */
5 
6 #include <aws/http/connection.h>
7 #include <aws/http/private/h1_connection.h>
8 #include <aws/http/private/proxy_impl.h>
9 #include <aws/http/proxy.h>
10 #include <aws/http/server.h>
11 
12 #include <aws/common/clock.h>
13 #include <aws/common/condition_variable.h>
14 #include <aws/common/log_writer.h>
15 #include <aws/common/string.h>
16 #include <aws/common/thread.h>
17 #include <aws/common/uuid.h>
18 #include <aws/http/request_response.h>
19 #include <aws/io/channel_bootstrap.h>
20 #include <aws/io/event_loop.h>
21 #include <aws/io/logging.h>
22 #include <aws/io/socket.h>
23 #include <aws/io/tls_channel_handler.h>
24 #include <aws/testing/aws_test_harness.h>
25 #include <aws/testing/io_testing_channel.h>
26 
27 #include "proxy_test_helper.h"
28 
29 enum {
30     TESTER_TIMEOUT_SEC = 60, /* Give enough time for non-sudo users to enter password */
31 };
32 
33 struct testing_channel_bootstrap_wrapper {
34     struct testing_channel *channel;
35     struct aws_http_client_bootstrap *bootstrap;
36 };
37 
s_get_current_channel_bootstrap_wrapper(struct proxy_tester * tester)38 static struct testing_channel_bootstrap_wrapper *s_get_current_channel_bootstrap_wrapper(struct proxy_tester *tester) {
39     struct testing_channel_bootstrap_wrapper *wrapper = NULL;
40 
41     size_t count = aws_array_list_length(&tester->testing_channels);
42     aws_array_list_get_at_ptr(&tester->testing_channels, (void **)&wrapper, count - 1);
43 
44     return wrapper;
45 }
46 
proxy_tester_on_client_connection_setup(struct aws_http_connection * connection,int error_code,void * user_data)47 void proxy_tester_on_client_connection_setup(struct aws_http_connection *connection, int error_code, void *user_data) {
48 
49     struct proxy_tester *tester = user_data;
50     AWS_FATAL_ASSERT(aws_mutex_lock(&tester->wait_lock) == AWS_OP_SUCCESS);
51 
52     tester->client_connection_is_setup = true;
53 
54     if (error_code) {
55         tester->client_connection = NULL;
56         tester->wait_result = error_code;
57         goto done;
58     }
59 
60     tester->client_connection = connection;
61 
62 done:
63     AWS_FATAL_ASSERT(aws_mutex_unlock(&tester->wait_lock) == AWS_OP_SUCCESS);
64     aws_condition_variable_notify_one(&tester->wait_cvar);
65 }
66 
proxy_tester_on_client_connection_shutdown(struct aws_http_connection * connection,int error_code,void * user_data)67 void proxy_tester_on_client_connection_shutdown(
68     struct aws_http_connection *connection,
69     int error_code,
70     void *user_data) {
71 
72     (void)connection;
73     (void)error_code;
74     struct proxy_tester *tester = user_data;
75     AWS_FATAL_ASSERT(aws_mutex_lock(&tester->wait_lock) == AWS_OP_SUCCESS);
76 
77     tester->client_connection_is_shutdown = true;
78 
79     AWS_FATAL_ASSERT(aws_mutex_unlock(&tester->wait_lock) == AWS_OP_SUCCESS);
80     aws_condition_variable_notify_one(&tester->wait_cvar);
81 }
82 
proxy_tester_wait(struct proxy_tester * tester,bool (* pred)(void * user_data))83 int proxy_tester_wait(struct proxy_tester *tester, bool (*pred)(void *user_data)) {
84     ASSERT_SUCCESS(aws_mutex_lock(&tester->wait_lock));
85     ASSERT_SUCCESS(aws_condition_variable_wait_pred(&tester->wait_cvar, &tester->wait_lock, pred, tester));
86     ASSERT_SUCCESS(aws_mutex_unlock(&tester->wait_lock));
87 
88     return AWS_OP_SUCCESS;
89 }
90 
proxy_tester_connection_setup_pred(void * user_data)91 bool proxy_tester_connection_setup_pred(void *user_data) {
92     struct proxy_tester *tester = user_data;
93     return tester->wait_result || tester->client_connection;
94 }
95 
proxy_tester_connection_complete_pred(void * user_data)96 bool proxy_tester_connection_complete_pred(void *user_data) {
97     struct proxy_tester *tester = user_data;
98     return tester->client_connection_is_setup;
99 }
100 
proxy_tester_connection_shutdown_pred(void * user_data)101 bool proxy_tester_connection_shutdown_pred(void *user_data) {
102     struct proxy_tester *tester = user_data;
103     return tester->wait_result || tester->client_connection_is_shutdown;
104 }
105 
proxy_tester_request_complete_pred_fn(void * user_data)106 bool proxy_tester_request_complete_pred_fn(void *user_data) {
107     struct proxy_tester *tester = user_data;
108     return tester->request_complete || tester->client_connection_is_shutdown;
109 }
110 
proxy_tester_init(struct proxy_tester * tester,const struct proxy_tester_options * options)111 int proxy_tester_init(struct proxy_tester *tester, const struct proxy_tester_options *options) {
112     AWS_ZERO_STRUCT(*tester);
113 
114     tester->alloc = options->alloc;
115 
116     aws_http_library_init(options->alloc);
117 
118     ASSERT_SUCCESS(aws_array_list_init_dynamic(
119         &tester->testing_channels, options->alloc, 1, sizeof(struct testing_channel_bootstrap_wrapper)));
120 
121     tester->host = options->host;
122     tester->port = options->port;
123     tester->proxy_options = *options->proxy_options;
124     tester->test_mode = options->test_mode;
125     tester->failure_type = options->failure_type;
126 
127     ASSERT_SUCCESS(aws_byte_buf_init(&tester->connection_host_name, tester->alloc, 128));
128 
129     ASSERT_SUCCESS(aws_mutex_init(&tester->wait_lock));
130     ASSERT_SUCCESS(aws_condition_variable_init(&tester->wait_cvar));
131 
132     ASSERT_SUCCESS(
133         aws_array_list_init_dynamic(&tester->connect_requests, tester->alloc, 1, sizeof(struct aws_http_message *)));
134 
135     uint32_t connect_response_count = 1;
136     if (options->desired_connect_response_count > connect_response_count) {
137         connect_response_count = options->desired_connect_response_count;
138     }
139     ASSERT_SUCCESS(aws_array_list_init_dynamic(
140         &tester->desired_connect_responses, tester->alloc, connect_response_count, sizeof(struct aws_string *)));
141 
142     for (size_t i = 0; i < options->desired_connect_response_count; ++i) {
143         struct aws_byte_cursor response_cursor = options->desired_connect_responses[i];
144         struct aws_string *response = aws_string_new_from_cursor(tester->alloc, &response_cursor);
145         ASSERT_SUCCESS(aws_array_list_push_back(&tester->desired_connect_responses, &response));
146     }
147 
148     tester->event_loop_group = aws_event_loop_group_new_default(tester->alloc, 1, NULL);
149 
150     struct aws_host_resolver_default_options resolver_options = {
151         .el_group = tester->event_loop_group,
152         .max_entries = 8,
153     };
154 
155     tester->host_resolver = aws_host_resolver_new_default(tester->alloc, &resolver_options);
156 
157     struct aws_socket_options socket_options = {
158         .type = AWS_SOCKET_STREAM,
159         .domain = AWS_SOCKET_IPV4,
160         .connect_timeout_ms =
161             (uint32_t)aws_timestamp_convert(TESTER_TIMEOUT_SEC, AWS_TIMESTAMP_SECS, AWS_TIMESTAMP_MILLIS, NULL),
162     };
163 
164     struct aws_client_bootstrap_options bootstrap_options = {
165         .event_loop_group = tester->event_loop_group,
166         .host_resolver = tester->host_resolver,
167     };
168     tester->client_bootstrap = aws_client_bootstrap_new(tester->alloc, &bootstrap_options);
169     ASSERT_NOT_NULL(tester->client_bootstrap);
170 
171     bool use_tls = options->test_mode == PTTM_HTTPS_TUNNEL;
172     if (use_tls) {
173         aws_tls_ctx_options_init_default_client(&tester->tls_ctx_options, tester->alloc);
174         aws_tls_ctx_options_set_alpn_list(&tester->tls_ctx_options, "http/1.1");
175         tester->tls_ctx_options.verify_peer = false;
176 
177         tester->tls_ctx = aws_tls_client_ctx_new(tester->alloc, &tester->tls_ctx_options);
178 
179         aws_tls_connection_options_init_from_ctx(&tester->tls_connection_options, tester->tls_ctx);
180         aws_tls_connection_options_set_server_name(&tester->tls_connection_options, tester->alloc, &tester->host);
181     }
182 
183     /* Connect */
184     struct aws_http_client_connection_options client_options = AWS_HTTP_CLIENT_CONNECTION_OPTIONS_INIT;
185     client_options.allocator = tester->alloc;
186     client_options.bootstrap = tester->client_bootstrap;
187     client_options.host_name = tester->host;
188     client_options.port = tester->port;
189     client_options.socket_options = &socket_options;
190     client_options.tls_options = use_tls ? &tester->tls_connection_options : NULL;
191     client_options.user_data = tester;
192     client_options.on_setup = proxy_tester_on_client_connection_setup;
193     client_options.on_shutdown = proxy_tester_on_client_connection_shutdown;
194     if (options->proxy_options) {
195         client_options.proxy_options = options->proxy_options;
196     }
197 
198     aws_http_client_connect(&client_options);
199 
200     /* Wait for server & client connections to finish setup */
201     ASSERT_SUCCESS(proxy_tester_wait(tester, proxy_tester_connection_setup_pred));
202 
203     return AWS_OP_SUCCESS;
204 }
205 
proxy_tester_clean_up(struct proxy_tester * tester)206 int proxy_tester_clean_up(struct proxy_tester *tester) {
207     if (tester->client_connection) {
208         aws_http_connection_release(tester->client_connection);
209     }
210 
211     size_t channel_count = aws_array_list_length(&tester->testing_channels);
212     for (size_t i = 0; i < channel_count; ++i) {
213         struct testing_channel_bootstrap_wrapper wrapper;
214         aws_array_list_get_at(&tester->testing_channels, &wrapper, i);
215         struct testing_channel *channel = wrapper.channel;
216         if (channel) {
217             ASSERT_SUCCESS(testing_channel_clean_up(channel));
218             while (!testing_channel_is_shutdown_completed(channel)) {
219                 aws_thread_current_sleep(1000000000);
220             }
221 
222             aws_mem_release(tester->alloc, channel);
223         }
224     }
225 
226     ASSERT_SUCCESS(proxy_tester_wait(tester, proxy_tester_connection_shutdown_pred));
227 
228     for (size_t i = 0; i < channel_count; ++i) {
229         struct testing_channel_bootstrap_wrapper wrapper;
230         aws_array_list_get_at(&tester->testing_channels, &wrapper, i);
231         if (wrapper.bootstrap != NULL) {
232             if (channel_count == 0 && wrapper.bootstrap->user_data) {
233                 aws_http_proxy_user_data_destroy(wrapper.bootstrap->user_data);
234             }
235             if (i + 1 < channel_count) {
236                 wrapper.bootstrap->on_shutdown(tester->client_connection, 0, wrapper.bootstrap->user_data);
237             }
238 
239             aws_http_client_bootstrap_destroy(wrapper.bootstrap);
240         }
241     }
242 
243     aws_array_list_clean_up(&tester->testing_channels);
244 
245     aws_client_bootstrap_release(tester->client_bootstrap);
246 
247     aws_host_resolver_release(tester->host_resolver);
248     aws_event_loop_group_release(tester->event_loop_group);
249 
250     if (tester->tls_ctx) {
251         aws_tls_connection_options_clean_up(&tester->tls_connection_options);
252         aws_tls_ctx_release(tester->tls_ctx);
253         aws_tls_ctx_options_clean_up(&tester->tls_ctx_options);
254     }
255 
256     size_t connect_request_count = aws_array_list_length(&tester->connect_requests);
257     for (size_t i = 0; i < connect_request_count; ++i) {
258         struct aws_http_message *request = NULL;
259 
260         aws_array_list_get_at(&tester->connect_requests, &request, i);
261         aws_http_message_release(request);
262     }
263     aws_array_list_clean_up(&tester->connect_requests);
264 
265     size_t connect_response_count = aws_array_list_length(&tester->desired_connect_responses);
266     for (size_t i = 0; i < connect_response_count; ++i) {
267         struct aws_string *response = NULL;
268 
269         aws_array_list_get_at(&tester->desired_connect_responses, &response, i);
270         aws_string_destroy(response);
271     }
272     aws_array_list_clean_up(&tester->desired_connect_responses);
273 
274     aws_http_library_clean_up();
275 
276     aws_byte_buf_clean_up(&tester->connection_host_name);
277 
278     return AWS_OP_SUCCESS;
279 }
280 
s_testing_channel_shutdown_callback(int error_code,void * user_data)281 static void s_testing_channel_shutdown_callback(int error_code, void *user_data) {
282     struct proxy_tester *tester = user_data;
283 
284     if (tester->wait_result == AWS_ERROR_SUCCESS) {
285         tester->wait_result = error_code;
286     }
287 
288     struct testing_channel_bootstrap_wrapper *wrapper = s_get_current_channel_bootstrap_wrapper(tester);
289 
290     wrapper->bootstrap->on_shutdown(tester->client_connection, tester->wait_result, wrapper->bootstrap->user_data);
291 }
292 
proxy_tester_create_testing_channel_connection(struct proxy_tester * tester,struct aws_http_client_bootstrap * http_bootstrap)293 int proxy_tester_create_testing_channel_connection(
294     struct proxy_tester *tester,
295     struct aws_http_client_bootstrap *http_bootstrap) {
296 
297     struct testing_channel_bootstrap_wrapper *old_wrapper = s_get_current_channel_bootstrap_wrapper(tester);
298     if (old_wrapper != NULL) {
299         old_wrapper->channel->channel_shutdown = NULL;
300     }
301 
302     struct testing_channel *testing_channel = aws_mem_calloc(tester->alloc, 1, sizeof(struct testing_channel));
303 
304     struct aws_testing_channel_options test_channel_options = {.clock_fn = aws_high_res_clock_get_ticks};
305     ASSERT_SUCCESS(testing_channel_init(testing_channel, tester->alloc, &test_channel_options));
306     testing_channel->channel_shutdown = s_testing_channel_shutdown_callback;
307     testing_channel->channel_shutdown_user_data = tester;
308 
309     /* Use small window so that we can observe it opening in tests.
310      * Channel may wait until the window is small before issuing the increment command. */
311     struct aws_http1_connection_options http1_options;
312     AWS_ZERO_STRUCT(http1_options);
313     struct aws_http_connection *connection =
314         aws_http_connection_new_http1_1_client(tester->alloc, true, 256, &http1_options);
315     ASSERT_NOT_NULL(connection);
316 
317     connection->user_data = http_bootstrap->user_data;
318     connection->client_data = &connection->client_or_server_data.client;
319     connection->proxy_request_transform = http_bootstrap->proxy_request_transform;
320 
321     struct aws_channel_slot *slot = aws_channel_slot_new(testing_channel->channel);
322     ASSERT_NOT_NULL(slot);
323     ASSERT_SUCCESS(aws_channel_slot_insert_end(testing_channel->channel, slot));
324     ASSERT_SUCCESS(aws_channel_slot_set_handler(slot, &connection->channel_handler));
325     connection->vtable->on_channel_handler_installed(&connection->channel_handler, slot);
326     testing_channel_drain_queued_tasks(testing_channel);
327 
328     tester->client_connection = connection;
329 
330     struct testing_channel_bootstrap_wrapper wrapper;
331     wrapper.channel = testing_channel;
332     wrapper.bootstrap = http_bootstrap;
333     aws_array_list_push_back(&tester->testing_channels, &wrapper);
334 
335     return AWS_OP_SUCCESS;
336 }
337 
s_line_feed_predicate(uint8_t value)338 bool s_line_feed_predicate(uint8_t value) {
339     return value == '\r';
340 }
341 
342 /*
343  * A very crude, sloppy http request parser that does just enough to test what we want to test
344  */
s_record_connect_request(struct aws_byte_buf * request_buffer,struct proxy_tester * tester)345 static int s_record_connect_request(struct aws_byte_buf *request_buffer, struct proxy_tester *tester) {
346     struct aws_byte_cursor request_cursor = aws_byte_cursor_from_buf(request_buffer);
347 
348     struct aws_array_list lines;
349     ASSERT_SUCCESS(aws_array_list_init_dynamic(&lines, tester->alloc, 10, sizeof(struct aws_byte_cursor)));
350     aws_byte_cursor_split_on_char(&request_cursor, '\n', &lines);
351 
352     size_t line_count = aws_array_list_length(&lines);
353     ASSERT_TRUE(line_count > 1);
354 
355     struct aws_http_message *message = aws_http_message_new_request(tester->alloc);
356 
357     struct aws_byte_cursor first_line_cursor;
358     AWS_ZERO_STRUCT(first_line_cursor);
359     aws_array_list_get_at(&lines, &first_line_cursor, 0);
360     first_line_cursor = aws_byte_cursor_trim_pred(&first_line_cursor, s_line_feed_predicate);
361 
362     struct aws_byte_cursor method_cursor;
363     AWS_ZERO_STRUCT(method_cursor);
364     aws_byte_cursor_next_split(&first_line_cursor, ' ', &method_cursor);
365 
366     aws_http_message_set_request_method(message, method_cursor);
367 
368     aws_byte_cursor_advance(&first_line_cursor, method_cursor.len + 1);
369 
370     struct aws_byte_cursor uri_cursor;
371     AWS_ZERO_STRUCT(uri_cursor);
372     aws_byte_cursor_next_split(&first_line_cursor, ' ', &uri_cursor);
373 
374     aws_http_message_set_request_path(message, uri_cursor);
375 
376     for (size_t i = 1; i < line_count; ++i) {
377         struct aws_byte_cursor line_cursor;
378         AWS_ZERO_STRUCT(line_cursor);
379         aws_array_list_get_at(&lines, &line_cursor, i);
380         line_cursor = aws_byte_cursor_trim_pred(&line_cursor, s_line_feed_predicate);
381 
382         if (line_cursor.len == 0) {
383             break;
384         }
385 
386         struct aws_byte_cursor name_cursor;
387         AWS_ZERO_STRUCT(name_cursor);
388         aws_byte_cursor_next_split(&line_cursor, ':', &name_cursor);
389 
390         aws_byte_cursor_advance(&line_cursor, name_cursor.len + 1);
391         line_cursor = aws_byte_cursor_trim_pred(&line_cursor, aws_isspace);
392 
393         struct aws_http_header header = {
394             .name = name_cursor,
395             .value = line_cursor,
396         };
397 
398         aws_http_message_add_header(message, header);
399     }
400 
401     /* we don't care about the body */
402 
403     aws_array_list_push_back(&tester->connect_requests, &message);
404 
405     aws_array_list_clean_up(&lines);
406 
407     return AWS_OP_SUCCESS;
408 }
409 
proxy_tester_verify_connect_request(struct proxy_tester * tester)410 int proxy_tester_verify_connect_request(struct proxy_tester *tester) {
411     struct aws_byte_buf output;
412     ASSERT_SUCCESS(aws_byte_buf_init(&output, tester->alloc, 1024));
413 
414     struct testing_channel *testing_channel = proxy_tester_get_current_channel(tester);
415     ASSERT_NOT_NULL(testing_channel);
416 
417     ASSERT_SUCCESS(testing_channel_drain_written_messages(testing_channel, &output));
418 
419     char connect_request_buffer[1024];
420     snprintf(
421         connect_request_buffer,
422         AWS_ARRAY_SIZE(connect_request_buffer),
423         "CONNECT " PRInSTR ":%d HTTP/1.1",
424         AWS_BYTE_CURSOR_PRI(tester->host),
425         (int)tester->port);
426 
427     struct aws_byte_cursor expected_connect_message_first_line_cursor =
428         aws_byte_cursor_from_c_str(connect_request_buffer);
429     ASSERT_TRUE(output.len >= expected_connect_message_first_line_cursor.len);
430 
431     struct aws_byte_cursor request_prefix = aws_byte_cursor_from_array(output.buffer, output.len);
432     struct aws_byte_cursor first_line_cursor;
433     AWS_ZERO_STRUCT(first_line_cursor);
434     ASSERT_TRUE(aws_byte_cursor_next_split(&request_prefix, '\r', &first_line_cursor));
435 
436     ASSERT_TRUE(aws_byte_cursor_eq(&first_line_cursor, &expected_connect_message_first_line_cursor));
437 
438     ASSERT_SUCCESS(s_record_connect_request(&output, tester));
439 
440     aws_byte_buf_clean_up(&output);
441 
442     return AWS_OP_SUCCESS;
443 }
444 
proxy_tester_send_connect_response(struct proxy_tester * tester)445 int proxy_tester_send_connect_response(struct proxy_tester *tester) {
446     (void)tester;
447 
448     const char *response_string = NULL;
449 
450     size_t desired_response_count = aws_array_list_length(&tester->desired_connect_responses);
451     if (desired_response_count > 0) {
452         struct aws_string *response = NULL;
453         aws_array_list_get_at(&tester->desired_connect_responses, &response, tester->current_response_index++);
454         response_string = (const char *)response->bytes;
455 
456     } else if (tester->failure_type == PTFT_CONNECT_REQUEST) {
457         response_string = "HTTP/1.0 407 Unauthorized\r\n\r\n";
458     } else {
459         /* adding close here because it's an edge case we need to exercise. The desired behavior is that it has
460          * absolutely no effect. */
461         response_string = "HTTP/1.0 200 Connection established\r\nconnection: close\r\n\r\n";
462     }
463 
464     struct testing_channel *channel = proxy_tester_get_current_channel(tester);
465 
466     /* send response */
467     ASSERT_SUCCESS(testing_channel_push_read_str(channel, response_string));
468 
469     testing_channel_drain_queued_tasks(channel);
470 
471     return AWS_OP_SUCCESS;
472 }
473 
proxy_tester_verify_connection_attempt_was_to_proxy(struct proxy_tester * tester,struct aws_byte_cursor expected_host,uint16_t expected_port)474 int proxy_tester_verify_connection_attempt_was_to_proxy(
475     struct proxy_tester *tester,
476     struct aws_byte_cursor expected_host,
477     uint16_t expected_port) {
478     ASSERT_BIN_ARRAYS_EQUALS(
479         tester->connection_host_name.buffer,
480         tester->connection_host_name.len,
481         expected_host.ptr,
482         expected_host.len,
483         "Connection host should have been \"" PRInSTR "\", but was \"" PRInSTR "\".",
484         AWS_BYTE_CURSOR_PRI(expected_host),
485         AWS_BYTE_BUF_PRI(tester->connection_host_name));
486 
487     ASSERT_TRUE(tester->connection_port == expected_port);
488 
489     return AWS_OP_SUCCESS;
490 }
491 
proxy_tester_get_current_channel(struct proxy_tester * tester)492 struct testing_channel *proxy_tester_get_current_channel(struct proxy_tester *tester) {
493     struct testing_channel_bootstrap_wrapper *wrapper = s_get_current_channel_bootstrap_wrapper(tester);
494     if (wrapper == NULL) {
495         return NULL;
496     }
497 
498     return wrapper->channel;
499 }
500