From 641173cac7eb209c2d478044648aa840f090b7e9 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Sun, 7 Jun 2020 14:59:48 -0700 Subject: [PATCH 01/54] WIP --- include/aws/http/connection.h | 36 +++++++++++- source/proxy_connection.c | 102 ++++++++++++++++++++------------- tests/integration_test_proxy.c | 13 +++-- tests/proxy_test_helper.c | 11 +--- tests/proxy_test_helper.h | 6 +- tests/test_proxy.c | 25 ++++---- 6 files changed, 122 insertions(+), 71 deletions(-) diff --git a/include/aws/http/connection.h b/include/aws/http/connection.h index c4b62901d..54935d236 100644 --- a/include/aws/http/connection.h +++ b/include/aws/http/connection.h @@ -131,6 +131,29 @@ struct aws_http_connection_monitoring_options { uint32_t allowable_throughput_failure_interval_seconds; }; +/** + * Supported proxy connection types + */ +enum aws_http_proxy_connection_type { + /** + * Use the proxy to forward http requests. Attempting to use both this mode and TLS on the tunnel destination + * is a configuration error. + */ + AWS_HPCT_HTTP_FORWARD = 0, + + /** + * Use the proxy to establish an http connection via a CONNECT request to the proxy. Works for both plaintext and + * tls connections. + */ + AWS_HPCT_HTTP_TUNNEL, + + /** + * Establish an http(s) connection through a socks5 proxy. + * Socks5 proxies are not yet supported + */ + AWS_HPCT_SOCKS5, +}; + /** * Supported proxy authentication modes */ @@ -145,7 +168,12 @@ enum aws_http_proxy_authentication_type { struct aws_http_proxy_options { /** - * Proxy host to connect to, in lieu of actual target + * Type of proxy connection to make + */ + enum aws_http_proxy_connection_type connection_type; + + /** + * Proxy host to connect to */ struct aws_byte_cursor host; @@ -478,6 +506,12 @@ int aws_http2_connection_ping( aws_http2_on_ping_complete_fn *on_completed, void *user_data); +/** + * Checks http proxy options for correctness + */ +AWS_HTTP_API +int aws_http_options_validate_proxy_configuration(const struct aws_http_client_connection_options *options); + AWS_EXTERN_C_END #endif /* AWS_HTTP_CONNECTION_H */ diff --git a/source/proxy_connection.c b/source/proxy_connection.c index d3a0cb0f1..9b9368ab1 100644 --- a/source/proxy_connection.c +++ b/source/proxy_connection.c @@ -328,7 +328,7 @@ static struct aws_http_message *s_build_proxy_connect_request(struct aws_http_pr } struct aws_http_header host_header = {.name = aws_byte_cursor_from_string(s_host_header_name), - .value = aws_byte_cursor_from_string(user_data->original_host)}; + .value = aws_byte_cursor_from_array(path_buffer.buffer, path_buffer.len)}; if (aws_http_message_add_header(request, host_header)) { goto on_error; } @@ -366,7 +366,7 @@ static struct aws_http_message *s_build_proxy_connect_request(struct aws_http_pr /* * Headers done callback for the CONNECT request made during tls proxy connections */ -static int s_aws_http_on_incoming_header_block_done_tls_proxy( +static int s_aws_http_on_incoming_header_block_done_tunnel_proxy( struct aws_http_stream *stream, enum aws_http_header_block header_block, void *user_data) { @@ -420,7 +420,7 @@ static void s_on_origin_server_tls_negotation_result( /* * Stream done callback for the CONNECT request made during tls proxy connections */ -static void s_aws_http_on_stream_complete_tls_proxy(struct aws_http_stream *stream, int error_code, void *user_data) { +static void s_aws_http_on_stream_complete_tunnel_proxy(struct aws_http_stream *stream, int error_code, void *user_data) { struct aws_http_proxy_user_data *context = user_data; AWS_FATAL_ASSERT(stream == context->connect_stream); @@ -449,28 +449,36 @@ static void s_aws_http_on_stream_complete_tls_proxy(struct aws_http_stream *stre AWS_LOGF_INFO(AWS_LS_HTTP_CONNECTION, "(%p) Beginning TLS negotiation", (void *)context->connection); - /* - * Perform TLS negotiation to the origin server through proxy - */ - context->tls_options->on_negotiation_result = s_on_origin_server_tls_negotation_result; - - context->state = AWS_PBS_TLS_NEGOTIATION; - struct aws_channel *channel = aws_http_connection_get_channel(context->connection); - - /* - * TODO: if making secure (double TLS) proxy connection, we need to go after the second slot: - * - * Socket -> TLS(proxy) -> TLS(origin server) -> Http - */ - if (channel == NULL || s_vtable->setup_client_tls(aws_channel_get_first_slot(channel), context->tls_options)) { - AWS_LOGF_ERROR( - AWS_LS_HTTP_CONNECTION, - "(%p) Proxy connection failed to start TLS negotiation with error %d(%s)", - (void *)context->connection, - aws_last_error(), - aws_error_str(aws_last_error())); - s_aws_http_proxy_user_data_shutdown(context); - return; + if (context->tls_options != NULL) { + /* + * Perform TLS negotiation to the origin server through proxy + */ + context->tls_options->on_negotiation_result = s_on_origin_server_tls_negotation_result; + + context->state = AWS_PBS_TLS_NEGOTIATION; + struct aws_channel *channel = aws_http_connection_get_channel(context->connection); + + /* + * TODO: if making secure (double TLS) proxy connection, we need to go after the second slot: + * + * Socket -> TLS(proxy) -> TLS(origin server) -> Http + */ + if (channel == NULL || s_vtable->setup_client_tls(aws_channel_get_first_slot(channel), context->tls_options)) { + AWS_LOGF_ERROR( + AWS_LS_HTTP_CONNECTION, + "(%p) Proxy connection failed to start TLS negotiation with error %d(%s)", + (void *)context->connection, + aws_last_error(), + aws_error_str(aws_last_error())); + s_aws_http_proxy_user_data_shutdown(context); + return; + } + } else { + /* + * The tunnel has been established. + */ + context->state = AWS_PBS_SUCCESS; + context->original_on_setup(context->connection, AWS_ERROR_SUCCESS, context->original_user_data); } } @@ -490,8 +498,8 @@ static int s_make_proxy_connect_request( .self_size = sizeof(request_options), .request = request, .user_data = user_data, - .on_response_header_block_done = s_aws_http_on_incoming_header_block_done_tls_proxy, - .on_complete = s_aws_http_on_stream_complete_tls_proxy, + .on_response_header_block_done = s_aws_http_on_incoming_header_block_done_tunnel_proxy, + .on_complete = s_aws_http_on_stream_complete_tunnel_proxy, }; struct aws_http_stream *stream = aws_http_connection_make_request(connection, &request_options); @@ -524,7 +532,7 @@ static int s_make_proxy_connect_request( * Connection setup callback for tls-based proxy connections. * Could be unified with non-tls version by checking tls options and branching post-success */ -static void s_aws_http_on_client_connection_http_tls_proxy_setup_fn( +static void s_aws_http_on_client_connection_http_tunnel_proxy_setup_fn( struct aws_http_connection *connection, int error_code, void *user_data) { @@ -734,16 +742,14 @@ static int s_aws_http_client_connect_via_proxy_http(const struct aws_http_client } /* - * Top-level function to route a TLS connection through a proxy server + * Top-level function to route a connection through a proxy server via a CONNECT request */ -static int s_aws_http_client_connect_via_proxy_https(const struct aws_http_client_connection_options *options) { - - AWS_FATAL_ASSERT(options->tls_options != NULL); +static int s_aws_http_client_connect_via_proxy_http_tunnel(const struct aws_http_client_connection_options *options) { AWS_FATAL_ASSERT(options->proxy_options != NULL); AWS_LOGF_INFO( AWS_LS_HTTP_CONNECTION, - "(STATIC) Connecting to \"" PRInSTR "\" through TLS via proxy \"" PRInSTR "\"", + "(STATIC) Connecting to \"" PRInSTR "\" through a tunnel via proxy \"" PRInSTR "\"", AWS_BYTE_CURSOR_PRI(options->host_name), AWS_BYTE_CURSOR_PRI(options->proxy_options->host)); @@ -757,11 +763,10 @@ static int s_aws_http_client_connect_via_proxy_https(const struct aws_http_clien struct aws_http_client_connection_options options_copy = *options; options_copy.proxy_options = NULL; - options_copy.tls_options = NULL; options_copy.host_name = options->proxy_options->host; options_copy.port = options->proxy_options->port; options_copy.user_data = user_data; - options_copy.on_setup = s_aws_http_on_client_connection_http_tls_proxy_setup_fn; + options_copy.on_setup = s_aws_http_on_client_connection_http_tunnel_proxy_setup_fn; options_copy.on_shutdown = s_aws_http_on_client_connection_http_proxy_shutdown_fn; options_copy.tls_options = options->proxy_options->tls_options; @@ -769,7 +774,7 @@ static int s_aws_http_client_connect_via_proxy_https(const struct aws_http_clien if (result == AWS_OP_ERR) { AWS_LOGF_ERROR( AWS_LS_HTTP_CONNECTION, - "(STATIC) Proxy https connection failed client connect with error %d(%s)", + "(STATIC) Proxy tunnel connection failed client connect with error %d(%s)", aws_last_error(), aws_error_str(aws_last_error())); aws_http_proxy_user_data_destroy(user_data); @@ -782,10 +787,12 @@ static int s_aws_http_client_connect_via_proxy_https(const struct aws_http_clien * Dispatches a proxy-enabled connection request to the appropriate top-level connection function */ int aws_http_client_connect_via_proxy(const struct aws_http_client_connection_options *options) { - AWS_FATAL_ASSERT(options->proxy_options != NULL); + if (aws_http_options_validate_proxy_configuration(options)) { + return AWS_OP_ERR; + } - if (options->tls_options != NULL) { - return s_aws_http_client_connect_via_proxy_https(options); + if (options->proxy_options->connection_type == AWS_HPCT_HTTP_TUNNEL) { + return s_aws_http_client_connect_via_proxy_http_tunnel(options); } else { return s_aws_http_client_connect_via_proxy_http(options); } @@ -861,3 +868,20 @@ void aws_http_proxy_options_init_from_config( options->port = config->port; options->tls_options = config->tls_options; } + +int aws_http_options_validate_proxy_configuration(const struct aws_http_client_connection_options *options) { + if (options == NULL || options->proxy_options == NULL) { + return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + } + + enum aws_http_proxy_connection_type proxy_type = options->proxy_options->connection_type; + if (proxy_type == AWS_HPCT_SOCKS5) { + return aws_raise_error(AWS_ERROR_UNIMPLEMENTED); + } + + if (proxy_type == AWS_HPCT_HTTP_FORWARD && options->tls_options != NULL) { + return aws_raise_error(AWS_ERROR_INVALID_STATE); + } + + return AWS_OP_SUCCESS; +} diff --git a/tests/integration_test_proxy.c b/tests/integration_test_proxy.c index 9463c69a1..4be7a8f15 100644 --- a/tests/integration_test_proxy.c +++ b/tests/integration_test_proxy.c @@ -91,6 +91,7 @@ static int s_setup_proxy_test( struct aws_byte_cursor host, enum proxy_tester_test_mode test_mode) { struct aws_http_proxy_options proxy_options = { + .connection_type = test_mode == PTTM_HTTP_FORWARD ? AWS_HPCT_HTTP_FORWARD : AWS_HPCT_HTTP_TUNNEL, .host = aws_byte_cursor_from_c_str("127.0.0.1"), .port = 3128, }; @@ -99,7 +100,7 @@ static int s_setup_proxy_test( .alloc = allocator, .proxy_options = &proxy_options, .host = host, - .port = test_mode == PTTM_HTTP ? 80 : 443, + .port = test_mode == PTTM_HTTPS_TUNNEL ? 443 : 80, .test_mode = test_mode, .failure_type = PTFT_NONE, }; @@ -165,7 +166,7 @@ static int s_do_proxy_request_test( static int s_test_http_proxy_connection_new_destroy(struct aws_allocator *allocator, void *ctx) { (void)ctx; - ASSERT_SUCCESS(s_setup_proxy_test(allocator, aws_byte_cursor_from_c_str("example.org"), PTTM_HTTP)); + ASSERT_SUCCESS(s_setup_proxy_test(allocator, aws_byte_cursor_from_c_str("example.org"), PTTM_HTTP_FORWARD)); ASSERT_TRUE(tester.wait_result == AWS_ERROR_SUCCESS); ASSERT_SUCCESS(proxy_tester_clean_up(&tester)); @@ -180,7 +181,7 @@ static int s_test_http_proxy_connection_get(struct aws_allocator *allocator, voi return s_do_proxy_request_test( allocator, aws_byte_cursor_from_c_str("example.org"), - PTTM_HTTP, + PTTM_HTTP_FORWARD, aws_byte_cursor_from_c_str("GET"), aws_byte_cursor_from_c_str("/")); } @@ -189,7 +190,7 @@ AWS_TEST_CASE(test_http_proxy_connection_get, s_test_http_proxy_connection_get); static int s_test_https_proxy_connection_new_destroy(struct aws_allocator *allocator, void *ctx) { (void)ctx; - ASSERT_SUCCESS(s_setup_proxy_test(allocator, aws_byte_cursor_from_c_str("aws.amazon.com"), PTTM_HTTPS)); + ASSERT_SUCCESS(s_setup_proxy_test(allocator, aws_byte_cursor_from_c_str("aws.amazon.com"), PTTM_HTTPS_TUNNEL)); ASSERT_TRUE(tester.wait_result == AWS_ERROR_SUCCESS); ASSERT_SUCCESS(proxy_tester_clean_up(&tester)); @@ -204,7 +205,7 @@ static int s_test_https_proxy_connection_get(struct aws_allocator *allocator, vo return s_do_proxy_request_test( allocator, aws_byte_cursor_from_c_str("aws.amazon.com"), - PTTM_HTTPS, + PTTM_HTTPS_TUNNEL, aws_byte_cursor_from_c_str("GET"), aws_byte_cursor_from_c_str("/")); } @@ -216,7 +217,7 @@ static int s_test_http_proxy_connection_options_star(struct aws_allocator *alloc return s_do_proxy_request_test( allocator, aws_byte_cursor_from_c_str("example.org"), - PTTM_HTTP, + PTTM_HTTP_FORWARD, aws_byte_cursor_from_c_str("OPTIONS"), aws_byte_cursor_from_c_str("*")); } diff --git a/tests/proxy_test_helper.c b/tests/proxy_test_helper.c index 345ccec96..823551d43 100644 --- a/tests/proxy_test_helper.c +++ b/tests/proxy_test_helper.c @@ -125,14 +125,6 @@ int proxy_tester_init(struct proxy_tester *tester, const struct proxy_tester_opt ASSERT_SUCCESS(aws_byte_buf_init(&tester->connection_host_name, tester->alloc, 128)); - struct aws_logger_standard_options logger_options = { - .level = AWS_LOG_LEVEL_TRACE, - .file = stderr, - }; - - ASSERT_SUCCESS(aws_logger_init_standard(&tester->logger, tester->alloc, &logger_options)); - aws_logger_set(&tester->logger); - ASSERT_SUCCESS(aws_mutex_init(&tester->wait_lock)); ASSERT_SUCCESS(aws_condition_variable_init(&tester->wait_cvar)); @@ -155,7 +147,7 @@ int proxy_tester_init(struct proxy_tester *tester, const struct proxy_tester_opt tester->client_bootstrap = aws_client_bootstrap_new(tester->alloc, &bootstrap_options); ASSERT_NOT_NULL(tester->client_bootstrap); - bool use_tls = options->test_mode == PTTM_HTTPS; + bool use_tls = options->test_mode == PTTM_HTTPS_TUNNEL; if (use_tls) { aws_tls_ctx_options_init_default_client(&tester->tls_ctx_options, tester->alloc); aws_tls_ctx_options_set_alpn_list(&tester->tls_ctx_options, "http/1.1"); @@ -228,7 +220,6 @@ int proxy_tester_clean_up(struct proxy_tester *tester) { } aws_http_library_clean_up(); - aws_logger_clean_up(&tester->logger); aws_byte_buf_clean_up(&tester->connection_host_name); diff --git a/tests/proxy_test_helper.h b/tests/proxy_test_helper.h index ec70738da..be1a14004 100644 --- a/tests/proxy_test_helper.h +++ b/tests/proxy_test_helper.h @@ -29,8 +29,9 @@ struct testing_channel; typedef void(aws_http_release_connection_fn)(struct aws_http_connection *connection); enum proxy_tester_test_mode { - PTTM_HTTP = 0, - PTTM_HTTPS, + PTTM_HTTP_FORWARD = 0, + PTTM_HTTP_TUNNEL, + PTTM_HTTPS_TUNNEL, }; enum proxy_tester_failure_type { @@ -52,7 +53,6 @@ struct proxy_tester_options { struct proxy_tester { struct aws_allocator *alloc; - struct aws_logger logger; struct aws_event_loop_group event_loop_group; struct aws_host_resolver host_resolver; struct aws_client_bootstrap *client_bootstrap; diff --git a/tests/test_proxy.c b/tests/test_proxy.c index 92aa64141..1b507f033 100644 --- a/tests/test_proxy.c +++ b/tests/test_proxy.c @@ -163,8 +163,8 @@ static int s_test_aws_proxy_new_socket_channel(struct aws_socket_channel_bootstr testing_channel_run_currently_queued_tasks(tester.testing_channel); - if (tester.test_mode == PTTM_HTTPS) { - /* For TLS proxies, send the CONNECT request and response */ + if (tester.proxy_options->connection_type == AWS_HPCT_HTTP_TUNNEL) { + /* For tunnel proxies, send the CONNECT request and response */ ASSERT_SUCCESS(proxy_tester_verify_connect_request(&tester)); ASSERT_SUCCESS(proxy_tester_send_connect_response(&tester)); } @@ -191,6 +191,7 @@ static int s_setup_proxy_test( aws_http_proxy_system_set_vtable(&s_proxy_table_for_tls); struct aws_http_proxy_options proxy_options = { + .connection_type = (test_mode == PTTM_HTTP_FORWARD) ? AWS_HPCT_HTTP_FORWARD : AWS_HPCT_HTTP_TUNNEL, .host = aws_byte_cursor_from_c_str(s_proxy_host_name), .port = s_proxy_port, }; @@ -218,13 +219,13 @@ static int s_setup_proxy_test( } /* - * For plaintext proxy connections: + * For forwarding proxy connections: * If we do pass in proxy options, verify we try and connect to the proxy */ static int s_test_http_proxy_connection_proxy_target(struct aws_allocator *allocator, void *ctx) { (void)ctx; - ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTP, PTFT_NONE, AWS_HPAT_NONE)); + ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTP_FORWARD, PTFT_NONE, AWS_HPAT_NONE)); ASSERT_SUCCESS(proxy_tester_verify_connection_attempt_was_to_proxy( &tester, aws_byte_cursor_from_c_str(s_proxy_host_name), s_proxy_port)); @@ -236,13 +237,13 @@ static int s_test_http_proxy_connection_proxy_target(struct aws_allocator *alloc AWS_TEST_CASE(test_http_proxy_connection_proxy_target, s_test_http_proxy_connection_proxy_target); /* - * For plaintext proxy connections: + * For forwarding proxy connections: * Verify a channel creation failure cleans up properly */ static int s_test_http_proxy_connection_channel_failure(struct aws_allocator *allocator, void *ctx) { (void)ctx; - ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTP, PTFT_CHANNEL, AWS_HPAT_NONE)); + ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTP_FORWARD, PTFT_CHANNEL, AWS_HPAT_NONE)); ASSERT_SUCCESS(proxy_tester_verify_connection_attempt_was_to_proxy( &tester, aws_byte_cursor_from_c_str(s_proxy_host_name), s_proxy_port)); @@ -256,13 +257,13 @@ static int s_test_http_proxy_connection_channel_failure(struct aws_allocator *al AWS_TEST_CASE(test_http_proxy_connection_channel_failure, s_test_http_proxy_connection_channel_failure); /* - * For plaintext proxy connections: + * For forwarding proxy connections: * Verify a connection establishment failure cleans up properly */ static int s_test_http_proxy_connection_connect_failure(struct aws_allocator *allocator, void *ctx) { (void)ctx; - ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTP, PTFT_CONNECTION, AWS_HPAT_NONE)); + ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTP_FORWARD, PTFT_CONNECTION, AWS_HPAT_NONE)); ASSERT_SUCCESS(proxy_tester_verify_connection_attempt_was_to_proxy( &tester, aws_byte_cursor_from_c_str(s_proxy_host_name), s_proxy_port)); @@ -282,7 +283,7 @@ AWS_TEST_CASE(test_http_proxy_connection_connect_failure, s_test_http_proxy_conn static int s_test_https_proxy_connection_success(struct aws_allocator *allocator, void *ctx) { (void)ctx; - ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTPS, PTFT_NONE, AWS_HPAT_NONE)); + ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTPS_TUNNEL, PTFT_NONE, AWS_HPAT_NONE)); ASSERT_SUCCESS(proxy_tester_verify_connection_attempt_was_to_proxy( &tester, aws_byte_cursor_from_c_str(s_proxy_host_name), s_proxy_port)); @@ -302,7 +303,7 @@ AWS_TEST_CASE(test_https_proxy_connection_success, s_test_https_proxy_connection static int s_test_https_proxy_connection_failure_connect(struct aws_allocator *allocator, void *ctx) { (void)ctx; - ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTPS, PTFT_CONNECT_REQUEST, AWS_HPAT_NONE)); + ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTPS_TUNNEL, PTFT_CONNECT_REQUEST, AWS_HPAT_NONE)); ASSERT_SUCCESS(proxy_tester_verify_connection_attempt_was_to_proxy( &tester, aws_byte_cursor_from_c_str(s_proxy_host_name), s_proxy_port)); @@ -322,7 +323,7 @@ AWS_TEST_CASE(test_https_proxy_connection_failure_connect, s_test_https_proxy_co static int s_test_https_proxy_connection_failure_tls(struct aws_allocator *allocator, void *ctx) { (void)ctx; - ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTPS, PTFT_TLS_NEGOTIATION, AWS_HPAT_NONE)); + ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTPS_TUNNEL, PTFT_TLS_NEGOTIATION, AWS_HPAT_NONE)); ASSERT_SUCCESS(proxy_tester_verify_connection_attempt_was_to_proxy( &tester, aws_byte_cursor_from_c_str(s_proxy_host_name), s_proxy_port)); @@ -396,7 +397,7 @@ static int s_verify_transformed_request( static int s_do_http_proxy_request_transform_test(struct aws_allocator *allocator, bool use_basic_auth) { ASSERT_SUCCESS( - s_setup_proxy_test(allocator, PTTM_HTTP, PTFT_NONE, use_basic_auth ? AWS_HPAT_BASIC : AWS_HPAT_NONE)); + s_setup_proxy_test(allocator, PTTM_HTTP_FORWARD, PTFT_NONE, use_basic_auth ? AWS_HPAT_BASIC : AWS_HPAT_NONE)); struct aws_http_message *untransformed_request = s_build_http_request(allocator); struct aws_http_message *request = s_build_http_request(allocator); From 7f5e43efb81308f1fdedc8a4a329bbce99481327 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Sun, 7 Jun 2020 15:19:32 -0700 Subject: [PATCH 02/54] Initial version; missing endpoint for live integration test --- source/proxy_connection.c | 5 +- tests/CMakeLists.txt | 22 +++++---- tests/test_proxy.c | 100 ++++++++++++++++++++++++++++---------- 3 files changed, 90 insertions(+), 37 deletions(-) diff --git a/source/proxy_connection.c b/source/proxy_connection.c index 9b9368ab1..e7f2323cc 100644 --- a/source/proxy_connection.c +++ b/source/proxy_connection.c @@ -420,7 +420,10 @@ static void s_on_origin_server_tls_negotation_result( /* * Stream done callback for the CONNECT request made during tls proxy connections */ -static void s_aws_http_on_stream_complete_tunnel_proxy(struct aws_http_stream *stream, int error_code, void *user_data) { +static void s_aws_http_on_stream_complete_tunnel_proxy( + struct aws_http_stream *stream, + int error_code, + void *user_data) { struct aws_http_proxy_user_data *context = user_data; AWS_FATAL_ASSERT(stream == context->connect_stream); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index cdbcd32ed..2f8ff05cf 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -472,16 +472,18 @@ add_test_case(h1_server_error_from_outgoing_body_callback_stops_sending) add_test_case(h1_server_close_from_off_thread_makes_not_open) add_test_case(h1_server_close_from_on_thread_makes_not_open) -add_test_case(test_http_proxy_connection_proxy_target) -add_test_case(test_http_proxy_connection_channel_failure) -add_test_case(test_http_proxy_connection_connect_failure) -add_test_case(test_https_proxy_connection_success) -add_test_case(test_https_proxy_connection_failure_connect) -add_test_case(test_https_proxy_connection_failure_tls) -add_test_case(test_http_proxy_request_transform) -add_test_case(test_http_proxy_request_transform_basic_auth) -add_test_case(test_http_proxy_uri_rewrite) -add_test_case(test_http_proxy_uri_rewrite_options_star) +add_test_case(test_http_forwarding_proxy_connection_proxy_target) +add_test_case(test_http_forwarding_proxy_connection_channel_failure) +add_test_case(test_http_forwarding_proxy_connection_connect_failure) +add_test_case(test_http_forwarding_proxy_request_transform) +add_test_case(test_http_forwarding_proxy_request_transform_basic_auth) +add_test_case(test_http_forwarding_proxy_uri_rewrite) +add_test_case(test_http_forwarding_proxy_uri_rewrite_options_star) +add_test_case(test_http_tunnel_proxy_connection_success) +add_test_case(test_https_tunnel_proxy_connection_success) +add_test_case(test_http_tunnel_proxy_connection_failure_connect) +add_test_case(test_https_tunnel_proxy_connection_failure_connect) +add_test_case(test_https_tunnel_proxy_connection_failure_tls) # integration tests that require a locally-installed proxy server if (ENABLE_PROXY_INTEGRATION_TESTS) diff --git a/tests/test_proxy.c b/tests/test_proxy.c index 1b507f033..1beda4f4d 100644 --- a/tests/test_proxy.c +++ b/tests/test_proxy.c @@ -222,7 +222,7 @@ static int s_setup_proxy_test( * For forwarding proxy connections: * If we do pass in proxy options, verify we try and connect to the proxy */ -static int s_test_http_proxy_connection_proxy_target(struct aws_allocator *allocator, void *ctx) { +static int s_test_http_forwarding_proxy_connection_proxy_target(struct aws_allocator *allocator, void *ctx) { (void)ctx; ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTP_FORWARD, PTFT_NONE, AWS_HPAT_NONE)); @@ -234,13 +234,13 @@ static int s_test_http_proxy_connection_proxy_target(struct aws_allocator *alloc return AWS_OP_SUCCESS; } -AWS_TEST_CASE(test_http_proxy_connection_proxy_target, s_test_http_proxy_connection_proxy_target); +AWS_TEST_CASE(test_http_forwarding_proxy_connection_proxy_target, s_test_http_forwarding_proxy_connection_proxy_target); /* * For forwarding proxy connections: * Verify a channel creation failure cleans up properly */ -static int s_test_http_proxy_connection_channel_failure(struct aws_allocator *allocator, void *ctx) { +static int s_test_http_forwarding_proxy_connection_channel_failure(struct aws_allocator *allocator, void *ctx) { (void)ctx; ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTP_FORWARD, PTFT_CHANNEL, AWS_HPAT_NONE)); @@ -254,13 +254,15 @@ static int s_test_http_proxy_connection_channel_failure(struct aws_allocator *al return AWS_OP_SUCCESS; } -AWS_TEST_CASE(test_http_proxy_connection_channel_failure, s_test_http_proxy_connection_channel_failure); +AWS_TEST_CASE( + test_http_forwarding_proxy_connection_channel_failure, + s_test_http_forwarding_proxy_connection_channel_failure); /* * For forwarding proxy connections: * Verify a connection establishment failure cleans up properly */ -static int s_test_http_proxy_connection_connect_failure(struct aws_allocator *allocator, void *ctx) { +static int s_test_http_forwarding_proxy_connection_connect_failure(struct aws_allocator *allocator, void *ctx) { (void)ctx; ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTP_FORWARD, PTFT_CONNECTION, AWS_HPAT_NONE)); @@ -274,13 +276,15 @@ static int s_test_http_proxy_connection_connect_failure(struct aws_allocator *al return AWS_OP_SUCCESS; } -AWS_TEST_CASE(test_http_proxy_connection_connect_failure, s_test_http_proxy_connection_connect_failure); +AWS_TEST_CASE( + test_http_forwarding_proxy_connection_connect_failure, + s_test_http_forwarding_proxy_connection_connect_failure); /* - * For tls-enabled proxy connections: + * For tls-enabled tunneling proxy connections: * Test the happy path by verifying CONNECT request, tls upgrade attempt */ -static int s_test_https_proxy_connection_success(struct aws_allocator *allocator, void *ctx) { +static int s_test_https_tunnel_proxy_connection_success(struct aws_allocator *allocator, void *ctx) { (void)ctx; ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTPS_TUNNEL, PTFT_NONE, AWS_HPAT_NONE)); @@ -294,13 +298,33 @@ static int s_test_https_proxy_connection_success(struct aws_allocator *allocator return AWS_OP_SUCCESS; } -AWS_TEST_CASE(test_https_proxy_connection_success, s_test_https_proxy_connection_success); +AWS_TEST_CASE(test_https_tunnel_proxy_connection_success, s_test_https_tunnel_proxy_connection_success); /* - * For tls-enabled proxy connections: + * For plaintext tunneling proxy connections: + * Test the happy path by verifying CONNECT request + */ +static int s_test_http_tunnel_proxy_connection_success(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTP_TUNNEL, PTFT_NONE, AWS_HPAT_NONE)); + + ASSERT_SUCCESS(proxy_tester_verify_connection_attempt_was_to_proxy( + &tester, aws_byte_cursor_from_c_str(s_proxy_host_name), s_proxy_port)); + ASSERT_TRUE(tester.client_connection != NULL); + ASSERT_TRUE(tester.wait_result == AWS_ERROR_SUCCESS); + + ASSERT_SUCCESS(proxy_tester_clean_up(&tester)); + + return AWS_OP_SUCCESS; +} +AWS_TEST_CASE(test_http_tunnel_proxy_connection_success, s_test_http_tunnel_proxy_connection_success); + +/* + * For tls-enabled tunneling proxy connections: * If the CONNECT request fails, verify error propagation and cleanup */ -static int s_test_https_proxy_connection_failure_connect(struct aws_allocator *allocator, void *ctx) { +static int s_test_https_tunnel_proxy_connection_failure_connect(struct aws_allocator *allocator, void *ctx) { (void)ctx; ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTPS_TUNNEL, PTFT_CONNECT_REQUEST, AWS_HPAT_NONE)); @@ -314,13 +338,33 @@ static int s_test_https_proxy_connection_failure_connect(struct aws_allocator *a return AWS_OP_SUCCESS; } -AWS_TEST_CASE(test_https_proxy_connection_failure_connect, s_test_https_proxy_connection_failure_connect); +AWS_TEST_CASE(test_https_tunnel_proxy_connection_failure_connect, s_test_https_tunnel_proxy_connection_failure_connect); + +/* + * For plaintext tunneling proxy connections: + * If the CONNECT request fails, verify error propagation and cleanup + */ +static int s_test_http_tunnel_proxy_connection_failure_connect(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTP_TUNNEL, PTFT_CONNECT_REQUEST, AWS_HPAT_NONE)); + + ASSERT_SUCCESS(proxy_tester_verify_connection_attempt_was_to_proxy( + &tester, aws_byte_cursor_from_c_str(s_proxy_host_name), s_proxy_port)); + ASSERT_TRUE(tester.client_connection == NULL); + ASSERT_TRUE(tester.wait_result != AWS_ERROR_SUCCESS); + + ASSERT_SUCCESS(proxy_tester_clean_up(&tester)); + + return AWS_OP_SUCCESS; +} +AWS_TEST_CASE(test_http_tunnel_proxy_connection_failure_connect, s_test_http_tunnel_proxy_connection_failure_connect); /* - * For tls-enabled proxy connections: + * For tls-enabled tunneling proxy connections: * If the TLS upgrade fails, verify error propagation and cleanup */ -static int s_test_https_proxy_connection_failure_tls(struct aws_allocator *allocator, void *ctx) { +static int s_test_https_tunnel_proxy_connection_failure_tls(struct aws_allocator *allocator, void *ctx) { (void)ctx; ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTPS_TUNNEL, PTFT_TLS_NEGOTIATION, AWS_HPAT_NONE)); @@ -334,7 +378,7 @@ static int s_test_https_proxy_connection_failure_tls(struct aws_allocator *alloc return AWS_OP_SUCCESS; } -AWS_TEST_CASE(test_https_proxy_connection_failure_tls, s_test_https_proxy_connection_failure_tls); +AWS_TEST_CASE(test_https_tunnel_proxy_connection_failure_tls, s_test_https_tunnel_proxy_connection_failure_tls); static int s_verify_transformed_request( struct aws_http_message *untransformed_request, @@ -394,7 +438,7 @@ static int s_verify_transformed_request( return AWS_OP_SUCCESS; } -static int s_do_http_proxy_request_transform_test(struct aws_allocator *allocator, bool use_basic_auth) { +static int s_do_http_forwarding_proxy_request_transform_test(struct aws_allocator *allocator, bool use_basic_auth) { ASSERT_SUCCESS( s_setup_proxy_test(allocator, PTTM_HTTP_FORWARD, PTFT_NONE, use_basic_auth ? AWS_HPAT_BASIC : AWS_HPAT_NONE)); @@ -430,26 +474,28 @@ static int s_do_http_proxy_request_transform_test(struct aws_allocator *allocato /* * If we do pass in proxy options, verify requests get properly transformed */ -static int s_test_http_proxy_request_transform(struct aws_allocator *allocator, void *ctx) { +static int s_test_http_forwarding_proxy_request_transform(struct aws_allocator *allocator, void *ctx) { (void)ctx; - ASSERT_SUCCESS(s_do_http_proxy_request_transform_test(allocator, false)); + ASSERT_SUCCESS(s_do_http_forwarding_proxy_request_transform_test(allocator, false)); return AWS_OP_SUCCESS; } -AWS_TEST_CASE(test_http_proxy_request_transform, s_test_http_proxy_request_transform); +AWS_TEST_CASE(test_http_forwarding_proxy_request_transform, s_test_http_forwarding_proxy_request_transform); /* * If we do pass in proxy options, verify requests get properly transformed with basic authentication */ -static int s_test_http_proxy_request_transform_basic_auth(struct aws_allocator *allocator, void *ctx) { +static int s_test_http_forwarding_proxy_request_transform_basic_auth(struct aws_allocator *allocator, void *ctx) { (void)ctx; - ASSERT_SUCCESS(s_do_http_proxy_request_transform_test(allocator, true)); + ASSERT_SUCCESS(s_do_http_forwarding_proxy_request_transform_test(allocator, true)); return AWS_OP_SUCCESS; } -AWS_TEST_CASE(test_http_proxy_request_transform_basic_auth, s_test_http_proxy_request_transform_basic_auth); +AWS_TEST_CASE( + test_http_forwarding_proxy_request_transform_basic_auth, + s_test_http_forwarding_proxy_request_transform_basic_auth); AWS_STATIC_STRING_FROM_LITERAL(s_rewrite_host, "www.uri.com"); AWS_STATIC_STRING_FROM_LITERAL(s_rewrite_path, "/main/index.html?foo=bar"); @@ -501,7 +547,7 @@ static int s_do_request_rewrite_test( return AWS_OP_SUCCESS; } -static int s_test_http_proxy_uri_rewrite(struct aws_allocator *allocator, void *ctx) { +static int s_test_http_forwarding_proxy_uri_rewrite(struct aws_allocator *allocator, void *ctx) { (void)ctx; ASSERT_SUCCESS(s_do_request_rewrite_test( @@ -509,13 +555,13 @@ static int s_test_http_proxy_uri_rewrite(struct aws_allocator *allocator, void * return AWS_OP_SUCCESS; } -AWS_TEST_CASE(test_http_proxy_uri_rewrite, s_test_http_proxy_uri_rewrite); +AWS_TEST_CASE(test_http_forwarding_proxy_uri_rewrite, s_test_http_forwarding_proxy_uri_rewrite); AWS_STATIC_STRING_FROM_LITERAL(s_options_request_method, "OPTIONS"); AWS_STATIC_STRING_FROM_LITERAL(s_options_star_path, "*"); AWS_STATIC_STRING_FROM_LITERAL(s_expected_rewritten_options_path, "http://www.uri.com:80"); -static int s_test_http_proxy_uri_rewrite_options_star(struct aws_allocator *allocator, void *ctx) { +static int s_test_http_forwarding_proxy_uri_rewrite_options_star(struct aws_allocator *allocator, void *ctx) { (void)ctx; ASSERT_SUCCESS(s_do_request_rewrite_test( @@ -523,4 +569,6 @@ static int s_test_http_proxy_uri_rewrite_options_star(struct aws_allocator *allo return AWS_OP_SUCCESS; } -AWS_TEST_CASE(test_http_proxy_uri_rewrite_options_star, s_test_http_proxy_uri_rewrite_options_star); +AWS_TEST_CASE( + test_http_forwarding_proxy_uri_rewrite_options_star, + s_test_http_forwarding_proxy_uri_rewrite_options_star); From a156edeac8937ee2a84b372dd4f1cd00eda32dbc Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Sun, 7 Jun 2020 17:25:19 -0700 Subject: [PATCH 03/54] Double tls theoretical support; need a test case --- source/proxy_connection.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/source/proxy_connection.c b/source/proxy_connection.c index e7f2323cc..56b2bbb78 100644 --- a/source/proxy_connection.c +++ b/source/proxy_connection.c @@ -461,12 +461,17 @@ static void s_aws_http_on_stream_complete_tunnel_proxy( context->state = AWS_PBS_TLS_NEGOTIATION; struct aws_channel *channel = aws_http_connection_get_channel(context->connection); - /* - * TODO: if making secure (double TLS) proxy connection, we need to go after the second slot: - * - * Socket -> TLS(proxy) -> TLS(origin server) -> Http - */ - if (channel == NULL || s_vtable->setup_client_tls(aws_channel_get_first_slot(channel), context->tls_options)) { + struct aws_channel_slot *left_of_tls_slot = aws_channel_get_first_slot(channel); + if (context->proxy_config->tls_options != NULL) { + /* + * If making secure (double TLS) proxy connection, we need to go after the second slot: + * + * Socket -> TLS(proxy) -> TLS(origin server) -> Http + */ + left_of_tls_slot = left_of_tls_slot->adj_right; + } + + if (channel == NULL || s_vtable->setup_client_tls(left_of_tls_slot, context->tls_options)) { AWS_LOGF_ERROR( AWS_LS_HTTP_CONNECTION, "(%p) Proxy connection failed to start TLS negotiation with error %d(%s)", From 093c2f4a1394a1ce212e3c30a7ce278e9101c6b9 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Wed, 2 Dec 2020 13:19:10 -0800 Subject: [PATCH 04/54] Proxy request flow interface/contract --- include/aws/http/connection.h | 25 +---- include/aws/http/proxy_request_flow.h | 129 ++++++++++++++++++++++++++ 2 files changed, 131 insertions(+), 23 deletions(-) create mode 100644 include/aws/http/proxy_request_flow.h diff --git a/include/aws/http/connection.h b/include/aws/http/connection.h index 2fb30ad4d..1f2e82bcd 100644 --- a/include/aws/http/connection.h +++ b/include/aws/http/connection.h @@ -144,13 +144,7 @@ enum aws_http_proxy_connection_type { AWS_HPCT_SOCKS5, }; -/** - * Supported proxy authentication modes - */ -enum aws_http_proxy_authentication_type { - AWS_HPAT_NONE = 0, - AWS_HPAT_BASIC, -}; +struct aws_proxy_request_flow; /** * Options for http proxy server usage @@ -179,22 +173,7 @@ struct aws_http_proxy_options { */ const struct aws_tls_connection_options *tls_options; - /** - * What type of proxy authentication to use, if any - */ - enum aws_http_proxy_authentication_type auth_type; - - /** - * Optional - * User name to use for authentication, basic only - */ - struct aws_byte_cursor auth_username; - - /** - * Optional - * Password to use for authentication, basic only - */ - struct aws_byte_cursor auth_password; + struct aws_proxy_request_flow *request_flow; }; /** diff --git a/include/aws/http/proxy_request_flow.h b/include/aws/http/proxy_request_flow.h new file mode 100644 index 000000000..9f404d61d --- /dev/null +++ b/include/aws/http/proxy_request_flow.h @@ -0,0 +1,129 @@ +#ifndef AWS_PROXY_REQUEST_FLOW_H +#define AWS_PROXY_REQUEST_FLOW_H + +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include +#include +#include + +struct aws_http_message; +struct aws_http_header; + +/** + * The proxy request transform callback implementation *MUST* call one of these two functions for every possible + * (async) execution pathway. + */ + +/** + * Custom request flow logic must call this function to indicate an unsuccessful outcome + */ +typedef void(aws_proxy_request_flow_terminate_fn)(int error_code, void *internal_proxy_user_data); + +/** + * Custom request flow logic must call this function to forward the potentially-mutated request back to the proxy + * flow coordination logic. + */ +typedef void(aws_proxy_request_flow_forward_fn)(struct aws_http_message *message, void *internal_proxy_user_data); + +/** + * Wrapper for the proxy flow callback functions that the user must use to continue or terminate the proxy + * request flow. + */ +struct aws_proxy_flow_callback_function_table { + aws_proxy_request_flow_terminate_fn *terminate_fn; + aws_proxy_request_flow_forward_fn *forward_fn; +}; + +/** + * User-supplied transform callback which implements the proxy request flow and ultimately, across all execution + * pathways, invokes either the terminate function or the forward function appropriately. + * + * For tunneling proxy connections, this request flow transform only applies to the CONNECT stage of proxy + * connection establishment. + * + * For forwarding proxy connections, this request flow transform applies to every single http request that goes + * out on the connection. + * + */ +typedef void(aws_proxy_request_transform_fn)( + struct aws_http_message *message, + struct aws_proxy_flow_callback_function_table flow_callback_table, + void *flow_user_data, + void *internal_proxy_user_data); + +/** + * Tunneling proxy connections only. A callback that lets the custom request flow examine the headers in the + * response to the most recent CONNECT request as they arrive. + */ +typedef int(aws_http_proxy_connect_on_incoming_headers_fn)( + enum aws_http_header_block header_block, + const struct aws_http_header *header_array, + size_t num_headers, + void *user_data); + +/** + * Tunneling proxy connections only. A callback that lets the custom request flow examine the status code of the + * response to the CONNECT request. + */ +typedef int(aws_http_proxy_connect_status_fn)(enum aws_http_status_code status_code, void *user_data); + +/** + * Tunneling proxy connections only. A callback that lets the custom request flow examine the body of the response + * to the CONNECT request. + */ +typedef int(aws_http_proxy_connect_on_incoming_body_fn)(const struct aws_byte_cursor *data, void *user_data); + +/** + * User-supplied destructor for the user_data associated with this custom request flow. A standard pattern is to + * make the user data a structure that also embeds the aws_proxy_request_flow as a member, letting the destructor + * clean up everything in a single shot. + */ +typedef void(aws_http_proxy_request_flow_user_data_destroy_fn)(void *user_data); + +/* + * Configuration definition of a custom proxy request flow, containing a transform for requests (CONNECT-only for + * tunneling proxy connections) and CONNECT response handling callbacks. + * + * A custom request flow works differently based on what kind of proxy connection is being asked for: + * + * (1) Tunneling - In a tunneling proxy connection, the request_transform is invoked on every CONNECT request, and + * nothing more. The request_transform implementation *MUST*, in turn, call one of terminate or forward functions + * from the supplied flow_callback_table. + * + * Every CONNECT request, if a response is obtained, will properly invoke the response handling callbacks supplied + * in the proxy request flow. + * + * (2) Forwarding - In a tunneling proxy connection, the request_transform is invoked on every request sent out + * on the connection. The response handling callbacks are unused. + */ +struct aws_proxy_request_flow { + + /* + * Required. A custom proxy request flow is nonsensical without doing something here. + */ + aws_proxy_request_transform_fn *request_transform; + + /* + * Tunnel-only response handling callbacks that let the request flow process the response to a CONNECT request. + */ + aws_http_proxy_connect_on_incoming_headers_fn *on_incoming_headers_callback; + aws_http_proxy_connect_status_fn *on_status_callback; + aws_http_proxy_connect_on_incoming_body_fn *on_incoming_body_callback; + + aws_http_proxy_request_flow_user_data_destroy_fn *destroy_fn; + void *user_data; +}; + +AWS_EXTERN_C_BEGIN + +struct aws_proxy_request_flow *aws_proxy_request_flow_new_basic_auth( + struct aws_byte_cursor user_name, + struct aws_byte_cursor password); + +AWS_EXTERN_C_END + +#endif /* AWS_PROXY_REQUEST_FLOW_H */ From 14ca5bda80d7148f8c2ad95adfd1c3de6e9bcfec Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Wed, 2 Dec 2020 13:24:42 -0800 Subject: [PATCH 05/54] Doc addition --- include/aws/http/proxy_request_flow.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/aws/http/proxy_request_flow.h b/include/aws/http/proxy_request_flow.h index 9f404d61d..b019e28b6 100644 --- a/include/aws/http/proxy_request_flow.h +++ b/include/aws/http/proxy_request_flow.h @@ -120,6 +120,14 @@ struct aws_proxy_request_flow { AWS_EXTERN_C_BEGIN +/** + * A constructor for a simple proxy request flow that performs basic authentication by adding the appropriate + * header and header value to requests. + * + * @param user_name user name to use in basic authentication + * @param password password to use in basic authentication + * @return a new proxy request flow if successfully constructed, otherwise NULL + */ struct aws_proxy_request_flow *aws_proxy_request_flow_new_basic_auth( struct aws_byte_cursor user_name, struct aws_byte_cursor password); From 2827688f2ed0d930043c74a7bff6799c6b0d44d3 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Wed, 2 Dec 2020 13:27:23 -0800 Subject: [PATCH 06/54] More doc updates --- include/aws/http/connection.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/aws/http/connection.h b/include/aws/http/connection.h index 1f2e82bcd..137535315 100644 --- a/include/aws/http/connection.h +++ b/include/aws/http/connection.h @@ -173,6 +173,12 @@ struct aws_http_proxy_options { */ const struct aws_tls_connection_options *tls_options; + /** + * Optional + * Advanced option that allows the user to create a custom request flow that gives low-level control of either the + * CONNECT stage (for tunneling proxy connections) or every outbound request going through the connection + * (for forwarding proxy connections). + */ struct aws_proxy_request_flow *request_flow; }; From eb5db2fad37c27ba4b4e30a402f0fc8a45bae047 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Sun, 13 Dec 2020 18:02:30 -0800 Subject: [PATCH 07/54] Passing original tests --- include/aws/http/connection.h | 13 +- include/aws/http/private/proxy_impl.h | 7 +- include/aws/http/proxy_request_flow.h | 137 ------- include/aws/http/proxy_strategy.h | 260 +++++++++++++ source/proxy_connection.c | 256 +++++------- source/proxy_strategy.c | 541 ++++++++++++++++++++++++++ tests/proxy_test_helper.c | 5 +- tests/proxy_test_helper.h | 2 +- tests/test_proxy.c | 38 +- 9 files changed, 940 insertions(+), 319 deletions(-) delete mode 100644 include/aws/http/proxy_request_flow.h create mode 100644 include/aws/http/proxy_strategy.h create mode 100644 source/proxy_strategy.c diff --git a/include/aws/http/connection.h b/include/aws/http/connection.h index 137535315..efb970ff1 100644 --- a/include/aws/http/connection.h +++ b/include/aws/http/connection.h @@ -144,7 +144,7 @@ enum aws_http_proxy_connection_type { AWS_HPCT_SOCKS5, }; -struct aws_proxy_request_flow; +struct aws_http_proxy_strategy_factory; /** * Options for http proxy server usage @@ -175,11 +175,14 @@ struct aws_http_proxy_options { /** * Optional - * Advanced option that allows the user to create a custom request flow that gives low-level control of either the - * CONNECT stage (for tunneling proxy connections) or every outbound request going through the connection - * (for forwarding proxy connections). + * Advanced option that allows the user to create a custom strategy that gives low-level control of + * certain logical flows within the proxy logic. + * + * For tunneling proxies it allows custom retry and adaptive negotiation of CONNECT requests. + * For forwarding proxies it allows custom request transformations. + * Other proxy connection types TBD. */ - struct aws_proxy_request_flow *request_flow; + struct aws_http_proxy_strategy_factory *proxy_strategy_factory; }; /** diff --git a/include/aws/http/private/proxy_impl.h b/include/aws/http/private/proxy_impl.h index 8a7864f90..058f80d32 100644 --- a/include/aws/http/private/proxy_impl.h +++ b/include/aws/http/private/proxy_impl.h @@ -43,11 +43,7 @@ struct aws_http_proxy_config { struct aws_tls_connection_options *tls_options; - enum aws_http_proxy_authentication_type auth_type; - - struct aws_byte_buf auth_username; - - struct aws_byte_buf auth_password; + struct aws_http_proxy_strategy_factory *proxy_strategy_factory; }; /* @@ -76,6 +72,7 @@ struct aws_http_proxy_user_data { struct aws_tls_connection_options *tls_options; struct aws_http_proxy_config *proxy_config; + struct aws_http_proxy_strategy *proxy_strategy; }; struct aws_http_proxy_system_vtable { diff --git a/include/aws/http/proxy_request_flow.h b/include/aws/http/proxy_request_flow.h deleted file mode 100644 index b019e28b6..000000000 --- a/include/aws/http/proxy_request_flow.h +++ /dev/null @@ -1,137 +0,0 @@ -#ifndef AWS_PROXY_REQUEST_FLOW_H -#define AWS_PROXY_REQUEST_FLOW_H - -/** - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -#include -#include -#include - -struct aws_http_message; -struct aws_http_header; - -/** - * The proxy request transform callback implementation *MUST* call one of these two functions for every possible - * (async) execution pathway. - */ - -/** - * Custom request flow logic must call this function to indicate an unsuccessful outcome - */ -typedef void(aws_proxy_request_flow_terminate_fn)(int error_code, void *internal_proxy_user_data); - -/** - * Custom request flow logic must call this function to forward the potentially-mutated request back to the proxy - * flow coordination logic. - */ -typedef void(aws_proxy_request_flow_forward_fn)(struct aws_http_message *message, void *internal_proxy_user_data); - -/** - * Wrapper for the proxy flow callback functions that the user must use to continue or terminate the proxy - * request flow. - */ -struct aws_proxy_flow_callback_function_table { - aws_proxy_request_flow_terminate_fn *terminate_fn; - aws_proxy_request_flow_forward_fn *forward_fn; -}; - -/** - * User-supplied transform callback which implements the proxy request flow and ultimately, across all execution - * pathways, invokes either the terminate function or the forward function appropriately. - * - * For tunneling proxy connections, this request flow transform only applies to the CONNECT stage of proxy - * connection establishment. - * - * For forwarding proxy connections, this request flow transform applies to every single http request that goes - * out on the connection. - * - */ -typedef void(aws_proxy_request_transform_fn)( - struct aws_http_message *message, - struct aws_proxy_flow_callback_function_table flow_callback_table, - void *flow_user_data, - void *internal_proxy_user_data); - -/** - * Tunneling proxy connections only. A callback that lets the custom request flow examine the headers in the - * response to the most recent CONNECT request as they arrive. - */ -typedef int(aws_http_proxy_connect_on_incoming_headers_fn)( - enum aws_http_header_block header_block, - const struct aws_http_header *header_array, - size_t num_headers, - void *user_data); - -/** - * Tunneling proxy connections only. A callback that lets the custom request flow examine the status code of the - * response to the CONNECT request. - */ -typedef int(aws_http_proxy_connect_status_fn)(enum aws_http_status_code status_code, void *user_data); - -/** - * Tunneling proxy connections only. A callback that lets the custom request flow examine the body of the response - * to the CONNECT request. - */ -typedef int(aws_http_proxy_connect_on_incoming_body_fn)(const struct aws_byte_cursor *data, void *user_data); - -/** - * User-supplied destructor for the user_data associated with this custom request flow. A standard pattern is to - * make the user data a structure that also embeds the aws_proxy_request_flow as a member, letting the destructor - * clean up everything in a single shot. - */ -typedef void(aws_http_proxy_request_flow_user_data_destroy_fn)(void *user_data); - -/* - * Configuration definition of a custom proxy request flow, containing a transform for requests (CONNECT-only for - * tunneling proxy connections) and CONNECT response handling callbacks. - * - * A custom request flow works differently based on what kind of proxy connection is being asked for: - * - * (1) Tunneling - In a tunneling proxy connection, the request_transform is invoked on every CONNECT request, and - * nothing more. The request_transform implementation *MUST*, in turn, call one of terminate or forward functions - * from the supplied flow_callback_table. - * - * Every CONNECT request, if a response is obtained, will properly invoke the response handling callbacks supplied - * in the proxy request flow. - * - * (2) Forwarding - In a tunneling proxy connection, the request_transform is invoked on every request sent out - * on the connection. The response handling callbacks are unused. - */ -struct aws_proxy_request_flow { - - /* - * Required. A custom proxy request flow is nonsensical without doing something here. - */ - aws_proxy_request_transform_fn *request_transform; - - /* - * Tunnel-only response handling callbacks that let the request flow process the response to a CONNECT request. - */ - aws_http_proxy_connect_on_incoming_headers_fn *on_incoming_headers_callback; - aws_http_proxy_connect_status_fn *on_status_callback; - aws_http_proxy_connect_on_incoming_body_fn *on_incoming_body_callback; - - aws_http_proxy_request_flow_user_data_destroy_fn *destroy_fn; - void *user_data; -}; - -AWS_EXTERN_C_BEGIN - -/** - * A constructor for a simple proxy request flow that performs basic authentication by adding the appropriate - * header and header value to requests. - * - * @param user_name user name to use in basic authentication - * @param password password to use in basic authentication - * @return a new proxy request flow if successfully constructed, otherwise NULL - */ -struct aws_proxy_request_flow *aws_proxy_request_flow_new_basic_auth( - struct aws_byte_cursor user_name, - struct aws_byte_cursor password); - -AWS_EXTERN_C_END - -#endif /* AWS_PROXY_REQUEST_FLOW_H */ diff --git a/include/aws/http/proxy_strategy.h b/include/aws/http/proxy_strategy.h new file mode 100644 index 000000000..1197d7f2b --- /dev/null +++ b/include/aws/http/proxy_strategy.h @@ -0,0 +1,260 @@ +#ifndef AWS_PROXY_STRATEGY_H +#define AWS_PROXY_STRATEGY_H + +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include +#include +#include +#include +#include + +struct aws_http_message; +struct aws_http_header; + +struct aws_http_proxy_strategy; +struct aws_http_proxy_strategy_factory; + +/** + * Proxy strategy logic must call this function to indicate an unsuccessful outcome + */ +typedef void(aws_http_proxy_strategy_terminate_fn)( + struct aws_http_message *message, + int error_code, + void *internal_proxy_user_data); + +/** + * Proxy strategy logic must call this function to forward the potentially-mutated request back to the proxy + * strategy coordination logic. + */ +typedef void( + aws_http_proxy_strategy_http_request_forward_fn)(struct aws_http_message *message, void *internal_proxy_user_data); + +/** + * User-supplied transform callback which implements the proxy request flow and ultimately, across all execution + * pathways, invokes either the terminate function or the forward function appropriately. + * + * For tunneling proxy connections, this request flow transform only applies to the CONNECT stage of proxy + * connection establishment. + * + * For forwarding proxy connections, this request flow transform applies to every single http request that goes + * out on the connection. + * + * Forwarding proxy connections cannot yet support a truly async request transform without major surgery on http + * stream creation, so for now, we split into an async version (for tunneling proxies) and a separate + * synchronous version for forwarding proxies. + * + */ +typedef void(aws_http_proxy_strategy_http_request_transform_async_fn)( + struct aws_http_message *message, + aws_http_proxy_strategy_terminate_fn *strategy_termination_callback, + aws_http_proxy_strategy_http_request_forward_fn *strategy_http_request_forward_callback, + void *strategy_user_data, + void *internal_proxy_user_data); + +typedef int( + aws_http_proxy_strategy_http_request_transform_fn)(struct aws_http_message *message, void *strategy_user_data); + +/** + * Tunneling proxy connections only. A callback that lets the strategy examine the headers in the + * response to the most recent CONNECT request as they arrive. + */ +typedef int(aws_http_proxy_strategy_connect_on_incoming_headers_fn)( + enum aws_http_header_block header_block, + const struct aws_http_header *header_array, + size_t num_headers, + void *strategy_user_data); + +/** + * Tunneling proxy connections only. A callback that lets the strategy examine the status code of the + * response to the most recent CONNECT request. + */ +typedef int(aws_http_proxy_strategy_connect_status_fn)(enum aws_http_status_code status_code, void *strategy_user_data); + +/** + * Tunneling proxy connections only. A callback that lets the strategy examine the body of the response + * to the most recent CONNECT request. + */ +typedef int(aws_http_proxy_strategy_connect_on_incoming_body_fn)(const struct aws_byte_cursor *data, void *user_data); + +/** + * Destructor for a proxy strategy. A standard pattern is to + * make the user data a structure that also embeds the aws_proxy_strategy as a member, letting the destructor + * clean up everything in a single shot. + */ +typedef void(aws_http_proxy_strategy_destroy_fn)(struct aws_http_proxy_strategy *proxy_strategy); + +struct aws_http_proxy_strategy_forwarding_vtable { + aws_http_proxy_strategy_http_request_transform_fn *forward_request_transform; +}; + +struct aws_http_proxy_strategy_tunnelling_vtable { + aws_http_proxy_strategy_http_request_transform_async_fn *connect_request_transform; + + aws_http_proxy_strategy_connect_on_incoming_headers_fn *on_incoming_headers_callback; + aws_http_proxy_strategy_connect_status_fn *on_status_callback; + aws_http_proxy_strategy_connect_on_incoming_body_fn *on_incoming_body_callback; +}; + +/* + * Configuration definition of a proxy stategy. Contains a proxy-type-specific vtable, user_data (usually a + * strategy-specific struct that embeds the aws_proxy_strategy), and a destructor. + * + * A strategy works differently based on what kind of proxy connection is being asked for: + * + * (1) Tunneling - In a tunneling proxy connection, the connect_request_transform is invoked on every CONNECT request. + * The connect_request_transform implementation *MUST*, in turn, eventually call one of the terminate or forward + * functions it gets supplied with. + * + * Every CONNECT request, if a response is obtained, will properly invoke the response handling callbacks supplied + * in the tunneling vtable. + * + * (2) Forwarding - In a forwarding proxy connection, the forward_request_transform is invoked on every request sent out + * on the connection. + * + * (3) Socks5 - not yet supported + */ +struct aws_http_proxy_strategy { + struct aws_ref_count ref_count; + + void *impl; + + union { + struct aws_http_proxy_strategy_forwarding_vtable *forwarding_vtable; + struct aws_http_proxy_strategy_tunnelling_vtable *tunnelling_vtable; + } strategy_vtable; +}; + +/*********************************************************************************************/ + +typedef struct aws_http_proxy_strategy *(aws_http_proxy_strategy_factory_create_strategy_fn)( + struct aws_http_proxy_strategy_factory *proxy_strategy_factory, + struct aws_allocator *allocator); + +struct aws_http_proxy_strategy_factory_vtable { + aws_http_proxy_strategy_factory_create_strategy_fn *create_strategy; +}; + +struct aws_http_proxy_strategy_factory { + struct aws_ref_count ref_count; + struct aws_http_proxy_strategy_factory_vtable *vtable; + void *impl; + enum aws_http_proxy_connection_type proxy_connection_type; +}; + +struct aws_http_proxy_strategy_factory_basic_auth_config { + + /* type of proxy connection being established, must be forwarding or tunnel */ + enum aws_http_proxy_connection_type proxy_connection_type; + + /* user name to use in basic authentication */ + struct aws_byte_cursor user_name; + + /* password to use in basic authentication */ + struct aws_byte_cursor password; +}; + +struct aws_http_proxy_strategy_factory_tunneling_chain_options { + struct aws_http_proxy_strategy **strategies; + + uint32_t strategy_count; +}; + +AWS_EXTERN_C_BEGIN + +/** + * Take a reference to an http proxy strategy + * @param proxy_strategy strategy to take a reference to + * @return the strategy + */ +AWS_HTTP_API +struct aws_http_proxy_strategy *aws_http_proxy_strategy_acquire(struct aws_http_proxy_strategy *proxy_strategy); + +/** + * Release a reference to an http proxy strategy + * @param proxy_strategy strategy to release a reference to + */ +AWS_HTTP_API +void aws_http_proxy_strategy_release(struct aws_http_proxy_strategy *proxy_strategy); + +/** + * Creates a new proxy strategy from the factory according to the factory's configuration + * @param allocator memory allocator to use + * @return a new proxy strategy if successful, otherwise NULL + */ +AWS_HTTP_API +struct aws_http_proxy_strategy *aws_http_proxy_strategy_factory_create_strategy( + struct aws_http_proxy_strategy_factory *factory, + struct aws_allocator *allocator); + +/** + * Take a reference to an http proxy strategy factory + * @param proxy_strategy_factory factory to take a reference to + * @return the factory + */ +AWS_HTTP_API +struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_acquire( + struct aws_http_proxy_strategy_factory *proxy_strategy_factory); + +/** + * Release a reference to an http proxy strategy factory + * @param proxy_strategy_factory factory to release a reference to + */ +AWS_HTTP_API +void aws_http_proxy_strategy_factory_release(struct aws_http_proxy_strategy_factory *proxy_strategy_factory); + +/** + * A constructor for a proxy strategy factory that performs basic authentication by adding the appropriate + * header and header value to requests or CONNECT requests. + * + * @param allocator memory allocator to use + * @param config basic authentication configuration info + * @return a new proxy strategy factory if successfully constructed, otherwise NULL + */ +AWS_HTTP_API +struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_basic_auth( + struct aws_allocator *allocator, + struct aws_http_proxy_strategy_factory_basic_auth_config *config); + +/** + * A constructor for a tunnel-only proxy request flow that does nothing. Intended to be the first link in an adaptive + * chain for a tunneling proxy: first try a basic CONNECT, then based on the response, later links are allowed to + * make attempts. + * + * @param allocator memory allocator to use + * @return a new proxy strategy factory if successfully constructed, otherwise NULL + */ +AWS_HTTP_API +struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunneling_one_time_identity( + struct aws_allocator *allocator); + +/** + * Constructor for a tunneling proxy strategy that contains a chain of sub-strategies which are tried sequentially + * in order. + * + * @param allocator memory allocator to use + * @param config chain configuration info + * @return a new proxy strategy factory if successfully constructed, otherwise NULL + */ +AWS_HTTP_API +struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunneling_chain( + struct aws_allocator *allocator, + struct aws_http_proxy_strategy_factory_tunneling_chain_options *config); + +/** + * A constructor for a forwarding-only proxy request flow that does nothing. Exists so that all proxy logic uses a + * strategy. + * + * @param allocator memory allocator to use + * @return a new proxy strategy factory if successfully constructed, otherwise NULL + */ +AWS_HTTP_API +struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_forwarding_identity( + struct aws_allocator *allocator); + +AWS_EXTERN_C_END + +#endif /* AWS_PROXY_STRATEGY_H */ diff --git a/source/proxy_connection.c b/source/proxy_connection.c index 629af1288..21b5e83c9 100644 --- a/source/proxy_connection.c +++ b/source/proxy_connection.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -20,8 +21,6 @@ #endif AWS_STATIC_STRING_FROM_LITERAL(s_host_header_name, "Host"); -AWS_STATIC_STRING_FROM_LITERAL(s_proxy_authorization_header_name, "Proxy-Authorization"); -AWS_STATIC_STRING_FROM_LITERAL(s_proxy_authorization_header_basic_prefix, "Basic "); AWS_STATIC_STRING_FROM_LITERAL(s_proxy_connection_header_name, "Proxy-Connection"); AWS_STATIC_STRING_FROM_LITERAL(s_proxy_connection_header_value, "Keep-Alive"); AWS_STATIC_STRING_FROM_LITERAL(s_options_method, "OPTIONS"); @@ -53,6 +52,8 @@ void aws_http_proxy_user_data_destroy(struct aws_http_proxy_user_data *user_data aws_mem_release(user_data->allocator, user_data->tls_options); } + aws_http_proxy_strategy_release(user_data->proxy_strategy); + aws_mem_release(user_data->allocator, user_data); } @@ -83,6 +84,12 @@ struct aws_http_proxy_user_data *aws_http_proxy_user_data_new( goto on_error; } + user_data->proxy_strategy = + aws_http_proxy_strategy_factory_create_strategy(user_data->proxy_config->proxy_strategy_factory, allocator); + if (user_data->proxy_strategy == NULL) { + goto on_error; + } + if (options->tls_options) { /* clone tls options, but redirect user data to what we're creating */ user_data->tls_options = aws_mem_calloc(allocator, 1, sizeof(struct aws_tls_connection_options)); @@ -113,85 +120,6 @@ struct aws_http_proxy_user_data *aws_http_proxy_user_data_new( return NULL; } -/* - * Adds a proxy authentication header based on the basic authentication mode, rfc7617 - */ -static int s_add_basic_proxy_authentication_header( - struct aws_http_message *request, - struct aws_http_proxy_user_data *proxy_user_data) { - - struct aws_byte_buf base64_input_value; - AWS_ZERO_STRUCT(base64_input_value); - - struct aws_byte_buf header_value; - AWS_ZERO_STRUCT(header_value); - - int result = AWS_OP_ERR; - - if (aws_byte_buf_init( - &base64_input_value, - proxy_user_data->allocator, - proxy_user_data->proxy_config->auth_username.len + proxy_user_data->proxy_config->auth_password.len + 1)) { - goto done; - } - - /* First build a buffer with "username:password" in it */ - struct aws_byte_cursor username_cursor = aws_byte_cursor_from_buf(&proxy_user_data->proxy_config->auth_username); - if (aws_byte_buf_append(&base64_input_value, &username_cursor)) { - goto done; - } - - struct aws_byte_cursor colon_cursor = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL(":"); - if (aws_byte_buf_append(&base64_input_value, &colon_cursor)) { - goto done; - } - - struct aws_byte_cursor password_cursor = aws_byte_cursor_from_buf(&proxy_user_data->proxy_config->auth_password); - if (aws_byte_buf_append(&base64_input_value, &password_cursor)) { - goto done; - } - - struct aws_byte_cursor base64_source_cursor = - aws_byte_cursor_from_array(base64_input_value.buffer, base64_input_value.len); - - /* Figure out how much room we need in our final header value buffer */ - size_t required_size = 0; - if (aws_base64_compute_encoded_len(base64_source_cursor.len, &required_size)) { - goto done; - } - - required_size += s_proxy_authorization_header_basic_prefix->len + 1; - if (aws_byte_buf_init(&header_value, proxy_user_data->allocator, required_size)) { - goto done; - } - - /* Build the final header value by appending the authorization type and the base64 encoding string together */ - struct aws_byte_cursor basic_prefix = aws_byte_cursor_from_string(s_proxy_authorization_header_basic_prefix); - if (aws_byte_buf_append_dynamic(&header_value, &basic_prefix)) { - goto done; - } - - if (aws_base64_encode(&base64_source_cursor, &header_value)) { - goto done; - } - - struct aws_http_header header = {.name = aws_byte_cursor_from_string(s_proxy_authorization_header_name), - .value = aws_byte_cursor_from_array(header_value.buffer, header_value.len)}; - - if (aws_http_message_add_header(request, header)) { - goto done; - } - - result = AWS_OP_SUCCESS; - -done: - - aws_byte_buf_clean_up(&header_value); - aws_byte_buf_clean_up(&base64_input_value); - - return result; -} - /* * Connection callback used ONLY by http proxy connections. After this, * the connection is live and the user is notified @@ -330,11 +258,6 @@ static struct aws_http_message *s_build_proxy_connect_request(struct aws_http_pr goto on_error; } - if (user_data->proxy_config->auth_type == AWS_HPAT_BASIC && - s_add_basic_proxy_authentication_header(request, user_data)) { - goto on_error; - } - aws_byte_buf_clean_up(&path_buffer); return request; @@ -481,50 +404,67 @@ static void s_aws_http_on_stream_complete_tunnel_proxy( } } -/* - * Issues a CONNECT request on a newly-established proxy connection with the intent - * of upgrading with TLS on success - */ -static int s_make_proxy_connect_request( - struct aws_http_connection *connection, - struct aws_http_proxy_user_data *user_data) { - struct aws_http_message *request = s_build_proxy_connect_request(user_data); - if (request == NULL) { - return AWS_OP_ERR; - } +static void s_terminate_tunneling_connect( + struct aws_http_message *message, + int error_code, + void *internal_proxy_user_data) { + (void)message; + + struct aws_http_proxy_user_data *proxy_ud = internal_proxy_user_data; + + AWS_LOGF_ERROR( + AWS_LS_HTTP_CONNECTION, + "(%p) Tunneling proxy connection failed to create request stream for CONNECT request with error %d(%s)", + (void *)proxy_ud->connection, + error_code, + aws_error_str(error_code)); + + s_aws_http_proxy_user_data_shutdown(proxy_ud); +} + +static void s_continue_tunneling_connect(struct aws_http_message *message, void *internal_proxy_user_data) { + struct aws_http_proxy_user_data *proxy_ud = internal_proxy_user_data; struct aws_http_make_request_options request_options = { .self_size = sizeof(request_options), - .request = request, - .user_data = user_data, + .request = message, + .user_data = proxy_ud, .on_response_header_block_done = s_aws_http_on_incoming_header_block_done_tunnel_proxy, .on_complete = s_aws_http_on_stream_complete_tunnel_proxy, }; - struct aws_http_stream *stream = aws_http_connection_make_request(connection, &request_options); - if (stream == NULL) { + proxy_ud->connect_stream = aws_http_connection_make_request(proxy_ud->connection, &request_options); + if (proxy_ud->connect_stream == NULL) { goto on_error; } - user_data->connect_stream = stream; - user_data->connect_request = request; + aws_http_stream_activate(proxy_ud->connect_stream); - aws_http_stream_activate(stream); - - return AWS_OP_SUCCESS; + return; on_error: - AWS_LOGF_ERROR( - AWS_LS_HTTP_CONNECTION, - "(%p) Proxy connection failed to create request stream for CONNECT request with error %d(%s)", - (void *)connection, - aws_last_error(), - aws_error_str(aws_last_error())); + s_aws_http_proxy_user_data_shutdown(proxy_ud); +} - aws_http_message_destroy(request); +/* + * Issues a CONNECT request on a newly-established proxy connection with the intent + * of upgrading with TLS on success + */ +static int s_make_proxy_connect_request(struct aws_http_proxy_user_data *user_data) { + user_data->connect_request = s_build_proxy_connect_request(user_data); + if (user_data->connect_request == NULL) { + return AWS_OP_ERR; + } - return AWS_OP_ERR; + (*user_data->proxy_strategy->strategy_vtable.tunnelling_vtable->connect_request_transform)( + user_data->connect_request, + s_terminate_tunneling_connect, + s_continue_tunneling_connect, + user_data->proxy_strategy, + user_data); + + return AWS_OP_SUCCESS; } /* @@ -547,7 +487,7 @@ static void s_aws_http_on_client_connection_http_tunnel_proxy_setup_fn( proxy_ud->connection = connection; proxy_ud->state = AWS_PBS_HTTP_CONNECT; - if (s_make_proxy_connect_request(connection, proxy_ud)) { + if (s_make_proxy_connect_request(proxy_ud)) { goto on_error; } @@ -672,33 +612,22 @@ int aws_http_rewrite_uri_for_proxy_request( static int s_proxy_http_request_transform(struct aws_http_message *request, void *user_data) { struct aws_http_proxy_user_data *proxy_ud = user_data; - struct aws_byte_buf auth_header_value; - AWS_ZERO_STRUCT(auth_header_value); - - int result = AWS_OP_ERR; - - if (proxy_ud->proxy_config->auth_type == AWS_HPAT_BASIC && - s_add_basic_proxy_authentication_header(request, proxy_ud)) { - goto done; - } - if (aws_http_rewrite_uri_for_proxy_request(request, proxy_ud)) { - goto done; + return AWS_OP_ERR; } - result = AWS_OP_SUCCESS; - -done: - - aws_byte_buf_clean_up(&auth_header_value); + if ((*proxy_ud->proxy_strategy->strategy_vtable.forwarding_vtable->forward_request_transform)( + request, proxy_ud->proxy_strategy)) { + return AWS_OP_ERR; + } - return result; + return AWS_OP_SUCCESS; } /* * Top-level function to route a connection request through a proxy server, with no channel security */ -static int s_aws_http_client_connect_via_proxy_http(const struct aws_http_client_connection_options *options) { +static int s_aws_http_client_connect_via_forwarding_proxy(const struct aws_http_client_connection_options *options) { AWS_FATAL_ASSERT(options->tls_options == NULL); AWS_LOGF_INFO( @@ -743,7 +672,7 @@ static int s_aws_http_client_connect_via_proxy_http(const struct aws_http_client /* * Top-level function to route a connection through a proxy server via a CONNECT request */ -static int s_aws_http_client_connect_via_proxy_http_tunnel(const struct aws_http_client_connection_options *options) { +static int s_aws_http_client_connect_via_tunneling_proxy(const struct aws_http_client_connection_options *options) { AWS_FATAL_ASSERT(options->proxy_options != NULL); AWS_LOGF_INFO( @@ -790,10 +719,15 @@ int aws_http_client_connect_via_proxy(const struct aws_http_client_connection_op return AWS_OP_ERR; } - if (options->proxy_options->connection_type == AWS_HPCT_HTTP_TUNNEL) { - return s_aws_http_client_connect_via_proxy_http_tunnel(options); - } else { - return s_aws_http_client_connect_via_proxy_http(options); + switch (options->proxy_options->connection_type) { + case AWS_HPCT_HTTP_FORWARD: + return s_aws_http_client_connect_via_forwarding_proxy(options); + + case AWS_HPCT_HTTP_TUNNEL: + return s_aws_http_client_connect_via_tunneling_proxy(options); + + default: + return aws_raise_error(AWS_ERROR_UNIMPLEMENTED); } } @@ -810,14 +744,6 @@ struct aws_http_proxy_config *aws_http_proxy_config_new( goto on_error; } - if (aws_byte_buf_init_copy_from_cursor(&config->auth_username, allocator, options->auth_username)) { - goto on_error; - } - - if (aws_byte_buf_init_copy_from_cursor(&config->auth_password, allocator, options->auth_password)) { - goto on_error; - } - if (options->tls_options) { config->tls_options = aws_mem_calloc(allocator, 1, sizeof(struct aws_tls_connection_options)); if (aws_tls_connection_options_copy(config->tls_options, options->tls_options)) { @@ -826,9 +752,30 @@ struct aws_http_proxy_config *aws_http_proxy_config_new( } config->allocator = allocator; - config->auth_type = options->auth_type; config->port = options->port; + if (options->proxy_strategy_factory != NULL) { + config->proxy_strategy_factory = aws_http_proxy_strategy_factory_acquire(options->proxy_strategy_factory); + } else { + switch (options->connection_type) { + case AWS_HPCT_HTTP_FORWARD: + config->proxy_strategy_factory = aws_http_proxy_strategy_factory_new_forwarding_identity(allocator); + break; + + case AWS_HPCT_HTTP_TUNNEL: + config->proxy_strategy_factory = + aws_http_proxy_strategy_factory_new_tunneling_one_time_identity(allocator); + break; + + default: + break; + } + + if (config->proxy_strategy_factory == NULL) { + goto on_error; + } + } + return config; on_error: @@ -844,14 +791,14 @@ void aws_http_proxy_config_destroy(struct aws_http_proxy_config *config) { } aws_byte_buf_clean_up(&config->host); - aws_byte_buf_clean_up(&config->auth_username); - aws_byte_buf_clean_up(&config->auth_password); if (config->tls_options) { aws_tls_connection_options_clean_up(config->tls_options); aws_mem_release(config->allocator, config->tls_options); } + aws_http_proxy_strategy_factory_release(config->proxy_strategy_factory); + aws_mem_release(config->allocator, config); } @@ -861,11 +808,9 @@ void aws_http_proxy_options_init_from_config( AWS_FATAL_ASSERT(options && config); options->host = aws_byte_cursor_from_buf(&config->host); - options->auth_username = aws_byte_cursor_from_buf(&config->auth_username); - options->auth_password = aws_byte_cursor_from_buf(&config->auth_password); - options->auth_type = config->auth_type; options->port = config->port; options->tls_options = config->tls_options; + options->proxy_strategy_factory = config->proxy_strategy_factory; } int aws_http_options_validate_proxy_configuration(const struct aws_http_client_connection_options *options) { @@ -882,5 +827,12 @@ int aws_http_options_validate_proxy_configuration(const struct aws_http_client_c return aws_raise_error(AWS_ERROR_INVALID_STATE); } + struct aws_http_proxy_strategy_factory *proxy_strategy_factory = options->proxy_options->proxy_strategy_factory; + if (proxy_strategy_factory != NULL) { + if (proxy_strategy_factory->proxy_connection_type != proxy_type) { + return aws_raise_error(AWS_ERROR_INVALID_STATE); + } + } + return AWS_OP_SUCCESS; } diff --git a/source/proxy_strategy.c b/source/proxy_strategy.c new file mode 100644 index 000000000..1ada9e0dd --- /dev/null +++ b/source/proxy_strategy.c @@ -0,0 +1,541 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include + +#include +#include + +struct aws_http_proxy_strategy *aws_http_proxy_strategy_acquire(struct aws_http_proxy_strategy *proxy_strategy) { + if (proxy_strategy != NULL) { + aws_ref_count_acquire(&proxy_strategy->ref_count); + } + + return proxy_strategy; +} + +void aws_http_proxy_strategy_release(struct aws_http_proxy_strategy *proxy_strategy) { + if (proxy_strategy != NULL) { + aws_ref_count_release(&proxy_strategy->ref_count); + } +} + +struct aws_http_proxy_strategy *aws_http_proxy_strategy_factory_create_strategy( + struct aws_http_proxy_strategy_factory *factory, + struct aws_allocator *allocator) { + if (factory == NULL || allocator == NULL) { + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + return NULL; + } + + return factory->vtable->create_strategy(factory, allocator); +} + +struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_acquire( + struct aws_http_proxy_strategy_factory *proxy_strategy_factory) { + if (proxy_strategy_factory != NULL) { + aws_ref_count_acquire(&proxy_strategy_factory->ref_count); + } + + return proxy_strategy_factory; +} + +void aws_http_proxy_strategy_factory_release(struct aws_http_proxy_strategy_factory *proxy_strategy_factory) { + if (proxy_strategy_factory != NULL) { + aws_ref_count_release(&proxy_strategy_factory->ref_count); + } +} + +/******************************************************************************************************************/ + +enum proxy_strategy_connect_state { + AWS_PSCS_READY, + AWS_PSCS_IN_PROGRESS, + AWS_PSCS_SUCCESS, + AWS_PSCS_FAILURE, +}; + +struct aws_http_proxy_strategy_factory_basic_auth { + struct aws_allocator *allocator; + + struct aws_string *user_name; + struct aws_string *password; + + struct aws_http_proxy_strategy_factory factory_base; +}; + +static void s_destroy_basic_auth_factory(struct aws_http_proxy_strategy_factory *proxy_strategy_factory) { + struct aws_http_proxy_strategy_factory_basic_auth *basic_auth_factory = proxy_strategy_factory->impl; + + aws_string_destroy(basic_auth_factory->user_name); + aws_string_destroy(basic_auth_factory->password); + + aws_mem_release(basic_auth_factory->allocator, basic_auth_factory); +} + +struct aws_http_proxy_strategy_basic_auth { + struct aws_allocator *allocator; + + struct aws_http_proxy_strategy_factory *factory; + + enum proxy_strategy_connect_state connect_state; + + struct aws_http_proxy_strategy strategy_base; +}; + +static void s_destroy_basic_auth_strategy(struct aws_http_proxy_strategy *proxy_strategy) { + struct aws_http_proxy_strategy_basic_auth *basic_auth_strategy = proxy_strategy->impl; + + aws_http_proxy_strategy_factory_release(basic_auth_strategy->factory); + + aws_mem_release(basic_auth_strategy->allocator, basic_auth_strategy); +} + +AWS_STATIC_STRING_FROM_LITERAL(s_proxy_authorization_header_name, "Proxy-Authorization"); +AWS_STATIC_STRING_FROM_LITERAL(s_proxy_authorization_header_basic_prefix, "Basic "); + +/* + * Adds a proxy authentication header based on the basic authentication mode, rfc7617 + */ +static int s_add_basic_proxy_authentication_header( + struct aws_allocator *allocator, + struct aws_http_message *request, + struct aws_http_proxy_strategy_basic_auth *basic_auth_strategy) { + + struct aws_byte_buf base64_input_value; + AWS_ZERO_STRUCT(base64_input_value); + + struct aws_byte_buf header_value; + AWS_ZERO_STRUCT(header_value); + + int result = AWS_OP_ERR; + + struct aws_http_proxy_strategy_factory_basic_auth *factory = basic_auth_strategy->factory->impl; + + if (aws_byte_buf_init(&base64_input_value, allocator, factory->user_name->len + factory->password->len + 1)) { + goto done; + } + + /* First build a buffer with "username:password" in it */ + struct aws_byte_cursor username_cursor = aws_byte_cursor_from_string(factory->user_name); + if (aws_byte_buf_append(&base64_input_value, &username_cursor)) { + goto done; + } + + struct aws_byte_cursor colon_cursor = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL(":"); + if (aws_byte_buf_append(&base64_input_value, &colon_cursor)) { + goto done; + } + + struct aws_byte_cursor password_cursor = aws_byte_cursor_from_string(factory->password); + if (aws_byte_buf_append(&base64_input_value, &password_cursor)) { + goto done; + } + + struct aws_byte_cursor base64_source_cursor = + aws_byte_cursor_from_array(base64_input_value.buffer, base64_input_value.len); + + /* Figure out how much room we need in our final header value buffer */ + size_t required_size = 0; + if (aws_base64_compute_encoded_len(base64_source_cursor.len, &required_size)) { + goto done; + } + + required_size += s_proxy_authorization_header_basic_prefix->len + 1; + if (aws_byte_buf_init(&header_value, allocator, required_size)) { + goto done; + } + + /* Build the final header value by appending the authorization type and the base64 encoding string together */ + struct aws_byte_cursor basic_prefix = aws_byte_cursor_from_string(s_proxy_authorization_header_basic_prefix); + if (aws_byte_buf_append_dynamic(&header_value, &basic_prefix)) { + goto done; + } + + if (aws_base64_encode(&base64_source_cursor, &header_value)) { + goto done; + } + + struct aws_http_header header = { + .name = aws_byte_cursor_from_string(s_proxy_authorization_header_name), + .value = aws_byte_cursor_from_array(header_value.buffer, header_value.len), + }; + + if (aws_http_message_add_header(request, header)) { + goto done; + } + + result = AWS_OP_SUCCESS; + +done: + + aws_byte_buf_clean_up(&header_value); + aws_byte_buf_clean_up(&base64_input_value); + + return result; +} + +int s_basic_auth_forward_add_header(struct aws_http_message *message, void *strategy_user_data) { + + struct aws_http_proxy_strategy *strategy = strategy_user_data; + struct aws_http_proxy_strategy_basic_auth *basic_auth_strategy = strategy->impl; + + return s_add_basic_proxy_authentication_header(basic_auth_strategy->allocator, message, basic_auth_strategy); +} + +void s_basic_auth_tunnel_add_header( + struct aws_http_message *message, + aws_http_proxy_strategy_terminate_fn *strategy_termination_callback, + aws_http_proxy_strategy_http_request_forward_fn *strategy_http_request_forward_callback, + void *strategy_user_data, + void *internal_proxy_user_data) { + + struct aws_http_proxy_strategy *strategy = strategy_user_data; + struct aws_http_proxy_strategy_basic_auth *basic_auth_strategy = strategy->impl; + if (basic_auth_strategy->connect_state != AWS_PSCS_READY) { + strategy_termination_callback(message, AWS_ERROR_INVALID_STATE, internal_proxy_user_data); + return; + } + + basic_auth_strategy->connect_state = AWS_PSCS_IN_PROGRESS; + + if (s_add_basic_proxy_authentication_header(basic_auth_strategy->allocator, message, basic_auth_strategy)) { + strategy_termination_callback(message, aws_last_error(), internal_proxy_user_data); + return; + } + + strategy_http_request_forward_callback(message, internal_proxy_user_data); +} + +static int s_basic_auth_on_connect_status(enum aws_http_status_code status_code, void *user_data) { + struct aws_http_proxy_strategy *strategy = user_data; + struct aws_http_proxy_strategy_basic_auth *basic_auth_strategy = strategy->impl; + + if (AWS_HTTP_STATUS_CODE_200_OK != status_code) { + basic_auth_strategy->connect_state = AWS_PSCS_FAILURE; + } else { + basic_auth_strategy->connect_state = AWS_PSCS_SUCCESS; + } + + return AWS_OP_SUCCESS; +} + +static struct aws_http_proxy_strategy_forwarding_vtable s_basic_auth_proxy_forwarding_vtable = { + .forward_request_transform = s_basic_auth_forward_add_header, +}; + +static struct aws_http_proxy_strategy_tunnelling_vtable s_basic_auth_proxy_tunneling_vtable = { + .on_status_callback = s_basic_auth_on_connect_status, + .connect_request_transform = s_basic_auth_tunnel_add_header, +}; + +static struct aws_http_proxy_strategy *s_create_basic_auth_strategy( + struct aws_http_proxy_strategy_factory *proxy_strategy_factory, + struct aws_allocator *allocator) { + if (proxy_strategy_factory == NULL || allocator == NULL) { + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + return NULL; + } + + struct aws_http_proxy_strategy_basic_auth *basic_auth_strategy = + aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_strategy_basic_auth)); + if (basic_auth_strategy == NULL) { + return NULL; + } + + basic_auth_strategy->allocator = allocator; + basic_auth_strategy->connect_state = AWS_PSCS_READY; + basic_auth_strategy->strategy_base.impl = basic_auth_strategy; + aws_ref_count_init( + &basic_auth_strategy->strategy_base.ref_count, + &basic_auth_strategy->strategy_base, + (aws_simple_completion_callback *)s_destroy_basic_auth_strategy); + + if (proxy_strategy_factory->proxy_connection_type == AWS_HPCT_HTTP_FORWARD) { + basic_auth_strategy->strategy_base.strategy_vtable.forwarding_vtable = &s_basic_auth_proxy_forwarding_vtable; + } else { + basic_auth_strategy->strategy_base.strategy_vtable.tunnelling_vtable = &s_basic_auth_proxy_tunneling_vtable; + } + + basic_auth_strategy->factory = aws_ref_count_acquire(&proxy_strategy_factory->ref_count); + + return &basic_auth_strategy->strategy_base; +} + +static struct aws_http_proxy_strategy_factory_vtable s_basic_auth_factory_vtable = { + .create_strategy = s_create_basic_auth_strategy, +}; + +struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_basic_auth( + struct aws_allocator *allocator, + struct aws_http_proxy_strategy_factory_basic_auth_config *config) { + if (config == NULL || allocator == NULL) { + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + return NULL; + } + + if (config->proxy_connection_type != AWS_HPCT_HTTP_FORWARD && + config->proxy_connection_type != AWS_HPCT_HTTP_TUNNEL) { + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + return NULL; + } + + struct aws_http_proxy_strategy_factory_basic_auth *basic_auth_factory = + aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_strategy_factory_basic_auth)); + if (basic_auth_factory == NULL) { + return NULL; + } + + basic_auth_factory->factory_base.impl = basic_auth_factory; + basic_auth_factory->factory_base.vtable = &s_basic_auth_factory_vtable; + basic_auth_factory->allocator = allocator; + basic_auth_factory->factory_base.proxy_connection_type = config->proxy_connection_type; + aws_ref_count_init( + &basic_auth_factory->factory_base.ref_count, + &basic_auth_factory->factory_base, + (aws_simple_completion_callback *)s_destroy_basic_auth_factory); + + basic_auth_factory->user_name = aws_string_new_from_cursor(allocator, &config->user_name); + if (basic_auth_factory->user_name == NULL) { + goto on_error; + } + + basic_auth_factory->password = aws_string_new_from_cursor(allocator, &config->password); + if (basic_auth_factory->password == NULL) { + goto on_error; + } + + return &basic_auth_factory->factory_base; + +on_error: + + aws_http_proxy_strategy_factory_release(&basic_auth_factory->factory_base); + + return NULL; +} + +/******************************************************************************************************************/ + +struct aws_http_proxy_strategy_factory_one_time_identity { + struct aws_allocator *allocator; + + struct aws_http_proxy_strategy_factory factory_base; +}; + +struct aws_http_proxy_strategy_one_time_identity { + struct aws_allocator *allocator; + + enum proxy_strategy_connect_state connect_state; + + struct aws_http_proxy_strategy strategy_base; +}; + +static void s_destroy_one_time_identity_strategy(struct aws_http_proxy_strategy *proxy_strategy) { + struct aws_http_proxy_strategy_one_time_identity *identity_strategy = proxy_strategy->impl; + + aws_mem_release(identity_strategy->allocator, identity_strategy); +} + +void s_one_time_identity_connect_transform( + struct aws_http_message *message, + aws_http_proxy_strategy_terminate_fn *strategy_termination_callback, + aws_http_proxy_strategy_http_request_forward_fn *strategy_http_request_forward_callback, + void *strategy_user_data, + void *internal_proxy_user_data) { + + struct aws_http_proxy_strategy *strategy = strategy_user_data; + struct aws_http_proxy_strategy_one_time_identity *one_time_identity_strategy = strategy->impl; + if (one_time_identity_strategy->connect_state != AWS_PSCS_READY) { + strategy_termination_callback(message, AWS_ERROR_INVALID_STATE, internal_proxy_user_data); + return; + } + + one_time_identity_strategy->connect_state = AWS_PSCS_IN_PROGRESS; + strategy_http_request_forward_callback(message, internal_proxy_user_data); +} + +static int s_one_time_identity_on_connect_status(enum aws_http_status_code status_code, void *user_data) { + struct aws_http_proxy_strategy_one_time_identity *one_time_identity_strategy = user_data; + + if (AWS_HTTP_STATUS_CODE_200_OK != status_code) { + one_time_identity_strategy->connect_state = AWS_PSCS_FAILURE; + } else { + one_time_identity_strategy->connect_state = AWS_PSCS_SUCCESS; + } + + return AWS_OP_SUCCESS; +} + +static struct aws_http_proxy_strategy_tunnelling_vtable s_one_time_identity_proxy_tunneling_vtable = { + .on_status_callback = s_one_time_identity_on_connect_status, + .connect_request_transform = s_one_time_identity_connect_transform, +}; + +static struct aws_http_proxy_strategy *s_create_one_time_identity_strategy( + struct aws_http_proxy_strategy_factory *proxy_strategy_factory, + struct aws_allocator *allocator) { + if (proxy_strategy_factory == NULL || allocator == NULL) { + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + return NULL; + } + + struct aws_http_proxy_strategy_one_time_identity *identity_strategy = + aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_strategy_one_time_identity)); + if (identity_strategy == NULL) { + return NULL; + } + + identity_strategy->allocator = allocator; + identity_strategy->connect_state = AWS_PSCS_READY; + identity_strategy->strategy_base.impl = identity_strategy; + aws_ref_count_init( + &identity_strategy->strategy_base.ref_count, + &identity_strategy->strategy_base, + (aws_simple_completion_callback *)s_destroy_one_time_identity_strategy); + + identity_strategy->strategy_base.strategy_vtable.tunnelling_vtable = &s_one_time_identity_proxy_tunneling_vtable; + + return &identity_strategy->strategy_base; +} + +static struct aws_http_proxy_strategy_factory_vtable s_one_time_identity_factory_vtable = { + .create_strategy = s_create_one_time_identity_strategy, +}; + +static void s_destroy_one_time_identity_factory(struct aws_http_proxy_strategy_factory *proxy_strategy_factory) { + struct aws_http_proxy_strategy_factory_one_time_identity *identity_factory = proxy_strategy_factory->impl; + + aws_mem_release(identity_factory->allocator, identity_factory); +} + +struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunneling_one_time_identity( + struct aws_allocator *allocator) { + if (allocator == NULL) { + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + return NULL; + } + + struct aws_http_proxy_strategy_factory_one_time_identity *identity_factory = + aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_strategy_factory_one_time_identity)); + if (identity_factory == NULL) { + return NULL; + } + + identity_factory->factory_base.impl = identity_factory; + identity_factory->factory_base.vtable = &s_one_time_identity_factory_vtable; + identity_factory->factory_base.proxy_connection_type = AWS_HPCT_HTTP_TUNNEL; + identity_factory->allocator = allocator; + + aws_ref_count_init( + &identity_factory->factory_base.ref_count, + &identity_factory->factory_base, + (aws_simple_completion_callback *)s_destroy_one_time_identity_factory); + + return &identity_factory->factory_base; +} + +/******************************************************************************************************************/ + +struct aws_http_proxy_strategy_factory_forwarding_identity { + struct aws_allocator *allocator; + + struct aws_http_proxy_strategy_factory factory_base; +}; + +struct aws_http_proxy_strategy_forwarding_identity { + struct aws_allocator *allocator; + + enum proxy_strategy_connect_state connect_state; + + struct aws_http_proxy_strategy strategy_base; +}; + +static void s_destroy_forwarding_identity_strategy(struct aws_http_proxy_strategy *proxy_strategy) { + struct aws_http_proxy_strategy_forwarding_identity *identity_strategy = proxy_strategy->impl; + + aws_mem_release(identity_strategy->allocator, identity_strategy); +} + +int s_forwarding_identity_connect_transform(struct aws_http_message *message, void *strategy_user_data) { + + (void)message; + (void)strategy_user_data; + + return AWS_OP_SUCCESS; +} + +static struct aws_http_proxy_strategy_forwarding_vtable s_forwarding_identity_proxy_tunneling_vtable = { + .forward_request_transform = s_forwarding_identity_connect_transform, +}; + +static struct aws_http_proxy_strategy *s_create_forwarding_identity_strategy( + struct aws_http_proxy_strategy_factory *proxy_strategy_factory, + struct aws_allocator *allocator) { + if (proxy_strategy_factory == NULL || allocator == NULL) { + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + return NULL; + } + + struct aws_http_proxy_strategy_forwarding_identity *identity_strategy = + aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_strategy_forwarding_identity)); + if (identity_strategy == NULL) { + return NULL; + } + + identity_strategy->allocator = allocator; + identity_strategy->connect_state = AWS_PSCS_READY; + identity_strategy->strategy_base.impl = identity_strategy; + aws_ref_count_init( + &identity_strategy->strategy_base.ref_count, + &identity_strategy->strategy_base, + (aws_simple_completion_callback *)s_destroy_forwarding_identity_strategy); + + identity_strategy->strategy_base.strategy_vtable.forwarding_vtable = &s_forwarding_identity_proxy_tunneling_vtable; + + return &identity_strategy->strategy_base; +} + +static struct aws_http_proxy_strategy_factory_vtable s_forwarding_identity_factory_vtable = { + .create_strategy = s_create_forwarding_identity_strategy, +}; + +static void s_destroy_forwarding_identity_factory(struct aws_http_proxy_strategy_factory *proxy_strategy_factory) { + struct aws_http_proxy_strategy_factory_forwarding_identity *identity_factory = proxy_strategy_factory->impl; + + aws_mem_release(identity_factory->allocator, identity_factory); +} + +struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_forwarding_identity( + struct aws_allocator *allocator) { + if (allocator == NULL) { + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + return NULL; + } + + struct aws_http_proxy_strategy_factory_forwarding_identity *identity_factory = + aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_strategy_factory_forwarding_identity)); + if (identity_factory == NULL) { + return NULL; + } + + identity_factory->factory_base.impl = identity_factory; + identity_factory->factory_base.vtable = &s_forwarding_identity_factory_vtable; + identity_factory->factory_base.proxy_connection_type = AWS_HPCT_HTTP_FORWARD; + identity_factory->allocator = allocator; + + aws_ref_count_init( + &identity_factory->factory_base.ref_count, + &identity_factory->factory_base, + (aws_simple_completion_callback *)s_destroy_forwarding_identity_factory); + + return &identity_factory->factory_base; +} + +#ifdef NEVER + +struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunneling_chain( + struct aws_allocator *allocator, + struct aws_http_proxy_strategy_factory_tunneling_chain_options *config) {} +#endif diff --git a/tests/proxy_test_helper.c b/tests/proxy_test_helper.c index 3470b2e07..78fbbde74 100644 --- a/tests/proxy_test_helper.c +++ b/tests/proxy_test_helper.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -94,7 +95,7 @@ int proxy_tester_init(struct proxy_tester *tester, const struct proxy_tester_opt tester->host = options->host; tester->port = options->port; - tester->proxy_options = options->proxy_options; + tester->proxy_options = *options->proxy_options; tester->test_mode = options->test_mode; tester->failure_type = options->failure_type; @@ -192,6 +193,8 @@ int proxy_tester_clean_up(struct proxy_tester *tester) { aws_tls_ctx_options_clean_up(&tester->tls_ctx_options); } + aws_http_proxy_strategy_factory_release(tester->proxy_options.proxy_strategy_factory); + aws_http_library_clean_up(); aws_byte_buf_clean_up(&tester->connection_host_name); diff --git a/tests/proxy_test_helper.h b/tests/proxy_test_helper.h index d19ed2e14..fbbb7faeb 100644 --- a/tests/proxy_test_helper.h +++ b/tests/proxy_test_helper.h @@ -52,7 +52,7 @@ struct proxy_tester { struct aws_tls_ctx_options tls_ctx_options; struct aws_tls_connection_options tls_connection_options; - struct aws_http_proxy_options *proxy_options; + struct aws_http_proxy_options proxy_options; struct aws_byte_cursor host; uint16_t port; diff --git a/tests/test_proxy.c b/tests/test_proxy.c index 705308742..64f446eab 100644 --- a/tests/test_proxy.c +++ b/tests/test_proxy.c @@ -8,6 +8,7 @@ #include #include #include +#include #include @@ -153,7 +154,7 @@ static int s_test_aws_proxy_new_socket_channel(struct aws_socket_channel_bootstr testing_channel_run_currently_queued_tasks(tester.testing_channel); - if (tester.proxy_options->connection_type == AWS_HPCT_HTTP_TUNNEL) { + if (tester.proxy_options.connection_type == AWS_HPCT_HTTP_TUNNEL) { /* For tunnel proxies, send the CONNECT request and response */ ASSERT_SUCCESS(proxy_tester_verify_connect_request(&tester)); ASSERT_SUCCESS(proxy_tester_send_connect_response(&tester)); @@ -173,9 +174,7 @@ static int s_setup_proxy_test( struct aws_allocator *allocator, enum proxy_tester_test_mode test_mode, enum proxy_tester_failure_type failure_type, - enum aws_http_proxy_authentication_type auth_type) { - - (void)auth_type; + bool use_basic_auth) { aws_http_connection_set_system_vtable(&s_proxy_connection_system_vtable); aws_http_proxy_system_set_vtable(&s_proxy_table_for_tls); @@ -186,10 +185,14 @@ static int s_setup_proxy_test( .port = s_proxy_port, }; - if (auth_type == AWS_HPAT_BASIC) { - proxy_options.auth_type = AWS_HPAT_BASIC; - proxy_options.auth_username = aws_byte_cursor_from_string(s_mock_request_username); - proxy_options.auth_password = aws_byte_cursor_from_string(s_mock_request_password); + if (use_basic_auth) { + struct aws_http_proxy_strategy_factory_basic_auth_config config = { + .proxy_connection_type = AWS_HPCT_HTTP_FORWARD, + .user_name = aws_byte_cursor_from_string(s_mock_request_username), + .password = aws_byte_cursor_from_string(s_mock_request_password), + }; + + proxy_options.proxy_strategy_factory = aws_http_proxy_strategy_factory_new_basic_auth(allocator, &config); } struct proxy_tester_options options = { @@ -215,7 +218,7 @@ static int s_setup_proxy_test( static int s_test_http_forwarding_proxy_connection_proxy_target(struct aws_allocator *allocator, void *ctx) { (void)ctx; - ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTP_FORWARD, PTFT_NONE, AWS_HPAT_NONE)); + ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTP_FORWARD, PTFT_NONE, false)); ASSERT_SUCCESS(proxy_tester_verify_connection_attempt_was_to_proxy( &tester, aws_byte_cursor_from_c_str(s_proxy_host_name), s_proxy_port)); @@ -233,7 +236,7 @@ AWS_TEST_CASE(test_http_forwarding_proxy_connection_proxy_target, s_test_http_fo static int s_test_http_forwarding_proxy_connection_channel_failure(struct aws_allocator *allocator, void *ctx) { (void)ctx; - ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTP_FORWARD, PTFT_CHANNEL, AWS_HPAT_NONE)); + ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTP_FORWARD, PTFT_CHANNEL, false)); ASSERT_SUCCESS(proxy_tester_verify_connection_attempt_was_to_proxy( &tester, aws_byte_cursor_from_c_str(s_proxy_host_name), s_proxy_port)); @@ -255,7 +258,7 @@ AWS_TEST_CASE( static int s_test_http_forwarding_proxy_connection_connect_failure(struct aws_allocator *allocator, void *ctx) { (void)ctx; - ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTP_FORWARD, PTFT_CONNECTION, AWS_HPAT_NONE)); + ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTP_FORWARD, PTFT_CONNECTION, false)); ASSERT_SUCCESS(proxy_tester_verify_connection_attempt_was_to_proxy( &tester, aws_byte_cursor_from_c_str(s_proxy_host_name), s_proxy_port)); @@ -277,7 +280,7 @@ AWS_TEST_CASE( static int s_test_https_tunnel_proxy_connection_success(struct aws_allocator *allocator, void *ctx) { (void)ctx; - ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTPS_TUNNEL, PTFT_NONE, AWS_HPAT_NONE)); + ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTPS_TUNNEL, PTFT_NONE, false)); ASSERT_SUCCESS(proxy_tester_verify_connection_attempt_was_to_proxy( &tester, aws_byte_cursor_from_c_str(s_proxy_host_name), s_proxy_port)); @@ -297,7 +300,7 @@ AWS_TEST_CASE(test_https_tunnel_proxy_connection_success, s_test_https_tunnel_pr static int s_test_http_tunnel_proxy_connection_success(struct aws_allocator *allocator, void *ctx) { (void)ctx; - ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTP_TUNNEL, PTFT_NONE, AWS_HPAT_NONE)); + ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTP_TUNNEL, PTFT_NONE, false)); ASSERT_SUCCESS(proxy_tester_verify_connection_attempt_was_to_proxy( &tester, aws_byte_cursor_from_c_str(s_proxy_host_name), s_proxy_port)); @@ -317,7 +320,7 @@ AWS_TEST_CASE(test_http_tunnel_proxy_connection_success, s_test_http_tunnel_prox static int s_test_https_tunnel_proxy_connection_failure_connect(struct aws_allocator *allocator, void *ctx) { (void)ctx; - ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTPS_TUNNEL, PTFT_CONNECT_REQUEST, AWS_HPAT_NONE)); + ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTPS_TUNNEL, PTFT_CONNECT_REQUEST, false)); ASSERT_SUCCESS(proxy_tester_verify_connection_attempt_was_to_proxy( &tester, aws_byte_cursor_from_c_str(s_proxy_host_name), s_proxy_port)); @@ -337,7 +340,7 @@ AWS_TEST_CASE(test_https_tunnel_proxy_connection_failure_connect, s_test_https_t static int s_test_http_tunnel_proxy_connection_failure_connect(struct aws_allocator *allocator, void *ctx) { (void)ctx; - ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTP_TUNNEL, PTFT_CONNECT_REQUEST, AWS_HPAT_NONE)); + ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTP_TUNNEL, PTFT_CONNECT_REQUEST, false)); ASSERT_SUCCESS(proxy_tester_verify_connection_attempt_was_to_proxy( &tester, aws_byte_cursor_from_c_str(s_proxy_host_name), s_proxy_port)); @@ -357,7 +360,7 @@ AWS_TEST_CASE(test_http_tunnel_proxy_connection_failure_connect, s_test_http_tun static int s_test_https_tunnel_proxy_connection_failure_tls(struct aws_allocator *allocator, void *ctx) { (void)ctx; - ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTPS_TUNNEL, PTFT_TLS_NEGOTIATION, AWS_HPAT_NONE)); + ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTPS_TUNNEL, PTFT_TLS_NEGOTIATION, false)); ASSERT_SUCCESS(proxy_tester_verify_connection_attempt_was_to_proxy( &tester, aws_byte_cursor_from_c_str(s_proxy_host_name), s_proxy_port)); @@ -430,8 +433,7 @@ static int s_verify_transformed_request( static int s_do_http_forwarding_proxy_request_transform_test(struct aws_allocator *allocator, bool use_basic_auth) { - ASSERT_SUCCESS( - s_setup_proxy_test(allocator, PTTM_HTTP_FORWARD, PTFT_NONE, use_basic_auth ? AWS_HPAT_BASIC : AWS_HPAT_NONE)); + ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTP_FORWARD, PTFT_NONE, use_basic_auth)); struct aws_http_message *untransformed_request = s_build_http_request(allocator); struct aws_http_message *request = s_build_http_request(allocator); From 4f6d0e3a6adc283c50038a871dcddd55674a154a Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Sun, 13 Dec 2020 20:30:22 -0800 Subject: [PATCH 08/54] resolver creation --- bin/elasticurl/main.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/bin/elasticurl/main.c b/bin/elasticurl/main.c index 4297a4e2f..f18751a7e 100644 --- a/bin/elasticurl/main.c +++ b/bin/elasticurl/main.c @@ -676,7 +676,12 @@ int main(int argc, char **argv) { } struct aws_event_loop_group *el_group = aws_event_loop_group_new_default(allocator, 1, NULL); - struct aws_host_resolver *resolver = aws_host_resolver_new_default(allocator, 8, el_group, NULL); + + struct aws_host_resolver_default_options resolver_options = { + .max_entries = 8, + .el_group = el_group, + }; + struct aws_host_resolver *resolver = aws_host_resolver_new_default(allocator, &resolver_options); struct aws_client_bootstrap_options bootstrap_options = { .event_loop_group = el_group, From 312d061e670f286a11322fc25bef552b3126f51a Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Sun, 13 Dec 2020 21:35:56 -0800 Subject: [PATCH 09/54] Validate mistake --- source/proxy_connection.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/proxy_connection.c b/source/proxy_connection.c index 21b5e83c9..485283d94 100644 --- a/source/proxy_connection.c +++ b/source/proxy_connection.c @@ -823,7 +823,7 @@ int aws_http_options_validate_proxy_configuration(const struct aws_http_client_c return aws_raise_error(AWS_ERROR_UNIMPLEMENTED); } - if (proxy_type == AWS_HPCT_HTTP_FORWARD && options->tls_options != NULL) { + if (proxy_type == AWS_HPCT_HTTP_FORWARD && options->proxy_options->tls_options != NULL) { return aws_raise_error(AWS_ERROR_INVALID_STATE); } From 9542770ed4265c1da3ea76abbb5c12cec96c3634 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Sun, 13 Dec 2020 21:41:21 -0800 Subject: [PATCH 10/54] Undo incorrect fix --- source/proxy_connection.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/proxy_connection.c b/source/proxy_connection.c index 485283d94..21b5e83c9 100644 --- a/source/proxy_connection.c +++ b/source/proxy_connection.c @@ -823,7 +823,7 @@ int aws_http_options_validate_proxy_configuration(const struct aws_http_client_c return aws_raise_error(AWS_ERROR_UNIMPLEMENTED); } - if (proxy_type == AWS_HPCT_HTTP_FORWARD && options->proxy_options->tls_options != NULL) { + if (proxy_type == AWS_HPCT_HTTP_FORWARD && options->tls_options != NULL) { return aws_raise_error(AWS_ERROR_INVALID_STATE); } From 441c0aed4cbc461bef9ab817522b6cc85d85c723 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Mon, 14 Dec 2020 08:58:07 -0800 Subject: [PATCH 11/54] connect http response callbacks invoke proxy strategy callbacks --- include/aws/http/proxy_strategy.h | 19 ++++++---- source/proxy_connection.c | 60 +++++++++++++++++++++++++++---- source/proxy_strategy.c | 55 +++++++++++++++------------- 3 files changed, 96 insertions(+), 38 deletions(-) diff --git a/include/aws/http/proxy_strategy.h b/include/aws/http/proxy_strategy.h index 1197d7f2b..470590364 100644 --- a/include/aws/http/proxy_strategy.h +++ b/include/aws/http/proxy_strategy.h @@ -49,36 +49,41 @@ typedef void( * */ typedef void(aws_http_proxy_strategy_http_request_transform_async_fn)( + struct aws_http_proxy_strategy *proxy_strategy, struct aws_http_message *message, aws_http_proxy_strategy_terminate_fn *strategy_termination_callback, aws_http_proxy_strategy_http_request_forward_fn *strategy_http_request_forward_callback, - void *strategy_user_data, void *internal_proxy_user_data); -typedef int( - aws_http_proxy_strategy_http_request_transform_fn)(struct aws_http_message *message, void *strategy_user_data); +typedef int(aws_http_proxy_strategy_http_request_transform_fn)( + struct aws_http_proxy_strategy *proxy_strategy, + struct aws_http_message *message); /** * Tunneling proxy connections only. A callback that lets the strategy examine the headers in the * response to the most recent CONNECT request as they arrive. */ typedef int(aws_http_proxy_strategy_connect_on_incoming_headers_fn)( + struct aws_http_proxy_strategy *proxy_strategy, enum aws_http_header_block header_block, const struct aws_http_header *header_array, - size_t num_headers, - void *strategy_user_data); + size_t num_headers); /** * Tunneling proxy connections only. A callback that lets the strategy examine the status code of the * response to the most recent CONNECT request. */ -typedef int(aws_http_proxy_strategy_connect_status_fn)(enum aws_http_status_code status_code, void *strategy_user_data); +typedef int(aws_http_proxy_strategy_connect_status_fn)( + struct aws_http_proxy_strategy *proxy_strategy, + enum aws_http_status_code status_code); /** * Tunneling proxy connections only. A callback that lets the strategy examine the body of the response * to the most recent CONNECT request. */ -typedef int(aws_http_proxy_strategy_connect_on_incoming_body_fn)(const struct aws_byte_cursor *data, void *user_data); +typedef int(aws_http_proxy_strategy_connect_on_incoming_body_fn)( + struct aws_http_proxy_strategy *proxy_strategy, + const struct aws_byte_cursor *data); /** * Destructor for a proxy strategy. A standard pattern is to diff --git a/source/proxy_connection.c b/source/proxy_connection.c index 21b5e83c9..756e46cd0 100644 --- a/source/proxy_connection.c +++ b/source/proxy_connection.c @@ -246,14 +246,18 @@ static struct aws_http_message *s_build_proxy_connect_request(struct aws_http_pr goto on_error; } - struct aws_http_header host_header = {.name = aws_byte_cursor_from_string(s_host_header_name), - .value = aws_byte_cursor_from_array(path_buffer.buffer, path_buffer.len)}; + struct aws_http_header host_header = { + .name = aws_byte_cursor_from_string(s_host_header_name), + .value = aws_byte_cursor_from_array(path_buffer.buffer, path_buffer.len), + }; if (aws_http_message_add_header(request, host_header)) { goto on_error; } - struct aws_http_header keep_alive_header = {.name = aws_byte_cursor_from_string(s_proxy_connection_header_name), - .value = aws_byte_cursor_from_string(s_proxy_connection_header_value)}; + struct aws_http_header keep_alive_header = { + .name = aws_byte_cursor_from_string(s_proxy_connection_header_name), + .value = aws_byte_cursor_from_string(s_proxy_connection_header_value), + }; if (aws_http_message_add_header(request, keep_alive_header)) { goto on_error; } @@ -277,6 +281,40 @@ static struct aws_http_message *s_build_proxy_connect_request(struct aws_http_pr return NULL; } +static int s_aws_http_on_incoming_body_tunnel_proxy( + struct aws_http_stream *stream, + const struct aws_byte_cursor *data, + void *user_data) { + (void)stream; + + struct aws_http_proxy_user_data *context = user_data; + aws_http_proxy_strategy_connect_on_incoming_body_fn *on_incoming_body = + context->proxy_strategy->strategy_vtable.tunnelling_vtable->on_incoming_body_callback; + if (on_incoming_body != NULL) { + (*on_incoming_body)(context->proxy_strategy, data); + } + + return AWS_OP_SUCCESS; +} + +static int s_aws_http_on_response_headers_tunnel_proxy( + struct aws_http_stream *stream, + enum aws_http_header_block header_block, + const struct aws_http_header *header_array, + size_t num_headers, + void *user_data) { + (void)stream; + + struct aws_http_proxy_user_data *context = user_data; + aws_http_proxy_strategy_connect_on_incoming_headers_fn *on_incoming_headers = + context->proxy_strategy->strategy_vtable.tunnelling_vtable->on_incoming_headers_callback; + if (on_incoming_headers != NULL) { + (*on_incoming_headers)(context->proxy_strategy, header_block, header_array, num_headers); + } + + return AWS_OP_SUCCESS; +} + /* * Headers done callback for the CONNECT request made during tls proxy connections */ @@ -297,6 +335,12 @@ static int s_aws_http_on_incoming_header_block_done_tunnel_proxy( status); context->error_code = AWS_ERROR_HTTP_PROXY_TLS_CONNECT_FAILED; } + + aws_http_proxy_strategy_connect_status_fn *on_status = + context->proxy_strategy->strategy_vtable.tunnelling_vtable->on_status_callback; + if (on_status != NULL) { + (*on_status)(context->proxy_strategy, status); + } } return AWS_OP_SUCCESS; @@ -341,6 +385,8 @@ static void s_aws_http_on_stream_complete_tunnel_proxy( struct aws_http_proxy_user_data *context = user_data; AWS_FATAL_ASSERT(stream == context->connect_stream); + /* TODO strategy */ + if (context->error_code == AWS_ERROR_SUCCESS && error_code != AWS_ERROR_SUCCESS) { context->error_code = error_code; } @@ -429,7 +475,9 @@ static void s_continue_tunneling_connect(struct aws_http_message *message, void .self_size = sizeof(request_options), .request = message, .user_data = proxy_ud, + .on_response_headers = s_aws_http_on_response_headers_tunnel_proxy, .on_response_header_block_done = s_aws_http_on_incoming_header_block_done_tunnel_proxy, + .on_response_body = s_aws_http_on_incoming_body_tunnel_proxy, .on_complete = s_aws_http_on_stream_complete_tunnel_proxy, }; @@ -458,10 +506,10 @@ static int s_make_proxy_connect_request(struct aws_http_proxy_user_data *user_da } (*user_data->proxy_strategy->strategy_vtable.tunnelling_vtable->connect_request_transform)( + user_data->proxy_strategy, user_data->connect_request, s_terminate_tunneling_connect, s_continue_tunneling_connect, - user_data->proxy_strategy, user_data); return AWS_OP_SUCCESS; @@ -617,7 +665,7 @@ static int s_proxy_http_request_transform(struct aws_http_message *request, void } if ((*proxy_ud->proxy_strategy->strategy_vtable.forwarding_vtable->forward_request_transform)( - request, proxy_ud->proxy_strategy)) { + proxy_ud->proxy_strategy, request)) { return AWS_OP_ERR; } diff --git a/source/proxy_strategy.c b/source/proxy_strategy.c index 1ada9e0dd..9b43d1d95 100644 --- a/source/proxy_strategy.c +++ b/source/proxy_strategy.c @@ -177,23 +177,20 @@ static int s_add_basic_proxy_authentication_header( return result; } -int s_basic_auth_forward_add_header(struct aws_http_message *message, void *strategy_user_data) { - - struct aws_http_proxy_strategy *strategy = strategy_user_data; - struct aws_http_proxy_strategy_basic_auth *basic_auth_strategy = strategy->impl; +int s_basic_auth_forward_add_header(struct aws_http_proxy_strategy *proxy_strategy, struct aws_http_message *message) { + struct aws_http_proxy_strategy_basic_auth *basic_auth_strategy = proxy_strategy->impl; return s_add_basic_proxy_authentication_header(basic_auth_strategy->allocator, message, basic_auth_strategy); } void s_basic_auth_tunnel_add_header( + struct aws_http_proxy_strategy *proxy_strategy, struct aws_http_message *message, aws_http_proxy_strategy_terminate_fn *strategy_termination_callback, aws_http_proxy_strategy_http_request_forward_fn *strategy_http_request_forward_callback, - void *strategy_user_data, void *internal_proxy_user_data) { - struct aws_http_proxy_strategy *strategy = strategy_user_data; - struct aws_http_proxy_strategy_basic_auth *basic_auth_strategy = strategy->impl; + struct aws_http_proxy_strategy_basic_auth *basic_auth_strategy = proxy_strategy->impl; if (basic_auth_strategy->connect_state != AWS_PSCS_READY) { strategy_termination_callback(message, AWS_ERROR_INVALID_STATE, internal_proxy_user_data); return; @@ -209,14 +206,17 @@ void s_basic_auth_tunnel_add_header( strategy_http_request_forward_callback(message, internal_proxy_user_data); } -static int s_basic_auth_on_connect_status(enum aws_http_status_code status_code, void *user_data) { - struct aws_http_proxy_strategy *strategy = user_data; - struct aws_http_proxy_strategy_basic_auth *basic_auth_strategy = strategy->impl; +static int s_basic_auth_on_connect_status( + struct aws_http_proxy_strategy *proxy_strategy, + enum aws_http_status_code status_code) { + struct aws_http_proxy_strategy_basic_auth *basic_auth_strategy = proxy_strategy->impl; - if (AWS_HTTP_STATUS_CODE_200_OK != status_code) { - basic_auth_strategy->connect_state = AWS_PSCS_FAILURE; - } else { - basic_auth_strategy->connect_state = AWS_PSCS_SUCCESS; + if (basic_auth_strategy->connect_state == AWS_PSCS_IN_PROGRESS) { + if (AWS_HTTP_STATUS_CODE_200_OK != status_code) { + basic_auth_strategy->connect_state = AWS_PSCS_FAILURE; + } else { + basic_auth_strategy->connect_state = AWS_PSCS_SUCCESS; + } } return AWS_OP_SUCCESS; @@ -339,14 +339,13 @@ static void s_destroy_one_time_identity_strategy(struct aws_http_proxy_strategy } void s_one_time_identity_connect_transform( + struct aws_http_proxy_strategy *proxy_strategy, struct aws_http_message *message, aws_http_proxy_strategy_terminate_fn *strategy_termination_callback, aws_http_proxy_strategy_http_request_forward_fn *strategy_http_request_forward_callback, - void *strategy_user_data, void *internal_proxy_user_data) { - struct aws_http_proxy_strategy *strategy = strategy_user_data; - struct aws_http_proxy_strategy_one_time_identity *one_time_identity_strategy = strategy->impl; + struct aws_http_proxy_strategy_one_time_identity *one_time_identity_strategy = proxy_strategy->impl; if (one_time_identity_strategy->connect_state != AWS_PSCS_READY) { strategy_termination_callback(message, AWS_ERROR_INVALID_STATE, internal_proxy_user_data); return; @@ -356,13 +355,17 @@ void s_one_time_identity_connect_transform( strategy_http_request_forward_callback(message, internal_proxy_user_data); } -static int s_one_time_identity_on_connect_status(enum aws_http_status_code status_code, void *user_data) { - struct aws_http_proxy_strategy_one_time_identity *one_time_identity_strategy = user_data; +static int s_one_time_identity_on_connect_status( + struct aws_http_proxy_strategy *proxy_strategy, + enum aws_http_status_code status_code) { + struct aws_http_proxy_strategy_one_time_identity *one_time_identity_strategy = proxy_strategy->impl; - if (AWS_HTTP_STATUS_CODE_200_OK != status_code) { - one_time_identity_strategy->connect_state = AWS_PSCS_FAILURE; - } else { - one_time_identity_strategy->connect_state = AWS_PSCS_SUCCESS; + if (one_time_identity_strategy->connect_state == AWS_PSCS_IN_PROGRESS) { + if (AWS_HTTP_STATUS_CODE_200_OK != status_code) { + one_time_identity_strategy->connect_state = AWS_PSCS_FAILURE; + } else { + one_time_identity_strategy->connect_state = AWS_PSCS_SUCCESS; + } } return AWS_OP_SUCCESS; @@ -458,10 +461,12 @@ static void s_destroy_forwarding_identity_strategy(struct aws_http_proxy_strateg aws_mem_release(identity_strategy->allocator, identity_strategy); } -int s_forwarding_identity_connect_transform(struct aws_http_message *message, void *strategy_user_data) { +int s_forwarding_identity_connect_transform( + struct aws_http_proxy_strategy *proxy_strategy, + struct aws_http_message *message) { (void)message; - (void)strategy_user_data; + (void)proxy_strategy; return AWS_OP_SUCCESS; } From e7dd6b1697eb38ca271ffa3862b28b1d88ba18d0 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Mon, 14 Dec 2020 11:14:43 -0800 Subject: [PATCH 12/54] Experimental proxy strategy --- include/aws/http/http.h | 1 + include/aws/http/proxy_strategy.h | 46 +++- source/http.c | 3 + source/proxy_strategy.c | 381 +++++++++++++++++++++++++++++- 4 files changed, 420 insertions(+), 11 deletions(-) diff --git a/include/aws/http/http.h b/include/aws/http/http.h index 5dc3be7ab..9a8ce432e 100644 --- a/include/aws/http/http.h +++ b/include/aws/http/http.h @@ -46,6 +46,7 @@ enum aws_http_errors { AWS_ERROR_HTTP_RST_STREAM_SENT, AWS_ERROR_HTTP_STREAM_NOT_ACTIVATED, AWS_ERROR_HTTP_STREAM_HAS_COMPLETED, + AWS_ERROR_HTTP_PROXY_STRATEGY_TRANSFORM_FAILED, AWS_ERROR_HTTP_END_RANGE = AWS_ERROR_ENUM_END_RANGE(AWS_C_HTTP_PACKAGE_ID) }; diff --git a/include/aws/http/proxy_strategy.h b/include/aws/http/proxy_strategy.h index 470590364..7ffd81e29 100644 --- a/include/aws/http/proxy_strategy.h +++ b/include/aws/http/proxy_strategy.h @@ -163,9 +163,23 @@ struct aws_http_proxy_strategy_factory_basic_auth_config { }; struct aws_http_proxy_strategy_factory_tunneling_chain_options { - struct aws_http_proxy_strategy **strategies; + struct aws_http_proxy_strategy_factory **factories; - uint32_t strategy_count; + uint32_t factory_count; +}; + +/* + * The adaptive test strategy attempts a vanilla CONNECT and if that fails it attempts a basic auth + * CONNECT. + * + * SA TBI: the second stage should make a proper kerberos-aware CONNECT instead. + */ +struct aws_http_proxy_strategy_factory_tunneling_adaptive_test_options { + /* user name to use in basic authentication */ + struct aws_byte_cursor user_name; + + /* password to use in basic authentication */ + struct aws_byte_cursor password; }; AWS_EXTERN_C_BEGIN @@ -225,9 +239,9 @@ struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_basi struct aws_http_proxy_strategy_factory_basic_auth_config *config); /** - * A constructor for a tunnel-only proxy request flow that does nothing. Intended to be the first link in an adaptive - * chain for a tunneling proxy: first try a basic CONNECT, then based on the response, later links are allowed to - * make attempts. + * Factory constructor for a tunnel-only proxy request flow that does nothing. Intended to be the first link in an + * adaptive chain for a tunneling proxy: first try a basic CONNECT, then based on the response, later links are allowed + * to make attempts. * * @param allocator memory allocator to use * @return a new proxy strategy factory if successfully constructed, otherwise NULL @@ -237,8 +251,8 @@ struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunn struct aws_allocator *allocator); /** - * Constructor for a tunneling proxy strategy that contains a chain of sub-strategies which are tried sequentially - * in order. + * Factory constructor for a tunneling proxy strategy that contains a chain of sub-strategies which are tried + * sequentially in order. * * @param allocator memory allocator to use * @param config chain configuration info @@ -250,7 +264,7 @@ struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunn struct aws_http_proxy_strategy_factory_tunneling_chain_options *config); /** - * A constructor for a forwarding-only proxy request flow that does nothing. Exists so that all proxy logic uses a + * Factory constructor for a forwarding-only proxy strategy that does nothing. Exists so that all proxy logic uses a * strategy. * * @param allocator memory allocator to use @@ -260,6 +274,22 @@ AWS_HTTP_API struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_forwarding_identity( struct aws_allocator *allocator); +/** + * This is an experimental API. + * + * Constructor for a WIP adaptive tunneling proxy strategy. This strategy attempts a vanilla CONNECT and if that + * fails it attempts a basic auth CONNECT. The second CONNECT should have basic auth replaced with a kerberos or + * other network identity tech stage instead. + * + * @param allocator memory allocator to use + * @param config configuration options for the strategy factory + * @return a new proxy strategy factory if successfully constructed, otherwise NULL + */ +AWS_HTTP_API +struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunneling_adaptive_test( + struct aws_allocator *allocator, + struct aws_http_proxy_strategy_factory_tunneling_adaptive_test_options *config); + AWS_EXTERN_C_END #endif /* AWS_PROXY_STRATEGY_H */ diff --git a/source/http.c b/source/http.c index c167f95b4..3d79abb96 100644 --- a/source/http.c +++ b/source/http.c @@ -115,6 +115,9 @@ static struct aws_error_info s_errors[] = { AWS_DEFINE_ERROR_INFO_HTTP( AWS_ERROR_HTTP_STREAM_HAS_COMPLETED, "HTTP-stream has completed, action cannot be performed."), + AWS_DEFINE_ERROR_INFO_HTTP( + AWS_ERROR_HTTP_PROXY_STRATEGY_TRANSFORM_FAILED, + "Proxy strategy transform has completely failed."), }; /* clang-format on */ diff --git a/source/proxy_strategy.c b/source/proxy_strategy.c index 9b43d1d95..98030e667 100644 --- a/source/proxy_strategy.c +++ b/source/proxy_strategy.c @@ -538,9 +538,384 @@ struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_forw return &identity_factory->factory_base; } -#ifdef NEVER +/******************************************************************************************************************/ + +struct aws_http_proxy_strategy_factory_tunneling_chain { + struct aws_allocator *allocator; + + struct aws_http_proxy_strategy_factory factory_base; + + struct aws_array_list strategy_factories; +}; + +struct aws_http_proxy_strategy_tunneling_chain { + struct aws_allocator *allocator; + + struct aws_array_list strategies; + size_t current_strategy_transform_index; + void *original_internal_proxy_user_data; + aws_http_proxy_strategy_terminate_fn *original_strategy_termination_callback; + aws_http_proxy_strategy_http_request_forward_fn *original_strategy_http_request_forward_callback; + + struct aws_http_proxy_strategy strategy_base; +}; + +static void s_chain_tunnel_try_next_strategy( + struct aws_http_proxy_strategy_tunneling_chain *chain_strategy, + struct aws_http_message *message); + +static void s_chain_tunnel_iteration_termination_callback( + struct aws_http_message *message, + int error_code, + void *user_data) { + + (void)error_code; /* TODO: log */ + + struct aws_http_proxy_strategy *proxy_strategy = user_data; + struct aws_http_proxy_strategy_tunneling_chain *chain_strategy = proxy_strategy->impl; + + chain_strategy->current_strategy_transform_index++; + + s_chain_tunnel_try_next_strategy(chain_strategy, message); +} + +static void s_chain_tunnel_iteration_forward_callback(struct aws_http_message *message, void *user_data) { + struct aws_http_proxy_strategy *proxy_strategy = user_data; + struct aws_http_proxy_strategy_tunneling_chain *chain_strategy = proxy_strategy->impl; + + chain_strategy->original_strategy_http_request_forward_callback( + message, chain_strategy->original_internal_proxy_user_data); +} + +static void s_chain_tunnel_try_next_strategy( + struct aws_http_proxy_strategy_tunneling_chain *chain_strategy, + struct aws_http_message *message) { + size_t strategy_count = aws_array_list_length(&chain_strategy->strategies); + if (chain_strategy->current_strategy_transform_index >= strategy_count) { + goto on_error; + } + + struct aws_http_proxy_strategy *current_strategy = NULL; + if (aws_array_list_get_at( + &chain_strategy->strategies, ¤t_strategy, chain_strategy->current_strategy_transform_index)) { + goto on_error; + } + + current_strategy->strategy_vtable.tunnelling_vtable->connect_request_transform( + current_strategy, + message, + s_chain_tunnel_iteration_termination_callback, + s_chain_tunnel_iteration_forward_callback, + chain_strategy); + return; + +on_error: + + chain_strategy->original_strategy_termination_callback( + message, AWS_ERROR_HTTP_PROXY_STRATEGY_TRANSFORM_FAILED, chain_strategy->original_internal_proxy_user_data); +} + +static void s_chain_tunnel_transform_connect( + struct aws_http_proxy_strategy *proxy_strategy, + struct aws_http_message *message, + aws_http_proxy_strategy_terminate_fn *strategy_termination_callback, + aws_http_proxy_strategy_http_request_forward_fn *strategy_http_request_forward_callback, + void *internal_proxy_user_data) { + + struct aws_http_proxy_strategy_tunneling_chain *chain_strategy = proxy_strategy->impl; + + chain_strategy->current_strategy_transform_index = 0; + chain_strategy->original_internal_proxy_user_data = internal_proxy_user_data; + chain_strategy->original_strategy_termination_callback = strategy_termination_callback; + chain_strategy->original_strategy_http_request_forward_callback = strategy_http_request_forward_callback; + + s_chain_tunnel_try_next_strategy(chain_strategy, message); +} + +static int s_chain_on_incoming_headers( + struct aws_http_proxy_strategy *proxy_strategy, + enum aws_http_header_block header_block, + const struct aws_http_header *header_array, + size_t num_headers) { + + struct aws_http_proxy_strategy_tunneling_chain *chain_strategy = proxy_strategy->impl; + size_t strategy_count = aws_array_list_length(&chain_strategy->strategies); + for (size_t i = 0; i < strategy_count; ++i) { + struct aws_http_proxy_strategy *strategy = NULL; + if (aws_array_list_get_at(&chain_strategy->strategies, &strategy, i)) { + continue; + } + + aws_http_proxy_strategy_connect_on_incoming_headers_fn *on_incoming_headers = + strategy->strategy_vtable.tunnelling_vtable->on_incoming_headers_callback; + if (on_incoming_headers != NULL) { + (*on_incoming_headers)(strategy, header_block, header_array, num_headers); + } + } + + return AWS_OP_SUCCESS; +} + +static int s_chain_on_connect_status( + struct aws_http_proxy_strategy *proxy_strategy, + enum aws_http_status_code status_code) { + + struct aws_http_proxy_strategy_tunneling_chain *chain_strategy = proxy_strategy->impl; + size_t strategy_count = aws_array_list_length(&chain_strategy->strategies); + for (size_t i = 0; i < strategy_count; ++i) { + struct aws_http_proxy_strategy *strategy = NULL; + if (aws_array_list_get_at(&chain_strategy->strategies, &strategy, i)) { + continue; + } + + aws_http_proxy_strategy_connect_status_fn *on_status = + strategy->strategy_vtable.tunnelling_vtable->on_status_callback; + if (on_status != NULL) { + (*on_status)(strategy, status_code); + } + } + + return AWS_OP_SUCCESS; +} + +static int s_chain_on_incoming_body( + struct aws_http_proxy_strategy *proxy_strategy, + const struct aws_byte_cursor *data) { + + struct aws_http_proxy_strategy_tunneling_chain *chain_strategy = proxy_strategy->impl; + size_t strategy_count = aws_array_list_length(&chain_strategy->strategies); + for (size_t i = 0; i < strategy_count; ++i) { + struct aws_http_proxy_strategy *strategy = NULL; + if (aws_array_list_get_at(&chain_strategy->strategies, &strategy, i)) { + continue; + } + + aws_http_proxy_strategy_connect_on_incoming_body_fn *on_incoming_body = + strategy->strategy_vtable.tunnelling_vtable->on_incoming_body_callback; + if (on_incoming_body != NULL) { + (*on_incoming_body)(strategy, data); + } + } + + return AWS_OP_SUCCESS; +} + +static struct aws_http_proxy_strategy_tunnelling_vtable s_tunneling_chain_proxy_tunneling_vtable = { + .on_incoming_body_callback = s_chain_on_incoming_body, + .on_incoming_headers_callback = s_chain_on_incoming_headers, + .on_status_callback = s_chain_on_connect_status, + .connect_request_transform = s_chain_tunnel_transform_connect, +}; + +static void s_destroy_tunneling_chain_strategy(struct aws_http_proxy_strategy *proxy_strategy) { + struct aws_http_proxy_strategy_tunneling_chain *chain_strategy = proxy_strategy->impl; + + size_t strategy_count = aws_array_list_length(&chain_strategy->strategies); + for (size_t i = 0; i < strategy_count; ++i) { + struct aws_http_proxy_strategy *strategy = NULL; + if (aws_array_list_get_at(&chain_strategy->strategies, &strategy, i)) { + continue; + } + + aws_http_proxy_strategy_release(strategy); + } + + aws_array_list_clean_up(&chain_strategy->strategies); + + aws_mem_release(chain_strategy->allocator, chain_strategy); +} + +static struct aws_http_proxy_strategy *s_create_tunneling_chain_strategy( + struct aws_http_proxy_strategy_factory *proxy_strategy_factory, + struct aws_allocator *allocator) { + if (proxy_strategy_factory == NULL || allocator == NULL) { + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + return NULL; + } + + struct aws_http_proxy_strategy_tunneling_chain *chain_strategy = + aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_strategy_tunneling_chain)); + if (chain_strategy == NULL) { + return NULL; + } + + chain_strategy->allocator = allocator; + chain_strategy->strategy_base.impl = chain_strategy; + aws_ref_count_init( + &chain_strategy->strategy_base.ref_count, + &chain_strategy->strategy_base, + (aws_simple_completion_callback *)s_destroy_tunneling_chain_strategy); + + chain_strategy->strategy_base.strategy_vtable.tunnelling_vtable = &s_tunneling_chain_proxy_tunneling_vtable; + + struct aws_http_proxy_strategy_factory_tunneling_chain *chain_factory = proxy_strategy_factory->impl; + size_t strategy_count = aws_array_list_length(&chain_factory->strategy_factories); + + if (aws_array_list_init_dynamic( + &chain_strategy->strategies, allocator, strategy_count, sizeof(struct aws_http_proxy_strategy *))) { + goto on_error; + } + + for (size_t i = 0; i < strategy_count; ++i) { + struct aws_http_proxy_strategy_factory *factory = NULL; + if (aws_array_list_get_at(&chain_factory->strategy_factories, &factory, i)) { + goto on_error; + } + + struct aws_http_proxy_strategy *strategy = aws_http_proxy_strategy_factory_create_strategy(factory, allocator); + if (strategy == NULL) { + goto on_error; + } + + if (aws_array_list_push_back(&chain_strategy->strategies, &strategy)) { + aws_http_proxy_strategy_release(strategy); + goto on_error; + } + } + + return &chain_strategy->strategy_base; + +on_error: + + aws_http_proxy_strategy_release(&chain_strategy->strategy_base); + + return NULL; +} + +static struct aws_http_proxy_strategy_factory_vtable s_tunneling_chain_factory_vtable = { + .create_strategy = s_create_tunneling_chain_strategy, +}; + +static void s_destroy_tunneling_chain_factory(struct aws_http_proxy_strategy_factory *proxy_strategy_factory) { + struct aws_http_proxy_strategy_factory_tunneling_chain *chain_factory = proxy_strategy_factory->impl; + + size_t factory_count = aws_array_list_length(&chain_factory->strategy_factories); + for (size_t i = 0; i < factory_count; ++i) { + struct aws_http_proxy_strategy_factory *factory = NULL; + if (aws_array_list_get_at(&chain_factory->strategy_factories, &factory, i)) { + continue; + } + + aws_http_proxy_strategy_factory_release(factory); + } + + aws_array_list_clean_up(&chain_factory->strategy_factories); + + aws_mem_release(chain_factory->allocator, chain_factory); +} struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunneling_chain( struct aws_allocator *allocator, - struct aws_http_proxy_strategy_factory_tunneling_chain_options *config) {} -#endif + struct aws_http_proxy_strategy_factory_tunneling_chain_options *config) { + + if (allocator == NULL || config == NULL) { + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + return NULL; + } + + struct aws_http_proxy_strategy_factory_tunneling_chain *chain_factory = + aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_strategy_factory_tunneling_chain)); + if (chain_factory == NULL) { + return NULL; + } + + chain_factory->factory_base.impl = chain_factory; + chain_factory->factory_base.vtable = &s_tunneling_chain_factory_vtable; + chain_factory->factory_base.proxy_connection_type = AWS_HPCT_HTTP_TUNNEL; + chain_factory->allocator = allocator; + + aws_ref_count_init( + &chain_factory->factory_base.ref_count, + &chain_factory->factory_base, + (aws_simple_completion_callback *)s_destroy_tunneling_chain_factory); + + if (aws_array_list_init_dynamic( + &chain_factory->strategy_factories, + allocator, + config->factory_count, + sizeof(struct aws_http_proxy_strategy_factory *))) { + goto on_error; + } + + for (size_t i = 0; i < config->factory_count; ++i) { + struct aws_http_proxy_strategy_factory *factory = config->factories[i]; + + if (aws_array_list_push_back(&chain_factory->strategy_factories, &factory)) { + goto on_error; + } + + aws_http_proxy_strategy_factory_acquire(factory); + } + + return &chain_factory->factory_base; + +on_error: + + aws_http_proxy_strategy_factory_release(&chain_factory->factory_base); + + return NULL; +} + +/******************************************************************************************************************/ + +AWS_HTTP_API +struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunneling_adaptive_test( + struct aws_allocator *allocator, + struct aws_http_proxy_strategy_factory_tunneling_adaptive_test_options *config) { + + if (allocator == NULL || config == NULL) { + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + return NULL; + } + + struct aws_http_proxy_strategy_factory *bad_basic_factory = NULL; + struct aws_http_proxy_strategy_factory *good_basic_factory = NULL; + struct aws_http_proxy_strategy_factory *adaptive_factory = NULL; + + struct aws_http_proxy_strategy_factory_basic_auth_config bad_config = { + .proxy_connection_type = AWS_HPCT_HTTP_TUNNEL, + .password = aws_byte_cursor_from_c_str("NotAUsername"), + .user_name = aws_byte_cursor_from_c_str("NotAPassword"), + }; + + bad_basic_factory = aws_http_proxy_strategy_factory_new_basic_auth(allocator, &bad_config); + if (bad_basic_factory == NULL) { + goto on_error; + } + + struct aws_http_proxy_strategy_factory_basic_auth_config good_config = { + .proxy_connection_type = AWS_HPCT_HTTP_TUNNEL, + .password = config->password, + .user_name = config->user_name, + }; + + good_basic_factory = aws_http_proxy_strategy_factory_new_basic_auth(allocator, &good_config); + if (good_basic_factory == NULL) { + goto on_error; + } + + struct aws_http_proxy_strategy_factory *factories[2] = { + bad_basic_factory, + good_basic_factory, + }; + + struct aws_http_proxy_strategy_factory_tunneling_chain_options chain_config = { + .factories = factories, + .factory_count = 2, + }; + + adaptive_factory = aws_http_proxy_strategy_factory_new_tunneling_chain(allocator, &chain_config); + if (adaptive_factory == NULL) { + goto on_error; + } + + return adaptive_factory; + +on_error: + + aws_http_proxy_strategy_factory_release(bad_basic_factory); + aws_http_proxy_strategy_factory_release(good_basic_factory); + aws_http_proxy_strategy_factory_release(adaptive_factory); + + return NULL; +} From 36e21033a991d9bfa92fbec498dd1d855e5d03b0 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Mon, 14 Dec 2020 11:32:42 -0800 Subject: [PATCH 13/54] Stick with generic strategy types in chain callbacks --- source/proxy_strategy.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/source/proxy_strategy.c b/source/proxy_strategy.c index 98030e667..131e55d96 100644 --- a/source/proxy_strategy.c +++ b/source/proxy_strategy.c @@ -561,7 +561,7 @@ struct aws_http_proxy_strategy_tunneling_chain { }; static void s_chain_tunnel_try_next_strategy( - struct aws_http_proxy_strategy_tunneling_chain *chain_strategy, + struct aws_http_proxy_strategy *proxy_strategy, struct aws_http_message *message); static void s_chain_tunnel_iteration_termination_callback( @@ -576,7 +576,7 @@ static void s_chain_tunnel_iteration_termination_callback( chain_strategy->current_strategy_transform_index++; - s_chain_tunnel_try_next_strategy(chain_strategy, message); + s_chain_tunnel_try_next_strategy(proxy_strategy, message); } static void s_chain_tunnel_iteration_forward_callback(struct aws_http_message *message, void *user_data) { @@ -588,8 +588,10 @@ static void s_chain_tunnel_iteration_forward_callback(struct aws_http_message *m } static void s_chain_tunnel_try_next_strategy( - struct aws_http_proxy_strategy_tunneling_chain *chain_strategy, + struct aws_http_proxy_strategy *proxy_strategy, struct aws_http_message *message) { + struct aws_http_proxy_strategy_tunneling_chain *chain_strategy = proxy_strategy->impl; + size_t strategy_count = aws_array_list_length(&chain_strategy->strategies); if (chain_strategy->current_strategy_transform_index >= strategy_count) { goto on_error; @@ -629,7 +631,7 @@ static void s_chain_tunnel_transform_connect( chain_strategy->original_strategy_termination_callback = strategy_termination_callback; chain_strategy->original_strategy_http_request_forward_callback = strategy_http_request_forward_callback; - s_chain_tunnel_try_next_strategy(chain_strategy, message); + s_chain_tunnel_try_next_strategy(proxy_strategy, message); } static int s_chain_on_incoming_headers( From 35baf67339dbd169fd55429a7aff7eeea43a09f6 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Mon, 14 Dec 2020 11:37:56 -0800 Subject: [PATCH 14/54] Incorrect user data --- source/proxy_strategy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/proxy_strategy.c b/source/proxy_strategy.c index 131e55d96..d83c49518 100644 --- a/source/proxy_strategy.c +++ b/source/proxy_strategy.c @@ -608,7 +608,7 @@ static void s_chain_tunnel_try_next_strategy( message, s_chain_tunnel_iteration_termination_callback, s_chain_tunnel_iteration_forward_callback, - chain_strategy); + proxy_strategy); return; on_error: From 5372464477bfc6d42a536ac254c703d56937fa70 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Mon, 14 Dec 2020 11:58:16 -0800 Subject: [PATCH 15/54] Window fix --- source/proxy_connection.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/proxy_connection.c b/source/proxy_connection.c index 756e46cd0..661d15b12 100644 --- a/source/proxy_connection.c +++ b/source/proxy_connection.c @@ -294,6 +294,8 @@ static int s_aws_http_on_incoming_body_tunnel_proxy( (*on_incoming_body)(context->proxy_strategy, data); } + aws_http_stream_update_window(stream, data->len); + return AWS_OP_SUCCESS; } From 21cbe5433ac0450bfd2ce444e7ed7a8f7a820763 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Mon, 14 Dec 2020 12:09:52 -0800 Subject: [PATCH 16/54] Iterate connect attempts until strategy explicitly gives up --- source/proxy_connection.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/source/proxy_connection.c b/source/proxy_connection.c index 661d15b12..c15146eb4 100644 --- a/source/proxy_connection.c +++ b/source/proxy_connection.c @@ -377,6 +377,8 @@ static void s_on_origin_server_tls_negotation_result( context->original_on_setup(context->connection, AWS_ERROR_SUCCESS, context->original_user_data); } +static int s_make_proxy_connect_request(struct aws_http_proxy_user_data *user_data); + /* * Stream done callback for the CONNECT request made during tls proxy connections */ @@ -387,14 +389,15 @@ static void s_aws_http_on_stream_complete_tunnel_proxy( struct aws_http_proxy_user_data *context = user_data; AWS_FATAL_ASSERT(stream == context->connect_stream); - /* TODO strategy */ - if (context->error_code == AWS_ERROR_SUCCESS && error_code != AWS_ERROR_SUCCESS) { context->error_code = error_code; } if (context->error_code != AWS_ERROR_SUCCESS) { - s_aws_http_proxy_user_data_shutdown(context); + /* retry until strategy terminates */ + if (s_make_proxy_connect_request(context)) { + s_aws_http_proxy_user_data_shutdown(context); + } return; } @@ -502,6 +505,11 @@ static void s_continue_tunneling_connect(struct aws_http_message *message, void * of upgrading with TLS on success */ static int s_make_proxy_connect_request(struct aws_http_proxy_user_data *user_data) { + if (user_data->connect_request != NULL) { + aws_http_message_destroy(user_data->connect_request); + user_data->connect_request = NULL; + } + user_data->connect_request = s_build_proxy_connect_request(user_data); if (user_data->connect_request == NULL) { return AWS_OP_ERR; From f09633e8ef22d1e80a53071e7638c43f874c4647 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Mon, 14 Dec 2020 12:24:05 -0800 Subject: [PATCH 17/54] Error code fixes on proxy strategy loop and termination --- source/proxy_connection.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/proxy_connection.c b/source/proxy_connection.c index c15146eb4..985fce649 100644 --- a/source/proxy_connection.c +++ b/source/proxy_connection.c @@ -395,6 +395,7 @@ static void s_aws_http_on_stream_complete_tunnel_proxy( if (context->error_code != AWS_ERROR_SUCCESS) { /* retry until strategy terminates */ + context->error_code = 0; if (s_make_proxy_connect_request(context)) { s_aws_http_proxy_user_data_shutdown(context); } @@ -470,6 +471,7 @@ static void s_terminate_tunneling_connect( error_code, aws_error_str(error_code)); + proxy_ud->error_code = error_code; s_aws_http_proxy_user_data_shutdown(proxy_ud); } From 82a528fdc08522eb3e3403b2484a06d74154bcdd Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Mon, 14 Dec 2020 15:56:28 -0800 Subject: [PATCH 18/54] Stubbed adaptive kerberos support --- include/aws/http/proxy_strategy.h | 35 ++++- source/proxy_strategy.c | 232 +++++++++++++++++++++++++++++- 2 files changed, 259 insertions(+), 8 deletions(-) diff --git a/include/aws/http/proxy_strategy.h b/include/aws/http/proxy_strategy.h index 7ffd81e29..fc9f161ce 100644 --- a/include/aws/http/proxy_strategy.h +++ b/include/aws/http/proxy_strategy.h @@ -169,10 +169,8 @@ struct aws_http_proxy_strategy_factory_tunneling_chain_options { }; /* - * The adaptive test strategy attempts a vanilla CONNECT and if that fails it attempts a basic auth + * The adaptive test strategy attempts a bad basic CONNECT and if that fails it attempts a regular basic auth * CONNECT. - * - * SA TBI: the second stage should make a proper kerberos-aware CONNECT instead. */ struct aws_http_proxy_strategy_factory_tunneling_adaptive_test_options { /* user name to use in basic authentication */ @@ -182,6 +180,17 @@ struct aws_http_proxy_strategy_factory_tunneling_adaptive_test_options { struct aws_byte_cursor password; }; +/* + * SA-TBI: add any configuration needed for kerberos auth negotiation here + */ +struct aws_http_proxy_strategy_factory_tunneling_kerberos_options { + bool placeholder; +}; + +struct aws_http_proxy_strategy_factory_tunneling_adaptive_kerberos_options { + struct aws_http_proxy_strategy_factory_tunneling_kerberos_options kerberos_options; +}; + AWS_EXTERN_C_BEGIN /** @@ -277,9 +286,8 @@ struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_forw /** * This is an experimental API. * - * Constructor for a WIP adaptive tunneling proxy strategy. This strategy attempts a vanilla CONNECT and if that - * fails it attempts a basic auth CONNECT. The second CONNECT should have basic auth replaced with a kerberos or - * other network identity tech stage instead. + * Constructor for a WIP adaptive tunneling proxy strategy. This strategy attempts a bad basic auth CONNECT and if that + * fails it attempts a configurable basic auth CONNECT. * * @param allocator memory allocator to use * @param config configuration options for the strategy factory @@ -290,6 +298,21 @@ struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunn struct aws_allocator *allocator, struct aws_http_proxy_strategy_factory_tunneling_adaptive_test_options *config); +/** + * This is an experimental API. + * + * Constructor for a WIP adaptive tunneling proxy strategy. This strategy attempts a vanilla CONNECT and if that + * fails it attempts a kerberos-oriented CONNECT (if applicable). + * + * @param allocator memory allocator to use + * @param config configuration options for the strategy factory + * @return a new proxy strategy factory if successfully constructed, otherwise NULL + */ +AWS_HTTP_API +struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunneling_adaptive_kerberos( + struct aws_allocator *allocator, + struct aws_http_proxy_strategy_factory_tunneling_adaptive_kerberos_options *config); + AWS_EXTERN_C_END #endif /* AWS_PROXY_STRATEGY_H */ diff --git a/source/proxy_strategy.c b/source/proxy_strategy.c index d83c49518..0cf84d9c8 100644 --- a/source/proxy_strategy.c +++ b/source/proxy_strategy.c @@ -543,9 +543,9 @@ struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_forw struct aws_http_proxy_strategy_factory_tunneling_chain { struct aws_allocator *allocator; - struct aws_http_proxy_strategy_factory factory_base; - struct aws_array_list strategy_factories; + + struct aws_http_proxy_strategy_factory factory_base; }; struct aws_http_proxy_strategy_tunneling_chain { @@ -921,3 +921,231 @@ struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunn return NULL; } + +/******************************************************************************************************************/ + +struct aws_http_proxy_strategy_factory_tunneling_kerberos { + struct aws_allocator *allocator; + + struct aws_http_proxy_strategy_factory factory_base; + + /* SA-TBI: add any factory state needed here */ +}; + +struct aws_http_proxy_strategy_tunneling_kerberos { + struct aws_allocator *allocator; + + struct aws_http_proxy_strategy strategy_base; + + enum proxy_strategy_connect_state state; + + /* + * SA-TBI: add any factory state needed here + * + * Likely things include response code (from the vanilla CONNECT) and the appropriate headers in + * the response + */ +}; + +static void s_kerberos_tunnel_transform_connect( + struct aws_http_proxy_strategy *proxy_strategy, + struct aws_http_message *message, + aws_http_proxy_strategy_terminate_fn *strategy_termination_callback, + aws_http_proxy_strategy_http_request_forward_fn *strategy_http_request_forward_callback, + void *internal_proxy_user_data) { + + struct aws_http_proxy_strategy_tunneling_chain *kerberos_strategy = proxy_strategy->impl; + (void)kerberos_strategy; + (void)message; + (void)strategy_termination_callback; + (void)strategy_http_request_forward_callback; + (void)internal_proxy_user_data; + + /* + * SA-TBI: modify message with kerberos auth data and call the request_forward callback or if + * encountering an error, invoke the strategy_termination callback. + * + * As written, a connection attempt using this strategy will hang because neither of these are currently + * invoked. + */ +} + +static int s_kerberos_on_incoming_headers( + struct aws_http_proxy_strategy *proxy_strategy, + enum aws_http_header_block header_block, + const struct aws_http_header *header_array, + size_t num_headers) { + + struct aws_http_proxy_strategy_tunneling_chain *kerberos_strategy = proxy_strategy->impl; + (void)kerberos_strategy; + (void)header_block; + (void)header_array; + (void)num_headers; + + /* SA-TBI: process CONNECT response headers here if needed */ + + return AWS_OP_SUCCESS; +} + +static int s_kerberos_on_connect_status( + struct aws_http_proxy_strategy *proxy_strategy, + enum aws_http_status_code status_code) { + + struct aws_http_proxy_strategy_tunneling_chain *kerberos_strategy = proxy_strategy->impl; + (void)kerberos_strategy; + (void)status_code; + + /* SA-TBI: process status code of CONNECT request here if needed */ + + return AWS_OP_SUCCESS; +} + +static int s_kerberos_on_incoming_body( + struct aws_http_proxy_strategy *proxy_strategy, + const struct aws_byte_cursor *data) { + + struct aws_http_proxy_strategy_tunneling_chain *kerberos_strategy = proxy_strategy->impl; + (void)kerberos_strategy; + (void)data; + + /* SA-TBI: process body of CONNECT request here if needed */ + + return AWS_OP_SUCCESS; +} + +static struct aws_http_proxy_strategy_tunnelling_vtable s_tunneling_kerberos_proxy_tunneling_vtable = { + .on_incoming_body_callback = s_kerberos_on_incoming_body, + .on_incoming_headers_callback = s_kerberos_on_incoming_headers, + .on_status_callback = s_kerberos_on_connect_status, + .connect_request_transform = s_kerberos_tunnel_transform_connect, +}; + +static void s_destroy_tunneling_kerberos_strategy(struct aws_http_proxy_strategy *proxy_strategy) { + struct aws_http_proxy_strategy_tunneling_kerberos *kerberos_strategy = proxy_strategy->impl; + + /* SA-TBI: any special kerberos strategy clean up here */ + + aws_mem_release(kerberos_strategy->allocator, kerberos_strategy); +} + +static struct aws_http_proxy_strategy *s_create_tunneling_kerberos_strategy( + struct aws_http_proxy_strategy_factory *proxy_strategy_factory, + struct aws_allocator *allocator) { + if (proxy_strategy_factory == NULL || allocator == NULL) { + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + return NULL; + } + + struct aws_http_proxy_strategy_tunneling_kerberos *kerberos_strategy = + aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_strategy_tunneling_kerberos)); + if (kerberos_strategy == NULL) { + return NULL; + } + + kerberos_strategy->allocator = allocator; + kerberos_strategy->strategy_base.impl = kerberos_strategy; + aws_ref_count_init( + &kerberos_strategy->strategy_base.ref_count, + &kerberos_strategy->strategy_base, + (aws_simple_completion_callback *)s_destroy_tunneling_kerberos_strategy); + + kerberos_strategy->strategy_base.strategy_vtable.tunnelling_vtable = &s_tunneling_kerberos_proxy_tunneling_vtable; + + /* SA-TBI: special kerberos strategy init here */ + + return &kerberos_strategy->strategy_base; +} + +static struct aws_http_proxy_strategy_factory_vtable s_tunneling_kerberos_factory_vtable = { + .create_strategy = s_create_tunneling_kerberos_strategy, +}; + +static void s_destroy_tunneling_kerberos_factory(struct aws_http_proxy_strategy_factory *proxy_strategy_factory) { + struct aws_http_proxy_strategy_factory_tunneling_kerberos *kerberos_factory = proxy_strategy_factory->impl; + + /* SA-TBI: any special factory clean up here */ + + aws_mem_release(kerberos_factory->allocator, kerberos_factory); +} + +struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunneling_kerberos( + struct aws_allocator *allocator, + struct aws_http_proxy_strategy_factory_tunneling_kerberos_options *config) { + + if (allocator == NULL || config == NULL) { + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + return NULL; + } + + struct aws_http_proxy_strategy_factory_tunneling_kerberos *kerberos_factory = + aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_strategy_factory_tunneling_kerberos)); + if (kerberos_factory == NULL) { + return NULL; + } + + kerberos_factory->factory_base.impl = kerberos_factory; + kerberos_factory->factory_base.vtable = &s_tunneling_kerberos_factory_vtable; + kerberos_factory->factory_base.proxy_connection_type = AWS_HPCT_HTTP_TUNNEL; + kerberos_factory->allocator = allocator; + + aws_ref_count_init( + &kerberos_factory->factory_base.ref_count, + &kerberos_factory->factory_base, + (aws_simple_completion_callback *)s_destroy_tunneling_kerberos_factory); + + /* SA-TBI: any other factory init here */ + + return &kerberos_factory->factory_base; +} + +/******************************************************************************************************************/ + +AWS_HTTP_API +struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunneling_adaptive_kerberos( + struct aws_allocator *allocator, + struct aws_http_proxy_strategy_factory_tunneling_adaptive_kerberos_options *config) { + + if (allocator == NULL || config == NULL) { + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + return NULL; + } + + struct aws_http_proxy_strategy_factory *identity_factory = NULL; + struct aws_http_proxy_strategy_factory *kerberos_factory = NULL; + struct aws_http_proxy_strategy_factory *adaptive_factory = NULL; + + identity_factory = aws_http_proxy_strategy_factory_new_tunneling_one_time_identity(allocator); + if (identity_factory == NULL) { + goto on_error; + } + + kerberos_factory = aws_http_proxy_strategy_factory_new_tunneling_kerberos(allocator, &config->kerberos_options); + if (kerberos_factory == NULL) { + goto on_error; + } + + struct aws_http_proxy_strategy_factory *factories[2] = { + identity_factory, + kerberos_factory, + }; + + struct aws_http_proxy_strategy_factory_tunneling_chain_options chain_config = { + .factories = factories, + .factory_count = 2, + }; + + adaptive_factory = aws_http_proxy_strategy_factory_new_tunneling_chain(allocator, &chain_config); + if (adaptive_factory == NULL) { + goto on_error; + } + + return adaptive_factory; + +on_error: + + aws_http_proxy_strategy_factory_release(identity_factory); + aws_http_proxy_strategy_factory_release(kerberos_factory); + aws_http_proxy_strategy_factory_release(adaptive_factory); + + return NULL; +} From 0a7bf938ee2b96fa1598b9a8d5ef49a2277627d3 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Tue, 15 Dec 2020 08:17:00 -0800 Subject: [PATCH 19/54] Disable windows warning --- source/proxy_strategy.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/source/proxy_strategy.c b/source/proxy_strategy.c index 0cf84d9c8..7e0a16a05 100644 --- a/source/proxy_strategy.c +++ b/source/proxy_strategy.c @@ -8,6 +8,11 @@ #include #include +#if defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable : 4221) +#endif /* _MSC_VER */ + struct aws_http_proxy_strategy *aws_http_proxy_strategy_acquire(struct aws_http_proxy_strategy *proxy_strategy) { if (proxy_strategy != NULL) { aws_ref_count_acquire(&proxy_strategy->ref_count); @@ -896,13 +901,13 @@ struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunn goto on_error; } - struct aws_http_proxy_strategy_factory *factories[2] = { + struct aws_http_proxy_strategy_factory *factory_array[2] = { bad_basic_factory, good_basic_factory, }; struct aws_http_proxy_strategy_factory_tunneling_chain_options chain_config = { - .factories = factories, + .factories = factory_array, .factory_count = 2, }; @@ -1124,13 +1129,13 @@ struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunn goto on_error; } - struct aws_http_proxy_strategy_factory *factories[2] = { + struct aws_http_proxy_strategy_factory *factory_array[2] = { identity_factory, kerberos_factory, }; struct aws_http_proxy_strategy_factory_tunneling_chain_options chain_config = { - .factories = factories, + .factories = factory_array, .factory_count = 2, }; @@ -1149,3 +1154,7 @@ struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunn return NULL; } + +#if defined(_MSC_VER) +# pragma warning(pop) +#endif /* _MSC_VER */ From 444f4f36fba23d4da877954d28af9f229438de74 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Thu, 7 Jan 2021 13:31:54 -0800 Subject: [PATCH 20/54] Propagate proxy connection type --- include/aws/http/private/proxy_impl.h | 2 ++ source/proxy_connection.c | 3 +++ 2 files changed, 5 insertions(+) diff --git a/include/aws/http/private/proxy_impl.h b/include/aws/http/private/proxy_impl.h index 058f80d32..f25f64e95 100644 --- a/include/aws/http/private/proxy_impl.h +++ b/include/aws/http/private/proxy_impl.h @@ -37,6 +37,8 @@ struct aws_http_proxy_config { struct aws_allocator *allocator; + enum aws_http_proxy_connection_type connection_type; + struct aws_byte_buf host; uint16_t port; diff --git a/source/proxy_connection.c b/source/proxy_connection.c index 985fce649..d012e70f4 100644 --- a/source/proxy_connection.c +++ b/source/proxy_connection.c @@ -800,6 +800,8 @@ struct aws_http_proxy_config *aws_http_proxy_config_new( return NULL; } + config->connection_type = options->connection_type; + if (aws_byte_buf_init_copy_from_cursor(&config->host, allocator, options->host)) { goto on_error; } @@ -867,6 +869,7 @@ void aws_http_proxy_options_init_from_config( const struct aws_http_proxy_config *config) { AWS_FATAL_ASSERT(options && config); + options->connection_type = config->connection_type; options->host = aws_byte_cursor_from_buf(&config->host); options->port = config->port; options->tls_options = config->tls_options; From 3143a50410f40100c7a23758a4c16341800ac397 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Wed, 13 Jan 2021 12:57:55 -0800 Subject: [PATCH 21/54] WIP --- include/aws/http/connection.h | 10 +++++++++- source/proxy_connection.c | 17 ++++++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/include/aws/http/connection.h b/include/aws/http/connection.h index efb970ff1..d2f73f6e5 100644 --- a/include/aws/http/connection.h +++ b/include/aws/http/connection.h @@ -125,11 +125,19 @@ struct aws_http_connection_monitoring_options { * Supported proxy connection types */ enum aws_http_proxy_connection_type { + /** + * Deprecated, but 0-valued for backwards compatibility + * + * If tls options are provided (for the main connection) then treat the proxy as a tunneling proxy + * If tls options are not provided (for the main connection), then treate the proxy as a forwarding proxy + */ + AWS_HPCT_HTTP_LEGACY = 0, + /** * Use the proxy to forward http requests. Attempting to use both this mode and TLS on the tunnel destination * is a configuration error. */ - AWS_HPCT_HTTP_FORWARD = 0, + AWS_HPCT_HTTP_FORWARD, /** * Use the proxy to establish an http connection via a CONNECT request to the proxy. Works for both plaintext and diff --git a/source/proxy_connection.c b/source/proxy_connection.c index d012e70f4..90e335f81 100644 --- a/source/proxy_connection.c +++ b/source/proxy_connection.c @@ -771,6 +771,19 @@ static int s_aws_http_client_connect_via_tunneling_proxy(const struct aws_http_c return result; } +static enum aws_http_proxy_connection_type s_determine_proxy_connection_type(const struct aws_http_client_connection_options *options) { + enum aws_http_proxy_connection_type proxy_connection_type = options->proxy_options->connection_type; + if (proxy_connection_type != AWS_HPCT_HTTP_LEGACY) { + return proxy_connection_type; + } + + if (options->tls_options != NULL) { + return AWS_HPCT_HTTP_TUNNEL; + } else { + return AWS_HPCT_HTTP_FORWARD; + } +} + /* * Dispatches a proxy-enabled connection request to the appropriate top-level connection function */ @@ -779,7 +792,9 @@ int aws_http_client_connect_via_proxy(const struct aws_http_client_connection_op return AWS_OP_ERR; } - switch (options->proxy_options->connection_type) { + enum aws_http_proxy_connection_type proxy_connection_type = s_determine_proxy_connection_type(options); + + switch (proxy_connection_type) { case AWS_HPCT_HTTP_FORWARD: return s_aws_http_client_connect_via_forwarding_proxy(options); From 9916a927489157f9206d1f790f8dfd38f2b3ef8b Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Thu, 14 Jan 2021 13:14:27 -0800 Subject: [PATCH 22/54] Backwards compat prep; type cast fix --- include/aws/http/private/proxy_impl.h | 10 ++++- source/connection_manager.c | 2 +- source/proxy_connection.c | 61 ++++++++++++++++++++------- source/proxy_strategy.c | 8 ++-- 4 files changed, 58 insertions(+), 23 deletions(-) diff --git a/include/aws/http/private/proxy_impl.h b/include/aws/http/private/proxy_impl.h index f25f64e95..9edcbb53c 100644 --- a/include/aws/http/private/proxy_impl.h +++ b/include/aws/http/private/proxy_impl.h @@ -10,6 +10,7 @@ #include +struct aws_http_connection_manager_options; struct aws_http_message; struct aws_channel_slot; struct aws_string; @@ -103,9 +104,14 @@ AWS_HTTP_API void aws_http_proxy_system_set_vtable(struct aws_http_proxy_system_vtable *vtable); AWS_HTTP_API -struct aws_http_proxy_config *aws_http_proxy_config_new( +struct aws_http_proxy_config *aws_http_proxy_config_new_from_connection_options( struct aws_allocator *allocator, - const struct aws_http_proxy_options *options); + const struct aws_http_client_connection_options *options); + +AWS_HTTP_API +struct aws_http_proxy_config *aws_http_proxy_config_new_from_manager_options( + struct aws_allocator *allocator, + const struct aws_http_connection_manager_options *options); AWS_HTTP_API void aws_http_proxy_config_destroy(struct aws_http_proxy_config *config); diff --git a/source/connection_manager.c b/source/connection_manager.c index 3d90849bf..90f3cd33b 100644 --- a/source/connection_manager.c +++ b/source/connection_manager.c @@ -802,7 +802,7 @@ struct aws_http_connection_manager *aws_http_connection_manager_new( } if (options->proxy_options) { - manager->proxy_config = aws_http_proxy_config_new(allocator, options->proxy_options); + manager->proxy_config = aws_http_proxy_config_new_from_manager_options(allocator, options); if (manager->proxy_config == NULL) { goto on_error; } diff --git a/source/proxy_connection.c b/source/proxy_connection.c index 90e335f81..95fde5cb9 100644 --- a/source/proxy_connection.c +++ b/source/proxy_connection.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -79,7 +80,7 @@ struct aws_http_proxy_user_data *aws_http_proxy_user_data_new( user_data->original_port = options->port; - user_data->proxy_config = aws_http_proxy_config_new(allocator, options->proxy_options); + user_data->proxy_config = aws_http_proxy_config_new_from_connection_options(allocator, options); if (user_data->proxy_config == NULL) { goto on_error; } @@ -771,13 +772,14 @@ static int s_aws_http_client_connect_via_tunneling_proxy(const struct aws_http_c return result; } -static enum aws_http_proxy_connection_type s_determine_proxy_connection_type(const struct aws_http_client_connection_options *options) { - enum aws_http_proxy_connection_type proxy_connection_type = options->proxy_options->connection_type; +static enum aws_http_proxy_connection_type s_determine_proxy_connection_type( + enum aws_http_proxy_connection_type proxy_connection_type, + const struct aws_tls_connection_options *tls_options) { if (proxy_connection_type != AWS_HPCT_HTTP_LEGACY) { return proxy_connection_type; } - if (options->tls_options != NULL) { + if (tls_options != NULL) { return AWS_HPCT_HTTP_TUNNEL; } else { return AWS_HPCT_HTTP_FORWARD; @@ -792,7 +794,8 @@ int aws_http_client_connect_via_proxy(const struct aws_http_client_connection_op return AWS_OP_ERR; } - enum aws_http_proxy_connection_type proxy_connection_type = s_determine_proxy_connection_type(options); + enum aws_http_proxy_connection_type proxy_connection_type = + s_determine_proxy_connection_type(options->proxy_options->connection_type, options->tls_options); switch (proxy_connection_type) { case AWS_HPCT_HTTP_FORWARD: @@ -806,35 +809,37 @@ int aws_http_client_connect_via_proxy(const struct aws_http_client_connection_op } } -struct aws_http_proxy_config *aws_http_proxy_config_new( +static struct aws_http_proxy_config *s_aws_http_proxy_config_new( struct aws_allocator *allocator, - const struct aws_http_proxy_options *options) { - AWS_FATAL_ASSERT(options != NULL); + const struct aws_http_proxy_options *proxy_options, + enum aws_http_proxy_connection_type override_proxy_connection_type) { + AWS_FATAL_ASSERT(proxy_options != NULL); + struct aws_http_proxy_config *config = aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_config)); if (config == NULL) { return NULL; } - config->connection_type = options->connection_type; + config->connection_type = override_proxy_connection_type; - if (aws_byte_buf_init_copy_from_cursor(&config->host, allocator, options->host)) { + if (aws_byte_buf_init_copy_from_cursor(&config->host, allocator, proxy_options->host)) { goto on_error; } - if (options->tls_options) { + if (proxy_options->tls_options) { config->tls_options = aws_mem_calloc(allocator, 1, sizeof(struct aws_tls_connection_options)); - if (aws_tls_connection_options_copy(config->tls_options, options->tls_options)) { + if (aws_tls_connection_options_copy(config->tls_options, proxy_options->tls_options)) { goto on_error; } } config->allocator = allocator; - config->port = options->port; + config->port = proxy_options->port; - if (options->proxy_strategy_factory != NULL) { - config->proxy_strategy_factory = aws_http_proxy_strategy_factory_acquire(options->proxy_strategy_factory); + if (proxy_options->proxy_strategy_factory != NULL) { + config->proxy_strategy_factory = aws_http_proxy_strategy_factory_acquire(proxy_options->proxy_strategy_factory); } else { - switch (options->connection_type) { + switch (override_proxy_connection_type) { case AWS_HPCT_HTTP_FORWARD: config->proxy_strategy_factory = aws_http_proxy_strategy_factory_new_forwarding_identity(allocator); break; @@ -862,6 +867,30 @@ struct aws_http_proxy_config *aws_http_proxy_config_new( return NULL; } +struct aws_http_proxy_config *aws_http_proxy_config_new_from_connection_options( + struct aws_allocator *allocator, + const struct aws_http_client_connection_options *options) { + AWS_FATAL_ASSERT(options != NULL); + AWS_FATAL_ASSERT(options->proxy_options != NULL); + + return s_aws_http_proxy_config_new( + allocator, + options->proxy_options, + s_determine_proxy_connection_type(options->proxy_options->connection_type, options->tls_options)); +} + +struct aws_http_proxy_config *aws_http_proxy_config_new_from_manager_options( + struct aws_allocator *allocator, + const struct aws_http_connection_manager_options *options) { + AWS_FATAL_ASSERT(options != NULL); + AWS_FATAL_ASSERT(options->proxy_options != NULL); + + return s_aws_http_proxy_config_new( + allocator, + options->proxy_options, + s_determine_proxy_connection_type(options->proxy_options->connection_type, options->tls_connection_options)); +} + void aws_http_proxy_config_destroy(struct aws_http_proxy_config *config) { if (config == NULL) { return; diff --git a/source/proxy_strategy.c b/source/proxy_strategy.c index 7e0a16a05..8ccc40ca2 100644 --- a/source/proxy_strategy.c +++ b/source/proxy_strategy.c @@ -959,7 +959,7 @@ static void s_kerberos_tunnel_transform_connect( aws_http_proxy_strategy_http_request_forward_fn *strategy_http_request_forward_callback, void *internal_proxy_user_data) { - struct aws_http_proxy_strategy_tunneling_chain *kerberos_strategy = proxy_strategy->impl; + struct aws_http_proxy_strategy_tunneling_kerberos *kerberos_strategy = proxy_strategy->impl; (void)kerberos_strategy; (void)message; (void)strategy_termination_callback; @@ -981,7 +981,7 @@ static int s_kerberos_on_incoming_headers( const struct aws_http_header *header_array, size_t num_headers) { - struct aws_http_proxy_strategy_tunneling_chain *kerberos_strategy = proxy_strategy->impl; + struct aws_http_proxy_strategy_tunneling_kerberos *kerberos_strategy = proxy_strategy->impl; (void)kerberos_strategy; (void)header_block; (void)header_array; @@ -996,7 +996,7 @@ static int s_kerberos_on_connect_status( struct aws_http_proxy_strategy *proxy_strategy, enum aws_http_status_code status_code) { - struct aws_http_proxy_strategy_tunneling_chain *kerberos_strategy = proxy_strategy->impl; + struct aws_http_proxy_strategy_tunneling_kerberos *kerberos_strategy = proxy_strategy->impl; (void)kerberos_strategy; (void)status_code; @@ -1009,7 +1009,7 @@ static int s_kerberos_on_incoming_body( struct aws_http_proxy_strategy *proxy_strategy, const struct aws_byte_cursor *data) { - struct aws_http_proxy_strategy_tunneling_chain *kerberos_strategy = proxy_strategy->impl; + struct aws_http_proxy_strategy_tunneling_kerberos *kerberos_strategy = proxy_strategy->impl; (void)kerberos_strategy; (void)data; From ac1f02aa5066d2587caad41bf8a930c4988a6b38 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Tue, 19 Jan 2021 11:47:32 -0800 Subject: [PATCH 23/54] Nested tls integration test --- tests/CMakeLists.txt | 1 + tests/integration_test_proxy.c | 79 ++++++++++++++++++++++++++++----- tests/test_connection_manager.c | 31 ++++++++++--- 3 files changed, 95 insertions(+), 16 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index b50c138c1..5152b9df5 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -509,6 +509,7 @@ if (ENABLE_PROXY_INTEGRATION_TESTS) add_test_case(test_http_proxy_connection_get) add_test_case(test_https_proxy_connection_new_destroy) add_test_case(test_https_proxy_connection_get) + add_test_case(test_nested_https_proxy_connection_get) endif() add_test_case(test_http_connection_monitor_options_is_valid) diff --git a/tests/integration_test_proxy.c b/tests/integration_test_proxy.c index f66244b43..5b33bbab5 100644 --- a/tests/integration_test_proxy.c +++ b/tests/integration_test_proxy.c @@ -3,6 +3,7 @@ * SPDX-License-Identifier: Apache-2.0. */ +#include #include #include #include @@ -13,6 +14,9 @@ #include "proxy_test_helper.h" +#define HTTP_PROXY_PORT 3128 +#define HTTPS_PROXY_PORT 3129 + static struct proxy_tester tester; static int s_response_status_code = 0; @@ -76,14 +80,20 @@ static void s_aws_http_on_stream_complete_proxy_test(struct aws_http_stream *str aws_condition_variable_notify_one(&context->wait_cvar); } +AWS_STATIC_STRING_FROM_LITERAL(s_proxy_host_name, "localhost"); + static int s_setup_proxy_test( struct aws_allocator *allocator, struct aws_byte_cursor host, - enum proxy_tester_test_mode test_mode) { + enum proxy_tester_test_mode test_mode, + uint16_t port, + struct aws_tls_connection_options *tls_connection_options) { + struct aws_http_proxy_options proxy_options = { .connection_type = test_mode == PTTM_HTTP_FORWARD ? AWS_HPCT_HTTP_FORWARD : AWS_HPCT_HTTP_TUNNEL, - .host = aws_byte_cursor_from_c_str("127.0.0.1"), - .port = 3128, + .host = aws_byte_cursor_from_string(s_proxy_host_name), + .port = port, + .tls_options = tls_connection_options, }; struct proxy_tester_options options = { @@ -106,8 +116,10 @@ static int s_do_proxy_request_test( struct aws_byte_cursor host, enum proxy_tester_test_mode test_mode, struct aws_byte_cursor method, - struct aws_byte_cursor path) { - ASSERT_SUCCESS(s_setup_proxy_test(allocator, host, test_mode)); + struct aws_byte_cursor path, + uint16_t port, + struct aws_tls_connection_options *tls_connection_options) { + ASSERT_SUCCESS(s_setup_proxy_test(allocator, host, test_mode, port, tls_connection_options)); ASSERT_TRUE(tester.wait_result == AWS_ERROR_SUCCESS); struct aws_http_message *request = aws_http_message_new_request(allocator); @@ -156,7 +168,8 @@ static int s_do_proxy_request_test( static int s_test_http_proxy_connection_new_destroy(struct aws_allocator *allocator, void *ctx) { (void)ctx; - ASSERT_SUCCESS(s_setup_proxy_test(allocator, aws_byte_cursor_from_c_str("example.org"), PTTM_HTTP_FORWARD)); + ASSERT_SUCCESS(s_setup_proxy_test( + allocator, aws_byte_cursor_from_c_str("example.org"), PTTM_HTTP_FORWARD, HTTP_PROXY_PORT, NULL)); ASSERT_TRUE(tester.wait_result == AWS_ERROR_SUCCESS); ASSERT_SUCCESS(proxy_tester_clean_up(&tester)); @@ -173,14 +186,17 @@ static int s_test_http_proxy_connection_get(struct aws_allocator *allocator, voi aws_byte_cursor_from_c_str("example.org"), PTTM_HTTP_FORWARD, aws_byte_cursor_from_c_str("GET"), - aws_byte_cursor_from_c_str("/")); + aws_byte_cursor_from_c_str("/"), + HTTP_PROXY_PORT, + NULL); } AWS_TEST_CASE(test_http_proxy_connection_get, s_test_http_proxy_connection_get); static int s_test_https_proxy_connection_new_destroy(struct aws_allocator *allocator, void *ctx) { (void)ctx; - ASSERT_SUCCESS(s_setup_proxy_test(allocator, aws_byte_cursor_from_c_str("aws.amazon.com"), PTTM_HTTPS_TUNNEL)); + ASSERT_SUCCESS(s_setup_proxy_test( + allocator, aws_byte_cursor_from_c_str("aws.amazon.com"), PTTM_HTTPS_TUNNEL, HTTP_PROXY_PORT, NULL)); ASSERT_TRUE(tester.wait_result == AWS_ERROR_SUCCESS); ASSERT_SUCCESS(proxy_tester_clean_up(&tester)); @@ -197,7 +213,9 @@ static int s_test_https_proxy_connection_get(struct aws_allocator *allocator, vo aws_byte_cursor_from_c_str("aws.amazon.com"), PTTM_HTTPS_TUNNEL, aws_byte_cursor_from_c_str("GET"), - aws_byte_cursor_from_c_str("/")); + aws_byte_cursor_from_c_str("/"), + HTTP_PROXY_PORT, + NULL); } AWS_TEST_CASE(test_https_proxy_connection_get, s_test_https_proxy_connection_get); @@ -209,6 +227,47 @@ static int s_test_http_proxy_connection_options_star(struct aws_allocator *alloc aws_byte_cursor_from_c_str("example.org"), PTTM_HTTP_FORWARD, aws_byte_cursor_from_c_str("OPTIONS"), - aws_byte_cursor_from_c_str("*")); + aws_byte_cursor_from_c_str("*"), + HTTP_PROXY_PORT, + NULL); } AWS_TEST_CASE(test_http_proxy_connection_options_star, s_test_http_proxy_connection_options_star); + +static int s_test_nested_https_proxy_connection_get(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + aws_http_library_init(allocator); + + struct aws_tls_ctx_options tls_ctx_options; + AWS_ZERO_STRUCT(tls_ctx_options); + + aws_tls_ctx_options_init_default_client(&tls_ctx_options, allocator); + aws_tls_ctx_options_set_alpn_list(&tls_ctx_options, "http/1.1"); + tls_ctx_options.verify_peer = false; + + struct aws_tls_ctx *tls_ctx = aws_tls_client_ctx_new(allocator, &tls_ctx_options); + + struct aws_tls_connection_options tls_connection_options; + AWS_ZERO_STRUCT(tls_connection_options); + + aws_tls_connection_options_init_from_ctx(&tls_connection_options, tls_ctx); + + struct aws_byte_cursor host_name_cursor = aws_byte_cursor_from_string(s_proxy_host_name); + aws_tls_connection_options_set_server_name(&tls_connection_options, allocator, &host_name_cursor); + + ASSERT_SUCCESS(s_do_proxy_request_test( + allocator, + aws_byte_cursor_from_c_str("aws.amazon.com"), + PTTM_HTTPS_TUNNEL, + aws_byte_cursor_from_c_str("GET"), + aws_byte_cursor_from_c_str("/"), + HTTPS_PROXY_PORT, + &tls_connection_options)); + + aws_tls_connection_options_clean_up(&tls_connection_options); + aws_tls_ctx_release(tls_ctx); + aws_tls_ctx_options_clean_up(&tls_ctx_options); + + return AWS_OP_SUCCESS; +} +AWS_TEST_CASE(test_nested_https_proxy_connection_get, s_test_nested_https_proxy_connection_get); diff --git a/tests/test_connection_manager.c b/tests/test_connection_manager.c index 874e113fe..a3daec5b4 100644 --- a/tests/test_connection_manager.c +++ b/tests/test_connection_manager.c @@ -21,7 +21,11 @@ #include #include -enum new_connection_result_type { AWS_NCRT_SUCCESS, AWS_NCRT_ERROR_VIA_CALLBACK, AWS_NCRT_ERROR_FROM_CREATE }; +enum new_connection_result_type { + AWS_NCRT_SUCCESS, + AWS_NCRT_ERROR_VIA_CALLBACK, + AWS_NCRT_ERROR_FROM_CREATE, +}; struct mock_connection { enum new_connection_result_type result; @@ -390,7 +394,10 @@ static int s_cm_tester_clean_up(void) { static int s_test_connection_manager_setup_shutdown(struct aws_allocator *allocator, void *ctx) { (void)ctx; - struct cm_tester_options options = {.allocator = allocator, .max_connections = 5}; + struct cm_tester_options options = { + .allocator = allocator, + .max_connections = 5, + }; ASSERT_SUCCESS(s_cm_tester_init(&options)); @@ -403,7 +410,10 @@ AWS_TEST_CASE(test_connection_manager_setup_shutdown, s_test_connection_manager_ static int s_test_connection_manager_single_connection(struct aws_allocator *allocator, void *ctx) { (void)ctx; - struct cm_tester_options options = {.allocator = allocator, .max_connections = 5}; + struct cm_tester_options options = { + .allocator = allocator, + .max_connections = 5, + }; ASSERT_SUCCESS(s_cm_tester_init(&options)); @@ -422,7 +432,10 @@ AWS_TEST_CASE(test_connection_manager_single_connection, s_test_connection_manag static int s_test_connection_manager_many_connections(struct aws_allocator *allocator, void *ctx) { (void)ctx; - struct cm_tester_options options = {.allocator = allocator, .max_connections = 20}; + struct cm_tester_options options = { + .allocator = allocator, + .max_connections = 20, + }; ASSERT_SUCCESS(s_cm_tester_init(&options)); @@ -441,7 +454,10 @@ AWS_TEST_CASE(test_connection_manager_many_connections, s_test_connection_manage static int s_test_connection_manager_acquire_release(struct aws_allocator *allocator, void *ctx) { (void)ctx; - struct cm_tester_options options = {.allocator = allocator, .max_connections = 4}; + struct cm_tester_options options = { + .allocator = allocator, + .max_connections = 4, + }; ASSERT_SUCCESS(s_cm_tester_init(&options)); @@ -464,7 +480,10 @@ AWS_TEST_CASE(test_connection_manager_acquire_release, s_test_connection_manager static int s_test_connection_manager_close_and_release(struct aws_allocator *allocator, void *ctx) { (void)ctx; - struct cm_tester_options options = {.allocator = allocator, .max_connections = 4}; + struct cm_tester_options options = { + .allocator = allocator, + .max_connections = 4, + }; ASSERT_SUCCESS(s_cm_tester_init(&options)); From 170778b71245263e95f5a7e06f27df5fed7a4cd6 Mon Sep 17 00:00:00 2001 From: Abhishek Jain <61713632+ajainaus@users.noreply.github.com> Date: Wed, 20 Jan 2021 15:27:16 -0600 Subject: [PATCH 24/54] =?UTF-8?q?add=20code=20for=20kerberos=20strategy=20?= =?UTF-8?q?with=20user=20token=20as=20input(straight)=20&=20m=E2=80=A6=20(?= =?UTF-8?q?#299)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add code for kerberos strategy with user token as input(straight) & modify CMakeList.txt to compile with 4221 warning * add function to get kerberos user token * add proxy connection type * add ntlm proxy strategy type * add proxy connection type * add ntlm strategy * remove the exception for wd4221 warning from CMakeList.txt file * add callback functions * add callback functions * modify header generation function for ntlm & kerberos * comment api for callback functions and add callback function thru config while creating strategy * remove enum define for sub-state,comment callback function API's and add it thru config * add ntlm adaptive strategy as a part of kerberos chain and rename function API to cleary state that both Kerberos & NTLM are being configured Co-authored-by: Bret Ambrose --- include/aws/http/proxy_strategy.h | 103 +++- source/proxy_strategy.c | 863 +++++++++++++++++++++++++++++- 2 files changed, 947 insertions(+), 19 deletions(-) diff --git a/include/aws/http/proxy_strategy.h b/include/aws/http/proxy_strategy.h index fc9f161ce..55103d765 100644 --- a/include/aws/http/proxy_strategy.h +++ b/include/aws/http/proxy_strategy.h @@ -18,6 +18,29 @@ struct aws_http_header; struct aws_http_proxy_strategy; struct aws_http_proxy_strategy_factory; +/*SA-Added Start*/ + +/*enum defination for callback state*/ +enum proxy_strategy_callback_state { + AWS_KERB_TOKEN, + AWS_NTLM_CRED, + AWS_NTLM_RESP, +}; + +/** + * User-supplied callback function that send data to user + *(example NTLM challenge received from proxy server) + */ +typedef void (*aws_http_proxy_send_user_data_callback_fn)(size_t data_length, uint8_t *data, void *userdata); + +/** + * User-supplied callback function that gets user data depending on callback state + *(example NTLM credentials/response) + */ +typedef char* (*aws_http_proxy_get_user_data_callback_fn)(int callback_state, void *userdata); + +/*SA-Added End*/ + /** * Proxy strategy logic must call this function to indicate an unsuccessful outcome */ @@ -185,12 +208,37 @@ struct aws_http_proxy_strategy_factory_tunneling_adaptive_test_options { */ struct aws_http_proxy_strategy_factory_tunneling_kerberos_options { bool placeholder; + aws_http_proxy_send_user_data_callback_fn func_1; + aws_http_proxy_get_user_data_callback_fn func_2; + void *userData; }; struct aws_http_proxy_strategy_factory_tunneling_adaptive_kerberos_options { struct aws_http_proxy_strategy_factory_tunneling_kerberos_options kerberos_options; + }; + +/*SA-Added Start*/ + struct aws_http_proxy_strategy_factory_kerberos_auth_config { + + /* type of proxy connection being established, must be forwarding or tunnel */ + enum aws_http_proxy_connection_type proxy_connection_type; + + /* user token to use in kerberos authentication which is base64 encoded and provided by user*/ + struct aws_byte_cursor user_token; + }; + +struct aws_http_proxy_strategy_factory_tunneling_ntlm_options { + bool placeholder; + aws_http_proxy_send_user_data_callback_fn func_1; + aws_http_proxy_get_user_data_callback_fn func_2; + void *userData; }; +struct aws_http_proxy_strategy_factory_tunneling_adaptive_ntlm_options { + struct aws_http_proxy_strategy_factory_tunneling_ntlm_options ntlm_options; +}; + +/*SA-Added End*/ AWS_EXTERN_C_BEGIN /** @@ -302,16 +350,65 @@ struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunn * This is an experimental API. * * Constructor for a WIP adaptive tunneling proxy strategy. This strategy attempts a vanilla CONNECT and if that - * fails it attempts a kerberos-oriented CONNECT (if applicable). + * fails it attempts a kerberos-oriented CONNECT request followed by a NTLM-oriented CONNECT request (if applicable). + * + * @param allocator memory allocator to use + * @param config configuration options for the strategy factory + * @return a new proxy strategy factory if successfully constructed, otherwise NULL + */ +AWS_HTTP_API +struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunneling_adaptive_kerberos_ntlm( + struct aws_allocator *allocator, + struct aws_http_proxy_strategy_factory_tunneling_adaptive_kerberos_options *kerberos_config, + struct aws_http_proxy_strategy_factory_tunneling_adaptive_ntlm_options *ntlm_config); + +/*SA-Added Start*/ + +/** + * A constructor for a proxy strategy factory that performs kerberos authentication by adding the appropriate + * header and header value to requests or CONNECT requests. + * + * @param allocator memory allocator to use + * @param config kerberos authentication configuration info + * @return a new proxy strategy factory if successfully constructed, otherwise NULL + */ +AWS_HTTP_API +struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_kerberos_auth( + struct aws_allocator *allocator, + struct aws_http_proxy_strategy_factory_kerberos_auth_config *config); + +/** + * This is an experimental API. + * + * Constructor for a WIP adaptive tunneling NTLM proxy strategy. This strategy attempts a vanilla CONNECT and if that + * fails it attempts a ntlm-oriented CONNECT (if applicable). * * @param allocator memory allocator to use * @param config configuration options for the strategy factory * @return a new proxy strategy factory if successfully constructed, otherwise NULL */ AWS_HTTP_API -struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunneling_adaptive_kerberos( +struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunneling_adaptive_ntlm( struct aws_allocator *allocator, - struct aws_http_proxy_strategy_factory_tunneling_adaptive_kerberos_options *config); + struct aws_http_proxy_strategy_factory_tunneling_adaptive_ntlm_options *config); + +/** + * This is an experimental API. + * + * Constructor for callback functions + * + * @param callback function for sending user data to user, example - NTLM chalenge + * @param callback function for getting user data, example - NTLM Cred,NTLM Response, Kerberos Token + * @return NULL + */ +/* +AWS_HTTP_API +int aws_http_proxy_connection_configure_callback( + aws_http_proxy_send_user_data_callback_fn func_1, + aws_http_proxy_get_user_data_callback_fn func_2, + void *userdata); +*/ +/*SA-Added End*/ AWS_EXTERN_C_END diff --git a/source/proxy_strategy.c b/source/proxy_strategy.c index 8ccc40ca2..eb2772b05 100644 --- a/source/proxy_strategy.c +++ b/source/proxy_strategy.c @@ -13,6 +13,50 @@ # pragma warning(disable : 4221) #endif /* _MSC_VER */ +/*SA-Added Start*/ +/* +#if defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable : 4152) +#endif /* _MSC_VER */ +/* +typedef void (*proxy_callback_fn_t_1)(); +typedef char*(*proxy_callback_fn_t_2)(); + +typedef struct { + proxy_callback_fn_t_2 func_1; + proxy_callback_fn_t_2 func_2; + unsigned user; + void *userdata; +} proxy_connection_callback_t; + +static proxy_connection_callback_t proxy_connection_callback; +*/ +/* function to initialize callback functions */ +/*static int aws_http_proxy_connection_init_callback(void *func_1,void *func_2, int user, void *userdata) { + + proxy_connection_callback.func_1 = func_1; + proxy_connection_callback.func_2 = func_2; + proxy_connection_callback.user = user; + proxy_connection_callback.userdata = userdata; + + return 0; +}*/ + +/* function to initialize callback functions */ + +/*int aws_http_proxy_connection_configure_callback( + aws_http_proxy_send_user_data_callback_fn func_1, + aws_http_proxy_get_user_data_callback_fn func_2, + void *userdata) { + + aws_http_proxy_connection_init_callback(func_1, func_2, 1, userdata); + + return 0; +}*/ + +/*SA-Added End*/ + struct aws_http_proxy_strategy *aws_http_proxy_strategy_acquire(struct aws_http_proxy_strategy *proxy_strategy) { if (proxy_strategy != NULL) { aws_ref_count_acquire(&proxy_strategy->ref_count); @@ -53,7 +97,7 @@ void aws_http_proxy_strategy_factory_release(struct aws_http_proxy_strategy_fact } } -/******************************************************************************************************************/ +/*****************************************************************************************************************/ enum proxy_strategy_connect_state { AWS_PSCS_READY, @@ -62,12 +106,12 @@ enum proxy_strategy_connect_state { AWS_PSCS_FAILURE, }; +/* Functions for factory basic auth strategy with Basic Header */ + struct aws_http_proxy_strategy_factory_basic_auth { struct aws_allocator *allocator; - struct aws_string *user_name; struct aws_string *password; - struct aws_http_proxy_strategy_factory factory_base; }; @@ -322,6 +366,247 @@ struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_basi } /******************************************************************************************************************/ +/*SA-Added Start*/ +/*straight kerberos*/ + +struct aws_http_proxy_strategy_factory_kerberos_auth { + struct aws_allocator *allocator; + struct aws_string *user_token; + struct aws_http_proxy_strategy_factory factory_base; +}; + +static void s_destroy_kerberos_auth_factory(struct aws_http_proxy_strategy_factory *proxy_strategy_factory) { + struct aws_http_proxy_strategy_factory_kerberos_auth *kerberos_auth_factory = proxy_strategy_factory->impl; + + aws_string_destroy(kerberos_auth_factory->user_token); + + aws_mem_release(kerberos_auth_factory->allocator, kerberos_auth_factory); +} + +struct aws_http_proxy_strategy_kerberos_auth { + struct aws_allocator *allocator; + + struct aws_http_proxy_strategy_factory *factory; + + enum proxy_strategy_connect_state connect_state; + + struct aws_http_proxy_strategy strategy_base; +}; + +static void s_destroy_kerberos_auth_strategy(struct aws_http_proxy_strategy *proxy_strategy) { + struct aws_http_proxy_strategy_kerberos_auth *kerberos_auth_strategy = proxy_strategy->impl; + + aws_http_proxy_strategy_factory_release(kerberos_auth_strategy->factory); + + aws_mem_release(kerberos_auth_strategy->allocator, kerberos_auth_strategy); +} + +AWS_STATIC_STRING_FROM_LITERAL(s_proxy_authorization_header_kerberos_prefix, "Negotiate "); + +/* + * Adds a proxy authentication header based on the kerberos authentication mode + * Uses a token that is already base64 encoded + */ +static int s_add_kerberos_proxy_authentication_header( + struct aws_allocator *allocator, + struct aws_http_message *request, + struct aws_http_proxy_strategy_kerberos_auth *kerberos_auth_strategy) { + + struct aws_byte_buf header_value; + AWS_ZERO_STRUCT(header_value); + + int result = AWS_OP_ERR; + + struct aws_http_proxy_strategy_factory_kerberos_auth *factory = kerberos_auth_strategy->factory->impl; + + if (aws_byte_buf_init( + &header_value, allocator, s_proxy_authorization_header_kerberos_prefix->len+factory->user_token->len + 1)) { + goto done; + } + + /* First append proxy authorization header kerberos prefix" in it */ + struct aws_byte_cursor auth_header_cursor = aws_byte_cursor_from_string(s_proxy_authorization_header_kerberos_prefix); + if (aws_byte_buf_append(&header_value, &auth_header_cursor)) { + goto done; + } + + /* Append token to it " in it */ + struct aws_byte_cursor usertoken_cursor = aws_byte_cursor_from_string(factory->user_token); + if (aws_byte_buf_append(&header_value, &usertoken_cursor)) { + goto done; + } + + struct aws_http_header header = { + .name = aws_byte_cursor_from_string(s_proxy_authorization_header_name), + .value = aws_byte_cursor_from_array(header_value.buffer, header_value.len), + }; + + if (aws_http_message_add_header(request, header)) { + goto done; + } + + result = AWS_OP_SUCCESS; + +done: + aws_byte_buf_clean_up(&header_value); + + return result; +} + +int s_kerberos_auth_forward_add_header(struct aws_http_proxy_strategy *proxy_strategy, struct aws_http_message *message) { + struct aws_http_proxy_strategy_kerberos_auth *kerberos_auth_strategy = proxy_strategy->impl; + + return s_add_kerberos_proxy_authentication_header(kerberos_auth_strategy->allocator, message, kerberos_auth_strategy); +} + +void s_kerberos_auth_tunnel_add_header( + struct aws_http_proxy_strategy *proxy_strategy, + struct aws_http_message *message, + aws_http_proxy_strategy_terminate_fn *strategy_termination_callback, + aws_http_proxy_strategy_http_request_forward_fn *strategy_http_request_forward_callback, + void *internal_proxy_user_data) { + + struct aws_http_proxy_strategy_kerberos_auth *kerberos_auth_strategy = proxy_strategy->impl; + if (kerberos_auth_strategy->connect_state != AWS_PSCS_READY) { + strategy_termination_callback(message, AWS_ERROR_INVALID_STATE, internal_proxy_user_data); + return; + } + + kerberos_auth_strategy->connect_state = AWS_PSCS_IN_PROGRESS; + + if (s_add_kerberos_proxy_authentication_header(kerberos_auth_strategy->allocator, message, kerberos_auth_strategy)) { + strategy_termination_callback(message, aws_last_error(), internal_proxy_user_data); + return; + } + + strategy_http_request_forward_callback(message, internal_proxy_user_data); +} + +static int s_kerberos_on_incoming_header( + struct aws_http_proxy_strategy *proxy_strategy, + enum aws_http_header_block header_block, + const struct aws_http_header *header_array, + size_t num_headers) { + + struct aws_http_proxy_strategy_tunneling_kerberos *kerberos_strategy = proxy_strategy->impl; + (void)kerberos_strategy; + (void)header_block; + (void)header_array; + (void)num_headers; + + /* SA-TBI: process CONNECT response headers here if needed */ + + return AWS_OP_SUCCESS; +} + +static int s_kerberos_auth_on_connect_status( + struct aws_http_proxy_strategy *proxy_strategy, + enum aws_http_status_code status_code) { + struct aws_http_proxy_strategy_kerberos_auth *kerberos_auth_strategy = proxy_strategy->impl; + + if (kerberos_auth_strategy->connect_state == AWS_PSCS_IN_PROGRESS) { + if (AWS_HTTP_STATUS_CODE_200_OK != status_code) { + kerberos_auth_strategy->connect_state = AWS_PSCS_FAILURE; + } else { + kerberos_auth_strategy->connect_state = AWS_PSCS_SUCCESS; + } + } + return AWS_OP_SUCCESS; +} + +static struct aws_http_proxy_strategy_forwarding_vtable s_kerberos_auth_proxy_forwarding_vtable = { + .forward_request_transform = s_kerberos_auth_forward_add_header, +}; + +static struct aws_http_proxy_strategy_tunnelling_vtable s_kerberos_auth_proxy_tunneling_vtable = { + .on_status_callback = s_kerberos_auth_on_connect_status, + .connect_request_transform = s_kerberos_auth_tunnel_add_header, + .on_incoming_headers_callback = s_kerberos_on_incoming_header, +}; + + +static struct aws_http_proxy_strategy *s_create_kerberos_auth_strategy( + struct aws_http_proxy_strategy_factory *proxy_strategy_factory, + struct aws_allocator *allocator) { + if (proxy_strategy_factory == NULL || allocator == NULL) { + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + return NULL; + } + + struct aws_http_proxy_strategy_kerberos_auth *kerberos_auth_strategy = + aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_strategy_kerberos_auth)); + if (kerberos_auth_strategy == NULL) { + return NULL; + } + + kerberos_auth_strategy->allocator = allocator; + kerberos_auth_strategy->connect_state = AWS_PSCS_READY; + kerberos_auth_strategy->strategy_base.impl = kerberos_auth_strategy; + aws_ref_count_init( + &kerberos_auth_strategy->strategy_base.ref_count, + &kerberos_auth_strategy->strategy_base, + (aws_simple_completion_callback *)s_destroy_kerberos_auth_strategy); + + if (proxy_strategy_factory->proxy_connection_type == AWS_HPCT_HTTP_FORWARD) { + kerberos_auth_strategy->strategy_base.strategy_vtable.forwarding_vtable = &s_kerberos_auth_proxy_forwarding_vtable; + } else { + kerberos_auth_strategy->strategy_base.strategy_vtable.tunnelling_vtable = &s_kerberos_auth_proxy_tunneling_vtable; + } + + kerberos_auth_strategy->factory = aws_ref_count_acquire(&proxy_strategy_factory->ref_count); + + return &kerberos_auth_strategy->strategy_base; +} + +static struct aws_http_proxy_strategy_factory_vtable s_kerberos_auth_factory_vtable = { + .create_strategy = s_create_kerberos_auth_strategy, +}; + +struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_kerberos_auth( + struct aws_allocator *allocator, + struct aws_http_proxy_strategy_factory_kerberos_auth_config *config) { + if (config == NULL || allocator == NULL) { + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + return NULL; + } + + if (config->proxy_connection_type != AWS_HPCT_HTTP_FORWARD && + config->proxy_connection_type != AWS_HPCT_HTTP_TUNNEL) { + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + return NULL; + } + + struct aws_http_proxy_strategy_factory_kerberos_auth *kerberos_auth_factory = + aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_strategy_factory_kerberos_auth)); + if (kerberos_auth_factory == NULL) { + return NULL; + } + + kerberos_auth_factory->factory_base.impl = kerberos_auth_factory; + kerberos_auth_factory->factory_base.vtable = &s_kerberos_auth_factory_vtable; + kerberos_auth_factory->allocator = allocator; + kerberos_auth_factory->factory_base.proxy_connection_type = config->proxy_connection_type; + aws_ref_count_init( + &kerberos_auth_factory->factory_base.ref_count, + &kerberos_auth_factory->factory_base, + (aws_simple_completion_callback *)s_destroy_kerberos_auth_factory); + + kerberos_auth_factory->user_token = aws_string_new_from_cursor(allocator, &config->user_token); + if (kerberos_auth_factory->user_token == NULL) { + goto on_error; + } + + return &kerberos_auth_factory->factory_base; + +on_error: + + aws_http_proxy_strategy_factory_release(&kerberos_auth_factory->factory_base); + + return NULL; +} +/*SA-Added End*/ + +/*****************************************************************************************************************/ struct aws_http_proxy_strategy_factory_one_time_identity { struct aws_allocator *allocator; @@ -561,7 +846,7 @@ struct aws_http_proxy_strategy_tunneling_chain { void *original_internal_proxy_user_data; aws_http_proxy_strategy_terminate_fn *original_strategy_termination_callback; aws_http_proxy_strategy_http_request_forward_fn *original_strategy_http_request_forward_callback; - + struct aws_http_proxy_strategy strategy_base; }; @@ -928,11 +1213,15 @@ struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunn } /******************************************************************************************************************/ +/*adaptive kerberos*/ struct aws_http_proxy_strategy_factory_tunneling_kerberos { struct aws_allocator *allocator; struct aws_http_proxy_strategy_factory factory_base; + aws_http_proxy_send_user_data_callback_fn func_1; + aws_http_proxy_get_user_data_callback_fn func_2; + void *userdata; /* SA-TBI: add any factory state needed here */ }; @@ -942,7 +1231,9 @@ struct aws_http_proxy_strategy_tunneling_kerberos { struct aws_http_proxy_strategy strategy_base; - enum proxy_strategy_connect_state state; + struct aws_http_proxy_strategy_factory *factory; + + enum proxy_strategy_connect_state connect_state; /* * SA-TBI: add any factory state needed here @@ -952,6 +1243,55 @@ struct aws_http_proxy_strategy_tunneling_kerberos { */ }; +/*SA-Added Start*/ +/* + * Adds a proxy authentication header based on the user kerberos authentication token + * This uses a token that is already base64 encoded + */ +static int s_add_kerberos_proxy_usertoken_authentication_header( + struct aws_allocator *allocator, + struct aws_http_message *request, + struct aws_string *user_token) { + + struct aws_byte_buf header_value; + AWS_ZERO_STRUCT(header_value); + + int result = AWS_OP_ERR; + + if (aws_byte_buf_init( + &header_value, allocator, s_proxy_authorization_header_kerberos_prefix->len+user_token->len + 1)) { + goto done; + } + + /* First append proxy authorization header kerberos prefix" in it */ + struct aws_byte_cursor auth_header_cursor = aws_byte_cursor_from_string(s_proxy_authorization_header_kerberos_prefix); + if (aws_byte_buf_append(&header_value, &auth_header_cursor)) { + goto done; + } + + /* Append token to it " in it */ + struct aws_byte_cursor usertoken_cursor = aws_byte_cursor_from_string(user_token); + if (aws_byte_buf_append(&header_value, &usertoken_cursor)) { + goto done; + } + + struct aws_http_header header = { + .name = aws_byte_cursor_from_string(s_proxy_authorization_header_name), + .value = aws_byte_cursor_from_array(header_value.buffer, header_value.len), + }; + + if (aws_http_message_add_header(request, header)) { + goto done; + } + + result = AWS_OP_SUCCESS; + +done: + + aws_byte_buf_clean_up(&header_value); + return result; +} +/*SA-Added End*/ static void s_kerberos_tunnel_transform_connect( struct aws_http_proxy_strategy *proxy_strategy, struct aws_http_message *message, @@ -960,22 +1300,51 @@ static void s_kerberos_tunnel_transform_connect( void *internal_proxy_user_data) { struct aws_http_proxy_strategy_tunneling_kerberos *kerberos_strategy = proxy_strategy->impl; + struct aws_http_proxy_strategy_factory_tunneling_kerberos *kerberos_factory = kerberos_strategy->factory->impl; + (void)kerberos_strategy; + (void)kerberos_factory; (void)message; (void)strategy_termination_callback; (void)strategy_http_request_forward_callback; (void)internal_proxy_user_data; - - /* + + /* * SA-TBI: modify message with kerberos auth data and call the request_forward callback or if * encountering an error, invoke the strategy_termination callback. * * As written, a connection attempt using this strategy will hang because neither of these are currently * invoked. */ + /*SA-Added Start*/ + if (kerberos_strategy->connect_state != AWS_PSCS_READY) { + strategy_termination_callback(message, AWS_ERROR_INVALID_STATE, internal_proxy_user_data); + return; + } + + enum proxy_strategy_callback_state callback_state; + callback_state = AWS_KERB_TOKEN; + + + /* we first need to get the kerberos usertoken from user*/ + + char *kerberos_usertoken = (kerberos_factory->func_2)(callback_state, kerberos_factory->userdata); + //char *kerberos_usertoken = (proxy_connection_callback.func_2)(callback_state,proxy_connection_callback.userdata); + struct aws_byte_cursor kerberos_usertoken_tmp = aws_byte_cursor_from_c_str(kerberos_usertoken); + struct aws_string *kerberos_token = aws_string_new_from_cursor(kerberos_strategy->allocator, &kerberos_usertoken_tmp); + + /*transform the header with proxy authenticate:Negotiate and kerberos token*/ + if (s_add_kerberos_proxy_usertoken_authentication_header(kerberos_strategy->allocator, message, kerberos_token)) { + strategy_termination_callback(message, aws_last_error(), internal_proxy_user_data); + return; + } + + kerberos_strategy->connect_state = AWS_PSCS_IN_PROGRESS; + strategy_http_request_forward_callback(message, internal_proxy_user_data); + /*SA-Added End*/ } -static int s_kerberos_on_incoming_headers( +static int s_kerberos_on_incoming_header_adaptive( struct aws_http_proxy_strategy *proxy_strategy, enum aws_http_header_block header_block, const struct aws_http_header *header_array, @@ -989,6 +1358,10 @@ static int s_kerberos_on_incoming_headers( /* SA-TBI: process CONNECT response headers here if needed */ + if (kerberos_strategy->connect_state == AWS_PSCS_IN_PROGRESS) { + + } + return AWS_OP_SUCCESS; } @@ -1002,6 +1375,15 @@ static int s_kerberos_on_connect_status( /* SA-TBI: process status code of CONNECT request here if needed */ + if (kerberos_strategy->connect_state == AWS_PSCS_IN_PROGRESS) { + if (AWS_HTTP_STATUS_CODE_200_OK != status_code) { + kerberos_strategy->connect_state = AWS_PSCS_FAILURE; + } else { + kerberos_strategy->connect_state = AWS_PSCS_SUCCESS; + } + + } + return AWS_OP_SUCCESS; } @@ -1013,14 +1395,18 @@ static int s_kerberos_on_incoming_body( (void)kerberos_strategy; (void)data; - /* SA-TBI: process body of CONNECT request here if needed */ + if (kerberos_strategy->connect_state == AWS_PSCS_IN_PROGRESS) { + /* SA-TBI: process body of CONNECT request here if needed */ + + } + return AWS_OP_SUCCESS; } static struct aws_http_proxy_strategy_tunnelling_vtable s_tunneling_kerberos_proxy_tunneling_vtable = { .on_incoming_body_callback = s_kerberos_on_incoming_body, - .on_incoming_headers_callback = s_kerberos_on_incoming_headers, + .on_incoming_headers_callback = s_kerberos_on_incoming_header_adaptive, .on_status_callback = s_kerberos_on_connect_status, .connect_request_transform = s_kerberos_tunnel_transform_connect, }; @@ -1057,6 +1443,7 @@ static struct aws_http_proxy_strategy *s_create_tunneling_kerberos_strategy( kerberos_strategy->strategy_base.strategy_vtable.tunnelling_vtable = &s_tunneling_kerberos_proxy_tunneling_vtable; /* SA-TBI: special kerberos strategy init here */ + kerberos_strategy->factory = aws_ref_count_acquire(&proxy_strategy_factory->ref_count); return &kerberos_strategy->strategy_base; } @@ -1100,23 +1487,406 @@ struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunn /* SA-TBI: any other factory init here */ + kerberos_factory->func_1 = config->func_1; + kerberos_factory->func_2 = config->func_2; + + if (kerberos_factory->func_1 == NULL) { + goto on_error; + } + + if (kerberos_factory->func_2 == NULL) { + goto on_error; + } + return &kerberos_factory->factory_base; + +on_error: + + aws_http_proxy_strategy_factory_release(&kerberos_factory->factory_base); + + return NULL; } /******************************************************************************************************************/ +/*SA-Added End*/ +/*adaptive ntlm*/ -AWS_HTTP_API -struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunneling_adaptive_kerberos( +struct aws_http_proxy_strategy_factory_tunneling_ntlm { + struct aws_allocator *allocator; + + struct aws_http_proxy_strategy_factory factory_base; + + aws_http_proxy_send_user_data_callback_fn func_1; + aws_http_proxy_get_user_data_callback_fn func_2; + void *userdata; + + /* SA-TBI: add any factory state needed here */ +}; + +struct aws_http_proxy_strategy_tunneling_ntlm { + struct aws_allocator *allocator; + + struct aws_http_proxy_strategy strategy_base; + + struct aws_http_proxy_strategy_factory *factory; + + enum proxy_strategy_connect_state connect_state; + + enum proxy_strategy_connect_state connect_sub_state; + + /* + * SA-TBI: add any factory state needed here + * + * Likely things include response code (from the vanilla CONNECT) and the appropriate headers in + * the response + */ +}; + +AWS_STATIC_STRING_FROM_LITERAL(s_proxy_authorization_header_ntlm_prefix, "NTLM "); + +/* + * Adds a proxy authentication header based on ntlm credential or response provided by user + */ +static int s_add_ntlm_proxy_usertoken_authentication_header( + struct aws_allocator *allocator, + struct aws_http_message *request, + struct aws_string *credential_response) { + + struct aws_byte_buf header_value; + AWS_ZERO_STRUCT(header_value); + + int result = AWS_OP_ERR; + + if (aws_byte_buf_init( + &header_value, allocator, s_proxy_authorization_header_ntlm_prefix->len + credential_response->len + 1)) { + goto done; + } + + /* First append proxy authorization header prefix" in it */ + struct aws_byte_cursor auth_header_cursor = + aws_byte_cursor_from_string(s_proxy_authorization_header_ntlm_prefix); + if (aws_byte_buf_append(&header_value, &auth_header_cursor)) { + goto done; + } + + /* Append user data to it " in it */ + struct aws_byte_cursor user_credential_response = aws_byte_cursor_from_string(credential_response); + if (aws_byte_buf_append(&header_value, &user_credential_response)) { + goto done; + } + + struct aws_http_header header = { + .name = aws_byte_cursor_from_string(s_proxy_authorization_header_name), + .value = aws_byte_cursor_from_array(header_value.buffer, header_value.len), + }; + + if (aws_http_message_add_header(request, header)) { + goto done; + } + + result = AWS_OP_SUCCESS; + +done: + + aws_byte_buf_clean_up(&header_value); + return result; +} + +static void s_ntlm_tunnel_transform_connect( + struct aws_http_proxy_strategy *proxy_strategy, + struct aws_http_message *message, + aws_http_proxy_strategy_terminate_fn *strategy_termination_callback, + aws_http_proxy_strategy_http_request_forward_fn *strategy_http_request_forward_callback, + void *internal_proxy_user_data) { + + struct aws_http_proxy_strategy_tunneling_ntlm *ntlm_strategy = proxy_strategy->impl; + struct aws_http_proxy_strategy_factory_tunneling_ntlm *ntlm_factory = ntlm_strategy->factory->impl; + + (void)ntlm_strategy; + (void)ntlm_factory; + (void)message; + (void)strategy_termination_callback; + (void)strategy_http_request_forward_callback; + (void)internal_proxy_user_data; + + /* + * SA-TBI: modify message with ntlm auth data and call the request_forward callback or if + * encountering an error, invoke the strategy_termination callback. + * + * As written, a connection attempt using this strategy will hang because neither of these are currently + * invoked. + */ + + /*Terminate when both states are not AWS_PSCS_READY*/ + /* Why there is a sub_state logic - > For NTLM we have to first send the credentials with CONNECT Request + * This CONNECT request will yeild a challenge in response + * This challenge will be followed by a new CONNECT request with response in it + * Instead of creating 2 different strategies for 2x CONNECT request i have created a sub state logic in which + * the strategy is terminated only when both CONNECT request have resulted in a failure or else first a CONNECT + request with credentials go out followed by response*/ + + + if ((ntlm_strategy->connect_state != AWS_PSCS_READY) + &&(ntlm_strategy->connect_sub_state != AWS_PSCS_READY)) { + strategy_termination_callback(message, AWS_ERROR_INVALID_STATE, internal_proxy_user_data); + return; + } + + if (ntlm_strategy->connect_state == AWS_PSCS_READY) { + + /* we first need to get the ntlm credential from user*/ + + enum proxy_strategy_callback_state callback_state; + callback_state = AWS_NTLM_CRED; + + //char *ntlm_credential_user = (proxy_connection_callback.func_2)(callback_state, proxy_connection_callback.userdata); + char *ntlm_credential_user = (ntlm_factory->func_2)(callback_state, ntlm_factory->userdata); + struct aws_byte_cursor ntlm_credential_tmp = aws_byte_cursor_from_c_str(ntlm_credential_user); + struct aws_string *ntlm_credential = aws_string_new_from_cursor(ntlm_strategy->allocator, &ntlm_credential_tmp); + + /*transform the header with proxy authenticate:NTLM and NTLM credentials*/ + if (s_add_ntlm_proxy_usertoken_authentication_header(ntlm_strategy->allocator, message, ntlm_credential)) { + strategy_termination_callback(message, aws_last_error(), internal_proxy_user_data); + return; + } + } + + else if (ntlm_strategy->connect_sub_state == AWS_PSCS_READY) { + + enum proxy_strategy_callback_state callback_state; + callback_state = AWS_NTLM_RESP; + + //char *ntlm_response_user = (proxy_connection_callback.func_2)(callback_state, proxy_connection_callback.userdata); + char *ntlm_response_user = (ntlm_factory->func_2)(callback_state, ntlm_factory->userdata); + struct aws_byte_cursor ntlm_response_tmp = aws_byte_cursor_from_c_str(ntlm_response_user); + struct aws_string *ntlm_response = + aws_string_new_from_cursor(ntlm_strategy->allocator, &ntlm_response_tmp); + + /*transform the header with proxy authenticate:NTLM and the response for challenge received from user*/ + if (s_add_ntlm_proxy_usertoken_authentication_header(ntlm_strategy->allocator, message, ntlm_response)) { + strategy_termination_callback(message, aws_last_error(), internal_proxy_user_data); + return; + } + + ntlm_strategy->connect_sub_state = AWS_PSCS_IN_PROGRESS; + } + + ntlm_strategy->connect_state = AWS_PSCS_IN_PROGRESS; + strategy_http_request_forward_callback(message, internal_proxy_user_data); +} + +static int s_ntlm_on_incoming_header_adaptive( + struct aws_http_proxy_strategy *proxy_strategy, + enum aws_http_header_block header_block, + const struct aws_http_header *header_array, + size_t num_headers) { + + struct aws_http_proxy_strategy_tunneling_ntlm *ntlm_strategy = proxy_strategy->impl; + + struct aws_http_proxy_strategy_factory_tunneling_ntlm *ntlm_factory = ntlm_strategy->factory->impl; + + (void)ntlm_strategy; + (void)header_block; + (void)header_array; + (void)num_headers; + + /* SA-TBI: process CONNECT response headers here if needed */ + //transfer the challenge to user area only when connect state in progress & sub state is not in progress + if ((ntlm_strategy->connect_state == AWS_PSCS_IN_PROGRESS) + &&(ntlm_strategy->connect_sub_state != AWS_PSCS_IN_PROGRESS)) + { + /* This section of code checks for header block when connect and connect sub state are both in progress + ,the code locates the header in which the challenge has come and triggers a callback to send challenge to user*/ + if (header_block == AWS_HTTP_HEADER_BLOCK_MAIN) { + + uint8_t *header_name = header_array->name.ptr; + char array_header[100]; + if ((header_array->name.len) > 0) { + size_t header_length = header_array->name.len; + + for (size_t i = 0; i < header_array->name.len; ++i) { + + array_header[i] = (char)header_name[i]; + } + if (strncmp(array_header, "Proxy-Authenticate", header_length) == 0) { + + (ntlm_factory->func_1)(header_array->value.len, header_array->value.ptr, ntlm_factory->userdata); + //(proxy_connection_callback.func_1)( + //header_array->value.len, header_array->value.ptr, proxy_connection_callback.userdata); + } + } + } + } + + return AWS_OP_SUCCESS; +} + +static int s_ntlm_on_connect_status( + struct aws_http_proxy_strategy *proxy_strategy, + enum aws_http_status_code status_code) { + + struct aws_http_proxy_strategy_tunneling_ntlm *ntlm_strategy = proxy_strategy->impl; + (void)ntlm_strategy; + (void)status_code; + + if ((ntlm_strategy->connect_state == AWS_PSCS_IN_PROGRESS)&&(ntlm_strategy->connect_sub_state != AWS_PSCS_IN_PROGRESS)) { + if (AWS_HTTP_STATUS_CODE_200_OK != status_code) { + ntlm_strategy->connect_state = AWS_PSCS_FAILURE; + } else { + ntlm_strategy->connect_state = AWS_PSCS_SUCCESS; + } + } + + if ((ntlm_strategy->connect_state == AWS_PSCS_IN_PROGRESS)&&(ntlm_strategy->connect_sub_state == AWS_PSCS_IN_PROGRESS)) { + if (AWS_HTTP_STATUS_CODE_200_OK != status_code) { + ntlm_strategy->connect_state = AWS_PSCS_FAILURE; + ntlm_strategy->connect_sub_state = AWS_PSCS_FAILURE; + } else { + ntlm_strategy->connect_state = AWS_PSCS_SUCCESS; + ntlm_strategy->connect_sub_state = AWS_PSCS_SUCCESS; + } + } + return AWS_OP_SUCCESS; +} + +static int s_ntlm_on_incoming_body( + struct aws_http_proxy_strategy *proxy_strategy, + const struct aws_byte_cursor *data) { + + struct aws_http_proxy_strategy_tunneling_ntlm *ntlm_strategy = proxy_strategy->impl; + (void)ntlm_strategy; + (void)data; + + if (ntlm_strategy->connect_state == AWS_PSCS_IN_PROGRESS) { + + /* SA-TBI: process body of CONNECT request here if needed */ + } + + return AWS_OP_SUCCESS; +} + +static struct aws_http_proxy_strategy_tunnelling_vtable s_tunneling_ntlm_proxy_tunneling_vtable = { + .on_incoming_body_callback = s_ntlm_on_incoming_body, + .on_incoming_headers_callback = s_ntlm_on_incoming_header_adaptive, + .on_status_callback = s_ntlm_on_connect_status, + .connect_request_transform = s_ntlm_tunnel_transform_connect, +}; + +static void s_destroy_tunneling_ntlm_strategy(struct aws_http_proxy_strategy *proxy_strategy) { + struct aws_http_proxy_strategy_tunneling_ntlm *ntlm_strategy = proxy_strategy->impl; + + /* SA-TBI: any special ntlm strategy clean up here */ + + aws_mem_release(ntlm_strategy->allocator, ntlm_strategy); +} + +static struct aws_http_proxy_strategy *s_create_tunneling_ntlm_strategy( + struct aws_http_proxy_strategy_factory *proxy_strategy_factory, + struct aws_allocator *allocator) { + if (proxy_strategy_factory == NULL || allocator == NULL) { + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + return NULL; + } + + struct aws_http_proxy_strategy_tunneling_ntlm *ntlm_strategy = + aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_strategy_tunneling_ntlm)); + if (ntlm_strategy == NULL) { + return NULL; + } + + ntlm_strategy->allocator = allocator; + ntlm_strategy->strategy_base.impl = ntlm_strategy; + aws_ref_count_init( + &ntlm_strategy->strategy_base.ref_count, + &ntlm_strategy->strategy_base, + (aws_simple_completion_callback *)s_destroy_tunneling_ntlm_strategy); + + ntlm_strategy->strategy_base.strategy_vtable.tunnelling_vtable = &s_tunneling_ntlm_proxy_tunneling_vtable; + + /* SA-TBI: special ntlm strategy init here */ + + ntlm_strategy->factory = aws_ref_count_acquire(&proxy_strategy_factory->ref_count); + + return &ntlm_strategy->strategy_base; +} + + +static struct aws_http_proxy_strategy_factory_vtable s_tunneling_ntlm_factory_vtable = { + .create_strategy = s_create_tunneling_ntlm_strategy, +}; + +static void s_destroy_tunneling_ntlm_factory(struct aws_http_proxy_strategy_factory *proxy_strategy_factory) { + struct aws_http_proxy_strategy_factory_tunneling_ntlm *ntlm_factory = proxy_strategy_factory->impl; + + /* SA-TBI: any special factory clean up here */ + + aws_mem_release(ntlm_factory->allocator, ntlm_factory); +} + +struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunneling_ntlm( struct aws_allocator *allocator, - struct aws_http_proxy_strategy_factory_tunneling_adaptive_kerberos_options *config) { + struct aws_http_proxy_strategy_factory_tunneling_ntlm_options *config) { if (allocator == NULL || config == NULL) { aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); return NULL; } + struct aws_http_proxy_strategy_factory_tunneling_ntlm *ntlm_factory = + aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_strategy_factory_tunneling_ntlm)); + if (ntlm_factory == NULL) { + return NULL; + } + + ntlm_factory->factory_base.impl = ntlm_factory; + ntlm_factory->factory_base.vtable = &s_tunneling_ntlm_factory_vtable; + ntlm_factory->factory_base.proxy_connection_type = AWS_HPCT_HTTP_TUNNEL; + ntlm_factory->func_1 = config->func_1; + ntlm_factory->allocator = allocator; + + aws_ref_count_init( + &ntlm_factory->factory_base.ref_count, + &ntlm_factory->factory_base, + (aws_simple_completion_callback *)s_destroy_tunneling_ntlm_factory); + + /* SA-TBI: any other factory init here */ + + ntlm_factory->func_1 = config->func_1; + ntlm_factory->func_2 = config->func_2; + + if (ntlm_factory->func_1 == NULL) { + goto on_error; + } + + if (ntlm_factory->func_2 == NULL) { + goto on_error; + } + + return &ntlm_factory->factory_base; + + on_error: + + aws_http_proxy_strategy_factory_release(&ntlm_factory->factory_base); + + return NULL; +} +/*SA-Added End*/ +/******************************************************************************************************************/ + +AWS_HTTP_API +struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunneling_adaptive_kerberos_ntlm( + struct aws_allocator *allocator, + struct aws_http_proxy_strategy_factory_tunneling_adaptive_kerberos_options *kerberos_config, + struct aws_http_proxy_strategy_factory_tunneling_adaptive_ntlm_options *ntlm_config) { + + if (allocator == NULL || kerberos_config == NULL || ntlm_config == NULL) { + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + return NULL; + } + struct aws_http_proxy_strategy_factory *identity_factory = NULL; struct aws_http_proxy_strategy_factory *kerberos_factory = NULL; + struct aws_http_proxy_strategy_factory *ntlm_factory = NULL; struct aws_http_proxy_strategy_factory *adaptive_factory = NULL; identity_factory = aws_http_proxy_strategy_factory_new_tunneling_one_time_identity(allocator); @@ -1124,19 +1894,26 @@ struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunn goto on_error; } - kerberos_factory = aws_http_proxy_strategy_factory_new_tunneling_kerberos(allocator, &config->kerberos_options); + kerberos_factory = aws_http_proxy_strategy_factory_new_tunneling_kerberos(allocator, &kerberos_config->kerberos_options); if (kerberos_factory == NULL) { goto on_error; } - struct aws_http_proxy_strategy_factory *factory_array[2] = { + ntlm_factory = aws_http_proxy_strategy_factory_new_tunneling_ntlm(allocator, &ntlm_config->ntlm_options); + if (ntlm_factory == NULL) { + goto on_error; + } + + struct aws_http_proxy_strategy_factory *factory_array[3] = { identity_factory, kerberos_factory, + ntlm_factory, + }; struct aws_http_proxy_strategy_factory_tunneling_chain_options chain_config = { .factories = factory_array, - .factory_count = 2, + .factory_count = 3, }; adaptive_factory = aws_http_proxy_strategy_factory_new_tunneling_chain(allocator, &chain_config); @@ -1150,10 +1927,64 @@ struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunn aws_http_proxy_strategy_factory_release(identity_factory); aws_http_proxy_strategy_factory_release(kerberos_factory); + aws_http_proxy_strategy_factory_release(ntlm_factory); + aws_http_proxy_strategy_factory_release(adaptive_factory); + + return NULL; +} + +/******************************************************************************************************************/ +/*SA-Added Start*/ +AWS_HTTP_API +struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunneling_adaptive_ntlm( + struct aws_allocator *allocator, + struct aws_http_proxy_strategy_factory_tunneling_adaptive_ntlm_options *config) { + + if (allocator == NULL || config == NULL) { + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + return NULL; + } + + struct aws_http_proxy_strategy_factory *identity_factory = NULL; + struct aws_http_proxy_strategy_factory *ntlm_factory = NULL; + struct aws_http_proxy_strategy_factory *adaptive_factory = NULL; + + identity_factory = aws_http_proxy_strategy_factory_new_tunneling_one_time_identity(allocator); + if (identity_factory == NULL) { + goto on_error; + } + + ntlm_factory = aws_http_proxy_strategy_factory_new_tunneling_ntlm(allocator, &config->ntlm_options); + if (ntlm_factory == NULL) { + goto on_error; + } + + struct aws_http_proxy_strategy_factory *factory_array[2] = { + identity_factory, + ntlm_factory, + }; + + struct aws_http_proxy_strategy_factory_tunneling_chain_options chain_config = { + .factories = factory_array, + .factory_count = 2, + }; + + adaptive_factory = aws_http_proxy_strategy_factory_new_tunneling_chain(allocator, &chain_config); + if (adaptive_factory == NULL) { + goto on_error; + } + + return adaptive_factory; + +on_error: + + aws_http_proxy_strategy_factory_release(identity_factory); + aws_http_proxy_strategy_factory_release(ntlm_factory); aws_http_proxy_strategy_factory_release(adaptive_factory); return NULL; } +/*SA-Added End*/ #if defined(_MSC_VER) # pragma warning(pop) From 50bf433867d402c152dc7d63d43e406b65a87cb4 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Thu, 21 Jan 2021 09:39:39 -0800 Subject: [PATCH 25/54] First pass on sync kerberos/ntlm strategy api refactor --- include/aws/http/http.h | 2 + include/aws/http/proxy_strategy.h | 130 ++--- source/http.c | 6 + source/proxy_strategy.c | 847 ++++++------------------------ 4 files changed, 214 insertions(+), 771 deletions(-) diff --git a/include/aws/http/http.h b/include/aws/http/http.h index 9a8ce432e..fc7b9dd55 100644 --- a/include/aws/http/http.h +++ b/include/aws/http/http.h @@ -47,6 +47,8 @@ enum aws_http_errors { AWS_ERROR_HTTP_STREAM_NOT_ACTIVATED, AWS_ERROR_HTTP_STREAM_HAS_COMPLETED, AWS_ERROR_HTTP_PROXY_STRATEGY_TRANSFORM_FAILED, + AWS_ERROR_HTTP_PROXY_STRATEGY_FAILED_PREVIOUSLY, + AWS_ERROR_HTTP_PROXY_STRATEGY_NTLM_CHALLENGE_TOKEN_MISSING, AWS_ERROR_HTTP_END_RANGE = AWS_ERROR_ENUM_END_RANGE(AWS_C_HTTP_PACKAGE_ID) }; diff --git a/include/aws/http/proxy_strategy.h b/include/aws/http/proxy_strategy.h index 55103d765..ea1896fea 100644 --- a/include/aws/http/proxy_strategy.h +++ b/include/aws/http/proxy_strategy.h @@ -18,28 +18,30 @@ struct aws_http_header; struct aws_http_proxy_strategy; struct aws_http_proxy_strategy_factory; -/*SA-Added Start*/ - /*enum defination for callback state*/ enum proxy_strategy_callback_state { - AWS_KERB_TOKEN, - AWS_NTLM_CRED, - AWS_NTLM_RESP, + AWS_PSCS_KERB_TOKEN, + AWS_PSCS_NTLM_CRED, + AWS_PSCS_NTLM_RESP, }; /** - * User-supplied callback function that send data to user - *(example NTLM challenge received from proxy server) + * Synchronous (for now) callback function to fetch a token used in modifying CONNECT requests */ -typedef void (*aws_http_proxy_send_user_data_callback_fn)(size_t data_length, uint8_t *data, void *userdata); +typedef int(aws_http_proxy_strategy_get_token_sync_fn)( + void *user_data, + struct aws_byte_cursor *out_token_value, + int *out_error_code); /** - * User-supplied callback function that gets user data depending on callback state - *(example NTLM credentials/response) + * Synchronous (for now) callback function to fetch a token used in modifying CONNECT request. Includes a (byte string) + * context intended to be used as part of a challenge-response flow. */ -typedef char* (*aws_http_proxy_get_user_data_callback_fn)(int callback_state, void *userdata); - -/*SA-Added End*/ +typedef int(aws_http_proxy_strategy_get_challenge_token_sync_fn)( + void *user_data, + const struct aws_byte_cursor *challenge_context, + struct aws_byte_cursor *out_token_value, + int *out_error_code); /** * Proxy strategy logic must call this function to indicate an unsuccessful outcome @@ -191,54 +193,32 @@ struct aws_http_proxy_strategy_factory_tunneling_chain_options { uint32_t factory_count; }; -/* - * The adaptive test strategy attempts a bad basic CONNECT and if that fails it attempts a regular basic auth - * CONNECT. - */ -struct aws_http_proxy_strategy_factory_tunneling_adaptive_test_options { - /* user name to use in basic authentication */ - struct aws_byte_cursor user_name; - - /* password to use in basic authentication */ - struct aws_byte_cursor password; -}; - -/* - * SA-TBI: add any configuration needed for kerberos auth negotiation here - */ struct aws_http_proxy_strategy_factory_tunneling_kerberos_options { - bool placeholder; - aws_http_proxy_send_user_data_callback_fn func_1; - aws_http_proxy_get_user_data_callback_fn func_2; - void *userData; -}; -struct aws_http_proxy_strategy_factory_tunneling_adaptive_kerberos_options { - struct aws_http_proxy_strategy_factory_tunneling_kerberos_options kerberos_options; - }; + aws_http_proxy_strategy_get_token_sync_fn *get_token; -/*SA-Added Start*/ - struct aws_http_proxy_strategy_factory_kerberos_auth_config { + void *get_token_user_data; +}; - /* type of proxy connection being established, must be forwarding or tunnel */ - enum aws_http_proxy_connection_type proxy_connection_type; +struct aws_http_proxy_strategy_factory_tunneling_ntlm_options { - /* user token to use in kerberos authentication which is base64 encoded and provided by user*/ - struct aws_byte_cursor user_token; - }; + aws_http_proxy_strategy_get_challenge_token_sync_fn *get_challenge_token; -struct aws_http_proxy_strategy_factory_tunneling_ntlm_options { - bool placeholder; - aws_http_proxy_send_user_data_callback_fn func_1; - aws_http_proxy_get_user_data_callback_fn func_2; - void *userData; + void *get_challenge_token_user_data; }; -struct aws_http_proxy_strategy_factory_tunneling_adaptive_ntlm_options { - struct aws_http_proxy_strategy_factory_tunneling_ntlm_options ntlm_options; +struct aws_http_proxy_strategy_factory_tunneling_adaptive_options { + /* + * If non-null, will insert a kerberos proxy strategy into the adaptive chain + */ + struct aws_http_proxy_strategy_factory_tunneling_kerberos_options *kerberos_options; + + /* + * If non-null will insert an ntlm proxy strategy into the adaptive chain + */ + struct aws_http_proxy_strategy_factory_tunneling_ntlm_options *ntlm_options; }; -/*SA-Added End*/ AWS_EXTERN_C_BEGIN /** @@ -331,21 +311,6 @@ AWS_HTTP_API struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_forwarding_identity( struct aws_allocator *allocator); -/** - * This is an experimental API. - * - * Constructor for a WIP adaptive tunneling proxy strategy. This strategy attempts a bad basic auth CONNECT and if that - * fails it attempts a configurable basic auth CONNECT. - * - * @param allocator memory allocator to use - * @param config configuration options for the strategy factory - * @return a new proxy strategy factory if successfully constructed, otherwise NULL - */ -AWS_HTTP_API -struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunneling_adaptive_test( - struct aws_allocator *allocator, - struct aws_http_proxy_strategy_factory_tunneling_adaptive_test_options *config); - /** * This is an experimental API. * @@ -357,12 +322,9 @@ struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunn * @return a new proxy strategy factory if successfully constructed, otherwise NULL */ AWS_HTTP_API -struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunneling_adaptive_kerberos_ntlm( +struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunneling_adaptive( struct aws_allocator *allocator, - struct aws_http_proxy_strategy_factory_tunneling_adaptive_kerberos_options *kerberos_config, - struct aws_http_proxy_strategy_factory_tunneling_adaptive_ntlm_options *ntlm_config); - -/*SA-Added Start*/ + struct aws_http_proxy_strategy_factory_tunneling_adaptive_options *config); /** * A constructor for a proxy strategy factory that performs kerberos authentication by adding the appropriate @@ -373,9 +335,9 @@ struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunn * @return a new proxy strategy factory if successfully constructed, otherwise NULL */ AWS_HTTP_API -struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_kerberos_auth( +struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunneling_kerberos( struct aws_allocator *allocator, - struct aws_http_proxy_strategy_factory_kerberos_auth_config *config); + struct aws_http_proxy_strategy_factory_tunneling_kerberos_options *config); /** * This is an experimental API. @@ -388,27 +350,9 @@ struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_kerb * @return a new proxy strategy factory if successfully constructed, otherwise NULL */ AWS_HTTP_API -struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunneling_adaptive_ntlm( +struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunneling_ntlm( struct aws_allocator *allocator, - struct aws_http_proxy_strategy_factory_tunneling_adaptive_ntlm_options *config); - -/** - * This is an experimental API. - * - * Constructor for callback functions - * - * @param callback function for sending user data to user, example - NTLM chalenge - * @param callback function for getting user data, example - NTLM Cred,NTLM Response, Kerberos Token - * @return NULL - */ -/* -AWS_HTTP_API -int aws_http_proxy_connection_configure_callback( - aws_http_proxy_send_user_data_callback_fn func_1, - aws_http_proxy_get_user_data_callback_fn func_2, - void *userdata); -*/ -/*SA-Added End*/ + struct aws_http_proxy_strategy_factory_tunneling_ntlm_options *config); AWS_EXTERN_C_END diff --git a/source/http.c b/source/http.c index 3d79abb96..ba0053c51 100644 --- a/source/http.c +++ b/source/http.c @@ -118,6 +118,12 @@ static struct aws_error_info s_errors[] = { AWS_DEFINE_ERROR_INFO_HTTP( AWS_ERROR_HTTP_PROXY_STRATEGY_TRANSFORM_FAILED, "Proxy strategy transform has completely failed."), + AWS_DEFINE_ERROR_INFO_HTTP( + AWS_ERROR_HTTP_PROXY_STRATEGY_FAILED_PREVIOUSLY, + "Proxy strategy was previously tried and failed"), + AWS_DEFINE_ERROR_INFO_HTTP( + AWS_ERROR_HTTP_PROXY_STRATEGY_NTLM_CHALLENGE_TOKEN_MISSING, + "NTLM Proxy strategy was initiated without a challenge token"), }; /* clang-format on */ diff --git a/source/proxy_strategy.c b/source/proxy_strategy.c index eb2772b05..1eb5c4b95 100644 --- a/source/proxy_strategy.c +++ b/source/proxy_strategy.c @@ -13,50 +13,6 @@ # pragma warning(disable : 4221) #endif /* _MSC_VER */ -/*SA-Added Start*/ -/* -#if defined(_MSC_VER) -# pragma warning(push) -# pragma warning(disable : 4152) -#endif /* _MSC_VER */ -/* -typedef void (*proxy_callback_fn_t_1)(); -typedef char*(*proxy_callback_fn_t_2)(); - -typedef struct { - proxy_callback_fn_t_2 func_1; - proxy_callback_fn_t_2 func_2; - unsigned user; - void *userdata; -} proxy_connection_callback_t; - -static proxy_connection_callback_t proxy_connection_callback; -*/ -/* function to initialize callback functions */ -/*static int aws_http_proxy_connection_init_callback(void *func_1,void *func_2, int user, void *userdata) { - - proxy_connection_callback.func_1 = func_1; - proxy_connection_callback.func_2 = func_2; - proxy_connection_callback.user = user; - proxy_connection_callback.userdata = userdata; - - return 0; -}*/ - -/* function to initialize callback functions */ - -/*int aws_http_proxy_connection_configure_callback( - aws_http_proxy_send_user_data_callback_fn func_1, - aws_http_proxy_get_user_data_callback_fn func_2, - void *userdata) { - - aws_http_proxy_connection_init_callback(func_1, func_2, 1, userdata); - - return 0; -}*/ - -/*SA-Added End*/ - struct aws_http_proxy_strategy *aws_http_proxy_strategy_acquire(struct aws_http_proxy_strategy *proxy_strategy) { if (proxy_strategy != NULL) { aws_ref_count_acquire(&proxy_strategy->ref_count); @@ -365,247 +321,6 @@ struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_basi return NULL; } -/******************************************************************************************************************/ -/*SA-Added Start*/ -/*straight kerberos*/ - -struct aws_http_proxy_strategy_factory_kerberos_auth { - struct aws_allocator *allocator; - struct aws_string *user_token; - struct aws_http_proxy_strategy_factory factory_base; -}; - -static void s_destroy_kerberos_auth_factory(struct aws_http_proxy_strategy_factory *proxy_strategy_factory) { - struct aws_http_proxy_strategy_factory_kerberos_auth *kerberos_auth_factory = proxy_strategy_factory->impl; - - aws_string_destroy(kerberos_auth_factory->user_token); - - aws_mem_release(kerberos_auth_factory->allocator, kerberos_auth_factory); -} - -struct aws_http_proxy_strategy_kerberos_auth { - struct aws_allocator *allocator; - - struct aws_http_proxy_strategy_factory *factory; - - enum proxy_strategy_connect_state connect_state; - - struct aws_http_proxy_strategy strategy_base; -}; - -static void s_destroy_kerberos_auth_strategy(struct aws_http_proxy_strategy *proxy_strategy) { - struct aws_http_proxy_strategy_kerberos_auth *kerberos_auth_strategy = proxy_strategy->impl; - - aws_http_proxy_strategy_factory_release(kerberos_auth_strategy->factory); - - aws_mem_release(kerberos_auth_strategy->allocator, kerberos_auth_strategy); -} - -AWS_STATIC_STRING_FROM_LITERAL(s_proxy_authorization_header_kerberos_prefix, "Negotiate "); - -/* - * Adds a proxy authentication header based on the kerberos authentication mode - * Uses a token that is already base64 encoded - */ -static int s_add_kerberos_proxy_authentication_header( - struct aws_allocator *allocator, - struct aws_http_message *request, - struct aws_http_proxy_strategy_kerberos_auth *kerberos_auth_strategy) { - - struct aws_byte_buf header_value; - AWS_ZERO_STRUCT(header_value); - - int result = AWS_OP_ERR; - - struct aws_http_proxy_strategy_factory_kerberos_auth *factory = kerberos_auth_strategy->factory->impl; - - if (aws_byte_buf_init( - &header_value, allocator, s_proxy_authorization_header_kerberos_prefix->len+factory->user_token->len + 1)) { - goto done; - } - - /* First append proxy authorization header kerberos prefix" in it */ - struct aws_byte_cursor auth_header_cursor = aws_byte_cursor_from_string(s_proxy_authorization_header_kerberos_prefix); - if (aws_byte_buf_append(&header_value, &auth_header_cursor)) { - goto done; - } - - /* Append token to it " in it */ - struct aws_byte_cursor usertoken_cursor = aws_byte_cursor_from_string(factory->user_token); - if (aws_byte_buf_append(&header_value, &usertoken_cursor)) { - goto done; - } - - struct aws_http_header header = { - .name = aws_byte_cursor_from_string(s_proxy_authorization_header_name), - .value = aws_byte_cursor_from_array(header_value.buffer, header_value.len), - }; - - if (aws_http_message_add_header(request, header)) { - goto done; - } - - result = AWS_OP_SUCCESS; - -done: - aws_byte_buf_clean_up(&header_value); - - return result; -} - -int s_kerberos_auth_forward_add_header(struct aws_http_proxy_strategy *proxy_strategy, struct aws_http_message *message) { - struct aws_http_proxy_strategy_kerberos_auth *kerberos_auth_strategy = proxy_strategy->impl; - - return s_add_kerberos_proxy_authentication_header(kerberos_auth_strategy->allocator, message, kerberos_auth_strategy); -} - -void s_kerberos_auth_tunnel_add_header( - struct aws_http_proxy_strategy *proxy_strategy, - struct aws_http_message *message, - aws_http_proxy_strategy_terminate_fn *strategy_termination_callback, - aws_http_proxy_strategy_http_request_forward_fn *strategy_http_request_forward_callback, - void *internal_proxy_user_data) { - - struct aws_http_proxy_strategy_kerberos_auth *kerberos_auth_strategy = proxy_strategy->impl; - if (kerberos_auth_strategy->connect_state != AWS_PSCS_READY) { - strategy_termination_callback(message, AWS_ERROR_INVALID_STATE, internal_proxy_user_data); - return; - } - - kerberos_auth_strategy->connect_state = AWS_PSCS_IN_PROGRESS; - - if (s_add_kerberos_proxy_authentication_header(kerberos_auth_strategy->allocator, message, kerberos_auth_strategy)) { - strategy_termination_callback(message, aws_last_error(), internal_proxy_user_data); - return; - } - - strategy_http_request_forward_callback(message, internal_proxy_user_data); -} - -static int s_kerberos_on_incoming_header( - struct aws_http_proxy_strategy *proxy_strategy, - enum aws_http_header_block header_block, - const struct aws_http_header *header_array, - size_t num_headers) { - - struct aws_http_proxy_strategy_tunneling_kerberos *kerberos_strategy = proxy_strategy->impl; - (void)kerberos_strategy; - (void)header_block; - (void)header_array; - (void)num_headers; - - /* SA-TBI: process CONNECT response headers here if needed */ - - return AWS_OP_SUCCESS; -} - -static int s_kerberos_auth_on_connect_status( - struct aws_http_proxy_strategy *proxy_strategy, - enum aws_http_status_code status_code) { - struct aws_http_proxy_strategy_kerberos_auth *kerberos_auth_strategy = proxy_strategy->impl; - - if (kerberos_auth_strategy->connect_state == AWS_PSCS_IN_PROGRESS) { - if (AWS_HTTP_STATUS_CODE_200_OK != status_code) { - kerberos_auth_strategy->connect_state = AWS_PSCS_FAILURE; - } else { - kerberos_auth_strategy->connect_state = AWS_PSCS_SUCCESS; - } - } - return AWS_OP_SUCCESS; -} - -static struct aws_http_proxy_strategy_forwarding_vtable s_kerberos_auth_proxy_forwarding_vtable = { - .forward_request_transform = s_kerberos_auth_forward_add_header, -}; - -static struct aws_http_proxy_strategy_tunnelling_vtable s_kerberos_auth_proxy_tunneling_vtable = { - .on_status_callback = s_kerberos_auth_on_connect_status, - .connect_request_transform = s_kerberos_auth_tunnel_add_header, - .on_incoming_headers_callback = s_kerberos_on_incoming_header, -}; - - -static struct aws_http_proxy_strategy *s_create_kerberos_auth_strategy( - struct aws_http_proxy_strategy_factory *proxy_strategy_factory, - struct aws_allocator *allocator) { - if (proxy_strategy_factory == NULL || allocator == NULL) { - aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); - return NULL; - } - - struct aws_http_proxy_strategy_kerberos_auth *kerberos_auth_strategy = - aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_strategy_kerberos_auth)); - if (kerberos_auth_strategy == NULL) { - return NULL; - } - - kerberos_auth_strategy->allocator = allocator; - kerberos_auth_strategy->connect_state = AWS_PSCS_READY; - kerberos_auth_strategy->strategy_base.impl = kerberos_auth_strategy; - aws_ref_count_init( - &kerberos_auth_strategy->strategy_base.ref_count, - &kerberos_auth_strategy->strategy_base, - (aws_simple_completion_callback *)s_destroy_kerberos_auth_strategy); - - if (proxy_strategy_factory->proxy_connection_type == AWS_HPCT_HTTP_FORWARD) { - kerberos_auth_strategy->strategy_base.strategy_vtable.forwarding_vtable = &s_kerberos_auth_proxy_forwarding_vtable; - } else { - kerberos_auth_strategy->strategy_base.strategy_vtable.tunnelling_vtable = &s_kerberos_auth_proxy_tunneling_vtable; - } - - kerberos_auth_strategy->factory = aws_ref_count_acquire(&proxy_strategy_factory->ref_count); - - return &kerberos_auth_strategy->strategy_base; -} - -static struct aws_http_proxy_strategy_factory_vtable s_kerberos_auth_factory_vtable = { - .create_strategy = s_create_kerberos_auth_strategy, -}; - -struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_kerberos_auth( - struct aws_allocator *allocator, - struct aws_http_proxy_strategy_factory_kerberos_auth_config *config) { - if (config == NULL || allocator == NULL) { - aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); - return NULL; - } - - if (config->proxy_connection_type != AWS_HPCT_HTTP_FORWARD && - config->proxy_connection_type != AWS_HPCT_HTTP_TUNNEL) { - aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); - return NULL; - } - - struct aws_http_proxy_strategy_factory_kerberos_auth *kerberos_auth_factory = - aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_strategy_factory_kerberos_auth)); - if (kerberos_auth_factory == NULL) { - return NULL; - } - - kerberos_auth_factory->factory_base.impl = kerberos_auth_factory; - kerberos_auth_factory->factory_base.vtable = &s_kerberos_auth_factory_vtable; - kerberos_auth_factory->allocator = allocator; - kerberos_auth_factory->factory_base.proxy_connection_type = config->proxy_connection_type; - aws_ref_count_init( - &kerberos_auth_factory->factory_base.ref_count, - &kerberos_auth_factory->factory_base, - (aws_simple_completion_callback *)s_destroy_kerberos_auth_factory); - - kerberos_auth_factory->user_token = aws_string_new_from_cursor(allocator, &config->user_token); - if (kerberos_auth_factory->user_token == NULL) { - goto on_error; - } - - return &kerberos_auth_factory->factory_base; - -on_error: - - aws_http_proxy_strategy_factory_release(&kerberos_auth_factory->factory_base); - - return NULL; -} -/*SA-Added End*/ - /*****************************************************************************************************************/ struct aws_http_proxy_strategy_factory_one_time_identity { @@ -846,7 +561,7 @@ struct aws_http_proxy_strategy_tunneling_chain { void *original_internal_proxy_user_data; aws_http_proxy_strategy_terminate_fn *original_strategy_termination_callback; aws_http_proxy_strategy_http_request_forward_fn *original_strategy_http_request_forward_callback; - + struct aws_http_proxy_strategy strategy_base; }; @@ -1149,101 +864,37 @@ struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunn } /******************************************************************************************************************/ +/* kerberos */ -AWS_HTTP_API -struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunneling_adaptive_test( - struct aws_allocator *allocator, - struct aws_http_proxy_strategy_factory_tunneling_adaptive_test_options *config) { - - if (allocator == NULL || config == NULL) { - aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); - return NULL; - } - - struct aws_http_proxy_strategy_factory *bad_basic_factory = NULL; - struct aws_http_proxy_strategy_factory *good_basic_factory = NULL; - struct aws_http_proxy_strategy_factory *adaptive_factory = NULL; - - struct aws_http_proxy_strategy_factory_basic_auth_config bad_config = { - .proxy_connection_type = AWS_HPCT_HTTP_TUNNEL, - .password = aws_byte_cursor_from_c_str("NotAUsername"), - .user_name = aws_byte_cursor_from_c_str("NotAPassword"), - }; - - bad_basic_factory = aws_http_proxy_strategy_factory_new_basic_auth(allocator, &bad_config); - if (bad_basic_factory == NULL) { - goto on_error; - } - - struct aws_http_proxy_strategy_factory_basic_auth_config good_config = { - .proxy_connection_type = AWS_HPCT_HTTP_TUNNEL, - .password = config->password, - .user_name = config->user_name, - }; - - good_basic_factory = aws_http_proxy_strategy_factory_new_basic_auth(allocator, &good_config); - if (good_basic_factory == NULL) { - goto on_error; - } - - struct aws_http_proxy_strategy_factory *factory_array[2] = { - bad_basic_factory, - good_basic_factory, - }; - - struct aws_http_proxy_strategy_factory_tunneling_chain_options chain_config = { - .factories = factory_array, - .factory_count = 2, - }; - - adaptive_factory = aws_http_proxy_strategy_factory_new_tunneling_chain(allocator, &chain_config); - if (adaptive_factory == NULL) { - goto on_error; - } - - return adaptive_factory; - -on_error: - - aws_http_proxy_strategy_factory_release(bad_basic_factory); - aws_http_proxy_strategy_factory_release(good_basic_factory); - aws_http_proxy_strategy_factory_release(adaptive_factory); - - return NULL; -} - -/******************************************************************************************************************/ -/*adaptive kerberos*/ +AWS_STATIC_STRING_FROM_LITERAL(s_proxy_authorization_header_kerberos_prefix, "Negotiate "); struct aws_http_proxy_strategy_factory_tunneling_kerberos { struct aws_allocator *allocator; - struct aws_http_proxy_strategy_factory factory_base; - aws_http_proxy_send_user_data_callback_fn func_1; - aws_http_proxy_get_user_data_callback_fn func_2; - void *userdata; + aws_http_proxy_strategy_get_token_sync_fn *get_token; + + void *get_token_user_data; - /* SA-TBI: add any factory state needed here */ + struct aws_http_proxy_strategy_factory factory_base; }; struct aws_http_proxy_strategy_tunneling_kerberos { struct aws_allocator *allocator; - struct aws_http_proxy_strategy strategy_base; - struct aws_http_proxy_strategy_factory *factory; enum proxy_strategy_connect_state connect_state; /* - * SA-TBI: add any factory state needed here + * ToDo: make adaptive and add any state needed here * * Likely things include response code (from the vanilla CONNECT) and the appropriate headers in * the response */ + + struct aws_http_proxy_strategy strategy_base; }; -/*SA-Added Start*/ /* * Adds a proxy authentication header based on the user kerberos authentication token * This uses a token that is already base64 encoded @@ -1251,7 +902,7 @@ struct aws_http_proxy_strategy_tunneling_kerberos { static int s_add_kerberos_proxy_usertoken_authentication_header( struct aws_allocator *allocator, struct aws_http_message *request, - struct aws_string *user_token) { + struct aws_byte_cursor user_token) { struct aws_byte_buf header_value; AWS_ZERO_STRUCT(header_value); @@ -1259,19 +910,19 @@ static int s_add_kerberos_proxy_usertoken_authentication_header( int result = AWS_OP_ERR; if (aws_byte_buf_init( - &header_value, allocator, s_proxy_authorization_header_kerberos_prefix->len+user_token->len + 1)) { + &header_value, allocator, s_proxy_authorization_header_kerberos_prefix->len + user_token.len)) { goto done; } - /* First append proxy authorization header kerberos prefix" in it */ - struct aws_byte_cursor auth_header_cursor = aws_byte_cursor_from_string(s_proxy_authorization_header_kerberos_prefix); + /* First append proxy authorization header kerberos prefix */ + struct aws_byte_cursor auth_header_cursor = + aws_byte_cursor_from_string(s_proxy_authorization_header_kerberos_prefix); if (aws_byte_buf_append(&header_value, &auth_header_cursor)) { goto done; } - /* Append token to it " in it */ - struct aws_byte_cursor usertoken_cursor = aws_byte_cursor_from_string(user_token); - if (aws_byte_buf_append(&header_value, &usertoken_cursor)) { + /* Append token to it */ + if (aws_byte_buf_append(&header_value, &user_token)) { goto done; } @@ -1291,7 +942,7 @@ static int s_add_kerberos_proxy_usertoken_authentication_header( aws_byte_buf_clean_up(&header_value); return result; } -/*SA-Added End*/ + static void s_kerberos_tunnel_transform_connect( struct aws_http_proxy_strategy *proxy_strategy, struct aws_http_message *message, @@ -1301,47 +952,48 @@ static void s_kerberos_tunnel_transform_connect( struct aws_http_proxy_strategy_tunneling_kerberos *kerberos_strategy = proxy_strategy->impl; struct aws_http_proxy_strategy_factory_tunneling_kerberos *kerberos_factory = kerberos_strategy->factory->impl; - - (void)kerberos_strategy; - (void)kerberos_factory; - (void)message; - (void)strategy_termination_callback; - (void)strategy_http_request_forward_callback; - (void)internal_proxy_user_data; - - /* - * SA-TBI: modify message with kerberos auth data and call the request_forward callback or if - * encountering an error, invoke the strategy_termination callback. - * - * As written, a connection attempt using this strategy will hang because neither of these are currently - * invoked. - */ - /*SA-Added Start*/ - if (kerberos_strategy->connect_state != AWS_PSCS_READY) { - strategy_termination_callback(message, AWS_ERROR_INVALID_STATE, internal_proxy_user_data); - return; + + int result = AWS_OP_ERR; + int error_code = AWS_ERROR_SUCCESS; + struct aws_byte_cursor kerberos_token; + AWS_ZERO_STRUCT(kerberos_token); + + if (kerberos_strategy->connect_state == AWS_PSCS_FAILURE) { + error_code = AWS_ERROR_HTTP_PROXY_STRATEGY_FAILED_PREVIOUSLY; + goto done; } - enum proxy_strategy_callback_state callback_state; - callback_state = AWS_KERB_TOKEN; + if (kerberos_strategy->connect_state != AWS_PSCS_READY) { + error_code = AWS_ERROR_INVALID_STATE; + goto done; + } + kerberos_strategy->connect_state = AWS_PSCS_IN_PROGRESS; - /* we first need to get the kerberos usertoken from user*/ - - char *kerberos_usertoken = (kerberos_factory->func_2)(callback_state, kerberos_factory->userdata); - //char *kerberos_usertoken = (proxy_connection_callback.func_2)(callback_state,proxy_connection_callback.userdata); - struct aws_byte_cursor kerberos_usertoken_tmp = aws_byte_cursor_from_c_str(kerberos_usertoken); - struct aws_string *kerberos_token = aws_string_new_from_cursor(kerberos_strategy->allocator, &kerberos_usertoken_tmp); + if (kerberos_factory->get_token(kerberos_factory->get_token_user_data, &kerberos_token, &error_code) || + error_code != AWS_ERROR_SUCCESS) { + goto done; + } /*transform the header with proxy authenticate:Negotiate and kerberos token*/ if (s_add_kerberos_proxy_usertoken_authentication_header(kerberos_strategy->allocator, message, kerberos_token)) { - strategy_termination_callback(message, aws_last_error(), internal_proxy_user_data); - return; + error_code = aws_last_error(); + goto done; } kerberos_strategy->connect_state = AWS_PSCS_IN_PROGRESS; - strategy_http_request_forward_callback(message, internal_proxy_user_data); - /*SA-Added End*/ + result = AWS_OP_SUCCESS; + +done: + + if (result != AWS_OP_SUCCESS) { + if (error_code == AWS_ERROR_SUCCESS) { + error_code = AWS_ERROR_UNKNOWN; + } + strategy_termination_callback(message, error_code, internal_proxy_user_data); + } else { + strategy_http_request_forward_callback(message, internal_proxy_user_data); + } } static int s_kerberos_on_incoming_header_adaptive( @@ -1356,12 +1008,8 @@ static int s_kerberos_on_incoming_header_adaptive( (void)header_array; (void)num_headers; - /* SA-TBI: process CONNECT response headers here if needed */ + /* TODO: process vanilla CONNECT response headers here to improve usage/application */ - if (kerberos_strategy->connect_state == AWS_PSCS_IN_PROGRESS) { - - } - return AWS_OP_SUCCESS; } @@ -1370,10 +1018,8 @@ static int s_kerberos_on_connect_status( enum aws_http_status_code status_code) { struct aws_http_proxy_strategy_tunneling_kerberos *kerberos_strategy = proxy_strategy->impl; - (void)kerberos_strategy; - (void)status_code; - /* SA-TBI: process status code of CONNECT request here if needed */ + /* TODO: process status code of vanilla CONNECT request here to improve usage/application */ if (kerberos_strategy->connect_state == AWS_PSCS_IN_PROGRESS) { if (AWS_HTTP_STATUS_CODE_200_OK != status_code) { @@ -1381,7 +1027,6 @@ static int s_kerberos_on_connect_status( } else { kerberos_strategy->connect_state = AWS_PSCS_SUCCESS; } - } return AWS_OP_SUCCESS; @@ -1395,12 +1040,6 @@ static int s_kerberos_on_incoming_body( (void)kerberos_strategy; (void)data; - if (kerberos_strategy->connect_state == AWS_PSCS_IN_PROGRESS) { - - /* SA-TBI: process body of CONNECT request here if needed */ - - } - return AWS_OP_SUCCESS; } @@ -1414,7 +1053,7 @@ static struct aws_http_proxy_strategy_tunnelling_vtable s_tunneling_kerberos_pro static void s_destroy_tunneling_kerberos_strategy(struct aws_http_proxy_strategy *proxy_strategy) { struct aws_http_proxy_strategy_tunneling_kerberos *kerberos_strategy = proxy_strategy->impl; - /* SA-TBI: any special kerberos strategy clean up here */ + aws_http_proxy_strategy_factory_release(kerberos_strategy->factory); aws_mem_release(kerberos_strategy->allocator, kerberos_strategy); } @@ -1442,7 +1081,6 @@ static struct aws_http_proxy_strategy *s_create_tunneling_kerberos_strategy( kerberos_strategy->strategy_base.strategy_vtable.tunnelling_vtable = &s_tunneling_kerberos_proxy_tunneling_vtable; - /* SA-TBI: special kerberos strategy init here */ kerberos_strategy->factory = aws_ref_count_acquire(&proxy_strategy_factory->ref_count); return &kerberos_strategy->strategy_base; @@ -1455,8 +1093,6 @@ static struct aws_http_proxy_strategy_factory_vtable s_tunneling_kerberos_factor static void s_destroy_tunneling_kerberos_factory(struct aws_http_proxy_strategy_factory *proxy_strategy_factory) { struct aws_http_proxy_strategy_factory_tunneling_kerberos *kerberos_factory = proxy_strategy_factory->impl; - /* SA-TBI: any special factory clean up here */ - aws_mem_release(kerberos_factory->allocator, kerberos_factory); } @@ -1464,7 +1100,7 @@ struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunn struct aws_allocator *allocator, struct aws_http_proxy_strategy_factory_tunneling_kerberos_options *config) { - if (allocator == NULL || config == NULL) { + if (allocator == NULL || config == NULL || config->get_token == NULL) { aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); return NULL; } @@ -1485,72 +1121,46 @@ struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunn &kerberos_factory->factory_base, (aws_simple_completion_callback *)s_destroy_tunneling_kerberos_factory); - /* SA-TBI: any other factory init here */ - - kerberos_factory->func_1 = config->func_1; - kerberos_factory->func_2 = config->func_2; - - if (kerberos_factory->func_1 == NULL) { - goto on_error; - } - - if (kerberos_factory->func_2 == NULL) { - goto on_error; - } + kerberos_factory->get_token = config->get_token; + kerberos_factory->get_token_user_data = config->get_token_user_data; return &kerberos_factory->factory_base; - -on_error: - - aws_http_proxy_strategy_factory_release(&kerberos_factory->factory_base); - - return NULL; } /******************************************************************************************************************/ -/*SA-Added End*/ /*adaptive ntlm*/ struct aws_http_proxy_strategy_factory_tunneling_ntlm { struct aws_allocator *allocator; - struct aws_http_proxy_strategy_factory factory_base; + aws_http_proxy_strategy_get_challenge_token_sync_fn *get_challenge_token; - aws_http_proxy_send_user_data_callback_fn func_1; - aws_http_proxy_get_user_data_callback_fn func_2; - void *userdata; + void *get_challenge_token_user_data; - /* SA-TBI: add any factory state needed here */ + struct aws_http_proxy_strategy_factory factory_base; }; struct aws_http_proxy_strategy_tunneling_ntlm { struct aws_allocator *allocator; - struct aws_http_proxy_strategy strategy_base; - struct aws_http_proxy_strategy_factory *factory; enum proxy_strategy_connect_state connect_state; - enum proxy_strategy_connect_state connect_sub_state; + struct aws_string *challenge_token; - /* - * SA-TBI: add any factory state needed here - * - * Likely things include response code (from the vanilla CONNECT) and the appropriate headers in - * the response - */ + struct aws_http_proxy_strategy strategy_base; }; AWS_STATIC_STRING_FROM_LITERAL(s_proxy_authorization_header_ntlm_prefix, "NTLM "); /* - * Adds a proxy authentication header based on ntlm credential or response provided by user + * Adds a proxy authentication header based on ntlm credential or response provided by user */ static int s_add_ntlm_proxy_usertoken_authentication_header( struct aws_allocator *allocator, struct aws_http_message *request, - struct aws_string *credential_response) { + struct aws_byte_cursor credential_response) { struct aws_byte_buf header_value; AWS_ZERO_STRUCT(header_value); @@ -1558,20 +1168,18 @@ static int s_add_ntlm_proxy_usertoken_authentication_header( int result = AWS_OP_ERR; if (aws_byte_buf_init( - &header_value, allocator, s_proxy_authorization_header_ntlm_prefix->len + credential_response->len + 1)) { + &header_value, allocator, s_proxy_authorization_header_ntlm_prefix->len + credential_response.len)) { goto done; } - /* First append proxy authorization header prefix" in it */ - struct aws_byte_cursor auth_header_cursor = - aws_byte_cursor_from_string(s_proxy_authorization_header_ntlm_prefix); + /* First append proxy authorization header prefix */ + struct aws_byte_cursor auth_header_cursor = aws_byte_cursor_from_string(s_proxy_authorization_header_ntlm_prefix); if (aws_byte_buf_append(&header_value, &auth_header_cursor)) { goto done; } - /* Append user data to it " in it */ - struct aws_byte_cursor user_credential_response = aws_byte_cursor_from_string(credential_response); - if (aws_byte_buf_append(&header_value, &user_credential_response)) { + /* Append the credential response to it; assumes already encoded properly (base64) */ + if (aws_byte_buf_append(&header_value, &credential_response)) { goto done; } @@ -1602,79 +1210,59 @@ static void s_ntlm_tunnel_transform_connect( struct aws_http_proxy_strategy_tunneling_ntlm *ntlm_strategy = proxy_strategy->impl; struct aws_http_proxy_strategy_factory_tunneling_ntlm *ntlm_factory = ntlm_strategy->factory->impl; - (void)ntlm_strategy; - (void)ntlm_factory; - (void)message; - (void)strategy_termination_callback; - (void)strategy_http_request_forward_callback; - (void)internal_proxy_user_data; - - /* - * SA-TBI: modify message with ntlm auth data and call the request_forward callback or if - * encountering an error, invoke the strategy_termination callback. - * - * As written, a connection attempt using this strategy will hang because neither of these are currently - * invoked. - */ - - /*Terminate when both states are not AWS_PSCS_READY*/ - /* Why there is a sub_state logic - > For NTLM we have to first send the credentials with CONNECT Request - * This CONNECT request will yeild a challenge in response - * This challenge will be followed by a new CONNECT request with response in it - * Instead of creating 2 different strategies for 2x CONNECT request i have created a sub state logic in which - * the strategy is terminated only when both CONNECT request have resulted in a failure or else first a CONNECT - request with credentials go out followed by response*/ - - - if ((ntlm_strategy->connect_state != AWS_PSCS_READY) - &&(ntlm_strategy->connect_sub_state != AWS_PSCS_READY)) { - strategy_termination_callback(message, AWS_ERROR_INVALID_STATE, internal_proxy_user_data); - return; + int result = AWS_OP_ERR; + int error_code = AWS_ERROR_SUCCESS; + struct aws_byte_cursor challenge_answer_token; + AWS_ZERO_STRUCT(challenge_answer_token); + struct aws_byte_cursor challenge_token; + AWS_ZERO_STRUCT(challenge_token); + + if (ntlm_strategy->connect_state == AWS_PSCS_FAILURE) { + error_code = AWS_ERROR_HTTP_PROXY_STRATEGY_FAILED_PREVIOUSLY; + goto done; } - if (ntlm_strategy->connect_state == AWS_PSCS_READY) { - - /* we first need to get the ntlm credential from user*/ + if (ntlm_strategy->connect_state != AWS_PSCS_READY) { + error_code = AWS_ERROR_INVALID_STATE; + goto done; + } - enum proxy_strategy_callback_state callback_state; - callback_state = AWS_NTLM_CRED; - - //char *ntlm_credential_user = (proxy_connection_callback.func_2)(callback_state, proxy_connection_callback.userdata); - char *ntlm_credential_user = (ntlm_factory->func_2)(callback_state, ntlm_factory->userdata); - struct aws_byte_cursor ntlm_credential_tmp = aws_byte_cursor_from_c_str(ntlm_credential_user); - struct aws_string *ntlm_credential = aws_string_new_from_cursor(ntlm_strategy->allocator, &ntlm_credential_tmp); + if (ntlm_strategy->challenge_token == NULL) { + error_code = AWS_ERROR_HTTP_PROXY_STRATEGY_NTLM_CHALLENGE_TOKEN_MISSING; + goto done; + } - /*transform the header with proxy authenticate:NTLM and NTLM credentials*/ - if (s_add_ntlm_proxy_usertoken_authentication_header(ntlm_strategy->allocator, message, ntlm_credential)) { - strategy_termination_callback(message, aws_last_error(), internal_proxy_user_data); - return; - } + ntlm_strategy->connect_state = AWS_PSCS_IN_PROGRESS; + challenge_token = aws_byte_cursor_from_string(ntlm_strategy->challenge_token); + if (ntlm_factory->get_challenge_token( + ntlm_factory->get_challenge_token_user_data, &challenge_token, &challenge_answer_token, &error_code) || + error_code != AWS_ERROR_SUCCESS) { + goto done; } - - else if (ntlm_strategy->connect_sub_state == AWS_PSCS_READY) { - - enum proxy_strategy_callback_state callback_state; - callback_state = AWS_NTLM_RESP; - - //char *ntlm_response_user = (proxy_connection_callback.func_2)(callback_state, proxy_connection_callback.userdata); - char *ntlm_response_user = (ntlm_factory->func_2)(callback_state, ntlm_factory->userdata); - struct aws_byte_cursor ntlm_response_tmp = aws_byte_cursor_from_c_str(ntlm_response_user); - struct aws_string *ntlm_response = - aws_string_new_from_cursor(ntlm_strategy->allocator, &ntlm_response_tmp); - - /*transform the header with proxy authenticate:NTLM and the response for challenge received from user*/ - if (s_add_ntlm_proxy_usertoken_authentication_header(ntlm_strategy->allocator, message, ntlm_response)) { - strategy_termination_callback(message, aws_last_error(), internal_proxy_user_data); - return; - } - ntlm_strategy->connect_sub_state = AWS_PSCS_IN_PROGRESS; + /*transform the header with proxy authenticate:Negotiate and kerberos token*/ + if (s_add_ntlm_proxy_usertoken_authentication_header(ntlm_strategy->allocator, message, challenge_answer_token)) { + error_code = aws_last_error(); + goto done; } - + ntlm_strategy->connect_state = AWS_PSCS_IN_PROGRESS; - strategy_http_request_forward_callback(message, internal_proxy_user_data); + result = AWS_OP_SUCCESS; + +done: + + if (result != AWS_OP_SUCCESS) { + if (error_code == AWS_ERROR_SUCCESS) { + error_code = AWS_ERROR_UNKNOWN; + } + strategy_termination_callback(message, error_code, internal_proxy_user_data); + } else { + strategy_http_request_forward_callback(message, internal_proxy_user_data); + } } +AWS_STATIC_STRING_FROM_LITERAL(s_ntlm_challenge_token_header, "Proxy-Authenticate"); + static int s_ntlm_on_incoming_header_adaptive( struct aws_http_proxy_strategy *proxy_strategy, enum aws_http_header_block header_block, @@ -1683,39 +1271,28 @@ static int s_ntlm_on_incoming_header_adaptive( struct aws_http_proxy_strategy_tunneling_ntlm *ntlm_strategy = proxy_strategy->impl; - struct aws_http_proxy_strategy_factory_tunneling_ntlm *ntlm_factory = ntlm_strategy->factory->impl; - - (void)ntlm_strategy; - (void)header_block; - (void)header_array; - (void)num_headers; - - /* SA-TBI: process CONNECT response headers here if needed */ - //transfer the challenge to user area only when connect state in progress & sub state is not in progress - if ((ntlm_strategy->connect_state == AWS_PSCS_IN_PROGRESS) - &&(ntlm_strategy->connect_sub_state != AWS_PSCS_IN_PROGRESS)) - { - /* This section of code checks for header block when connect and connect sub state are both in progress - ,the code locates the header in which the challenge has come and triggers a callback to send challenge to user*/ + /* + * only extract the challenge before we've started our own CONNECT attempt + * + * ToDo: we currently overwrite previous challenge tokens since it is unknown if multiple CONNECT requests + * cause new challenges to be issued such that old challenges become invalid even if successfully computed + */ + if (ntlm_strategy->connect_state == AWS_PSCS_READY) { if (header_block == AWS_HTTP_HEADER_BLOCK_MAIN) { - - uint8_t *header_name = header_array->name.ptr; - char array_header[100]; - if ((header_array->name.len) > 0) { - size_t header_length = header_array->name.len; - - for (size_t i = 0; i < header_array->name.len; ++i) { - - array_header[i] = (char)header_name[i]; - } - if (strncmp(array_header, "Proxy-Authenticate", header_length) == 0) { - - (ntlm_factory->func_1)(header_array->value.len, header_array->value.ptr, ntlm_factory->userdata); - //(proxy_connection_callback.func_1)( - //header_array->value.len, header_array->value.ptr, proxy_connection_callback.userdata); - } + struct aws_byte_cursor proxy_authenticate_header_name = + aws_byte_cursor_from_string(s_ntlm_challenge_token_header); + for (size_t i = 0; i < num_headers; ++i) { + struct aws_byte_cursor header_name_cursor = header_array[i].name; + if (aws_byte_cursor_eq_ignore_case(&proxy_authenticate_header_name, &header_name_cursor)) { + aws_string_destroy(ntlm_strategy->challenge_token); + + struct aws_byte_cursor challenge_value_cursor = header_array[i].value; + ntlm_strategy->challenge_token = + aws_string_new_from_cursor(ntlm_strategy->allocator, &challenge_value_cursor); + break; } - } + } + } } return AWS_OP_SUCCESS; @@ -1726,10 +1303,8 @@ static int s_ntlm_on_connect_status( enum aws_http_status_code status_code) { struct aws_http_proxy_strategy_tunneling_ntlm *ntlm_strategy = proxy_strategy->impl; - (void)ntlm_strategy; - (void)status_code; - if ((ntlm_strategy->connect_state == AWS_PSCS_IN_PROGRESS)&&(ntlm_strategy->connect_sub_state != AWS_PSCS_IN_PROGRESS)) { + if (ntlm_strategy->connect_state == AWS_PSCS_IN_PROGRESS) { if (AWS_HTTP_STATUS_CODE_200_OK != status_code) { ntlm_strategy->connect_state = AWS_PSCS_FAILURE; } else { @@ -1737,31 +1312,15 @@ static int s_ntlm_on_connect_status( } } - if ((ntlm_strategy->connect_state == AWS_PSCS_IN_PROGRESS)&&(ntlm_strategy->connect_sub_state == AWS_PSCS_IN_PROGRESS)) { - if (AWS_HTTP_STATUS_CODE_200_OK != status_code) { - ntlm_strategy->connect_state = AWS_PSCS_FAILURE; - ntlm_strategy->connect_sub_state = AWS_PSCS_FAILURE; - } else { - ntlm_strategy->connect_state = AWS_PSCS_SUCCESS; - ntlm_strategy->connect_sub_state = AWS_PSCS_SUCCESS; - } - } return AWS_OP_SUCCESS; } -static int s_ntlm_on_incoming_body( - struct aws_http_proxy_strategy *proxy_strategy, - const struct aws_byte_cursor *data) { +static int s_ntlm_on_incoming_body(struct aws_http_proxy_strategy *proxy_strategy, const struct aws_byte_cursor *data) { struct aws_http_proxy_strategy_tunneling_ntlm *ntlm_strategy = proxy_strategy->impl; (void)ntlm_strategy; (void)data; - if (ntlm_strategy->connect_state == AWS_PSCS_IN_PROGRESS) { - - /* SA-TBI: process body of CONNECT request here if needed */ - } - return AWS_OP_SUCCESS; } @@ -1775,7 +1334,8 @@ static struct aws_http_proxy_strategy_tunnelling_vtable s_tunneling_ntlm_proxy_t static void s_destroy_tunneling_ntlm_strategy(struct aws_http_proxy_strategy *proxy_strategy) { struct aws_http_proxy_strategy_tunneling_ntlm *ntlm_strategy = proxy_strategy->impl; - /* SA-TBI: any special ntlm strategy clean up here */ + aws_string_destroy(ntlm_strategy->challenge_token); + aws_http_proxy_strategy_factory_release(ntlm_strategy->factory); aws_mem_release(ntlm_strategy->allocator, ntlm_strategy); } @@ -1803,14 +1363,11 @@ static struct aws_http_proxy_strategy *s_create_tunneling_ntlm_strategy( ntlm_strategy->strategy_base.strategy_vtable.tunnelling_vtable = &s_tunneling_ntlm_proxy_tunneling_vtable; - /* SA-TBI: special ntlm strategy init here */ - ntlm_strategy->factory = aws_ref_count_acquire(&proxy_strategy_factory->ref_count); return &ntlm_strategy->strategy_base; } - static struct aws_http_proxy_strategy_factory_vtable s_tunneling_ntlm_factory_vtable = { .create_strategy = s_create_tunneling_ntlm_strategy, }; @@ -1818,8 +1375,6 @@ static struct aws_http_proxy_strategy_factory_vtable s_tunneling_ntlm_factory_vt static void s_destroy_tunneling_ntlm_factory(struct aws_http_proxy_strategy_factory *proxy_strategy_factory) { struct aws_http_proxy_strategy_factory_tunneling_ntlm *ntlm_factory = proxy_strategy_factory->impl; - /* SA-TBI: any special factory clean up here */ - aws_mem_release(ntlm_factory->allocator, ntlm_factory); } @@ -1827,7 +1382,7 @@ struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunn struct aws_allocator *allocator, struct aws_http_proxy_strategy_factory_tunneling_ntlm_options *config) { - if (allocator == NULL || config == NULL) { + if (allocator == NULL || config == NULL || config->get_challenge_token == NULL) { aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); return NULL; } @@ -1841,7 +1396,7 @@ struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunn ntlm_factory->factory_base.impl = ntlm_factory; ntlm_factory->factory_base.vtable = &s_tunneling_ntlm_factory_vtable; ntlm_factory->factory_base.proxy_connection_type = AWS_HPCT_HTTP_TUNNEL; - ntlm_factory->func_1 = config->func_1; + ntlm_factory->allocator = allocator; aws_ref_count_init( @@ -1849,142 +1404,78 @@ struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunn &ntlm_factory->factory_base, (aws_simple_completion_callback *)s_destroy_tunneling_ntlm_factory); - /* SA-TBI: any other factory init here */ - - ntlm_factory->func_1 = config->func_1; - ntlm_factory->func_2 = config->func_2; - - if (ntlm_factory->func_1 == NULL) { - goto on_error; - } - - if (ntlm_factory->func_2 == NULL) { - goto on_error; - } + ntlm_factory->get_challenge_token = config->get_challenge_token; + ntlm_factory->get_challenge_token_user_data = config->get_challenge_token_user_data; return &ntlm_factory->factory_base; - - on_error: - - aws_http_proxy_strategy_factory_release(&ntlm_factory->factory_base); - - return NULL; } -/*SA-Added End*/ + /******************************************************************************************************************/ -AWS_HTTP_API -struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunneling_adaptive_kerberos_ntlm( +#define PROXY_STRATEGY_MAX_ADAPTIVE_FACTORIES 3 + +struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunneling_adaptive( struct aws_allocator *allocator, - struct aws_http_proxy_strategy_factory_tunneling_adaptive_kerberos_options *kerberos_config, - struct aws_http_proxy_strategy_factory_tunneling_adaptive_ntlm_options *ntlm_config) { + struct aws_http_proxy_strategy_factory_tunneling_adaptive_options *config) { - if (allocator == NULL || kerberos_config == NULL || ntlm_config == NULL) { + if (allocator == NULL || config == NULL) { aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); return NULL; } + struct aws_http_proxy_strategy_factory *factories[PROXY_STRATEGY_MAX_ADAPTIVE_FACTORIES]; + + uint32_t factory_count = 0; struct aws_http_proxy_strategy_factory *identity_factory = NULL; struct aws_http_proxy_strategy_factory *kerberos_factory = NULL; struct aws_http_proxy_strategy_factory *ntlm_factory = NULL; - struct aws_http_proxy_strategy_factory *adaptive_factory = NULL; + struct aws_http_proxy_strategy_factory *adaptive_chain_factory = NULL; identity_factory = aws_http_proxy_strategy_factory_new_tunneling_one_time_identity(allocator); if (identity_factory == NULL) { goto on_error; } + factories[factory_count++] = identity_factory; - kerberos_factory = aws_http_proxy_strategy_factory_new_tunneling_kerberos(allocator, &kerberos_config->kerberos_options); - if (kerberos_factory == NULL) { - goto on_error; - } - - ntlm_factory = aws_http_proxy_strategy_factory_new_tunneling_ntlm(allocator, &ntlm_config->ntlm_options); - if (ntlm_factory == NULL) { - goto on_error; - } - - struct aws_http_proxy_strategy_factory *factory_array[3] = { - identity_factory, - kerberos_factory, - ntlm_factory, - - }; - - struct aws_http_proxy_strategy_factory_tunneling_chain_options chain_config = { - .factories = factory_array, - .factory_count = 3, - }; - - adaptive_factory = aws_http_proxy_strategy_factory_new_tunneling_chain(allocator, &chain_config); - if (adaptive_factory == NULL) { - goto on_error; - } - - return adaptive_factory; - -on_error: - - aws_http_proxy_strategy_factory_release(identity_factory); - aws_http_proxy_strategy_factory_release(kerberos_factory); - aws_http_proxy_strategy_factory_release(ntlm_factory); - aws_http_proxy_strategy_factory_release(adaptive_factory); - - return NULL; -} - -/******************************************************************************************************************/ -/*SA-Added Start*/ -AWS_HTTP_API -struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunneling_adaptive_ntlm( - struct aws_allocator *allocator, - struct aws_http_proxy_strategy_factory_tunneling_adaptive_ntlm_options *config) { + if (config->kerberos_options != NULL) { + kerberos_factory = aws_http_proxy_strategy_factory_new_tunneling_kerberos(allocator, config->kerberos_options); + if (kerberos_factory == NULL) { + goto on_error; + } - if (allocator == NULL || config == NULL) { - aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); - return NULL; + factories[factory_count++] = kerberos_factory; } - struct aws_http_proxy_strategy_factory *identity_factory = NULL; - struct aws_http_proxy_strategy_factory *ntlm_factory = NULL; - struct aws_http_proxy_strategy_factory *adaptive_factory = NULL; - - identity_factory = aws_http_proxy_strategy_factory_new_tunneling_one_time_identity(allocator); - if (identity_factory == NULL) { - goto on_error; - } + if (config->ntlm_options != NULL) { + ntlm_factory = aws_http_proxy_strategy_factory_new_tunneling_ntlm(allocator, config->ntlm_options); + if (ntlm_factory == NULL) { + goto on_error; + } - ntlm_factory = aws_http_proxy_strategy_factory_new_tunneling_ntlm(allocator, &config->ntlm_options); - if (ntlm_factory == NULL) { - goto on_error; + factories[factory_count++] = ntlm_factory; } - struct aws_http_proxy_strategy_factory *factory_array[2] = { - identity_factory, - ntlm_factory, - }; - struct aws_http_proxy_strategy_factory_tunneling_chain_options chain_config = { - .factories = factory_array, - .factory_count = 2, + .factories = factories, + .factory_count = factory_count, }; - adaptive_factory = aws_http_proxy_strategy_factory_new_tunneling_chain(allocator, &chain_config); - if (adaptive_factory == NULL) { + adaptive_chain_factory = aws_http_proxy_strategy_factory_new_tunneling_chain(allocator, &chain_config); + if (adaptive_chain_factory == NULL) { goto on_error; } - return adaptive_factory; + return adaptive_chain_factory; on_error: aws_http_proxy_strategy_factory_release(identity_factory); + aws_http_proxy_strategy_factory_release(kerberos_factory); aws_http_proxy_strategy_factory_release(ntlm_factory); - aws_http_proxy_strategy_factory_release(adaptive_factory); + aws_http_proxy_strategy_factory_release(adaptive_chain_factory); return NULL; } -/*SA-Added End*/ #if defined(_MSC_VER) # pragma warning(pop) From d4a738db1df29406bb2ee4787654d404113a9d5b Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Thu, 21 Jan 2021 11:35:36 -0800 Subject: [PATCH 26/54] Proxy strategy rename fest --- include/aws/http/connection.h | 4 +- include/aws/http/private/proxy_impl.h | 4 +- include/aws/http/proxy_strategy.h | 246 +++--- source/proxy_connection.c | 55 +- source/proxy_strategy.c | 1024 +++++++++++++------------ tests/proxy_test_helper.c | 2 +- tests/test_proxy.c | 4 +- 7 files changed, 668 insertions(+), 671 deletions(-) diff --git a/include/aws/http/connection.h b/include/aws/http/connection.h index d2f73f6e5..2bfacaa24 100644 --- a/include/aws/http/connection.h +++ b/include/aws/http/connection.h @@ -152,7 +152,7 @@ enum aws_http_proxy_connection_type { AWS_HPCT_SOCKS5, }; -struct aws_http_proxy_strategy_factory; +struct aws_http_proxy_strategy; /** * Options for http proxy server usage @@ -190,7 +190,7 @@ struct aws_http_proxy_options { * For forwarding proxies it allows custom request transformations. * Other proxy connection types TBD. */ - struct aws_http_proxy_strategy_factory *proxy_strategy_factory; + struct aws_http_proxy_strategy *proxy_strategy; }; /** diff --git a/include/aws/http/private/proxy_impl.h b/include/aws/http/private/proxy_impl.h index 9edcbb53c..395b3e47c 100644 --- a/include/aws/http/private/proxy_impl.h +++ b/include/aws/http/private/proxy_impl.h @@ -46,7 +46,7 @@ struct aws_http_proxy_config { struct aws_tls_connection_options *tls_options; - struct aws_http_proxy_strategy_factory *proxy_strategy_factory; + struct aws_http_proxy_strategy *proxy_strategy; }; /* @@ -75,7 +75,7 @@ struct aws_http_proxy_user_data { struct aws_tls_connection_options *tls_options; struct aws_http_proxy_config *proxy_config; - struct aws_http_proxy_strategy *proxy_strategy; + struct aws_http_proxy_negotiator *proxy_negotiator; }; struct aws_http_proxy_system_vtable { diff --git a/include/aws/http/proxy_strategy.h b/include/aws/http/proxy_strategy.h index ea1896fea..34a929580 100644 --- a/include/aws/http/proxy_strategy.h +++ b/include/aws/http/proxy_strategy.h @@ -15,20 +15,13 @@ struct aws_http_message; struct aws_http_header; +struct aws_http_proxy_negotiator; struct aws_http_proxy_strategy; -struct aws_http_proxy_strategy_factory; - -/*enum defination for callback state*/ -enum proxy_strategy_callback_state { - AWS_PSCS_KERB_TOKEN, - AWS_PSCS_NTLM_CRED, - AWS_PSCS_NTLM_RESP, -}; /** * Synchronous (for now) callback function to fetch a token used in modifying CONNECT requests */ -typedef int(aws_http_proxy_strategy_get_token_sync_fn)( +typedef int(aws_http_proxy_negotiation_get_token_sync_fn)( void *user_data, struct aws_byte_cursor *out_token_value, int *out_error_code); @@ -37,26 +30,27 @@ typedef int(aws_http_proxy_strategy_get_token_sync_fn)( * Synchronous (for now) callback function to fetch a token used in modifying CONNECT request. Includes a (byte string) * context intended to be used as part of a challenge-response flow. */ -typedef int(aws_http_proxy_strategy_get_challenge_token_sync_fn)( +typedef int(aws_http_proxy_negotiation_get_challenge_token_sync_fn)( void *user_data, const struct aws_byte_cursor *challenge_context, struct aws_byte_cursor *out_token_value, int *out_error_code); /** - * Proxy strategy logic must call this function to indicate an unsuccessful outcome + * Proxy negotiation logic must call this function to indicate an unsuccessful outcome */ -typedef void(aws_http_proxy_strategy_terminate_fn)( +typedef void(aws_http_proxy_negotiation_terminate_fn)( struct aws_http_message *message, int error_code, void *internal_proxy_user_data); /** - * Proxy strategy logic must call this function to forward the potentially-mutated request back to the proxy - * strategy coordination logic. + * Proxy negotiation logic must call this function to forward the potentially-mutated request back to the proxy + * connection logic. */ -typedef void( - aws_http_proxy_strategy_http_request_forward_fn)(struct aws_http_message *message, void *internal_proxy_user_data); +typedef void(aws_http_proxy_negotiation_http_request_forward_fn)( + struct aws_http_message *message, + void *internal_proxy_user_data); /** * User-supplied transform callback which implements the proxy request flow and ultimately, across all execution @@ -70,70 +64,69 @@ typedef void( * * Forwarding proxy connections cannot yet support a truly async request transform without major surgery on http * stream creation, so for now, we split into an async version (for tunneling proxies) and a separate - * synchronous version for forwarding proxies. + * synchronous version for forwarding proxies. Also forwarding proxies are a kind of legacy dead-end in some + * sense. * */ -typedef void(aws_http_proxy_strategy_http_request_transform_async_fn)( - struct aws_http_proxy_strategy *proxy_strategy, +typedef void(aws_http_proxy_negotiation_http_request_transform_async_fn)( + struct aws_http_proxy_negotiator *proxy_negotiator, struct aws_http_message *message, - aws_http_proxy_strategy_terminate_fn *strategy_termination_callback, - aws_http_proxy_strategy_http_request_forward_fn *strategy_http_request_forward_callback, + aws_http_proxy_negotiation_terminate_fn *negotiation_termination_callback, + aws_http_proxy_negotiation_http_request_forward_fn *negotiation_http_request_forward_callback, void *internal_proxy_user_data); -typedef int(aws_http_proxy_strategy_http_request_transform_fn)( - struct aws_http_proxy_strategy *proxy_strategy, +typedef int(aws_http_proxy_negotiation_http_request_transform_fn)( + struct aws_http_proxy_negotiator *proxy_negotiator, struct aws_http_message *message); /** - * Tunneling proxy connections only. A callback that lets the strategy examine the headers in the + * Tunneling proxy connections only. A callback that lets the negotiator examine the headers in the * response to the most recent CONNECT request as they arrive. */ -typedef int(aws_http_proxy_strategy_connect_on_incoming_headers_fn)( - struct aws_http_proxy_strategy *proxy_strategy, +typedef int(aws_http_proxy_negotiation_connect_on_incoming_headers_fn)( + struct aws_http_proxy_negotiator *proxy_negotiator, enum aws_http_header_block header_block, const struct aws_http_header *header_array, size_t num_headers); /** - * Tunneling proxy connections only. A callback that lets the strategy examine the status code of the + * Tunneling proxy connections only. A callback that lets the negotiator examine the status code of the * response to the most recent CONNECT request. */ -typedef int(aws_http_proxy_strategy_connect_status_fn)( - struct aws_http_proxy_strategy *proxy_strategy, +typedef int(aws_http_proxy_negotiator_connect_status_fn)( + struct aws_http_proxy_negotiator *proxy_negotiator, enum aws_http_status_code status_code); /** - * Tunneling proxy connections only. A callback that lets the strategy examine the body of the response + * Tunneling proxy connections only. A callback that lets the negotiator examine the body of the response * to the most recent CONNECT request. */ -typedef int(aws_http_proxy_strategy_connect_on_incoming_body_fn)( - struct aws_http_proxy_strategy *proxy_strategy, +typedef int(aws_http_proxy_negotiator_connect_on_incoming_body_fn)( + struct aws_http_proxy_negotiator *proxy_negotiator, const struct aws_byte_cursor *data); /** - * Destructor for a proxy strategy. A standard pattern is to - * make the user data a structure that also embeds the aws_proxy_strategy as a member, letting the destructor - * clean up everything in a single shot. + * Vtable for forwarding-based proxy negotiators */ -typedef void(aws_http_proxy_strategy_destroy_fn)(struct aws_http_proxy_strategy *proxy_strategy); - -struct aws_http_proxy_strategy_forwarding_vtable { - aws_http_proxy_strategy_http_request_transform_fn *forward_request_transform; +struct aws_http_proxy_negotiator_forwarding_vtable { + aws_http_proxy_negotiation_http_request_transform_fn *forward_request_transform; }; -struct aws_http_proxy_strategy_tunnelling_vtable { - aws_http_proxy_strategy_http_request_transform_async_fn *connect_request_transform; +/** + * Vtable for tunneling-based proxy negotiators + */ +struct aws_http_proxy_negotiator_tunnelling_vtable { + aws_http_proxy_negotiation_http_request_transform_async_fn *connect_request_transform; - aws_http_proxy_strategy_connect_on_incoming_headers_fn *on_incoming_headers_callback; - aws_http_proxy_strategy_connect_status_fn *on_status_callback; - aws_http_proxy_strategy_connect_on_incoming_body_fn *on_incoming_body_callback; + aws_http_proxy_negotiation_connect_on_incoming_headers_fn *on_incoming_headers_callback; + aws_http_proxy_negotiator_connect_status_fn *on_status_callback; + aws_http_proxy_negotiator_connect_on_incoming_body_fn *on_incoming_body_callback; }; /* - * Configuration definition of a proxy stategy. Contains a proxy-type-specific vtable, user_data (usually a - * strategy-specific struct that embeds the aws_proxy_strategy), and a destructor. + * Base definition of a proxy negotiator. * - * A strategy works differently based on what kind of proxy connection is being asked for: + * A negotiator works differently based on what kind of proxy connection is being asked for: * * (1) Tunneling - In a tunneling proxy connection, the connect_request_transform is invoked on every CONNECT request. * The connect_request_transform implementation *MUST*, in turn, eventually call one of the terminate or forward @@ -147,35 +140,35 @@ struct aws_http_proxy_strategy_tunnelling_vtable { * * (3) Socks5 - not yet supported */ -struct aws_http_proxy_strategy { +struct aws_http_proxy_negotiator { struct aws_ref_count ref_count; void *impl; union { - struct aws_http_proxy_strategy_forwarding_vtable *forwarding_vtable; - struct aws_http_proxy_strategy_tunnelling_vtable *tunnelling_vtable; + struct aws_http_proxy_negotiator_forwarding_vtable *forwarding_vtable; + struct aws_http_proxy_negotiator_tunnelling_vtable *tunnelling_vtable; } strategy_vtable; }; /*********************************************************************************************/ -typedef struct aws_http_proxy_strategy *(aws_http_proxy_strategy_factory_create_strategy_fn)( - struct aws_http_proxy_strategy_factory *proxy_strategy_factory, +typedef struct aws_http_proxy_negotiator *(aws_http_proxy_strategy_create_negotiator_fn)( + struct aws_http_proxy_strategy *proxy_strategy, struct aws_allocator *allocator); -struct aws_http_proxy_strategy_factory_vtable { - aws_http_proxy_strategy_factory_create_strategy_fn *create_strategy; +struct aws_http_proxy_strategy_vtable { + aws_http_proxy_strategy_create_negotiator_fn *create_negotiator; }; -struct aws_http_proxy_strategy_factory { +struct aws_http_proxy_strategy { struct aws_ref_count ref_count; - struct aws_http_proxy_strategy_factory_vtable *vtable; + struct aws_http_proxy_strategy_vtable *vtable; void *impl; enum aws_http_proxy_connection_type proxy_connection_type; }; -struct aws_http_proxy_strategy_factory_basic_auth_config { +struct aws_http_proxy_strategy_basic_auth_options { /* type of proxy connection being established, must be forwarding or tunnel */ enum aws_http_proxy_connection_type proxy_connection_type; @@ -187,172 +180,169 @@ struct aws_http_proxy_strategy_factory_basic_auth_config { struct aws_byte_cursor password; }; -struct aws_http_proxy_strategy_factory_tunneling_chain_options { - struct aws_http_proxy_strategy_factory **factories; +struct aws_http_proxy_strategy_tunneling_chain_options { + struct aws_http_proxy_strategy **strategies; - uint32_t factory_count; + uint32_t strategy_count; }; -struct aws_http_proxy_strategy_factory_tunneling_kerberos_options { +struct aws_http_proxy_strategy_tunneling_kerberos_options { - aws_http_proxy_strategy_get_token_sync_fn *get_token; + aws_http_proxy_negotiation_get_token_sync_fn *get_token; void *get_token_user_data; }; -struct aws_http_proxy_strategy_factory_tunneling_ntlm_options { +struct aws_http_proxy_strategy_tunneling_ntlm_options { - aws_http_proxy_strategy_get_challenge_token_sync_fn *get_challenge_token; + aws_http_proxy_negotiation_get_challenge_token_sync_fn *get_challenge_token; void *get_challenge_token_user_data; }; -struct aws_http_proxy_strategy_factory_tunneling_adaptive_options { +struct aws_http_proxy_strategy_tunneling_adaptive_options { /* * If non-null, will insert a kerberos proxy strategy into the adaptive chain */ - struct aws_http_proxy_strategy_factory_tunneling_kerberos_options *kerberos_options; + struct aws_http_proxy_strategy_tunneling_kerberos_options *kerberos_options; /* * If non-null will insert an ntlm proxy strategy into the adaptive chain */ - struct aws_http_proxy_strategy_factory_tunneling_ntlm_options *ntlm_options; + struct aws_http_proxy_strategy_tunneling_ntlm_options *ntlm_options; }; AWS_EXTERN_C_BEGIN /** - * Take a reference to an http proxy strategy - * @param proxy_strategy strategy to take a reference to + * Take a reference to an http proxy negotiator + * @param proxy_negotiator negotiator to take a reference to * @return the strategy */ AWS_HTTP_API -struct aws_http_proxy_strategy *aws_http_proxy_strategy_acquire(struct aws_http_proxy_strategy *proxy_strategy); +struct aws_http_proxy_negotiator *aws_http_proxy_negotiator_acquire(struct aws_http_proxy_negotiator *proxy_negotiator); /** - * Release a reference to an http proxy strategy - * @param proxy_strategy strategy to release a reference to + * Release a reference to an http proxy negotiator + * @param proxy_negotiator negotiator to release a reference to */ AWS_HTTP_API -void aws_http_proxy_strategy_release(struct aws_http_proxy_strategy *proxy_strategy); +void aws_http_proxy_negotiator_release(struct aws_http_proxy_negotiator *proxy_negotiator); /** - * Creates a new proxy strategy from the factory according to the factory's configuration + * Creates a new proxy negotiator from a proxy strategy * @param allocator memory allocator to use - * @return a new proxy strategy if successful, otherwise NULL + * @param strategy strategy to creation a new negotiator for + * @return a new proxy negotiator if successful, otherwise NULL */ AWS_HTTP_API -struct aws_http_proxy_strategy *aws_http_proxy_strategy_factory_create_strategy( - struct aws_http_proxy_strategy_factory *factory, +struct aws_http_proxy_negotiator *aws_http_proxy_strategy_create_negotiator( + struct aws_http_proxy_strategy *strategy, struct aws_allocator *allocator); /** - * Take a reference to an http proxy strategy factory - * @param proxy_strategy_factory factory to take a reference to - * @return the factory + * Take a reference to an http proxy strategy + * @param proxy_strategy strategy to take a reference to + * @return the strategy */ AWS_HTTP_API -struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_acquire( - struct aws_http_proxy_strategy_factory *proxy_strategy_factory); +struct aws_http_proxy_strategy *aws_http_proxy_strategy_acquire(struct aws_http_proxy_strategy *proxy_strategy); /** - * Release a reference to an http proxy strategy factory - * @param proxy_strategy_factory factory to release a reference to + * Release a reference to an http proxy strategy + * @param proxy_strategy strategy to release a reference to */ AWS_HTTP_API -void aws_http_proxy_strategy_factory_release(struct aws_http_proxy_strategy_factory *proxy_strategy_factory); +void aws_http_proxy_strategy_release(struct aws_http_proxy_strategy *proxy_strategy); /** - * A constructor for a proxy strategy factory that performs basic authentication by adding the appropriate + * A constructor for a proxy strategy that performs basic authentication by adding the appropriate * header and header value to requests or CONNECT requests. * * @param allocator memory allocator to use * @param config basic authentication configuration info - * @return a new proxy strategy factory if successfully constructed, otherwise NULL + * @return a new proxy strategy if successfully constructed, otherwise NULL */ AWS_HTTP_API -struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_basic_auth( +struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_basic_auth( struct aws_allocator *allocator, - struct aws_http_proxy_strategy_factory_basic_auth_config *config); + struct aws_http_proxy_strategy_basic_auth_options *config); /** - * Factory constructor for a tunnel-only proxy request flow that does nothing. Intended to be the first link in an - * adaptive chain for a tunneling proxy: first try a basic CONNECT, then based on the response, later links are allowed - * to make attempts. + * Constructor for a tunnel-only proxy strategy that applies no changes to outbound CONNECT requests. Intended to be + * the first link in an adaptive chain for a tunneling proxy: first try a basic CONNECT, then based on the response, + * later links are allowed to make attempts. * * @param allocator memory allocator to use - * @return a new proxy strategy factory if successfully constructed, otherwise NULL + * @return a new proxy strategy if successfully constructed, otherwise NULL */ AWS_HTTP_API -struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunneling_one_time_identity( +struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_tunneling_one_time_identity( struct aws_allocator *allocator); /** - * Factory constructor for a tunneling proxy strategy that contains a chain of sub-strategies which are tried - * sequentially in order. + * Constructor for a forwarding-only proxy strategy that does nothing. Exists so that all proxy logic uses a + * strategy. * * @param allocator memory allocator to use - * @param config chain configuration info - * @return a new proxy strategy factory if successfully constructed, otherwise NULL + * @return a new proxy strategy if successfully constructed, otherwise NULL */ AWS_HTTP_API -struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunneling_chain( - struct aws_allocator *allocator, - struct aws_http_proxy_strategy_factory_tunneling_chain_options *config); +struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_forwarding_identity(struct aws_allocator *allocator); /** - * Factory constructor for a forwarding-only proxy strategy that does nothing. Exists so that all proxy logic uses a - * strategy. + * Constructor for a tunneling proxy strategy that contains a chain of sub-strategies which are tried + * sequentially in order. * * @param allocator memory allocator to use - * @return a new proxy strategy factory if successfully constructed, otherwise NULL + * @param config chain configuration options + * @return a new proxy strategy if successfully constructed, otherwise NULL */ AWS_HTTP_API -struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_forwarding_identity( - struct aws_allocator *allocator); +struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_tunneling_chain( + struct aws_allocator *allocator, + struct aws_http_proxy_strategy_tunneling_chain_options *config); /** - * This is an experimental API. - * - * Constructor for a WIP adaptive tunneling proxy strategy. This strategy attempts a vanilla CONNECT and if that - * fails it attempts a kerberos-oriented CONNECT request followed by a NTLM-oriented CONNECT request (if applicable). + * A constructor for a proxy strategy that performs kerberos authentication by adding the appropriate + * header and header value to CONNECT requests. * * @param allocator memory allocator to use - * @param config configuration options for the strategy factory - * @return a new proxy strategy factory if successfully constructed, otherwise NULL + * @param config kerberos authentication configuration info + * @return a new proxy strategy if successfully constructed, otherwise NULL */ AWS_HTTP_API -struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunneling_adaptive( +struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_tunneling_kerberos( struct aws_allocator *allocator, - struct aws_http_proxy_strategy_factory_tunneling_adaptive_options *config); + struct aws_http_proxy_strategy_tunneling_kerberos_options *config); /** - * A constructor for a proxy strategy factory that performs kerberos authentication by adding the appropriate - * header and header value to requests or CONNECT requests. + * Constructor for an NTLM proxy strategy. Because ntlm is a challenge-response authentication protocol, this + * strategy will only succeed in a chain in a non-leading position. The strategy extracts the challenge from the + * proxy's response to a previous CONNECT request in the chain. * * @param allocator memory allocator to use - * @param config kerberos authentication configuration info - * @return a new proxy strategy factory if successfully constructed, otherwise NULL + * @param config configuration options for the strategy + * @return a new proxy strategy if successfully constructed, otherwise NULL */ AWS_HTTP_API -struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunneling_kerberos( +struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_tunneling_ntlm( struct aws_allocator *allocator, - struct aws_http_proxy_strategy_factory_tunneling_kerberos_options *config); + struct aws_http_proxy_strategy_tunneling_ntlm_options *config); /** - * This is an experimental API. - * - * Constructor for a WIP adaptive tunneling NTLM proxy strategy. This strategy attempts a vanilla CONNECT and if that - * fails it attempts a ntlm-oriented CONNECT (if applicable). + * Constructor for an adaptive tunneling proxy strategy. This strategy attempts a vanilla CONNECT and if that + * fails it may make followup CONNECT attempts using kerberos or ntlm tokens, based on configuration and proxy + * response properties. * * @param allocator memory allocator to use - * @param config configuration options for the strategy factory - * @return a new proxy strategy factory if successfully constructed, otherwise NULL + * @param config configuration options for the strategy + * @return a new proxy strategy if successfully constructed, otherwise NULL */ AWS_HTTP_API -struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunneling_ntlm( +struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_tunneling_adaptive( struct aws_allocator *allocator, - struct aws_http_proxy_strategy_factory_tunneling_ntlm_options *config); + struct aws_http_proxy_strategy_tunneling_adaptive_options *config); AWS_EXTERN_C_END diff --git a/source/proxy_connection.c b/source/proxy_connection.c index 95fde5cb9..e3183e83e 100644 --- a/source/proxy_connection.c +++ b/source/proxy_connection.c @@ -53,7 +53,7 @@ void aws_http_proxy_user_data_destroy(struct aws_http_proxy_user_data *user_data aws_mem_release(user_data->allocator, user_data->tls_options); } - aws_http_proxy_strategy_release(user_data->proxy_strategy); + aws_http_proxy_negotiator_release(user_data->proxy_negotiator); aws_mem_release(user_data->allocator, user_data); } @@ -85,9 +85,9 @@ struct aws_http_proxy_user_data *aws_http_proxy_user_data_new( goto on_error; } - user_data->proxy_strategy = - aws_http_proxy_strategy_factory_create_strategy(user_data->proxy_config->proxy_strategy_factory, allocator); - if (user_data->proxy_strategy == NULL) { + user_data->proxy_negotiator = + aws_http_proxy_strategy_create_negotiator(user_data->proxy_config->proxy_strategy, allocator); + if (user_data->proxy_negotiator == NULL) { goto on_error; } @@ -289,10 +289,10 @@ static int s_aws_http_on_incoming_body_tunnel_proxy( (void)stream; struct aws_http_proxy_user_data *context = user_data; - aws_http_proxy_strategy_connect_on_incoming_body_fn *on_incoming_body = - context->proxy_strategy->strategy_vtable.tunnelling_vtable->on_incoming_body_callback; + aws_http_proxy_negotiator_connect_on_incoming_body_fn *on_incoming_body = + context->proxy_negotiator->strategy_vtable.tunnelling_vtable->on_incoming_body_callback; if (on_incoming_body != NULL) { - (*on_incoming_body)(context->proxy_strategy, data); + (*on_incoming_body)(context->proxy_negotiator, data); } aws_http_stream_update_window(stream, data->len); @@ -309,10 +309,10 @@ static int s_aws_http_on_response_headers_tunnel_proxy( (void)stream; struct aws_http_proxy_user_data *context = user_data; - aws_http_proxy_strategy_connect_on_incoming_headers_fn *on_incoming_headers = - context->proxy_strategy->strategy_vtable.tunnelling_vtable->on_incoming_headers_callback; + aws_http_proxy_negotiation_connect_on_incoming_headers_fn *on_incoming_headers = + context->proxy_negotiator->strategy_vtable.tunnelling_vtable->on_incoming_headers_callback; if (on_incoming_headers != NULL) { - (*on_incoming_headers)(context->proxy_strategy, header_block, header_array, num_headers); + (*on_incoming_headers)(context->proxy_negotiator, header_block, header_array, num_headers); } return AWS_OP_SUCCESS; @@ -339,10 +339,10 @@ static int s_aws_http_on_incoming_header_block_done_tunnel_proxy( context->error_code = AWS_ERROR_HTTP_PROXY_TLS_CONNECT_FAILED; } - aws_http_proxy_strategy_connect_status_fn *on_status = - context->proxy_strategy->strategy_vtable.tunnelling_vtable->on_status_callback; + aws_http_proxy_negotiator_connect_status_fn *on_status = + context->proxy_negotiator->strategy_vtable.tunnelling_vtable->on_status_callback; if (on_status != NULL) { - (*on_status)(context->proxy_strategy, status); + (*on_status)(context->proxy_negotiator, status); } } @@ -518,8 +518,8 @@ static int s_make_proxy_connect_request(struct aws_http_proxy_user_data *user_da return AWS_OP_ERR; } - (*user_data->proxy_strategy->strategy_vtable.tunnelling_vtable->connect_request_transform)( - user_data->proxy_strategy, + (*user_data->proxy_negotiator->strategy_vtable.tunnelling_vtable->connect_request_transform)( + user_data->proxy_negotiator, user_data->connect_request, s_terminate_tunneling_connect, s_continue_tunneling_connect, @@ -677,8 +677,8 @@ static int s_proxy_http_request_transform(struct aws_http_message *request, void return AWS_OP_ERR; } - if ((*proxy_ud->proxy_strategy->strategy_vtable.forwarding_vtable->forward_request_transform)( - proxy_ud->proxy_strategy, request)) { + if ((*proxy_ud->proxy_negotiator->strategy_vtable.forwarding_vtable->forward_request_transform)( + proxy_ud->proxy_negotiator, request)) { return AWS_OP_ERR; } @@ -836,24 +836,23 @@ static struct aws_http_proxy_config *s_aws_http_proxy_config_new( config->allocator = allocator; config->port = proxy_options->port; - if (proxy_options->proxy_strategy_factory != NULL) { - config->proxy_strategy_factory = aws_http_proxy_strategy_factory_acquire(proxy_options->proxy_strategy_factory); + if (proxy_options->proxy_strategy != NULL) { + config->proxy_strategy = aws_http_proxy_strategy_acquire(proxy_options->proxy_strategy); } else { switch (override_proxy_connection_type) { case AWS_HPCT_HTTP_FORWARD: - config->proxy_strategy_factory = aws_http_proxy_strategy_factory_new_forwarding_identity(allocator); + config->proxy_strategy = aws_http_proxy_strategy_new_forwarding_identity(allocator); break; case AWS_HPCT_HTTP_TUNNEL: - config->proxy_strategy_factory = - aws_http_proxy_strategy_factory_new_tunneling_one_time_identity(allocator); + config->proxy_strategy = aws_http_proxy_strategy_new_tunneling_one_time_identity(allocator); break; default: break; } - if (config->proxy_strategy_factory == NULL) { + if (config->proxy_strategy == NULL) { goto on_error; } } @@ -903,7 +902,7 @@ void aws_http_proxy_config_destroy(struct aws_http_proxy_config *config) { aws_mem_release(config->allocator, config->tls_options); } - aws_http_proxy_strategy_factory_release(config->proxy_strategy_factory); + aws_http_proxy_strategy_release(config->proxy_strategy); aws_mem_release(config->allocator, config); } @@ -917,7 +916,7 @@ void aws_http_proxy_options_init_from_config( options->host = aws_byte_cursor_from_buf(&config->host); options->port = config->port; options->tls_options = config->tls_options; - options->proxy_strategy_factory = config->proxy_strategy_factory; + options->proxy_strategy = config->proxy_strategy; } int aws_http_options_validate_proxy_configuration(const struct aws_http_client_connection_options *options) { @@ -934,9 +933,9 @@ int aws_http_options_validate_proxy_configuration(const struct aws_http_client_c return aws_raise_error(AWS_ERROR_INVALID_STATE); } - struct aws_http_proxy_strategy_factory *proxy_strategy_factory = options->proxy_options->proxy_strategy_factory; - if (proxy_strategy_factory != NULL) { - if (proxy_strategy_factory->proxy_connection_type != proxy_type) { + struct aws_http_proxy_strategy *proxy_strategy = options->proxy_options->proxy_strategy; + if (proxy_strategy != NULL) { + if (proxy_strategy->proxy_connection_type != proxy_type) { return aws_raise_error(AWS_ERROR_INVALID_STATE); } } diff --git a/source/proxy_strategy.c b/source/proxy_strategy.c index 1eb5c4b95..fee53b1b0 100644 --- a/source/proxy_strategy.c +++ b/source/proxy_strategy.c @@ -13,89 +13,89 @@ # pragma warning(disable : 4221) #endif /* _MSC_VER */ -struct aws_http_proxy_strategy *aws_http_proxy_strategy_acquire(struct aws_http_proxy_strategy *proxy_strategy) { - if (proxy_strategy != NULL) { - aws_ref_count_acquire(&proxy_strategy->ref_count); +struct aws_http_proxy_negotiator *aws_http_proxy_negotiator_acquire( + struct aws_http_proxy_negotiator *proxy_negotiator) { + if (proxy_negotiator != NULL) { + aws_ref_count_acquire(&proxy_negotiator->ref_count); } - return proxy_strategy; + return proxy_negotiator; } -void aws_http_proxy_strategy_release(struct aws_http_proxy_strategy *proxy_strategy) { - if (proxy_strategy != NULL) { - aws_ref_count_release(&proxy_strategy->ref_count); +void aws_http_proxy_negotiator_release(struct aws_http_proxy_negotiator *proxy_negotiator) { + if (proxy_negotiator != NULL) { + aws_ref_count_release(&proxy_negotiator->ref_count); } } -struct aws_http_proxy_strategy *aws_http_proxy_strategy_factory_create_strategy( - struct aws_http_proxy_strategy_factory *factory, +struct aws_http_proxy_negotiator *aws_http_proxy_strategy_create_negotiator( + struct aws_http_proxy_strategy *strategy, struct aws_allocator *allocator) { - if (factory == NULL || allocator == NULL) { + if (strategy == NULL || allocator == NULL) { aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); return NULL; } - return factory->vtable->create_strategy(factory, allocator); + return strategy->vtable->create_negotiator(strategy, allocator); } -struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_acquire( - struct aws_http_proxy_strategy_factory *proxy_strategy_factory) { - if (proxy_strategy_factory != NULL) { - aws_ref_count_acquire(&proxy_strategy_factory->ref_count); +struct aws_http_proxy_strategy *aws_http_proxy_strategy_acquire(struct aws_http_proxy_strategy *proxy_strategy) { + if (proxy_strategy != NULL) { + aws_ref_count_acquire(&proxy_strategy->ref_count); } - return proxy_strategy_factory; + return proxy_strategy; } -void aws_http_proxy_strategy_factory_release(struct aws_http_proxy_strategy_factory *proxy_strategy_factory) { - if (proxy_strategy_factory != NULL) { - aws_ref_count_release(&proxy_strategy_factory->ref_count); +void aws_http_proxy_strategy_release(struct aws_http_proxy_strategy *proxy_strategy) { + if (proxy_strategy != NULL) { + aws_ref_count_release(&proxy_strategy->ref_count); } } /*****************************************************************************************************************/ -enum proxy_strategy_connect_state { - AWS_PSCS_READY, - AWS_PSCS_IN_PROGRESS, - AWS_PSCS_SUCCESS, - AWS_PSCS_FAILURE, +enum proxy_negotiator_connect_state { + AWS_PNCS_READY, + AWS_PNCS_IN_PROGRESS, + AWS_PNCS_SUCCESS, + AWS_PNCS_FAILURE, }; -/* Functions for factory basic auth strategy with Basic Header */ +/* Functions for basic auth strategy */ -struct aws_http_proxy_strategy_factory_basic_auth { +struct aws_http_proxy_strategy_basic_auth { struct aws_allocator *allocator; struct aws_string *user_name; struct aws_string *password; - struct aws_http_proxy_strategy_factory factory_base; + struct aws_http_proxy_strategy strategy_base; }; -static void s_destroy_basic_auth_factory(struct aws_http_proxy_strategy_factory *proxy_strategy_factory) { - struct aws_http_proxy_strategy_factory_basic_auth *basic_auth_factory = proxy_strategy_factory->impl; +static void s_destroy_basic_auth_strategy(struct aws_http_proxy_strategy *proxy_strategy) { + struct aws_http_proxy_strategy_basic_auth *basic_auth_strategy = proxy_strategy->impl; - aws_string_destroy(basic_auth_factory->user_name); - aws_string_destroy(basic_auth_factory->password); + aws_string_destroy(basic_auth_strategy->user_name); + aws_string_destroy(basic_auth_strategy->password); - aws_mem_release(basic_auth_factory->allocator, basic_auth_factory); + aws_mem_release(basic_auth_strategy->allocator, basic_auth_strategy); } -struct aws_http_proxy_strategy_basic_auth { +struct aws_http_proxy_negotiator_basic_auth { struct aws_allocator *allocator; - struct aws_http_proxy_strategy_factory *factory; + struct aws_http_proxy_strategy *strategy; - enum proxy_strategy_connect_state connect_state; + enum proxy_negotiator_connect_state connect_state; - struct aws_http_proxy_strategy strategy_base; + struct aws_http_proxy_negotiator negotiator_base; }; -static void s_destroy_basic_auth_strategy(struct aws_http_proxy_strategy *proxy_strategy) { - struct aws_http_proxy_strategy_basic_auth *basic_auth_strategy = proxy_strategy->impl; +static void s_destroy_basic_auth_negotiator(struct aws_http_proxy_negotiator *proxy_negotiator) { + struct aws_http_proxy_negotiator_basic_auth *basic_auth_negotiator = proxy_negotiator->impl; - aws_http_proxy_strategy_factory_release(basic_auth_strategy->factory); + aws_http_proxy_strategy_release(basic_auth_negotiator->strategy); - aws_mem_release(basic_auth_strategy->allocator, basic_auth_strategy); + aws_mem_release(basic_auth_negotiator->allocator, basic_auth_negotiator); } AWS_STATIC_STRING_FROM_LITERAL(s_proxy_authorization_header_name, "Proxy-Authorization"); @@ -107,7 +107,7 @@ AWS_STATIC_STRING_FROM_LITERAL(s_proxy_authorization_header_basic_prefix, "Basic static int s_add_basic_proxy_authentication_header( struct aws_allocator *allocator, struct aws_http_message *request, - struct aws_http_proxy_strategy_basic_auth *basic_auth_strategy) { + struct aws_http_proxy_negotiator_basic_auth *basic_auth_negotiator) { struct aws_byte_buf base64_input_value; AWS_ZERO_STRUCT(base64_input_value); @@ -117,14 +117,17 @@ static int s_add_basic_proxy_authentication_header( int result = AWS_OP_ERR; - struct aws_http_proxy_strategy_factory_basic_auth *factory = basic_auth_strategy->factory->impl; + struct aws_http_proxy_strategy_basic_auth *basic_auth_strategy = basic_auth_negotiator->strategy->impl; - if (aws_byte_buf_init(&base64_input_value, allocator, factory->user_name->len + factory->password->len + 1)) { + if (aws_byte_buf_init( + &base64_input_value, + allocator, + basic_auth_strategy->user_name->len + basic_auth_strategy->password->len + 1)) { goto done; } /* First build a buffer with "username:password" in it */ - struct aws_byte_cursor username_cursor = aws_byte_cursor_from_string(factory->user_name); + struct aws_byte_cursor username_cursor = aws_byte_cursor_from_string(basic_auth_strategy->user_name); if (aws_byte_buf_append(&base64_input_value, &username_cursor)) { goto done; } @@ -134,7 +137,7 @@ static int s_add_basic_proxy_authentication_header( goto done; } - struct aws_byte_cursor password_cursor = aws_byte_cursor_from_string(factory->password); + struct aws_byte_cursor password_cursor = aws_byte_cursor_from_string(basic_auth_strategy->password); if (aws_byte_buf_append(&base64_input_value, &password_cursor)) { goto done; } @@ -182,100 +185,104 @@ static int s_add_basic_proxy_authentication_header( return result; } -int s_basic_auth_forward_add_header(struct aws_http_proxy_strategy *proxy_strategy, struct aws_http_message *message) { - struct aws_http_proxy_strategy_basic_auth *basic_auth_strategy = proxy_strategy->impl; +int s_basic_auth_forward_add_header( + struct aws_http_proxy_negotiator *proxy_negotiator, + struct aws_http_message *message) { + struct aws_http_proxy_negotiator_basic_auth *basic_auth_negotiator = proxy_negotiator->impl; - return s_add_basic_proxy_authentication_header(basic_auth_strategy->allocator, message, basic_auth_strategy); + return s_add_basic_proxy_authentication_header(basic_auth_negotiator->allocator, message, basic_auth_negotiator); } void s_basic_auth_tunnel_add_header( - struct aws_http_proxy_strategy *proxy_strategy, + struct aws_http_proxy_negotiator *proxy_negotiator, struct aws_http_message *message, - aws_http_proxy_strategy_terminate_fn *strategy_termination_callback, - aws_http_proxy_strategy_http_request_forward_fn *strategy_http_request_forward_callback, + aws_http_proxy_negotiation_terminate_fn *negotiation_termination_callback, + aws_http_proxy_negotiation_http_request_forward_fn *negotiation_http_request_forward_callback, void *internal_proxy_user_data) { - struct aws_http_proxy_strategy_basic_auth *basic_auth_strategy = proxy_strategy->impl; - if (basic_auth_strategy->connect_state != AWS_PSCS_READY) { - strategy_termination_callback(message, AWS_ERROR_INVALID_STATE, internal_proxy_user_data); + struct aws_http_proxy_negotiator_basic_auth *basic_auth_negotiator = proxy_negotiator->impl; + if (basic_auth_negotiator->connect_state != AWS_PNCS_READY) { + negotiation_termination_callback(message, AWS_ERROR_INVALID_STATE, internal_proxy_user_data); return; } - basic_auth_strategy->connect_state = AWS_PSCS_IN_PROGRESS; + basic_auth_negotiator->connect_state = AWS_PNCS_IN_PROGRESS; - if (s_add_basic_proxy_authentication_header(basic_auth_strategy->allocator, message, basic_auth_strategy)) { - strategy_termination_callback(message, aws_last_error(), internal_proxy_user_data); + if (s_add_basic_proxy_authentication_header(basic_auth_negotiator->allocator, message, basic_auth_negotiator)) { + negotiation_termination_callback(message, aws_last_error(), internal_proxy_user_data); return; } - strategy_http_request_forward_callback(message, internal_proxy_user_data); + negotiation_http_request_forward_callback(message, internal_proxy_user_data); } static int s_basic_auth_on_connect_status( - struct aws_http_proxy_strategy *proxy_strategy, + struct aws_http_proxy_negotiator *proxy_negotiator, enum aws_http_status_code status_code) { - struct aws_http_proxy_strategy_basic_auth *basic_auth_strategy = proxy_strategy->impl; + struct aws_http_proxy_negotiator_basic_auth *basic_auth_negotiator = proxy_negotiator->impl; - if (basic_auth_strategy->connect_state == AWS_PSCS_IN_PROGRESS) { + if (basic_auth_negotiator->connect_state == AWS_PNCS_IN_PROGRESS) { if (AWS_HTTP_STATUS_CODE_200_OK != status_code) { - basic_auth_strategy->connect_state = AWS_PSCS_FAILURE; + basic_auth_negotiator->connect_state = AWS_PNCS_FAILURE; } else { - basic_auth_strategy->connect_state = AWS_PSCS_SUCCESS; + basic_auth_negotiator->connect_state = AWS_PNCS_SUCCESS; } } return AWS_OP_SUCCESS; } -static struct aws_http_proxy_strategy_forwarding_vtable s_basic_auth_proxy_forwarding_vtable = { +static struct aws_http_proxy_negotiator_forwarding_vtable s_basic_auth_proxy_negotiator_forwarding_vtable = { .forward_request_transform = s_basic_auth_forward_add_header, }; -static struct aws_http_proxy_strategy_tunnelling_vtable s_basic_auth_proxy_tunneling_vtable = { +static struct aws_http_proxy_negotiator_tunnelling_vtable s_basic_auth_proxy_negotiator_tunneling_vtable = { .on_status_callback = s_basic_auth_on_connect_status, .connect_request_transform = s_basic_auth_tunnel_add_header, }; -static struct aws_http_proxy_strategy *s_create_basic_auth_strategy( - struct aws_http_proxy_strategy_factory *proxy_strategy_factory, +static struct aws_http_proxy_negotiator *s_create_basic_auth_negotiator( + struct aws_http_proxy_strategy *proxy_strategy, struct aws_allocator *allocator) { - if (proxy_strategy_factory == NULL || allocator == NULL) { + if (proxy_strategy == NULL || allocator == NULL) { aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); return NULL; } - struct aws_http_proxy_strategy_basic_auth *basic_auth_strategy = - aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_strategy_basic_auth)); - if (basic_auth_strategy == NULL) { + struct aws_http_proxy_negotiator_basic_auth *basic_auth_negotiator = + aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_negotiator_basic_auth)); + if (basic_auth_negotiator == NULL) { return NULL; } - basic_auth_strategy->allocator = allocator; - basic_auth_strategy->connect_state = AWS_PSCS_READY; - basic_auth_strategy->strategy_base.impl = basic_auth_strategy; + basic_auth_negotiator->allocator = allocator; + basic_auth_negotiator->connect_state = AWS_PNCS_READY; + basic_auth_negotiator->negotiator_base.impl = basic_auth_negotiator; aws_ref_count_init( - &basic_auth_strategy->strategy_base.ref_count, - &basic_auth_strategy->strategy_base, - (aws_simple_completion_callback *)s_destroy_basic_auth_strategy); + &basic_auth_negotiator->negotiator_base.ref_count, + &basic_auth_negotiator->negotiator_base, + (aws_simple_completion_callback *)s_destroy_basic_auth_negotiator); - if (proxy_strategy_factory->proxy_connection_type == AWS_HPCT_HTTP_FORWARD) { - basic_auth_strategy->strategy_base.strategy_vtable.forwarding_vtable = &s_basic_auth_proxy_forwarding_vtable; + if (proxy_strategy->proxy_connection_type == AWS_HPCT_HTTP_FORWARD) { + basic_auth_negotiator->negotiator_base.strategy_vtable.forwarding_vtable = + &s_basic_auth_proxy_negotiator_forwarding_vtable; } else { - basic_auth_strategy->strategy_base.strategy_vtable.tunnelling_vtable = &s_basic_auth_proxy_tunneling_vtable; + basic_auth_negotiator->negotiator_base.strategy_vtable.tunnelling_vtable = + &s_basic_auth_proxy_negotiator_tunneling_vtable; } - basic_auth_strategy->factory = aws_ref_count_acquire(&proxy_strategy_factory->ref_count); + basic_auth_negotiator->strategy = aws_http_proxy_strategy_acquire(proxy_strategy); - return &basic_auth_strategy->strategy_base; + return &basic_auth_negotiator->negotiator_base; } -static struct aws_http_proxy_strategy_factory_vtable s_basic_auth_factory_vtable = { - .create_strategy = s_create_basic_auth_strategy, +static struct aws_http_proxy_strategy_vtable s_basic_auth_proxy_strategy_vtable = { + .create_negotiator = s_create_basic_auth_negotiator, }; -struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_basic_auth( +struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_basic_auth( struct aws_allocator *allocator, - struct aws_http_proxy_strategy_factory_basic_auth_config *config) { + struct aws_http_proxy_strategy_basic_auth_options *config) { if (config == NULL || allocator == NULL) { aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); return NULL; @@ -287,286 +294,284 @@ struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_basi return NULL; } - struct aws_http_proxy_strategy_factory_basic_auth *basic_auth_factory = - aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_strategy_factory_basic_auth)); - if (basic_auth_factory == NULL) { + struct aws_http_proxy_strategy_basic_auth *basic_auth_strategy = + aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_strategy_basic_auth)); + if (basic_auth_strategy == NULL) { return NULL; } - basic_auth_factory->factory_base.impl = basic_auth_factory; - basic_auth_factory->factory_base.vtable = &s_basic_auth_factory_vtable; - basic_auth_factory->allocator = allocator; - basic_auth_factory->factory_base.proxy_connection_type = config->proxy_connection_type; + basic_auth_strategy->strategy_base.impl = basic_auth_strategy; + basic_auth_strategy->strategy_base.vtable = &s_basic_auth_proxy_strategy_vtable; + basic_auth_strategy->allocator = allocator; + basic_auth_strategy->strategy_base.proxy_connection_type = config->proxy_connection_type; aws_ref_count_init( - &basic_auth_factory->factory_base.ref_count, - &basic_auth_factory->factory_base, - (aws_simple_completion_callback *)s_destroy_basic_auth_factory); + &basic_auth_strategy->strategy_base.ref_count, + &basic_auth_strategy->strategy_base, + (aws_simple_completion_callback *)s_destroy_basic_auth_strategy); - basic_auth_factory->user_name = aws_string_new_from_cursor(allocator, &config->user_name); - if (basic_auth_factory->user_name == NULL) { + basic_auth_strategy->user_name = aws_string_new_from_cursor(allocator, &config->user_name); + if (basic_auth_strategy->user_name == NULL) { goto on_error; } - basic_auth_factory->password = aws_string_new_from_cursor(allocator, &config->password); - if (basic_auth_factory->password == NULL) { + basic_auth_strategy->password = aws_string_new_from_cursor(allocator, &config->password); + if (basic_auth_strategy->password == NULL) { goto on_error; } - return &basic_auth_factory->factory_base; + return &basic_auth_strategy->strategy_base; on_error: - aws_http_proxy_strategy_factory_release(&basic_auth_factory->factory_base); + aws_http_proxy_strategy_release(&basic_auth_strategy->strategy_base); return NULL; } /*****************************************************************************************************************/ -struct aws_http_proxy_strategy_factory_one_time_identity { +struct aws_http_proxy_strategy_one_time_identity { struct aws_allocator *allocator; - struct aws_http_proxy_strategy_factory factory_base; + struct aws_http_proxy_strategy strategy_base; }; -struct aws_http_proxy_strategy_one_time_identity { +struct aws_http_proxy_negotiator_one_time_identity { struct aws_allocator *allocator; - enum proxy_strategy_connect_state connect_state; + enum proxy_negotiator_connect_state connect_state; - struct aws_http_proxy_strategy strategy_base; + struct aws_http_proxy_negotiator negotiator_base; }; -static void s_destroy_one_time_identity_strategy(struct aws_http_proxy_strategy *proxy_strategy) { - struct aws_http_proxy_strategy_one_time_identity *identity_strategy = proxy_strategy->impl; +static void s_destroy_one_time_identity_negotiator(struct aws_http_proxy_negotiator *proxy_negotiator) { + struct aws_http_proxy_negotiator_one_time_identity *identity_negotiator = proxy_negotiator->impl; - aws_mem_release(identity_strategy->allocator, identity_strategy); + aws_mem_release(identity_negotiator->allocator, identity_negotiator); } void s_one_time_identity_connect_transform( - struct aws_http_proxy_strategy *proxy_strategy, + struct aws_http_proxy_negotiator *proxy_negotiator, struct aws_http_message *message, - aws_http_proxy_strategy_terminate_fn *strategy_termination_callback, - aws_http_proxy_strategy_http_request_forward_fn *strategy_http_request_forward_callback, + aws_http_proxy_negotiation_terminate_fn *negotiation_termination_callback, + aws_http_proxy_negotiation_http_request_forward_fn *negotiation_http_request_forward_callback, void *internal_proxy_user_data) { - struct aws_http_proxy_strategy_one_time_identity *one_time_identity_strategy = proxy_strategy->impl; - if (one_time_identity_strategy->connect_state != AWS_PSCS_READY) { - strategy_termination_callback(message, AWS_ERROR_INVALID_STATE, internal_proxy_user_data); + struct aws_http_proxy_negotiator_one_time_identity *one_time_identity_negotiator = proxy_negotiator->impl; + if (one_time_identity_negotiator->connect_state != AWS_PNCS_READY) { + negotiation_termination_callback(message, AWS_ERROR_INVALID_STATE, internal_proxy_user_data); return; } - one_time_identity_strategy->connect_state = AWS_PSCS_IN_PROGRESS; - strategy_http_request_forward_callback(message, internal_proxy_user_data); + one_time_identity_negotiator->connect_state = AWS_PNCS_IN_PROGRESS; + negotiation_http_request_forward_callback(message, internal_proxy_user_data); } static int s_one_time_identity_on_connect_status( - struct aws_http_proxy_strategy *proxy_strategy, + struct aws_http_proxy_negotiator *proxy_negotiator, enum aws_http_status_code status_code) { - struct aws_http_proxy_strategy_one_time_identity *one_time_identity_strategy = proxy_strategy->impl; + struct aws_http_proxy_negotiator_one_time_identity *one_time_identity_negotiator = proxy_negotiator->impl; - if (one_time_identity_strategy->connect_state == AWS_PSCS_IN_PROGRESS) { + if (one_time_identity_negotiator->connect_state == AWS_PNCS_IN_PROGRESS) { if (AWS_HTTP_STATUS_CODE_200_OK != status_code) { - one_time_identity_strategy->connect_state = AWS_PSCS_FAILURE; + one_time_identity_negotiator->connect_state = AWS_PNCS_FAILURE; } else { - one_time_identity_strategy->connect_state = AWS_PSCS_SUCCESS; + one_time_identity_negotiator->connect_state = AWS_PNCS_SUCCESS; } } return AWS_OP_SUCCESS; } -static struct aws_http_proxy_strategy_tunnelling_vtable s_one_time_identity_proxy_tunneling_vtable = { +static struct aws_http_proxy_negotiator_tunnelling_vtable s_one_time_identity_proxy_negotiator_tunneling_vtable = { .on_status_callback = s_one_time_identity_on_connect_status, .connect_request_transform = s_one_time_identity_connect_transform, }; -static struct aws_http_proxy_strategy *s_create_one_time_identity_strategy( - struct aws_http_proxy_strategy_factory *proxy_strategy_factory, +static struct aws_http_proxy_negotiator *s_create_one_time_identity_negotiator( + struct aws_http_proxy_strategy *proxy_strategy, struct aws_allocator *allocator) { - if (proxy_strategy_factory == NULL || allocator == NULL) { + if (proxy_strategy == NULL || allocator == NULL) { aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); return NULL; } - struct aws_http_proxy_strategy_one_time_identity *identity_strategy = - aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_strategy_one_time_identity)); - if (identity_strategy == NULL) { + struct aws_http_proxy_negotiator_one_time_identity *identity_negotiator = + aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_negotiator_one_time_identity)); + if (identity_negotiator == NULL) { return NULL; } - identity_strategy->allocator = allocator; - identity_strategy->connect_state = AWS_PSCS_READY; - identity_strategy->strategy_base.impl = identity_strategy; + identity_negotiator->allocator = allocator; + identity_negotiator->connect_state = AWS_PNCS_READY; + identity_negotiator->negotiator_base.impl = identity_negotiator; aws_ref_count_init( - &identity_strategy->strategy_base.ref_count, - &identity_strategy->strategy_base, - (aws_simple_completion_callback *)s_destroy_one_time_identity_strategy); + &identity_negotiator->negotiator_base.ref_count, + &identity_negotiator->negotiator_base, + (aws_simple_completion_callback *)s_destroy_one_time_identity_negotiator); - identity_strategy->strategy_base.strategy_vtable.tunnelling_vtable = &s_one_time_identity_proxy_tunneling_vtable; + identity_negotiator->negotiator_base.strategy_vtable.tunnelling_vtable = + &s_one_time_identity_proxy_negotiator_tunneling_vtable; - return &identity_strategy->strategy_base; + return &identity_negotiator->negotiator_base; } -static struct aws_http_proxy_strategy_factory_vtable s_one_time_identity_factory_vtable = { - .create_strategy = s_create_one_time_identity_strategy, +static struct aws_http_proxy_strategy_vtable s_one_time_identity_proxy_strategy_vtable = { + .create_negotiator = s_create_one_time_identity_negotiator, }; -static void s_destroy_one_time_identity_factory(struct aws_http_proxy_strategy_factory *proxy_strategy_factory) { - struct aws_http_proxy_strategy_factory_one_time_identity *identity_factory = proxy_strategy_factory->impl; +static void s_destroy_one_time_identity_strategy(struct aws_http_proxy_strategy *proxy_strategy) { + struct aws_http_proxy_strategy_one_time_identity *identity_strategy = proxy_strategy->impl; - aws_mem_release(identity_factory->allocator, identity_factory); + aws_mem_release(identity_strategy->allocator, identity_strategy); } -struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunneling_one_time_identity( +struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_tunneling_one_time_identity( struct aws_allocator *allocator) { if (allocator == NULL) { aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); return NULL; } - struct aws_http_proxy_strategy_factory_one_time_identity *identity_factory = - aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_strategy_factory_one_time_identity)); - if (identity_factory == NULL) { + struct aws_http_proxy_strategy_one_time_identity *identity_strategy = + aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_strategy_one_time_identity)); + if (identity_strategy == NULL) { return NULL; } - identity_factory->factory_base.impl = identity_factory; - identity_factory->factory_base.vtable = &s_one_time_identity_factory_vtable; - identity_factory->factory_base.proxy_connection_type = AWS_HPCT_HTTP_TUNNEL; - identity_factory->allocator = allocator; + identity_strategy->strategy_base.impl = identity_strategy; + identity_strategy->strategy_base.vtable = &s_one_time_identity_proxy_strategy_vtable; + identity_strategy->strategy_base.proxy_connection_type = AWS_HPCT_HTTP_TUNNEL; + identity_strategy->allocator = allocator; aws_ref_count_init( - &identity_factory->factory_base.ref_count, - &identity_factory->factory_base, - (aws_simple_completion_callback *)s_destroy_one_time_identity_factory); + &identity_strategy->strategy_base.ref_count, + &identity_strategy->strategy_base, + (aws_simple_completion_callback *)s_destroy_one_time_identity_strategy); - return &identity_factory->factory_base; + return &identity_strategy->strategy_base; } /******************************************************************************************************************/ -struct aws_http_proxy_strategy_factory_forwarding_identity { +struct aws_http_proxy_strategy_forwarding_identity { struct aws_allocator *allocator; - struct aws_http_proxy_strategy_factory factory_base; + struct aws_http_proxy_strategy strategy_base; }; -struct aws_http_proxy_strategy_forwarding_identity { +struct aws_http_proxy_negotiator_forwarding_identity { struct aws_allocator *allocator; - enum proxy_strategy_connect_state connect_state; - - struct aws_http_proxy_strategy strategy_base; + struct aws_http_proxy_negotiator negotiator_base; }; -static void s_destroy_forwarding_identity_strategy(struct aws_http_proxy_strategy *proxy_strategy) { - struct aws_http_proxy_strategy_forwarding_identity *identity_strategy = proxy_strategy->impl; +static void s_destroy_forwarding_identity_negotiator(struct aws_http_proxy_negotiator *proxy_negotiator) { + struct aws_http_proxy_negotiator_forwarding_identity *identity_negotiator = proxy_negotiator->impl; - aws_mem_release(identity_strategy->allocator, identity_strategy); + aws_mem_release(identity_negotiator->allocator, identity_negotiator); } int s_forwarding_identity_connect_transform( - struct aws_http_proxy_strategy *proxy_strategy, + struct aws_http_proxy_negotiator *proxy_negotiator, struct aws_http_message *message) { (void)message; - (void)proxy_strategy; + (void)proxy_negotiator; return AWS_OP_SUCCESS; } -static struct aws_http_proxy_strategy_forwarding_vtable s_forwarding_identity_proxy_tunneling_vtable = { +static struct aws_http_proxy_negotiator_forwarding_vtable s_forwarding_identity_proxy_negotiator_tunneling_vtable = { .forward_request_transform = s_forwarding_identity_connect_transform, }; -static struct aws_http_proxy_strategy *s_create_forwarding_identity_strategy( - struct aws_http_proxy_strategy_factory *proxy_strategy_factory, +static struct aws_http_proxy_negotiator *s_create_forwarding_identity_negotiator( + struct aws_http_proxy_strategy *proxy_strategy, struct aws_allocator *allocator) { - if (proxy_strategy_factory == NULL || allocator == NULL) { + if (proxy_strategy == NULL || allocator == NULL) { aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); return NULL; } - struct aws_http_proxy_strategy_forwarding_identity *identity_strategy = - aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_strategy_forwarding_identity)); - if (identity_strategy == NULL) { + struct aws_http_proxy_negotiator_forwarding_identity *identity_negotiator = + aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_negotiator_forwarding_identity)); + if (identity_negotiator == NULL) { return NULL; } - identity_strategy->allocator = allocator; - identity_strategy->connect_state = AWS_PSCS_READY; - identity_strategy->strategy_base.impl = identity_strategy; + identity_negotiator->allocator = allocator; + identity_negotiator->negotiator_base.impl = identity_negotiator; aws_ref_count_init( - &identity_strategy->strategy_base.ref_count, - &identity_strategy->strategy_base, - (aws_simple_completion_callback *)s_destroy_forwarding_identity_strategy); + &identity_negotiator->negotiator_base.ref_count, + &identity_negotiator->negotiator_base, + (aws_simple_completion_callback *)s_destroy_forwarding_identity_negotiator); - identity_strategy->strategy_base.strategy_vtable.forwarding_vtable = &s_forwarding_identity_proxy_tunneling_vtable; + identity_negotiator->negotiator_base.strategy_vtable.forwarding_vtable = + &s_forwarding_identity_proxy_negotiator_tunneling_vtable; - return &identity_strategy->strategy_base; + return &identity_negotiator->negotiator_base; } -static struct aws_http_proxy_strategy_factory_vtable s_forwarding_identity_factory_vtable = { - .create_strategy = s_create_forwarding_identity_strategy, +static struct aws_http_proxy_strategy_vtable s_forwarding_identity_strategy_vtable = { + .create_negotiator = s_create_forwarding_identity_negotiator, }; -static void s_destroy_forwarding_identity_factory(struct aws_http_proxy_strategy_factory *proxy_strategy_factory) { - struct aws_http_proxy_strategy_factory_forwarding_identity *identity_factory = proxy_strategy_factory->impl; +static void s_destroy_forwarding_identity_strategy(struct aws_http_proxy_strategy *proxy_strategy) { + struct aws_http_proxy_strategy_forwarding_identity *identity_strategy = proxy_strategy->impl; - aws_mem_release(identity_factory->allocator, identity_factory); + aws_mem_release(identity_strategy->allocator, identity_strategy); } -struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_forwarding_identity( - struct aws_allocator *allocator) { +struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_forwarding_identity(struct aws_allocator *allocator) { if (allocator == NULL) { aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); return NULL; } - struct aws_http_proxy_strategy_factory_forwarding_identity *identity_factory = - aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_strategy_factory_forwarding_identity)); - if (identity_factory == NULL) { + struct aws_http_proxy_strategy_forwarding_identity *identity_strategy = + aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_strategy_forwarding_identity)); + if (identity_strategy == NULL) { return NULL; } - identity_factory->factory_base.impl = identity_factory; - identity_factory->factory_base.vtable = &s_forwarding_identity_factory_vtable; - identity_factory->factory_base.proxy_connection_type = AWS_HPCT_HTTP_FORWARD; - identity_factory->allocator = allocator; + identity_strategy->strategy_base.impl = identity_strategy; + identity_strategy->strategy_base.vtable = &s_forwarding_identity_strategy_vtable; + identity_strategy->strategy_base.proxy_connection_type = AWS_HPCT_HTTP_FORWARD; + identity_strategy->allocator = allocator; aws_ref_count_init( - &identity_factory->factory_base.ref_count, - &identity_factory->factory_base, - (aws_simple_completion_callback *)s_destroy_forwarding_identity_factory); + &identity_strategy->strategy_base.ref_count, + &identity_strategy->strategy_base, + (aws_simple_completion_callback *)s_destroy_forwarding_identity_strategy); - return &identity_factory->factory_base; + return &identity_strategy->strategy_base; } /******************************************************************************************************************/ -struct aws_http_proxy_strategy_factory_tunneling_chain { +struct aws_http_proxy_strategy_tunneling_chain { struct aws_allocator *allocator; - struct aws_array_list strategy_factories; + struct aws_array_list strategies; - struct aws_http_proxy_strategy_factory factory_base; + struct aws_http_proxy_strategy strategy_base; }; -struct aws_http_proxy_strategy_tunneling_chain { +struct aws_http_proxy_negotiator_tunneling_chain { struct aws_allocator *allocator; - struct aws_array_list strategies; - size_t current_strategy_transform_index; + struct aws_array_list negotiators; + size_t current_negotiator_transform_index; void *original_internal_proxy_user_data; - aws_http_proxy_strategy_terminate_fn *original_strategy_termination_callback; - aws_http_proxy_strategy_http_request_forward_fn *original_strategy_http_request_forward_callback; + aws_http_proxy_negotiation_terminate_fn *original_negotiation_termination_callback; + aws_http_proxy_negotiation_http_request_forward_fn *original_negotiation_http_request_forward_callback; - struct aws_http_proxy_strategy strategy_base; + struct aws_http_proxy_negotiator negotiator_base; }; -static void s_chain_tunnel_try_next_strategy( - struct aws_http_proxy_strategy *proxy_strategy, +static void s_chain_tunnel_try_next_negotiator( + struct aws_http_proxy_negotiator *proxy_negotiator, struct aws_http_message *message); static void s_chain_tunnel_iteration_termination_callback( @@ -576,87 +581,89 @@ static void s_chain_tunnel_iteration_termination_callback( (void)error_code; /* TODO: log */ - struct aws_http_proxy_strategy *proxy_strategy = user_data; - struct aws_http_proxy_strategy_tunneling_chain *chain_strategy = proxy_strategy->impl; + struct aws_http_proxy_negotiator *proxy_negotiator = user_data; + struct aws_http_proxy_negotiator_tunneling_chain *chain_negotiator = proxy_negotiator->impl; - chain_strategy->current_strategy_transform_index++; + chain_negotiator->current_negotiator_transform_index++; - s_chain_tunnel_try_next_strategy(proxy_strategy, message); + s_chain_tunnel_try_next_negotiator(proxy_negotiator, message); } static void s_chain_tunnel_iteration_forward_callback(struct aws_http_message *message, void *user_data) { - struct aws_http_proxy_strategy *proxy_strategy = user_data; - struct aws_http_proxy_strategy_tunneling_chain *chain_strategy = proxy_strategy->impl; + struct aws_http_proxy_negotiator *proxy_negotiator = user_data; + struct aws_http_proxy_negotiator_tunneling_chain *chain_negotiator = proxy_negotiator->impl; - chain_strategy->original_strategy_http_request_forward_callback( - message, chain_strategy->original_internal_proxy_user_data); + chain_negotiator->original_negotiation_http_request_forward_callback( + message, chain_negotiator->original_internal_proxy_user_data); } -static void s_chain_tunnel_try_next_strategy( - struct aws_http_proxy_strategy *proxy_strategy, +static void s_chain_tunnel_try_next_negotiator( + struct aws_http_proxy_negotiator *proxy_negotiator, struct aws_http_message *message) { - struct aws_http_proxy_strategy_tunneling_chain *chain_strategy = proxy_strategy->impl; + struct aws_http_proxy_negotiator_tunneling_chain *chain_negotiator = proxy_negotiator->impl; - size_t strategy_count = aws_array_list_length(&chain_strategy->strategies); - if (chain_strategy->current_strategy_transform_index >= strategy_count) { + size_t negotiator_count = aws_array_list_length(&chain_negotiator->negotiators); + if (chain_negotiator->current_negotiator_transform_index >= negotiator_count) { goto on_error; } - struct aws_http_proxy_strategy *current_strategy = NULL; + struct aws_http_proxy_negotiator *current_negotiator = NULL; if (aws_array_list_get_at( - &chain_strategy->strategies, ¤t_strategy, chain_strategy->current_strategy_transform_index)) { + &chain_negotiator->negotiators, + ¤t_negotiator, + chain_negotiator->current_negotiator_transform_index)) { goto on_error; } - current_strategy->strategy_vtable.tunnelling_vtable->connect_request_transform( - current_strategy, + current_negotiator->strategy_vtable.tunnelling_vtable->connect_request_transform( + current_negotiator, message, s_chain_tunnel_iteration_termination_callback, s_chain_tunnel_iteration_forward_callback, - proxy_strategy); + proxy_negotiator); return; on_error: - chain_strategy->original_strategy_termination_callback( - message, AWS_ERROR_HTTP_PROXY_STRATEGY_TRANSFORM_FAILED, chain_strategy->original_internal_proxy_user_data); + chain_negotiator->original_negotiation_termination_callback( + message, AWS_ERROR_HTTP_PROXY_STRATEGY_TRANSFORM_FAILED, chain_negotiator->original_internal_proxy_user_data); } static void s_chain_tunnel_transform_connect( - struct aws_http_proxy_strategy *proxy_strategy, + struct aws_http_proxy_negotiator *proxy_negotiator, struct aws_http_message *message, - aws_http_proxy_strategy_terminate_fn *strategy_termination_callback, - aws_http_proxy_strategy_http_request_forward_fn *strategy_http_request_forward_callback, + aws_http_proxy_negotiation_terminate_fn *negotiation_termination_callback, + aws_http_proxy_negotiation_http_request_forward_fn *negotiation_http_request_forward_callback, void *internal_proxy_user_data) { - struct aws_http_proxy_strategy_tunneling_chain *chain_strategy = proxy_strategy->impl; + struct aws_http_proxy_negotiator_tunneling_chain *chain_negotiator = proxy_negotiator->impl; - chain_strategy->current_strategy_transform_index = 0; - chain_strategy->original_internal_proxy_user_data = internal_proxy_user_data; - chain_strategy->original_strategy_termination_callback = strategy_termination_callback; - chain_strategy->original_strategy_http_request_forward_callback = strategy_http_request_forward_callback; + chain_negotiator->current_negotiator_transform_index = 0; + chain_negotiator->original_internal_proxy_user_data = internal_proxy_user_data; + chain_negotiator->original_negotiation_termination_callback = negotiation_termination_callback; + chain_negotiator->original_negotiation_http_request_forward_callback = negotiation_http_request_forward_callback; - s_chain_tunnel_try_next_strategy(proxy_strategy, message); + s_chain_tunnel_try_next_negotiator(proxy_negotiator, message); } static int s_chain_on_incoming_headers( - struct aws_http_proxy_strategy *proxy_strategy, + struct aws_http_proxy_negotiator *proxy_negotiator, enum aws_http_header_block header_block, const struct aws_http_header *header_array, size_t num_headers) { - struct aws_http_proxy_strategy_tunneling_chain *chain_strategy = proxy_strategy->impl; - size_t strategy_count = aws_array_list_length(&chain_strategy->strategies); - for (size_t i = 0; i < strategy_count; ++i) { - struct aws_http_proxy_strategy *strategy = NULL; - if (aws_array_list_get_at(&chain_strategy->strategies, &strategy, i)) { + struct aws_http_proxy_negotiator_tunneling_chain *chain_negotiator = proxy_negotiator->impl; + size_t negotiator_count = aws_array_list_length(&chain_negotiator->negotiators); + for (size_t i = 0; i < negotiator_count; ++i) { + struct aws_http_proxy_negotiator *negotiator = NULL; + if (aws_array_list_get_at(&chain_negotiator->negotiators, &negotiator, i)) { continue; } - aws_http_proxy_strategy_connect_on_incoming_headers_fn *on_incoming_headers = - strategy->strategy_vtable.tunnelling_vtable->on_incoming_headers_callback; + aws_http_proxy_negotiation_connect_on_incoming_headers_fn *on_incoming_headers = + negotiator->strategy_vtable.tunnelling_vtable->on_incoming_headers_callback; if (on_incoming_headers != NULL) { - (*on_incoming_headers)(strategy, header_block, header_array, num_headers); + (*on_incoming_headers)(negotiator, header_block, header_array, num_headers); } } @@ -664,21 +671,21 @@ static int s_chain_on_incoming_headers( } static int s_chain_on_connect_status( - struct aws_http_proxy_strategy *proxy_strategy, + struct aws_http_proxy_negotiator *proxy_negotiator, enum aws_http_status_code status_code) { - struct aws_http_proxy_strategy_tunneling_chain *chain_strategy = proxy_strategy->impl; - size_t strategy_count = aws_array_list_length(&chain_strategy->strategies); - for (size_t i = 0; i < strategy_count; ++i) { - struct aws_http_proxy_strategy *strategy = NULL; - if (aws_array_list_get_at(&chain_strategy->strategies, &strategy, i)) { + struct aws_http_proxy_negotiator_tunneling_chain *chain_negotiator = proxy_negotiator->impl; + size_t negotiator_count = aws_array_list_length(&chain_negotiator->negotiators); + for (size_t i = 0; i < negotiator_count; ++i) { + struct aws_http_proxy_negotiator *negotiator = NULL; + if (aws_array_list_get_at(&chain_negotiator->negotiators, &negotiator, i)) { continue; } - aws_http_proxy_strategy_connect_status_fn *on_status = - strategy->strategy_vtable.tunnelling_vtable->on_status_callback; + aws_http_proxy_negotiator_connect_status_fn *on_status = + negotiator->strategy_vtable.tunnelling_vtable->on_status_callback; if (on_status != NULL) { - (*on_status)(strategy, status_code); + (*on_status)(negotiator, status_code); } } @@ -686,179 +693,177 @@ static int s_chain_on_connect_status( } static int s_chain_on_incoming_body( - struct aws_http_proxy_strategy *proxy_strategy, + struct aws_http_proxy_negotiator *proxy_negotiator, const struct aws_byte_cursor *data) { - struct aws_http_proxy_strategy_tunneling_chain *chain_strategy = proxy_strategy->impl; - size_t strategy_count = aws_array_list_length(&chain_strategy->strategies); - for (size_t i = 0; i < strategy_count; ++i) { - struct aws_http_proxy_strategy *strategy = NULL; - if (aws_array_list_get_at(&chain_strategy->strategies, &strategy, i)) { + struct aws_http_proxy_negotiator_tunneling_chain *chain_negotiator = proxy_negotiator->impl; + size_t negotiator_count = aws_array_list_length(&chain_negotiator->negotiators); + for (size_t i = 0; i < negotiator_count; ++i) { + struct aws_http_proxy_negotiator *negotiator = NULL; + if (aws_array_list_get_at(&chain_negotiator->negotiators, &negotiator, i)) { continue; } - aws_http_proxy_strategy_connect_on_incoming_body_fn *on_incoming_body = - strategy->strategy_vtable.tunnelling_vtable->on_incoming_body_callback; + aws_http_proxy_negotiator_connect_on_incoming_body_fn *on_incoming_body = + negotiator->strategy_vtable.tunnelling_vtable->on_incoming_body_callback; if (on_incoming_body != NULL) { - (*on_incoming_body)(strategy, data); + (*on_incoming_body)(negotiator, data); } } return AWS_OP_SUCCESS; } -static struct aws_http_proxy_strategy_tunnelling_vtable s_tunneling_chain_proxy_tunneling_vtable = { +static struct aws_http_proxy_negotiator_tunnelling_vtable s_tunneling_chain_proxy_negotiator_tunneling_vtable = { .on_incoming_body_callback = s_chain_on_incoming_body, .on_incoming_headers_callback = s_chain_on_incoming_headers, .on_status_callback = s_chain_on_connect_status, .connect_request_transform = s_chain_tunnel_transform_connect, }; -static void s_destroy_tunneling_chain_strategy(struct aws_http_proxy_strategy *proxy_strategy) { - struct aws_http_proxy_strategy_tunneling_chain *chain_strategy = proxy_strategy->impl; +static void s_destroy_tunneling_chain_negotiator(struct aws_http_proxy_negotiator *proxy_negotiator) { + struct aws_http_proxy_negotiator_tunneling_chain *chain_negotiator = proxy_negotiator->impl; - size_t strategy_count = aws_array_list_length(&chain_strategy->strategies); - for (size_t i = 0; i < strategy_count; ++i) { - struct aws_http_proxy_strategy *strategy = NULL; - if (aws_array_list_get_at(&chain_strategy->strategies, &strategy, i)) { + size_t negotiator_count = aws_array_list_length(&chain_negotiator->negotiators); + for (size_t i = 0; i < negotiator_count; ++i) { + struct aws_http_proxy_negotiator *negotiator = NULL; + if (aws_array_list_get_at(&chain_negotiator->negotiators, &negotiator, i)) { continue; } - aws_http_proxy_strategy_release(strategy); + aws_http_proxy_negotiator_release(negotiator); } - aws_array_list_clean_up(&chain_strategy->strategies); + aws_array_list_clean_up(&chain_negotiator->negotiators); - aws_mem_release(chain_strategy->allocator, chain_strategy); + aws_mem_release(chain_negotiator->allocator, chain_negotiator); } -static struct aws_http_proxy_strategy *s_create_tunneling_chain_strategy( - struct aws_http_proxy_strategy_factory *proxy_strategy_factory, +static struct aws_http_proxy_negotiator *s_create_tunneling_chain_negotiator( + struct aws_http_proxy_strategy *proxy_strategy, struct aws_allocator *allocator) { - if (proxy_strategy_factory == NULL || allocator == NULL) { + if (proxy_strategy == NULL || allocator == NULL) { aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); return NULL; } - struct aws_http_proxy_strategy_tunneling_chain *chain_strategy = - aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_strategy_tunneling_chain)); - if (chain_strategy == NULL) { + struct aws_http_proxy_negotiator_tunneling_chain *chain_negotiator = + aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_negotiator_tunneling_chain)); + if (chain_negotiator == NULL) { return NULL; } - chain_strategy->allocator = allocator; - chain_strategy->strategy_base.impl = chain_strategy; + chain_negotiator->allocator = allocator; + chain_negotiator->negotiator_base.impl = chain_negotiator; aws_ref_count_init( - &chain_strategy->strategy_base.ref_count, - &chain_strategy->strategy_base, - (aws_simple_completion_callback *)s_destroy_tunneling_chain_strategy); + &chain_negotiator->negotiator_base.ref_count, + &chain_negotiator->negotiator_base, + (aws_simple_completion_callback *)s_destroy_tunneling_chain_negotiator); - chain_strategy->strategy_base.strategy_vtable.tunnelling_vtable = &s_tunneling_chain_proxy_tunneling_vtable; + chain_negotiator->negotiator_base.strategy_vtable.tunnelling_vtable = + &s_tunneling_chain_proxy_negotiator_tunneling_vtable; - struct aws_http_proxy_strategy_factory_tunneling_chain *chain_factory = proxy_strategy_factory->impl; - size_t strategy_count = aws_array_list_length(&chain_factory->strategy_factories); + struct aws_http_proxy_strategy_tunneling_chain *chain_strategy = proxy_strategy->impl; + size_t strategy_count = aws_array_list_length(&chain_strategy->strategies); if (aws_array_list_init_dynamic( - &chain_strategy->strategies, allocator, strategy_count, sizeof(struct aws_http_proxy_strategy *))) { + &chain_negotiator->negotiators, allocator, strategy_count, sizeof(struct aws_http_proxy_negotiator *))) { goto on_error; } for (size_t i = 0; i < strategy_count; ++i) { - struct aws_http_proxy_strategy_factory *factory = NULL; - if (aws_array_list_get_at(&chain_factory->strategy_factories, &factory, i)) { + struct aws_http_proxy_strategy *strategy = NULL; + if (aws_array_list_get_at(&chain_strategy->strategies, &strategy, i)) { goto on_error; } - struct aws_http_proxy_strategy *strategy = aws_http_proxy_strategy_factory_create_strategy(factory, allocator); - if (strategy == NULL) { + struct aws_http_proxy_negotiator *negotiator = aws_http_proxy_strategy_create_negotiator(strategy, allocator); + if (negotiator == NULL) { goto on_error; } - if (aws_array_list_push_back(&chain_strategy->strategies, &strategy)) { - aws_http_proxy_strategy_release(strategy); + if (aws_array_list_push_back(&chain_negotiator->negotiators, &negotiator)) { + aws_http_proxy_negotiator_release(negotiator); goto on_error; } } - return &chain_strategy->strategy_base; + return &chain_negotiator->negotiator_base; on_error: - aws_http_proxy_strategy_release(&chain_strategy->strategy_base); + aws_http_proxy_negotiator_release(&chain_negotiator->negotiator_base); return NULL; } -static struct aws_http_proxy_strategy_factory_vtable s_tunneling_chain_factory_vtable = { - .create_strategy = s_create_tunneling_chain_strategy, +static struct aws_http_proxy_strategy_vtable s_tunneling_chain_strategy_vtable = { + .create_negotiator = s_create_tunneling_chain_negotiator, }; -static void s_destroy_tunneling_chain_factory(struct aws_http_proxy_strategy_factory *proxy_strategy_factory) { - struct aws_http_proxy_strategy_factory_tunneling_chain *chain_factory = proxy_strategy_factory->impl; +static void s_destroy_tunneling_chain_strategy(struct aws_http_proxy_strategy *proxy_strategy) { + struct aws_http_proxy_strategy_tunneling_chain *chain_strategy = proxy_strategy->impl; - size_t factory_count = aws_array_list_length(&chain_factory->strategy_factories); - for (size_t i = 0; i < factory_count; ++i) { - struct aws_http_proxy_strategy_factory *factory = NULL; - if (aws_array_list_get_at(&chain_factory->strategy_factories, &factory, i)) { + size_t strategy_count = aws_array_list_length(&chain_strategy->strategies); + for (size_t i = 0; i < strategy_count; ++i) { + struct aws_http_proxy_strategy *strategy = NULL; + if (aws_array_list_get_at(&chain_strategy->strategies, &strategy, i)) { continue; } - aws_http_proxy_strategy_factory_release(factory); + aws_http_proxy_strategy_release(strategy); } - aws_array_list_clean_up(&chain_factory->strategy_factories); + aws_array_list_clean_up(&chain_strategy->strategies); - aws_mem_release(chain_factory->allocator, chain_factory); + aws_mem_release(chain_strategy->allocator, chain_strategy); } -struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunneling_chain( +struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_tunneling_chain( struct aws_allocator *allocator, - struct aws_http_proxy_strategy_factory_tunneling_chain_options *config) { + struct aws_http_proxy_strategy_tunneling_chain_options *config) { if (allocator == NULL || config == NULL) { aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); return NULL; } - struct aws_http_proxy_strategy_factory_tunneling_chain *chain_factory = - aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_strategy_factory_tunneling_chain)); - if (chain_factory == NULL) { + struct aws_http_proxy_strategy_tunneling_chain *chain_strategy = + aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_strategy_tunneling_chain)); + if (chain_strategy == NULL) { return NULL; } - chain_factory->factory_base.impl = chain_factory; - chain_factory->factory_base.vtable = &s_tunneling_chain_factory_vtable; - chain_factory->factory_base.proxy_connection_type = AWS_HPCT_HTTP_TUNNEL; - chain_factory->allocator = allocator; + chain_strategy->strategy_base.impl = chain_strategy; + chain_strategy->strategy_base.vtable = &s_tunneling_chain_strategy_vtable; + chain_strategy->strategy_base.proxy_connection_type = AWS_HPCT_HTTP_TUNNEL; + chain_strategy->allocator = allocator; aws_ref_count_init( - &chain_factory->factory_base.ref_count, - &chain_factory->factory_base, - (aws_simple_completion_callback *)s_destroy_tunneling_chain_factory); + &chain_strategy->strategy_base.ref_count, + &chain_strategy->strategy_base, + (aws_simple_completion_callback *)s_destroy_tunneling_chain_strategy); if (aws_array_list_init_dynamic( - &chain_factory->strategy_factories, - allocator, - config->factory_count, - sizeof(struct aws_http_proxy_strategy_factory *))) { + &chain_strategy->strategies, allocator, config->strategy_count, sizeof(struct aws_http_proxy_strategy *))) { goto on_error; } - for (size_t i = 0; i < config->factory_count; ++i) { - struct aws_http_proxy_strategy_factory *factory = config->factories[i]; + for (size_t i = 0; i < config->strategy_count; ++i) { + struct aws_http_proxy_strategy *strategy = config->strategies[i]; - if (aws_array_list_push_back(&chain_factory->strategy_factories, &factory)) { + if (aws_array_list_push_back(&chain_strategy->strategies, &strategy)) { goto on_error; } - aws_http_proxy_strategy_factory_acquire(factory); + aws_http_proxy_strategy_acquire(strategy); } - return &chain_factory->factory_base; + return &chain_strategy->strategy_base; on_error: - aws_http_proxy_strategy_factory_release(&chain_factory->factory_base); + aws_http_proxy_strategy_release(&chain_strategy->strategy_base); return NULL; } @@ -868,22 +873,22 @@ struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunn AWS_STATIC_STRING_FROM_LITERAL(s_proxy_authorization_header_kerberos_prefix, "Negotiate "); -struct aws_http_proxy_strategy_factory_tunneling_kerberos { +struct aws_http_proxy_strategy_tunneling_kerberos { struct aws_allocator *allocator; - aws_http_proxy_strategy_get_token_sync_fn *get_token; + aws_http_proxy_negotiation_get_token_sync_fn *get_token; void *get_token_user_data; - struct aws_http_proxy_strategy_factory factory_base; + struct aws_http_proxy_strategy strategy_base; }; -struct aws_http_proxy_strategy_tunneling_kerberos { +struct aws_http_proxy_negotiator_tunneling_kerberos { struct aws_allocator *allocator; - struct aws_http_proxy_strategy_factory *factory; + struct aws_http_proxy_strategy *strategy; - enum proxy_strategy_connect_state connect_state; + enum proxy_negotiator_connect_state connect_state; /* * ToDo: make adaptive and add any state needed here @@ -892,7 +897,7 @@ struct aws_http_proxy_strategy_tunneling_kerberos { * the response */ - struct aws_http_proxy_strategy strategy_base; + struct aws_http_proxy_negotiator negotiator_base; }; /* @@ -944,44 +949,44 @@ static int s_add_kerberos_proxy_usertoken_authentication_header( } static void s_kerberos_tunnel_transform_connect( - struct aws_http_proxy_strategy *proxy_strategy, + struct aws_http_proxy_negotiator *proxy_negotiator, struct aws_http_message *message, - aws_http_proxy_strategy_terminate_fn *strategy_termination_callback, - aws_http_proxy_strategy_http_request_forward_fn *strategy_http_request_forward_callback, + aws_http_proxy_negotiation_terminate_fn *negotiation_termination_callback, + aws_http_proxy_negotiation_http_request_forward_fn *negotiation_http_request_forward_callback, void *internal_proxy_user_data) { - struct aws_http_proxy_strategy_tunneling_kerberos *kerberos_strategy = proxy_strategy->impl; - struct aws_http_proxy_strategy_factory_tunneling_kerberos *kerberos_factory = kerberos_strategy->factory->impl; + struct aws_http_proxy_negotiator_tunneling_kerberos *kerberos_negotiator = proxy_negotiator->impl; + struct aws_http_proxy_strategy_tunneling_kerberos *kerberos_strategy = kerberos_negotiator->strategy->impl; int result = AWS_OP_ERR; int error_code = AWS_ERROR_SUCCESS; struct aws_byte_cursor kerberos_token; AWS_ZERO_STRUCT(kerberos_token); - if (kerberos_strategy->connect_state == AWS_PSCS_FAILURE) { + if (kerberos_negotiator->connect_state == AWS_PNCS_FAILURE) { error_code = AWS_ERROR_HTTP_PROXY_STRATEGY_FAILED_PREVIOUSLY; goto done; } - if (kerberos_strategy->connect_state != AWS_PSCS_READY) { + if (kerberos_negotiator->connect_state != AWS_PNCS_READY) { error_code = AWS_ERROR_INVALID_STATE; goto done; } - kerberos_strategy->connect_state = AWS_PSCS_IN_PROGRESS; + kerberos_negotiator->connect_state = AWS_PNCS_IN_PROGRESS; - if (kerberos_factory->get_token(kerberos_factory->get_token_user_data, &kerberos_token, &error_code) || + if (kerberos_strategy->get_token(kerberos_strategy->get_token_user_data, &kerberos_token, &error_code) || error_code != AWS_ERROR_SUCCESS) { goto done; } /*transform the header with proxy authenticate:Negotiate and kerberos token*/ - if (s_add_kerberos_proxy_usertoken_authentication_header(kerberos_strategy->allocator, message, kerberos_token)) { + if (s_add_kerberos_proxy_usertoken_authentication_header(kerberos_negotiator->allocator, message, kerberos_token)) { error_code = aws_last_error(); goto done; } - kerberos_strategy->connect_state = AWS_PSCS_IN_PROGRESS; + kerberos_negotiator->connect_state = AWS_PNCS_IN_PROGRESS; result = AWS_OP_SUCCESS; done: @@ -990,20 +995,20 @@ static void s_kerberos_tunnel_transform_connect( if (error_code == AWS_ERROR_SUCCESS) { error_code = AWS_ERROR_UNKNOWN; } - strategy_termination_callback(message, error_code, internal_proxy_user_data); + negotiation_termination_callback(message, error_code, internal_proxy_user_data); } else { - strategy_http_request_forward_callback(message, internal_proxy_user_data); + negotiation_http_request_forward_callback(message, internal_proxy_user_data); } } static int s_kerberos_on_incoming_header_adaptive( - struct aws_http_proxy_strategy *proxy_strategy, + struct aws_http_proxy_negotiator *proxy_negotiator, enum aws_http_header_block header_block, const struct aws_http_header *header_array, size_t num_headers) { - struct aws_http_proxy_strategy_tunneling_kerberos *kerberos_strategy = proxy_strategy->impl; - (void)kerberos_strategy; + struct aws_http_proxy_negotiator_tunneling_kerberos *kerberos_negotiator = proxy_negotiator->impl; + (void)kerberos_negotiator; (void)header_block; (void)header_array; (void)num_headers; @@ -1014,18 +1019,18 @@ static int s_kerberos_on_incoming_header_adaptive( } static int s_kerberos_on_connect_status( - struct aws_http_proxy_strategy *proxy_strategy, + struct aws_http_proxy_negotiator *proxy_negotiator, enum aws_http_status_code status_code) { - struct aws_http_proxy_strategy_tunneling_kerberos *kerberos_strategy = proxy_strategy->impl; + struct aws_http_proxy_negotiator_tunneling_kerberos *kerberos_negotiator = proxy_negotiator->impl; /* TODO: process status code of vanilla CONNECT request here to improve usage/application */ - if (kerberos_strategy->connect_state == AWS_PSCS_IN_PROGRESS) { + if (kerberos_negotiator->connect_state == AWS_PNCS_IN_PROGRESS) { if (AWS_HTTP_STATUS_CODE_200_OK != status_code) { - kerberos_strategy->connect_state = AWS_PSCS_FAILURE; + kerberos_negotiator->connect_state = AWS_PNCS_FAILURE; } else { - kerberos_strategy->connect_state = AWS_PSCS_SUCCESS; + kerberos_negotiator->connect_state = AWS_PNCS_SUCCESS; } } @@ -1033,123 +1038,123 @@ static int s_kerberos_on_connect_status( } static int s_kerberos_on_incoming_body( - struct aws_http_proxy_strategy *proxy_strategy, + struct aws_http_proxy_negotiator *proxy_negotiator, const struct aws_byte_cursor *data) { - struct aws_http_proxy_strategy_tunneling_kerberos *kerberos_strategy = proxy_strategy->impl; - (void)kerberos_strategy; + struct aws_http_proxy_negotiator_tunneling_kerberos *kerberos_negotiator = proxy_negotiator->impl; + (void)kerberos_negotiator; (void)data; return AWS_OP_SUCCESS; } -static struct aws_http_proxy_strategy_tunnelling_vtable s_tunneling_kerberos_proxy_tunneling_vtable = { +static struct aws_http_proxy_negotiator_tunnelling_vtable s_tunneling_kerberos_proxy_negotiator_tunneling_vtable = { .on_incoming_body_callback = s_kerberos_on_incoming_body, .on_incoming_headers_callback = s_kerberos_on_incoming_header_adaptive, .on_status_callback = s_kerberos_on_connect_status, .connect_request_transform = s_kerberos_tunnel_transform_connect, }; -static void s_destroy_tunneling_kerberos_strategy(struct aws_http_proxy_strategy *proxy_strategy) { - struct aws_http_proxy_strategy_tunneling_kerberos *kerberos_strategy = proxy_strategy->impl; +static void s_destroy_tunneling_kerberos_negotiator(struct aws_http_proxy_negotiator *proxy_negotiator) { + struct aws_http_proxy_negotiator_tunneling_kerberos *kerberos_negotiator = proxy_negotiator->impl; - aws_http_proxy_strategy_factory_release(kerberos_strategy->factory); + aws_http_proxy_strategy_release(kerberos_negotiator->strategy); - aws_mem_release(kerberos_strategy->allocator, kerberos_strategy); + aws_mem_release(kerberos_negotiator->allocator, kerberos_negotiator); } -static struct aws_http_proxy_strategy *s_create_tunneling_kerberos_strategy( - struct aws_http_proxy_strategy_factory *proxy_strategy_factory, +static struct aws_http_proxy_negotiator *s_create_tunneling_kerberos_negotiator( + struct aws_http_proxy_strategy *proxy_strategy, struct aws_allocator *allocator) { - if (proxy_strategy_factory == NULL || allocator == NULL) { + if (proxy_strategy == NULL || allocator == NULL) { aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); return NULL; } - struct aws_http_proxy_strategy_tunneling_kerberos *kerberos_strategy = - aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_strategy_tunneling_kerberos)); - if (kerberos_strategy == NULL) { + struct aws_http_proxy_negotiator_tunneling_kerberos *kerberos_negotiator = + aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_negotiator_tunneling_kerberos)); + if (kerberos_negotiator == NULL) { return NULL; } - kerberos_strategy->allocator = allocator; - kerberos_strategy->strategy_base.impl = kerberos_strategy; + kerberos_negotiator->allocator = allocator; + kerberos_negotiator->negotiator_base.impl = kerberos_negotiator; aws_ref_count_init( - &kerberos_strategy->strategy_base.ref_count, - &kerberos_strategy->strategy_base, - (aws_simple_completion_callback *)s_destroy_tunneling_kerberos_strategy); + &kerberos_negotiator->negotiator_base.ref_count, + &kerberos_negotiator->negotiator_base, + (aws_simple_completion_callback *)s_destroy_tunneling_kerberos_negotiator); - kerberos_strategy->strategy_base.strategy_vtable.tunnelling_vtable = &s_tunneling_kerberos_proxy_tunneling_vtable; + kerberos_negotiator->negotiator_base.strategy_vtable.tunnelling_vtable = + &s_tunneling_kerberos_proxy_negotiator_tunneling_vtable; - kerberos_strategy->factory = aws_ref_count_acquire(&proxy_strategy_factory->ref_count); + kerberos_negotiator->strategy = aws_http_proxy_strategy_acquire(proxy_strategy); - return &kerberos_strategy->strategy_base; + return &kerberos_negotiator->negotiator_base; } -static struct aws_http_proxy_strategy_factory_vtable s_tunneling_kerberos_factory_vtable = { - .create_strategy = s_create_tunneling_kerberos_strategy, +static struct aws_http_proxy_strategy_vtable s_tunneling_kerberos_strategy_vtable = { + .create_negotiator = s_create_tunneling_kerberos_negotiator, }; -static void s_destroy_tunneling_kerberos_factory(struct aws_http_proxy_strategy_factory *proxy_strategy_factory) { - struct aws_http_proxy_strategy_factory_tunneling_kerberos *kerberos_factory = proxy_strategy_factory->impl; +static void s_destroy_tunneling_kerberos_strategy(struct aws_http_proxy_strategy *proxy_strategy) { + struct aws_http_proxy_strategy_tunneling_kerberos *kerberos_strategy = proxy_strategy->impl; - aws_mem_release(kerberos_factory->allocator, kerberos_factory); + aws_mem_release(kerberos_strategy->allocator, kerberos_strategy); } -struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunneling_kerberos( +struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_tunneling_kerberos( struct aws_allocator *allocator, - struct aws_http_proxy_strategy_factory_tunneling_kerberos_options *config) { + struct aws_http_proxy_strategy_tunneling_kerberos_options *config) { if (allocator == NULL || config == NULL || config->get_token == NULL) { aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); return NULL; } - struct aws_http_proxy_strategy_factory_tunneling_kerberos *kerberos_factory = - aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_strategy_factory_tunneling_kerberos)); - if (kerberos_factory == NULL) { + struct aws_http_proxy_strategy_tunneling_kerberos *kerberos_strategy = + aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_strategy_tunneling_kerberos)); + if (kerberos_strategy == NULL) { return NULL; } - kerberos_factory->factory_base.impl = kerberos_factory; - kerberos_factory->factory_base.vtable = &s_tunneling_kerberos_factory_vtable; - kerberos_factory->factory_base.proxy_connection_type = AWS_HPCT_HTTP_TUNNEL; - kerberos_factory->allocator = allocator; + kerberos_strategy->strategy_base.impl = kerberos_strategy; + kerberos_strategy->strategy_base.vtable = &s_tunneling_kerberos_strategy_vtable; + kerberos_strategy->strategy_base.proxy_connection_type = AWS_HPCT_HTTP_TUNNEL; + kerberos_strategy->allocator = allocator; aws_ref_count_init( - &kerberos_factory->factory_base.ref_count, - &kerberos_factory->factory_base, - (aws_simple_completion_callback *)s_destroy_tunneling_kerberos_factory); + &kerberos_strategy->strategy_base.ref_count, + &kerberos_strategy->strategy_base, + (aws_simple_completion_callback *)s_destroy_tunneling_kerberos_strategy); - kerberos_factory->get_token = config->get_token; - kerberos_factory->get_token_user_data = config->get_token_user_data; + kerberos_strategy->get_token = config->get_token; + kerberos_strategy->get_token_user_data = config->get_token_user_data; - return &kerberos_factory->factory_base; + return &kerberos_strategy->strategy_base; } /******************************************************************************************************************/ -/*adaptive ntlm*/ -struct aws_http_proxy_strategy_factory_tunneling_ntlm { +struct aws_http_proxy_strategy_tunneling_ntlm { struct aws_allocator *allocator; - aws_http_proxy_strategy_get_challenge_token_sync_fn *get_challenge_token; + aws_http_proxy_negotiation_get_challenge_token_sync_fn *get_challenge_token; void *get_challenge_token_user_data; - struct aws_http_proxy_strategy_factory factory_base; + struct aws_http_proxy_strategy strategy_base; }; -struct aws_http_proxy_strategy_tunneling_ntlm { +struct aws_http_proxy_negotiator_tunneling_ntlm { struct aws_allocator *allocator; - struct aws_http_proxy_strategy_factory *factory; + struct aws_http_proxy_strategy *strategy; - enum proxy_strategy_connect_state connect_state; + enum proxy_negotiator_connect_state connect_state; struct aws_string *challenge_token; - struct aws_http_proxy_strategy strategy_base; + struct aws_http_proxy_negotiator negotiator_base; }; AWS_STATIC_STRING_FROM_LITERAL(s_proxy_authorization_header_ntlm_prefix, "NTLM "); @@ -1201,14 +1206,14 @@ static int s_add_ntlm_proxy_usertoken_authentication_header( } static void s_ntlm_tunnel_transform_connect( - struct aws_http_proxy_strategy *proxy_strategy, + struct aws_http_proxy_negotiator *proxy_negotiator, struct aws_http_message *message, - aws_http_proxy_strategy_terminate_fn *strategy_termination_callback, - aws_http_proxy_strategy_http_request_forward_fn *strategy_http_request_forward_callback, + aws_http_proxy_negotiation_terminate_fn *negotiation_termination_callback, + aws_http_proxy_negotiation_http_request_forward_fn *negotiation_http_request_forward_callback, void *internal_proxy_user_data) { - struct aws_http_proxy_strategy_tunneling_ntlm *ntlm_strategy = proxy_strategy->impl; - struct aws_http_proxy_strategy_factory_tunneling_ntlm *ntlm_factory = ntlm_strategy->factory->impl; + struct aws_http_proxy_negotiator_tunneling_ntlm *ntlm_negotiator = proxy_negotiator->impl; + struct aws_http_proxy_strategy_tunneling_ntlm *ntlm_strategy = ntlm_negotiator->strategy->impl; int result = AWS_OP_ERR; int error_code = AWS_ERROR_SUCCESS; @@ -1217,36 +1222,36 @@ static void s_ntlm_tunnel_transform_connect( struct aws_byte_cursor challenge_token; AWS_ZERO_STRUCT(challenge_token); - if (ntlm_strategy->connect_state == AWS_PSCS_FAILURE) { + if (ntlm_negotiator->connect_state == AWS_PNCS_FAILURE) { error_code = AWS_ERROR_HTTP_PROXY_STRATEGY_FAILED_PREVIOUSLY; goto done; } - if (ntlm_strategy->connect_state != AWS_PSCS_READY) { + if (ntlm_negotiator->connect_state != AWS_PNCS_READY) { error_code = AWS_ERROR_INVALID_STATE; goto done; } - if (ntlm_strategy->challenge_token == NULL) { + if (ntlm_negotiator->challenge_token == NULL) { error_code = AWS_ERROR_HTTP_PROXY_STRATEGY_NTLM_CHALLENGE_TOKEN_MISSING; goto done; } - ntlm_strategy->connect_state = AWS_PSCS_IN_PROGRESS; - challenge_token = aws_byte_cursor_from_string(ntlm_strategy->challenge_token); - if (ntlm_factory->get_challenge_token( - ntlm_factory->get_challenge_token_user_data, &challenge_token, &challenge_answer_token, &error_code) || + ntlm_negotiator->connect_state = AWS_PNCS_IN_PROGRESS; + challenge_token = aws_byte_cursor_from_string(ntlm_negotiator->challenge_token); + if (ntlm_strategy->get_challenge_token( + ntlm_strategy->get_challenge_token_user_data, &challenge_token, &challenge_answer_token, &error_code) || error_code != AWS_ERROR_SUCCESS) { goto done; } /*transform the header with proxy authenticate:Negotiate and kerberos token*/ - if (s_add_ntlm_proxy_usertoken_authentication_header(ntlm_strategy->allocator, message, challenge_answer_token)) { + if (s_add_ntlm_proxy_usertoken_authentication_header(ntlm_negotiator->allocator, message, challenge_answer_token)) { error_code = aws_last_error(); goto done; } - ntlm_strategy->connect_state = AWS_PSCS_IN_PROGRESS; + ntlm_negotiator->connect_state = AWS_PNCS_IN_PROGRESS; result = AWS_OP_SUCCESS; done: @@ -1255,21 +1260,21 @@ static void s_ntlm_tunnel_transform_connect( if (error_code == AWS_ERROR_SUCCESS) { error_code = AWS_ERROR_UNKNOWN; } - strategy_termination_callback(message, error_code, internal_proxy_user_data); + negotiation_termination_callback(message, error_code, internal_proxy_user_data); } else { - strategy_http_request_forward_callback(message, internal_proxy_user_data); + negotiation_http_request_forward_callback(message, internal_proxy_user_data); } } AWS_STATIC_STRING_FROM_LITERAL(s_ntlm_challenge_token_header, "Proxy-Authenticate"); static int s_ntlm_on_incoming_header_adaptive( - struct aws_http_proxy_strategy *proxy_strategy, + struct aws_http_proxy_negotiator *proxy_negotiator, enum aws_http_header_block header_block, const struct aws_http_header *header_array, size_t num_headers) { - struct aws_http_proxy_strategy_tunneling_ntlm *ntlm_strategy = proxy_strategy->impl; + struct aws_http_proxy_negotiator_tunneling_ntlm *ntlm_negotiator = proxy_negotiator->impl; /* * only extract the challenge before we've started our own CONNECT attempt @@ -1277,18 +1282,18 @@ static int s_ntlm_on_incoming_header_adaptive( * ToDo: we currently overwrite previous challenge tokens since it is unknown if multiple CONNECT requests * cause new challenges to be issued such that old challenges become invalid even if successfully computed */ - if (ntlm_strategy->connect_state == AWS_PSCS_READY) { + if (ntlm_negotiator->connect_state == AWS_PNCS_READY) { if (header_block == AWS_HTTP_HEADER_BLOCK_MAIN) { struct aws_byte_cursor proxy_authenticate_header_name = aws_byte_cursor_from_string(s_ntlm_challenge_token_header); for (size_t i = 0; i < num_headers; ++i) { struct aws_byte_cursor header_name_cursor = header_array[i].name; if (aws_byte_cursor_eq_ignore_case(&proxy_authenticate_header_name, &header_name_cursor)) { - aws_string_destroy(ntlm_strategy->challenge_token); + aws_string_destroy(ntlm_negotiator->challenge_token); struct aws_byte_cursor challenge_value_cursor = header_array[i].value; - ntlm_strategy->challenge_token = - aws_string_new_from_cursor(ntlm_strategy->allocator, &challenge_value_cursor); + ntlm_negotiator->challenge_token = + aws_string_new_from_cursor(ntlm_negotiator->allocator, &challenge_value_cursor); break; } } @@ -1299,180 +1304,183 @@ static int s_ntlm_on_incoming_header_adaptive( } static int s_ntlm_on_connect_status( - struct aws_http_proxy_strategy *proxy_strategy, + struct aws_http_proxy_negotiator *proxy_negotiator, enum aws_http_status_code status_code) { - struct aws_http_proxy_strategy_tunneling_ntlm *ntlm_strategy = proxy_strategy->impl; + struct aws_http_proxy_negotiator_tunneling_ntlm *ntlm_negotiator = proxy_negotiator->impl; - if (ntlm_strategy->connect_state == AWS_PSCS_IN_PROGRESS) { + if (ntlm_negotiator->connect_state == AWS_PNCS_IN_PROGRESS) { if (AWS_HTTP_STATUS_CODE_200_OK != status_code) { - ntlm_strategy->connect_state = AWS_PSCS_FAILURE; + ntlm_negotiator->connect_state = AWS_PNCS_FAILURE; } else { - ntlm_strategy->connect_state = AWS_PSCS_SUCCESS; + ntlm_negotiator->connect_state = AWS_PNCS_SUCCESS; } } return AWS_OP_SUCCESS; } -static int s_ntlm_on_incoming_body(struct aws_http_proxy_strategy *proxy_strategy, const struct aws_byte_cursor *data) { +static int s_ntlm_on_incoming_body( + struct aws_http_proxy_negotiator *proxy_negotiator, + const struct aws_byte_cursor *data) { - struct aws_http_proxy_strategy_tunneling_ntlm *ntlm_strategy = proxy_strategy->impl; - (void)ntlm_strategy; + struct aws_http_proxy_negotiator_tunneling_ntlm *ntlm_negotiator = proxy_negotiator->impl; + (void)ntlm_negotiator; (void)data; return AWS_OP_SUCCESS; } -static struct aws_http_proxy_strategy_tunnelling_vtable s_tunneling_ntlm_proxy_tunneling_vtable = { +static struct aws_http_proxy_negotiator_tunnelling_vtable s_tunneling_ntlm_proxy_negotiator_tunneling_vtable = { .on_incoming_body_callback = s_ntlm_on_incoming_body, .on_incoming_headers_callback = s_ntlm_on_incoming_header_adaptive, .on_status_callback = s_ntlm_on_connect_status, .connect_request_transform = s_ntlm_tunnel_transform_connect, }; -static void s_destroy_tunneling_ntlm_strategy(struct aws_http_proxy_strategy *proxy_strategy) { - struct aws_http_proxy_strategy_tunneling_ntlm *ntlm_strategy = proxy_strategy->impl; +static void s_destroy_tunneling_ntlm_negotiator(struct aws_http_proxy_negotiator *proxy_negotiator) { + struct aws_http_proxy_negotiator_tunneling_ntlm *ntlm_negotiator = proxy_negotiator->impl; - aws_string_destroy(ntlm_strategy->challenge_token); - aws_http_proxy_strategy_factory_release(ntlm_strategy->factory); + aws_string_destroy(ntlm_negotiator->challenge_token); + aws_http_proxy_strategy_release(ntlm_negotiator->strategy); - aws_mem_release(ntlm_strategy->allocator, ntlm_strategy); + aws_mem_release(ntlm_negotiator->allocator, ntlm_negotiator); } -static struct aws_http_proxy_strategy *s_create_tunneling_ntlm_strategy( - struct aws_http_proxy_strategy_factory *proxy_strategy_factory, +static struct aws_http_proxy_negotiator *s_create_tunneling_ntlm_negotiator( + struct aws_http_proxy_strategy *proxy_strategy, struct aws_allocator *allocator) { - if (proxy_strategy_factory == NULL || allocator == NULL) { + if (proxy_strategy == NULL || allocator == NULL) { aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); return NULL; } - struct aws_http_proxy_strategy_tunneling_ntlm *ntlm_strategy = - aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_strategy_tunneling_ntlm)); - if (ntlm_strategy == NULL) { + struct aws_http_proxy_negotiator_tunneling_ntlm *ntlm_negotiator = + aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_negotiator_tunneling_ntlm)); + if (ntlm_negotiator == NULL) { return NULL; } - ntlm_strategy->allocator = allocator; - ntlm_strategy->strategy_base.impl = ntlm_strategy; + ntlm_negotiator->allocator = allocator; + ntlm_negotiator->negotiator_base.impl = ntlm_negotiator; aws_ref_count_init( - &ntlm_strategy->strategy_base.ref_count, - &ntlm_strategy->strategy_base, - (aws_simple_completion_callback *)s_destroy_tunneling_ntlm_strategy); + &ntlm_negotiator->negotiator_base.ref_count, + &ntlm_negotiator->negotiator_base, + (aws_simple_completion_callback *)s_destroy_tunneling_ntlm_negotiator); - ntlm_strategy->strategy_base.strategy_vtable.tunnelling_vtable = &s_tunneling_ntlm_proxy_tunneling_vtable; + ntlm_negotiator->negotiator_base.strategy_vtable.tunnelling_vtable = + &s_tunneling_ntlm_proxy_negotiator_tunneling_vtable; - ntlm_strategy->factory = aws_ref_count_acquire(&proxy_strategy_factory->ref_count); + ntlm_negotiator->strategy = aws_http_proxy_strategy_acquire(proxy_strategy); - return &ntlm_strategy->strategy_base; + return &ntlm_negotiator->negotiator_base; } -static struct aws_http_proxy_strategy_factory_vtable s_tunneling_ntlm_factory_vtable = { - .create_strategy = s_create_tunneling_ntlm_strategy, +static struct aws_http_proxy_strategy_vtable s_tunneling_ntlm_strategy_vtable = { + .create_negotiator = s_create_tunneling_ntlm_negotiator, }; -static void s_destroy_tunneling_ntlm_factory(struct aws_http_proxy_strategy_factory *proxy_strategy_factory) { - struct aws_http_proxy_strategy_factory_tunneling_ntlm *ntlm_factory = proxy_strategy_factory->impl; +static void s_destroy_tunneling_ntlm_strategy(struct aws_http_proxy_strategy *proxy_strategy) { + struct aws_http_proxy_strategy_tunneling_ntlm *ntlm_strategy = proxy_strategy->impl; - aws_mem_release(ntlm_factory->allocator, ntlm_factory); + aws_mem_release(ntlm_strategy->allocator, ntlm_strategy); } -struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunneling_ntlm( +struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_tunneling_ntlm( struct aws_allocator *allocator, - struct aws_http_proxy_strategy_factory_tunneling_ntlm_options *config) { + struct aws_http_proxy_strategy_tunneling_ntlm_options *config) { if (allocator == NULL || config == NULL || config->get_challenge_token == NULL) { aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); return NULL; } - struct aws_http_proxy_strategy_factory_tunneling_ntlm *ntlm_factory = - aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_strategy_factory_tunneling_ntlm)); - if (ntlm_factory == NULL) { + struct aws_http_proxy_strategy_tunneling_ntlm *ntlm_strategy = + aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_strategy_tunneling_ntlm)); + if (ntlm_strategy == NULL) { return NULL; } - ntlm_factory->factory_base.impl = ntlm_factory; - ntlm_factory->factory_base.vtable = &s_tunneling_ntlm_factory_vtable; - ntlm_factory->factory_base.proxy_connection_type = AWS_HPCT_HTTP_TUNNEL; + ntlm_strategy->strategy_base.impl = ntlm_strategy; + ntlm_strategy->strategy_base.vtable = &s_tunneling_ntlm_strategy_vtable; + ntlm_strategy->strategy_base.proxy_connection_type = AWS_HPCT_HTTP_TUNNEL; - ntlm_factory->allocator = allocator; + ntlm_strategy->allocator = allocator; aws_ref_count_init( - &ntlm_factory->factory_base.ref_count, - &ntlm_factory->factory_base, - (aws_simple_completion_callback *)s_destroy_tunneling_ntlm_factory); + &ntlm_strategy->strategy_base.ref_count, + &ntlm_strategy->strategy_base, + (aws_simple_completion_callback *)s_destroy_tunneling_ntlm_strategy); - ntlm_factory->get_challenge_token = config->get_challenge_token; - ntlm_factory->get_challenge_token_user_data = config->get_challenge_token_user_data; + ntlm_strategy->get_challenge_token = config->get_challenge_token; + ntlm_strategy->get_challenge_token_user_data = config->get_challenge_token_user_data; - return &ntlm_factory->factory_base; + return &ntlm_strategy->strategy_base; } /******************************************************************************************************************/ -#define PROXY_STRATEGY_MAX_ADAPTIVE_FACTORIES 3 +#define PROXY_STRATEGY_MAX_ADAPTIVE_STRATEGIES 3 -struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunneling_adaptive( +struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_tunneling_adaptive( struct aws_allocator *allocator, - struct aws_http_proxy_strategy_factory_tunneling_adaptive_options *config) { + struct aws_http_proxy_strategy_tunneling_adaptive_options *config) { if (allocator == NULL || config == NULL) { aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); return NULL; } - struct aws_http_proxy_strategy_factory *factories[PROXY_STRATEGY_MAX_ADAPTIVE_FACTORIES]; + struct aws_http_proxy_strategy *strategies[PROXY_STRATEGY_MAX_ADAPTIVE_STRATEGIES]; - uint32_t factory_count = 0; - struct aws_http_proxy_strategy_factory *identity_factory = NULL; - struct aws_http_proxy_strategy_factory *kerberos_factory = NULL; - struct aws_http_proxy_strategy_factory *ntlm_factory = NULL; - struct aws_http_proxy_strategy_factory *adaptive_chain_factory = NULL; + uint32_t strategy_count = 0; + struct aws_http_proxy_strategy *identity_strategy = NULL; + struct aws_http_proxy_strategy *kerberos_strategy = NULL; + struct aws_http_proxy_strategy *ntlm_strategy = NULL; + struct aws_http_proxy_strategy *adaptive_chain_strategy = NULL; - identity_factory = aws_http_proxy_strategy_factory_new_tunneling_one_time_identity(allocator); - if (identity_factory == NULL) { + identity_strategy = aws_http_proxy_strategy_new_tunneling_one_time_identity(allocator); + if (identity_strategy == NULL) { goto on_error; } - factories[factory_count++] = identity_factory; + strategies[strategy_count++] = identity_strategy; if (config->kerberos_options != NULL) { - kerberos_factory = aws_http_proxy_strategy_factory_new_tunneling_kerberos(allocator, config->kerberos_options); - if (kerberos_factory == NULL) { + kerberos_strategy = aws_http_proxy_strategy_new_tunneling_kerberos(allocator, config->kerberos_options); + if (kerberos_strategy == NULL) { goto on_error; } - factories[factory_count++] = kerberos_factory; + strategies[strategy_count++] = kerberos_strategy; } if (config->ntlm_options != NULL) { - ntlm_factory = aws_http_proxy_strategy_factory_new_tunneling_ntlm(allocator, config->ntlm_options); - if (ntlm_factory == NULL) { + ntlm_strategy = aws_http_proxy_strategy_new_tunneling_ntlm(allocator, config->ntlm_options); + if (ntlm_strategy == NULL) { goto on_error; } - factories[factory_count++] = ntlm_factory; + strategies[strategy_count++] = ntlm_strategy; } - struct aws_http_proxy_strategy_factory_tunneling_chain_options chain_config = { - .factories = factories, - .factory_count = factory_count, + struct aws_http_proxy_strategy_tunneling_chain_options chain_config = { + .strategies = strategies, + .strategy_count = strategy_count, }; - adaptive_chain_factory = aws_http_proxy_strategy_factory_new_tunneling_chain(allocator, &chain_config); - if (adaptive_chain_factory == NULL) { + adaptive_chain_strategy = aws_http_proxy_strategy_new_tunneling_chain(allocator, &chain_config); + if (adaptive_chain_strategy == NULL) { goto on_error; } - return adaptive_chain_factory; + return adaptive_chain_strategy; on_error: - aws_http_proxy_strategy_factory_release(identity_factory); - aws_http_proxy_strategy_factory_release(kerberos_factory); - aws_http_proxy_strategy_factory_release(ntlm_factory); - aws_http_proxy_strategy_factory_release(adaptive_chain_factory); + aws_http_proxy_strategy_release(identity_strategy); + aws_http_proxy_strategy_release(kerberos_strategy); + aws_http_proxy_strategy_release(ntlm_strategy); + aws_http_proxy_strategy_release(adaptive_chain_strategy); return NULL; } diff --git a/tests/proxy_test_helper.c b/tests/proxy_test_helper.c index ffc8149a3..6ffe33b40 100644 --- a/tests/proxy_test_helper.c +++ b/tests/proxy_test_helper.c @@ -199,7 +199,7 @@ int proxy_tester_clean_up(struct proxy_tester *tester) { aws_tls_ctx_options_clean_up(&tester->tls_ctx_options); } - aws_http_proxy_strategy_factory_release(tester->proxy_options.proxy_strategy_factory); + aws_http_proxy_strategy_release(tester->proxy_options.proxy_strategy); aws_http_library_clean_up(); diff --git a/tests/test_proxy.c b/tests/test_proxy.c index 64f446eab..f6e904e2e 100644 --- a/tests/test_proxy.c +++ b/tests/test_proxy.c @@ -186,13 +186,13 @@ static int s_setup_proxy_test( }; if (use_basic_auth) { - struct aws_http_proxy_strategy_factory_basic_auth_config config = { + struct aws_http_proxy_strategy_basic_auth_options config = { .proxy_connection_type = AWS_HPCT_HTTP_FORWARD, .user_name = aws_byte_cursor_from_string(s_mock_request_username), .password = aws_byte_cursor_from_string(s_mock_request_password), }; - proxy_options.proxy_strategy_factory = aws_http_proxy_strategy_factory_new_basic_auth(allocator, &config); + proxy_options.proxy_strategy = aws_http_proxy_strategy_new_basic_auth(allocator, &config); } struct proxy_tester_options options = { From 7208ae90752f5fd61b21fccbbe7c1d17f9cf6e9b Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Thu, 21 Jan 2021 11:43:44 -0800 Subject: [PATCH 27/54] Proxy request flow merge (#302) * support for synchronous kerberos and ntlm auth strategies Co-authored-by: Bret Ambrose Co-authored-by: Abhishek Jain <61713632+ajainaus@users.noreply.github.com> --- include/aws/http/connection.h | 4 +- include/aws/http/http.h | 2 + include/aws/http/private/proxy_impl.h | 4 +- include/aws/http/proxy_strategy.h | 271 ++--- source/http.c | 6 + source/proxy_connection.c | 55 +- source/proxy_strategy.c | 1306 ++++++++++++++++--------- tests/proxy_test_helper.c | 2 +- tests/test_proxy.c | 4 +- 9 files changed, 1011 insertions(+), 643 deletions(-) diff --git a/include/aws/http/connection.h b/include/aws/http/connection.h index d2f73f6e5..2bfacaa24 100644 --- a/include/aws/http/connection.h +++ b/include/aws/http/connection.h @@ -152,7 +152,7 @@ enum aws_http_proxy_connection_type { AWS_HPCT_SOCKS5, }; -struct aws_http_proxy_strategy_factory; +struct aws_http_proxy_strategy; /** * Options for http proxy server usage @@ -190,7 +190,7 @@ struct aws_http_proxy_options { * For forwarding proxies it allows custom request transformations. * Other proxy connection types TBD. */ - struct aws_http_proxy_strategy_factory *proxy_strategy_factory; + struct aws_http_proxy_strategy *proxy_strategy; }; /** diff --git a/include/aws/http/http.h b/include/aws/http/http.h index 9a8ce432e..fc7b9dd55 100644 --- a/include/aws/http/http.h +++ b/include/aws/http/http.h @@ -47,6 +47,8 @@ enum aws_http_errors { AWS_ERROR_HTTP_STREAM_NOT_ACTIVATED, AWS_ERROR_HTTP_STREAM_HAS_COMPLETED, AWS_ERROR_HTTP_PROXY_STRATEGY_TRANSFORM_FAILED, + AWS_ERROR_HTTP_PROXY_STRATEGY_FAILED_PREVIOUSLY, + AWS_ERROR_HTTP_PROXY_STRATEGY_NTLM_CHALLENGE_TOKEN_MISSING, AWS_ERROR_HTTP_END_RANGE = AWS_ERROR_ENUM_END_RANGE(AWS_C_HTTP_PACKAGE_ID) }; diff --git a/include/aws/http/private/proxy_impl.h b/include/aws/http/private/proxy_impl.h index 9edcbb53c..395b3e47c 100644 --- a/include/aws/http/private/proxy_impl.h +++ b/include/aws/http/private/proxy_impl.h @@ -46,7 +46,7 @@ struct aws_http_proxy_config { struct aws_tls_connection_options *tls_options; - struct aws_http_proxy_strategy_factory *proxy_strategy_factory; + struct aws_http_proxy_strategy *proxy_strategy; }; /* @@ -75,7 +75,7 @@ struct aws_http_proxy_user_data { struct aws_tls_connection_options *tls_options; struct aws_http_proxy_config *proxy_config; - struct aws_http_proxy_strategy *proxy_strategy; + struct aws_http_proxy_negotiator *proxy_negotiator; }; struct aws_http_proxy_system_vtable { diff --git a/include/aws/http/proxy_strategy.h b/include/aws/http/proxy_strategy.h index fc9f161ce..34a929580 100644 --- a/include/aws/http/proxy_strategy.h +++ b/include/aws/http/proxy_strategy.h @@ -15,23 +15,42 @@ struct aws_http_message; struct aws_http_header; +struct aws_http_proxy_negotiator; struct aws_http_proxy_strategy; -struct aws_http_proxy_strategy_factory; /** - * Proxy strategy logic must call this function to indicate an unsuccessful outcome + * Synchronous (for now) callback function to fetch a token used in modifying CONNECT requests */ -typedef void(aws_http_proxy_strategy_terminate_fn)( +typedef int(aws_http_proxy_negotiation_get_token_sync_fn)( + void *user_data, + struct aws_byte_cursor *out_token_value, + int *out_error_code); + +/** + * Synchronous (for now) callback function to fetch a token used in modifying CONNECT request. Includes a (byte string) + * context intended to be used as part of a challenge-response flow. + */ +typedef int(aws_http_proxy_negotiation_get_challenge_token_sync_fn)( + void *user_data, + const struct aws_byte_cursor *challenge_context, + struct aws_byte_cursor *out_token_value, + int *out_error_code); + +/** + * Proxy negotiation logic must call this function to indicate an unsuccessful outcome + */ +typedef void(aws_http_proxy_negotiation_terminate_fn)( struct aws_http_message *message, int error_code, void *internal_proxy_user_data); /** - * Proxy strategy logic must call this function to forward the potentially-mutated request back to the proxy - * strategy coordination logic. + * Proxy negotiation logic must call this function to forward the potentially-mutated request back to the proxy + * connection logic. */ -typedef void( - aws_http_proxy_strategy_http_request_forward_fn)(struct aws_http_message *message, void *internal_proxy_user_data); +typedef void(aws_http_proxy_negotiation_http_request_forward_fn)( + struct aws_http_message *message, + void *internal_proxy_user_data); /** * User-supplied transform callback which implements the proxy request flow and ultimately, across all execution @@ -45,70 +64,69 @@ typedef void( * * Forwarding proxy connections cannot yet support a truly async request transform without major surgery on http * stream creation, so for now, we split into an async version (for tunneling proxies) and a separate - * synchronous version for forwarding proxies. + * synchronous version for forwarding proxies. Also forwarding proxies are a kind of legacy dead-end in some + * sense. * */ -typedef void(aws_http_proxy_strategy_http_request_transform_async_fn)( - struct aws_http_proxy_strategy *proxy_strategy, +typedef void(aws_http_proxy_negotiation_http_request_transform_async_fn)( + struct aws_http_proxy_negotiator *proxy_negotiator, struct aws_http_message *message, - aws_http_proxy_strategy_terminate_fn *strategy_termination_callback, - aws_http_proxy_strategy_http_request_forward_fn *strategy_http_request_forward_callback, + aws_http_proxy_negotiation_terminate_fn *negotiation_termination_callback, + aws_http_proxy_negotiation_http_request_forward_fn *negotiation_http_request_forward_callback, void *internal_proxy_user_data); -typedef int(aws_http_proxy_strategy_http_request_transform_fn)( - struct aws_http_proxy_strategy *proxy_strategy, +typedef int(aws_http_proxy_negotiation_http_request_transform_fn)( + struct aws_http_proxy_negotiator *proxy_negotiator, struct aws_http_message *message); /** - * Tunneling proxy connections only. A callback that lets the strategy examine the headers in the + * Tunneling proxy connections only. A callback that lets the negotiator examine the headers in the * response to the most recent CONNECT request as they arrive. */ -typedef int(aws_http_proxy_strategy_connect_on_incoming_headers_fn)( - struct aws_http_proxy_strategy *proxy_strategy, +typedef int(aws_http_proxy_negotiation_connect_on_incoming_headers_fn)( + struct aws_http_proxy_negotiator *proxy_negotiator, enum aws_http_header_block header_block, const struct aws_http_header *header_array, size_t num_headers); /** - * Tunneling proxy connections only. A callback that lets the strategy examine the status code of the + * Tunneling proxy connections only. A callback that lets the negotiator examine the status code of the * response to the most recent CONNECT request. */ -typedef int(aws_http_proxy_strategy_connect_status_fn)( - struct aws_http_proxy_strategy *proxy_strategy, +typedef int(aws_http_proxy_negotiator_connect_status_fn)( + struct aws_http_proxy_negotiator *proxy_negotiator, enum aws_http_status_code status_code); /** - * Tunneling proxy connections only. A callback that lets the strategy examine the body of the response + * Tunneling proxy connections only. A callback that lets the negotiator examine the body of the response * to the most recent CONNECT request. */ -typedef int(aws_http_proxy_strategy_connect_on_incoming_body_fn)( - struct aws_http_proxy_strategy *proxy_strategy, +typedef int(aws_http_proxy_negotiator_connect_on_incoming_body_fn)( + struct aws_http_proxy_negotiator *proxy_negotiator, const struct aws_byte_cursor *data); /** - * Destructor for a proxy strategy. A standard pattern is to - * make the user data a structure that also embeds the aws_proxy_strategy as a member, letting the destructor - * clean up everything in a single shot. + * Vtable for forwarding-based proxy negotiators */ -typedef void(aws_http_proxy_strategy_destroy_fn)(struct aws_http_proxy_strategy *proxy_strategy); - -struct aws_http_proxy_strategy_forwarding_vtable { - aws_http_proxy_strategy_http_request_transform_fn *forward_request_transform; +struct aws_http_proxy_negotiator_forwarding_vtable { + aws_http_proxy_negotiation_http_request_transform_fn *forward_request_transform; }; -struct aws_http_proxy_strategy_tunnelling_vtable { - aws_http_proxy_strategy_http_request_transform_async_fn *connect_request_transform; +/** + * Vtable for tunneling-based proxy negotiators + */ +struct aws_http_proxy_negotiator_tunnelling_vtable { + aws_http_proxy_negotiation_http_request_transform_async_fn *connect_request_transform; - aws_http_proxy_strategy_connect_on_incoming_headers_fn *on_incoming_headers_callback; - aws_http_proxy_strategy_connect_status_fn *on_status_callback; - aws_http_proxy_strategy_connect_on_incoming_body_fn *on_incoming_body_callback; + aws_http_proxy_negotiation_connect_on_incoming_headers_fn *on_incoming_headers_callback; + aws_http_proxy_negotiator_connect_status_fn *on_status_callback; + aws_http_proxy_negotiator_connect_on_incoming_body_fn *on_incoming_body_callback; }; /* - * Configuration definition of a proxy stategy. Contains a proxy-type-specific vtable, user_data (usually a - * strategy-specific struct that embeds the aws_proxy_strategy), and a destructor. + * Base definition of a proxy negotiator. * - * A strategy works differently based on what kind of proxy connection is being asked for: + * A negotiator works differently based on what kind of proxy connection is being asked for: * * (1) Tunneling - In a tunneling proxy connection, the connect_request_transform is invoked on every CONNECT request. * The connect_request_transform implementation *MUST*, in turn, eventually call one of the terminate or forward @@ -122,35 +140,35 @@ struct aws_http_proxy_strategy_tunnelling_vtable { * * (3) Socks5 - not yet supported */ -struct aws_http_proxy_strategy { +struct aws_http_proxy_negotiator { struct aws_ref_count ref_count; void *impl; union { - struct aws_http_proxy_strategy_forwarding_vtable *forwarding_vtable; - struct aws_http_proxy_strategy_tunnelling_vtable *tunnelling_vtable; + struct aws_http_proxy_negotiator_forwarding_vtable *forwarding_vtable; + struct aws_http_proxy_negotiator_tunnelling_vtable *tunnelling_vtable; } strategy_vtable; }; /*********************************************************************************************/ -typedef struct aws_http_proxy_strategy *(aws_http_proxy_strategy_factory_create_strategy_fn)( - struct aws_http_proxy_strategy_factory *proxy_strategy_factory, +typedef struct aws_http_proxy_negotiator *(aws_http_proxy_strategy_create_negotiator_fn)( + struct aws_http_proxy_strategy *proxy_strategy, struct aws_allocator *allocator); -struct aws_http_proxy_strategy_factory_vtable { - aws_http_proxy_strategy_factory_create_strategy_fn *create_strategy; +struct aws_http_proxy_strategy_vtable { + aws_http_proxy_strategy_create_negotiator_fn *create_negotiator; }; -struct aws_http_proxy_strategy_factory { +struct aws_http_proxy_strategy { struct aws_ref_count ref_count; - struct aws_http_proxy_strategy_factory_vtable *vtable; + struct aws_http_proxy_strategy_vtable *vtable; void *impl; enum aws_http_proxy_connection_type proxy_connection_type; }; -struct aws_http_proxy_strategy_factory_basic_auth_config { +struct aws_http_proxy_strategy_basic_auth_options { /* type of proxy connection being established, must be forwarding or tunnel */ enum aws_http_proxy_connection_type proxy_connection_type; @@ -162,156 +180,169 @@ struct aws_http_proxy_strategy_factory_basic_auth_config { struct aws_byte_cursor password; }; -struct aws_http_proxy_strategy_factory_tunneling_chain_options { - struct aws_http_proxy_strategy_factory **factories; +struct aws_http_proxy_strategy_tunneling_chain_options { + struct aws_http_proxy_strategy **strategies; - uint32_t factory_count; + uint32_t strategy_count; }; -/* - * The adaptive test strategy attempts a bad basic CONNECT and if that fails it attempts a regular basic auth - * CONNECT. - */ -struct aws_http_proxy_strategy_factory_tunneling_adaptive_test_options { - /* user name to use in basic authentication */ - struct aws_byte_cursor user_name; +struct aws_http_proxy_strategy_tunneling_kerberos_options { - /* password to use in basic authentication */ - struct aws_byte_cursor password; + aws_http_proxy_negotiation_get_token_sync_fn *get_token; + + void *get_token_user_data; }; -/* - * SA-TBI: add any configuration needed for kerberos auth negotiation here - */ -struct aws_http_proxy_strategy_factory_tunneling_kerberos_options { - bool placeholder; +struct aws_http_proxy_strategy_tunneling_ntlm_options { + + aws_http_proxy_negotiation_get_challenge_token_sync_fn *get_challenge_token; + + void *get_challenge_token_user_data; }; -struct aws_http_proxy_strategy_factory_tunneling_adaptive_kerberos_options { - struct aws_http_proxy_strategy_factory_tunneling_kerberos_options kerberos_options; +struct aws_http_proxy_strategy_tunneling_adaptive_options { + /* + * If non-null, will insert a kerberos proxy strategy into the adaptive chain + */ + struct aws_http_proxy_strategy_tunneling_kerberos_options *kerberos_options; + + /* + * If non-null will insert an ntlm proxy strategy into the adaptive chain + */ + struct aws_http_proxy_strategy_tunneling_ntlm_options *ntlm_options; }; AWS_EXTERN_C_BEGIN /** - * Take a reference to an http proxy strategy - * @param proxy_strategy strategy to take a reference to + * Take a reference to an http proxy negotiator + * @param proxy_negotiator negotiator to take a reference to * @return the strategy */ AWS_HTTP_API -struct aws_http_proxy_strategy *aws_http_proxy_strategy_acquire(struct aws_http_proxy_strategy *proxy_strategy); +struct aws_http_proxy_negotiator *aws_http_proxy_negotiator_acquire(struct aws_http_proxy_negotiator *proxy_negotiator); /** - * Release a reference to an http proxy strategy - * @param proxy_strategy strategy to release a reference to + * Release a reference to an http proxy negotiator + * @param proxy_negotiator negotiator to release a reference to */ AWS_HTTP_API -void aws_http_proxy_strategy_release(struct aws_http_proxy_strategy *proxy_strategy); +void aws_http_proxy_negotiator_release(struct aws_http_proxy_negotiator *proxy_negotiator); /** - * Creates a new proxy strategy from the factory according to the factory's configuration + * Creates a new proxy negotiator from a proxy strategy * @param allocator memory allocator to use - * @return a new proxy strategy if successful, otherwise NULL + * @param strategy strategy to creation a new negotiator for + * @return a new proxy negotiator if successful, otherwise NULL */ AWS_HTTP_API -struct aws_http_proxy_strategy *aws_http_proxy_strategy_factory_create_strategy( - struct aws_http_proxy_strategy_factory *factory, +struct aws_http_proxy_negotiator *aws_http_proxy_strategy_create_negotiator( + struct aws_http_proxy_strategy *strategy, struct aws_allocator *allocator); /** - * Take a reference to an http proxy strategy factory - * @param proxy_strategy_factory factory to take a reference to - * @return the factory + * Take a reference to an http proxy strategy + * @param proxy_strategy strategy to take a reference to + * @return the strategy */ AWS_HTTP_API -struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_acquire( - struct aws_http_proxy_strategy_factory *proxy_strategy_factory); +struct aws_http_proxy_strategy *aws_http_proxy_strategy_acquire(struct aws_http_proxy_strategy *proxy_strategy); /** - * Release a reference to an http proxy strategy factory - * @param proxy_strategy_factory factory to release a reference to + * Release a reference to an http proxy strategy + * @param proxy_strategy strategy to release a reference to */ AWS_HTTP_API -void aws_http_proxy_strategy_factory_release(struct aws_http_proxy_strategy_factory *proxy_strategy_factory); +void aws_http_proxy_strategy_release(struct aws_http_proxy_strategy *proxy_strategy); /** - * A constructor for a proxy strategy factory that performs basic authentication by adding the appropriate + * A constructor for a proxy strategy that performs basic authentication by adding the appropriate * header and header value to requests or CONNECT requests. * * @param allocator memory allocator to use * @param config basic authentication configuration info - * @return a new proxy strategy factory if successfully constructed, otherwise NULL + * @return a new proxy strategy if successfully constructed, otherwise NULL */ AWS_HTTP_API -struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_basic_auth( +struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_basic_auth( struct aws_allocator *allocator, - struct aws_http_proxy_strategy_factory_basic_auth_config *config); + struct aws_http_proxy_strategy_basic_auth_options *config); /** - * Factory constructor for a tunnel-only proxy request flow that does nothing. Intended to be the first link in an - * adaptive chain for a tunneling proxy: first try a basic CONNECT, then based on the response, later links are allowed - * to make attempts. + * Constructor for a tunnel-only proxy strategy that applies no changes to outbound CONNECT requests. Intended to be + * the first link in an adaptive chain for a tunneling proxy: first try a basic CONNECT, then based on the response, + * later links are allowed to make attempts. * * @param allocator memory allocator to use - * @return a new proxy strategy factory if successfully constructed, otherwise NULL + * @return a new proxy strategy if successfully constructed, otherwise NULL */ AWS_HTTP_API -struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunneling_one_time_identity( +struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_tunneling_one_time_identity( struct aws_allocator *allocator); /** - * Factory constructor for a tunneling proxy strategy that contains a chain of sub-strategies which are tried + * Constructor for a forwarding-only proxy strategy that does nothing. Exists so that all proxy logic uses a + * strategy. + * + * @param allocator memory allocator to use + * @return a new proxy strategy if successfully constructed, otherwise NULL + */ +AWS_HTTP_API +struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_forwarding_identity(struct aws_allocator *allocator); + +/** + * Constructor for a tunneling proxy strategy that contains a chain of sub-strategies which are tried * sequentially in order. * * @param allocator memory allocator to use - * @param config chain configuration info - * @return a new proxy strategy factory if successfully constructed, otherwise NULL + * @param config chain configuration options + * @return a new proxy strategy if successfully constructed, otherwise NULL */ AWS_HTTP_API -struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunneling_chain( +struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_tunneling_chain( struct aws_allocator *allocator, - struct aws_http_proxy_strategy_factory_tunneling_chain_options *config); + struct aws_http_proxy_strategy_tunneling_chain_options *config); /** - * Factory constructor for a forwarding-only proxy strategy that does nothing. Exists so that all proxy logic uses a - * strategy. + * A constructor for a proxy strategy that performs kerberos authentication by adding the appropriate + * header and header value to CONNECT requests. * * @param allocator memory allocator to use - * @return a new proxy strategy factory if successfully constructed, otherwise NULL + * @param config kerberos authentication configuration info + * @return a new proxy strategy if successfully constructed, otherwise NULL */ AWS_HTTP_API -struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_forwarding_identity( - struct aws_allocator *allocator); +struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_tunneling_kerberos( + struct aws_allocator *allocator, + struct aws_http_proxy_strategy_tunneling_kerberos_options *config); /** - * This is an experimental API. - * - * Constructor for a WIP adaptive tunneling proxy strategy. This strategy attempts a bad basic auth CONNECT and if that - * fails it attempts a configurable basic auth CONNECT. + * Constructor for an NTLM proxy strategy. Because ntlm is a challenge-response authentication protocol, this + * strategy will only succeed in a chain in a non-leading position. The strategy extracts the challenge from the + * proxy's response to a previous CONNECT request in the chain. * * @param allocator memory allocator to use - * @param config configuration options for the strategy factory - * @return a new proxy strategy factory if successfully constructed, otherwise NULL + * @param config configuration options for the strategy + * @return a new proxy strategy if successfully constructed, otherwise NULL */ AWS_HTTP_API -struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunneling_adaptive_test( +struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_tunneling_ntlm( struct aws_allocator *allocator, - struct aws_http_proxy_strategy_factory_tunneling_adaptive_test_options *config); + struct aws_http_proxy_strategy_tunneling_ntlm_options *config); /** - * This is an experimental API. - * - * Constructor for a WIP adaptive tunneling proxy strategy. This strategy attempts a vanilla CONNECT and if that - * fails it attempts a kerberos-oriented CONNECT (if applicable). + * Constructor for an adaptive tunneling proxy strategy. This strategy attempts a vanilla CONNECT and if that + * fails it may make followup CONNECT attempts using kerberos or ntlm tokens, based on configuration and proxy + * response properties. * * @param allocator memory allocator to use - * @param config configuration options for the strategy factory - * @return a new proxy strategy factory if successfully constructed, otherwise NULL + * @param config configuration options for the strategy + * @return a new proxy strategy if successfully constructed, otherwise NULL */ AWS_HTTP_API -struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunneling_adaptive_kerberos( +struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_tunneling_adaptive( struct aws_allocator *allocator, - struct aws_http_proxy_strategy_factory_tunneling_adaptive_kerberos_options *config); + struct aws_http_proxy_strategy_tunneling_adaptive_options *config); AWS_EXTERN_C_END diff --git a/source/http.c b/source/http.c index 3d79abb96..ba0053c51 100644 --- a/source/http.c +++ b/source/http.c @@ -118,6 +118,12 @@ static struct aws_error_info s_errors[] = { AWS_DEFINE_ERROR_INFO_HTTP( AWS_ERROR_HTTP_PROXY_STRATEGY_TRANSFORM_FAILED, "Proxy strategy transform has completely failed."), + AWS_DEFINE_ERROR_INFO_HTTP( + AWS_ERROR_HTTP_PROXY_STRATEGY_FAILED_PREVIOUSLY, + "Proxy strategy was previously tried and failed"), + AWS_DEFINE_ERROR_INFO_HTTP( + AWS_ERROR_HTTP_PROXY_STRATEGY_NTLM_CHALLENGE_TOKEN_MISSING, + "NTLM Proxy strategy was initiated without a challenge token"), }; /* clang-format on */ diff --git a/source/proxy_connection.c b/source/proxy_connection.c index 95fde5cb9..e3183e83e 100644 --- a/source/proxy_connection.c +++ b/source/proxy_connection.c @@ -53,7 +53,7 @@ void aws_http_proxy_user_data_destroy(struct aws_http_proxy_user_data *user_data aws_mem_release(user_data->allocator, user_data->tls_options); } - aws_http_proxy_strategy_release(user_data->proxy_strategy); + aws_http_proxy_negotiator_release(user_data->proxy_negotiator); aws_mem_release(user_data->allocator, user_data); } @@ -85,9 +85,9 @@ struct aws_http_proxy_user_data *aws_http_proxy_user_data_new( goto on_error; } - user_data->proxy_strategy = - aws_http_proxy_strategy_factory_create_strategy(user_data->proxy_config->proxy_strategy_factory, allocator); - if (user_data->proxy_strategy == NULL) { + user_data->proxy_negotiator = + aws_http_proxy_strategy_create_negotiator(user_data->proxy_config->proxy_strategy, allocator); + if (user_data->proxy_negotiator == NULL) { goto on_error; } @@ -289,10 +289,10 @@ static int s_aws_http_on_incoming_body_tunnel_proxy( (void)stream; struct aws_http_proxy_user_data *context = user_data; - aws_http_proxy_strategy_connect_on_incoming_body_fn *on_incoming_body = - context->proxy_strategy->strategy_vtable.tunnelling_vtable->on_incoming_body_callback; + aws_http_proxy_negotiator_connect_on_incoming_body_fn *on_incoming_body = + context->proxy_negotiator->strategy_vtable.tunnelling_vtable->on_incoming_body_callback; if (on_incoming_body != NULL) { - (*on_incoming_body)(context->proxy_strategy, data); + (*on_incoming_body)(context->proxy_negotiator, data); } aws_http_stream_update_window(stream, data->len); @@ -309,10 +309,10 @@ static int s_aws_http_on_response_headers_tunnel_proxy( (void)stream; struct aws_http_proxy_user_data *context = user_data; - aws_http_proxy_strategy_connect_on_incoming_headers_fn *on_incoming_headers = - context->proxy_strategy->strategy_vtable.tunnelling_vtable->on_incoming_headers_callback; + aws_http_proxy_negotiation_connect_on_incoming_headers_fn *on_incoming_headers = + context->proxy_negotiator->strategy_vtable.tunnelling_vtable->on_incoming_headers_callback; if (on_incoming_headers != NULL) { - (*on_incoming_headers)(context->proxy_strategy, header_block, header_array, num_headers); + (*on_incoming_headers)(context->proxy_negotiator, header_block, header_array, num_headers); } return AWS_OP_SUCCESS; @@ -339,10 +339,10 @@ static int s_aws_http_on_incoming_header_block_done_tunnel_proxy( context->error_code = AWS_ERROR_HTTP_PROXY_TLS_CONNECT_FAILED; } - aws_http_proxy_strategy_connect_status_fn *on_status = - context->proxy_strategy->strategy_vtable.tunnelling_vtable->on_status_callback; + aws_http_proxy_negotiator_connect_status_fn *on_status = + context->proxy_negotiator->strategy_vtable.tunnelling_vtable->on_status_callback; if (on_status != NULL) { - (*on_status)(context->proxy_strategy, status); + (*on_status)(context->proxy_negotiator, status); } } @@ -518,8 +518,8 @@ static int s_make_proxy_connect_request(struct aws_http_proxy_user_data *user_da return AWS_OP_ERR; } - (*user_data->proxy_strategy->strategy_vtable.tunnelling_vtable->connect_request_transform)( - user_data->proxy_strategy, + (*user_data->proxy_negotiator->strategy_vtable.tunnelling_vtable->connect_request_transform)( + user_data->proxy_negotiator, user_data->connect_request, s_terminate_tunneling_connect, s_continue_tunneling_connect, @@ -677,8 +677,8 @@ static int s_proxy_http_request_transform(struct aws_http_message *request, void return AWS_OP_ERR; } - if ((*proxy_ud->proxy_strategy->strategy_vtable.forwarding_vtable->forward_request_transform)( - proxy_ud->proxy_strategy, request)) { + if ((*proxy_ud->proxy_negotiator->strategy_vtable.forwarding_vtable->forward_request_transform)( + proxy_ud->proxy_negotiator, request)) { return AWS_OP_ERR; } @@ -836,24 +836,23 @@ static struct aws_http_proxy_config *s_aws_http_proxy_config_new( config->allocator = allocator; config->port = proxy_options->port; - if (proxy_options->proxy_strategy_factory != NULL) { - config->proxy_strategy_factory = aws_http_proxy_strategy_factory_acquire(proxy_options->proxy_strategy_factory); + if (proxy_options->proxy_strategy != NULL) { + config->proxy_strategy = aws_http_proxy_strategy_acquire(proxy_options->proxy_strategy); } else { switch (override_proxy_connection_type) { case AWS_HPCT_HTTP_FORWARD: - config->proxy_strategy_factory = aws_http_proxy_strategy_factory_new_forwarding_identity(allocator); + config->proxy_strategy = aws_http_proxy_strategy_new_forwarding_identity(allocator); break; case AWS_HPCT_HTTP_TUNNEL: - config->proxy_strategy_factory = - aws_http_proxy_strategy_factory_new_tunneling_one_time_identity(allocator); + config->proxy_strategy = aws_http_proxy_strategy_new_tunneling_one_time_identity(allocator); break; default: break; } - if (config->proxy_strategy_factory == NULL) { + if (config->proxy_strategy == NULL) { goto on_error; } } @@ -903,7 +902,7 @@ void aws_http_proxy_config_destroy(struct aws_http_proxy_config *config) { aws_mem_release(config->allocator, config->tls_options); } - aws_http_proxy_strategy_factory_release(config->proxy_strategy_factory); + aws_http_proxy_strategy_release(config->proxy_strategy); aws_mem_release(config->allocator, config); } @@ -917,7 +916,7 @@ void aws_http_proxy_options_init_from_config( options->host = aws_byte_cursor_from_buf(&config->host); options->port = config->port; options->tls_options = config->tls_options; - options->proxy_strategy_factory = config->proxy_strategy_factory; + options->proxy_strategy = config->proxy_strategy; } int aws_http_options_validate_proxy_configuration(const struct aws_http_client_connection_options *options) { @@ -934,9 +933,9 @@ int aws_http_options_validate_proxy_configuration(const struct aws_http_client_c return aws_raise_error(AWS_ERROR_INVALID_STATE); } - struct aws_http_proxy_strategy_factory *proxy_strategy_factory = options->proxy_options->proxy_strategy_factory; - if (proxy_strategy_factory != NULL) { - if (proxy_strategy_factory->proxy_connection_type != proxy_type) { + struct aws_http_proxy_strategy *proxy_strategy = options->proxy_options->proxy_strategy; + if (proxy_strategy != NULL) { + if (proxy_strategy->proxy_connection_type != proxy_type) { return aws_raise_error(AWS_ERROR_INVALID_STATE); } } diff --git a/source/proxy_strategy.c b/source/proxy_strategy.c index 8ccc40ca2..fee53b1b0 100644 --- a/source/proxy_strategy.c +++ b/source/proxy_strategy.c @@ -13,89 +13,89 @@ # pragma warning(disable : 4221) #endif /* _MSC_VER */ -struct aws_http_proxy_strategy *aws_http_proxy_strategy_acquire(struct aws_http_proxy_strategy *proxy_strategy) { - if (proxy_strategy != NULL) { - aws_ref_count_acquire(&proxy_strategy->ref_count); +struct aws_http_proxy_negotiator *aws_http_proxy_negotiator_acquire( + struct aws_http_proxy_negotiator *proxy_negotiator) { + if (proxy_negotiator != NULL) { + aws_ref_count_acquire(&proxy_negotiator->ref_count); } - return proxy_strategy; + return proxy_negotiator; } -void aws_http_proxy_strategy_release(struct aws_http_proxy_strategy *proxy_strategy) { - if (proxy_strategy != NULL) { - aws_ref_count_release(&proxy_strategy->ref_count); +void aws_http_proxy_negotiator_release(struct aws_http_proxy_negotiator *proxy_negotiator) { + if (proxy_negotiator != NULL) { + aws_ref_count_release(&proxy_negotiator->ref_count); } } -struct aws_http_proxy_strategy *aws_http_proxy_strategy_factory_create_strategy( - struct aws_http_proxy_strategy_factory *factory, +struct aws_http_proxy_negotiator *aws_http_proxy_strategy_create_negotiator( + struct aws_http_proxy_strategy *strategy, struct aws_allocator *allocator) { - if (factory == NULL || allocator == NULL) { + if (strategy == NULL || allocator == NULL) { aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); return NULL; } - return factory->vtable->create_strategy(factory, allocator); + return strategy->vtable->create_negotiator(strategy, allocator); } -struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_acquire( - struct aws_http_proxy_strategy_factory *proxy_strategy_factory) { - if (proxy_strategy_factory != NULL) { - aws_ref_count_acquire(&proxy_strategy_factory->ref_count); +struct aws_http_proxy_strategy *aws_http_proxy_strategy_acquire(struct aws_http_proxy_strategy *proxy_strategy) { + if (proxy_strategy != NULL) { + aws_ref_count_acquire(&proxy_strategy->ref_count); } - return proxy_strategy_factory; + return proxy_strategy; } -void aws_http_proxy_strategy_factory_release(struct aws_http_proxy_strategy_factory *proxy_strategy_factory) { - if (proxy_strategy_factory != NULL) { - aws_ref_count_release(&proxy_strategy_factory->ref_count); +void aws_http_proxy_strategy_release(struct aws_http_proxy_strategy *proxy_strategy) { + if (proxy_strategy != NULL) { + aws_ref_count_release(&proxy_strategy->ref_count); } } -/******************************************************************************************************************/ +/*****************************************************************************************************************/ -enum proxy_strategy_connect_state { - AWS_PSCS_READY, - AWS_PSCS_IN_PROGRESS, - AWS_PSCS_SUCCESS, - AWS_PSCS_FAILURE, +enum proxy_negotiator_connect_state { + AWS_PNCS_READY, + AWS_PNCS_IN_PROGRESS, + AWS_PNCS_SUCCESS, + AWS_PNCS_FAILURE, }; -struct aws_http_proxy_strategy_factory_basic_auth { - struct aws_allocator *allocator; +/* Functions for basic auth strategy */ +struct aws_http_proxy_strategy_basic_auth { + struct aws_allocator *allocator; struct aws_string *user_name; struct aws_string *password; - - struct aws_http_proxy_strategy_factory factory_base; + struct aws_http_proxy_strategy strategy_base; }; -static void s_destroy_basic_auth_factory(struct aws_http_proxy_strategy_factory *proxy_strategy_factory) { - struct aws_http_proxy_strategy_factory_basic_auth *basic_auth_factory = proxy_strategy_factory->impl; +static void s_destroy_basic_auth_strategy(struct aws_http_proxy_strategy *proxy_strategy) { + struct aws_http_proxy_strategy_basic_auth *basic_auth_strategy = proxy_strategy->impl; - aws_string_destroy(basic_auth_factory->user_name); - aws_string_destroy(basic_auth_factory->password); + aws_string_destroy(basic_auth_strategy->user_name); + aws_string_destroy(basic_auth_strategy->password); - aws_mem_release(basic_auth_factory->allocator, basic_auth_factory); + aws_mem_release(basic_auth_strategy->allocator, basic_auth_strategy); } -struct aws_http_proxy_strategy_basic_auth { +struct aws_http_proxy_negotiator_basic_auth { struct aws_allocator *allocator; - struct aws_http_proxy_strategy_factory *factory; + struct aws_http_proxy_strategy *strategy; - enum proxy_strategy_connect_state connect_state; + enum proxy_negotiator_connect_state connect_state; - struct aws_http_proxy_strategy strategy_base; + struct aws_http_proxy_negotiator negotiator_base; }; -static void s_destroy_basic_auth_strategy(struct aws_http_proxy_strategy *proxy_strategy) { - struct aws_http_proxy_strategy_basic_auth *basic_auth_strategy = proxy_strategy->impl; +static void s_destroy_basic_auth_negotiator(struct aws_http_proxy_negotiator *proxy_negotiator) { + struct aws_http_proxy_negotiator_basic_auth *basic_auth_negotiator = proxy_negotiator->impl; - aws_http_proxy_strategy_factory_release(basic_auth_strategy->factory); + aws_http_proxy_strategy_release(basic_auth_negotiator->strategy); - aws_mem_release(basic_auth_strategy->allocator, basic_auth_strategy); + aws_mem_release(basic_auth_negotiator->allocator, basic_auth_negotiator); } AWS_STATIC_STRING_FROM_LITERAL(s_proxy_authorization_header_name, "Proxy-Authorization"); @@ -107,7 +107,7 @@ AWS_STATIC_STRING_FROM_LITERAL(s_proxy_authorization_header_basic_prefix, "Basic static int s_add_basic_proxy_authentication_header( struct aws_allocator *allocator, struct aws_http_message *request, - struct aws_http_proxy_strategy_basic_auth *basic_auth_strategy) { + struct aws_http_proxy_negotiator_basic_auth *basic_auth_negotiator) { struct aws_byte_buf base64_input_value; AWS_ZERO_STRUCT(base64_input_value); @@ -117,14 +117,17 @@ static int s_add_basic_proxy_authentication_header( int result = AWS_OP_ERR; - struct aws_http_proxy_strategy_factory_basic_auth *factory = basic_auth_strategy->factory->impl; + struct aws_http_proxy_strategy_basic_auth *basic_auth_strategy = basic_auth_negotiator->strategy->impl; - if (aws_byte_buf_init(&base64_input_value, allocator, factory->user_name->len + factory->password->len + 1)) { + if (aws_byte_buf_init( + &base64_input_value, + allocator, + basic_auth_strategy->user_name->len + basic_auth_strategy->password->len + 1)) { goto done; } /* First build a buffer with "username:password" in it */ - struct aws_byte_cursor username_cursor = aws_byte_cursor_from_string(factory->user_name); + struct aws_byte_cursor username_cursor = aws_byte_cursor_from_string(basic_auth_strategy->user_name); if (aws_byte_buf_append(&base64_input_value, &username_cursor)) { goto done; } @@ -134,7 +137,7 @@ static int s_add_basic_proxy_authentication_header( goto done; } - struct aws_byte_cursor password_cursor = aws_byte_cursor_from_string(factory->password); + struct aws_byte_cursor password_cursor = aws_byte_cursor_from_string(basic_auth_strategy->password); if (aws_byte_buf_append(&base64_input_value, &password_cursor)) { goto done; } @@ -182,100 +185,104 @@ static int s_add_basic_proxy_authentication_header( return result; } -int s_basic_auth_forward_add_header(struct aws_http_proxy_strategy *proxy_strategy, struct aws_http_message *message) { - struct aws_http_proxy_strategy_basic_auth *basic_auth_strategy = proxy_strategy->impl; +int s_basic_auth_forward_add_header( + struct aws_http_proxy_negotiator *proxy_negotiator, + struct aws_http_message *message) { + struct aws_http_proxy_negotiator_basic_auth *basic_auth_negotiator = proxy_negotiator->impl; - return s_add_basic_proxy_authentication_header(basic_auth_strategy->allocator, message, basic_auth_strategy); + return s_add_basic_proxy_authentication_header(basic_auth_negotiator->allocator, message, basic_auth_negotiator); } void s_basic_auth_tunnel_add_header( - struct aws_http_proxy_strategy *proxy_strategy, + struct aws_http_proxy_negotiator *proxy_negotiator, struct aws_http_message *message, - aws_http_proxy_strategy_terminate_fn *strategy_termination_callback, - aws_http_proxy_strategy_http_request_forward_fn *strategy_http_request_forward_callback, + aws_http_proxy_negotiation_terminate_fn *negotiation_termination_callback, + aws_http_proxy_negotiation_http_request_forward_fn *negotiation_http_request_forward_callback, void *internal_proxy_user_data) { - struct aws_http_proxy_strategy_basic_auth *basic_auth_strategy = proxy_strategy->impl; - if (basic_auth_strategy->connect_state != AWS_PSCS_READY) { - strategy_termination_callback(message, AWS_ERROR_INVALID_STATE, internal_proxy_user_data); + struct aws_http_proxy_negotiator_basic_auth *basic_auth_negotiator = proxy_negotiator->impl; + if (basic_auth_negotiator->connect_state != AWS_PNCS_READY) { + negotiation_termination_callback(message, AWS_ERROR_INVALID_STATE, internal_proxy_user_data); return; } - basic_auth_strategy->connect_state = AWS_PSCS_IN_PROGRESS; + basic_auth_negotiator->connect_state = AWS_PNCS_IN_PROGRESS; - if (s_add_basic_proxy_authentication_header(basic_auth_strategy->allocator, message, basic_auth_strategy)) { - strategy_termination_callback(message, aws_last_error(), internal_proxy_user_data); + if (s_add_basic_proxy_authentication_header(basic_auth_negotiator->allocator, message, basic_auth_negotiator)) { + negotiation_termination_callback(message, aws_last_error(), internal_proxy_user_data); return; } - strategy_http_request_forward_callback(message, internal_proxy_user_data); + negotiation_http_request_forward_callback(message, internal_proxy_user_data); } static int s_basic_auth_on_connect_status( - struct aws_http_proxy_strategy *proxy_strategy, + struct aws_http_proxy_negotiator *proxy_negotiator, enum aws_http_status_code status_code) { - struct aws_http_proxy_strategy_basic_auth *basic_auth_strategy = proxy_strategy->impl; + struct aws_http_proxy_negotiator_basic_auth *basic_auth_negotiator = proxy_negotiator->impl; - if (basic_auth_strategy->connect_state == AWS_PSCS_IN_PROGRESS) { + if (basic_auth_negotiator->connect_state == AWS_PNCS_IN_PROGRESS) { if (AWS_HTTP_STATUS_CODE_200_OK != status_code) { - basic_auth_strategy->connect_state = AWS_PSCS_FAILURE; + basic_auth_negotiator->connect_state = AWS_PNCS_FAILURE; } else { - basic_auth_strategy->connect_state = AWS_PSCS_SUCCESS; + basic_auth_negotiator->connect_state = AWS_PNCS_SUCCESS; } } return AWS_OP_SUCCESS; } -static struct aws_http_proxy_strategy_forwarding_vtable s_basic_auth_proxy_forwarding_vtable = { +static struct aws_http_proxy_negotiator_forwarding_vtable s_basic_auth_proxy_negotiator_forwarding_vtable = { .forward_request_transform = s_basic_auth_forward_add_header, }; -static struct aws_http_proxy_strategy_tunnelling_vtable s_basic_auth_proxy_tunneling_vtable = { +static struct aws_http_proxy_negotiator_tunnelling_vtable s_basic_auth_proxy_negotiator_tunneling_vtable = { .on_status_callback = s_basic_auth_on_connect_status, .connect_request_transform = s_basic_auth_tunnel_add_header, }; -static struct aws_http_proxy_strategy *s_create_basic_auth_strategy( - struct aws_http_proxy_strategy_factory *proxy_strategy_factory, +static struct aws_http_proxy_negotiator *s_create_basic_auth_negotiator( + struct aws_http_proxy_strategy *proxy_strategy, struct aws_allocator *allocator) { - if (proxy_strategy_factory == NULL || allocator == NULL) { + if (proxy_strategy == NULL || allocator == NULL) { aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); return NULL; } - struct aws_http_proxy_strategy_basic_auth *basic_auth_strategy = - aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_strategy_basic_auth)); - if (basic_auth_strategy == NULL) { + struct aws_http_proxy_negotiator_basic_auth *basic_auth_negotiator = + aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_negotiator_basic_auth)); + if (basic_auth_negotiator == NULL) { return NULL; } - basic_auth_strategy->allocator = allocator; - basic_auth_strategy->connect_state = AWS_PSCS_READY; - basic_auth_strategy->strategy_base.impl = basic_auth_strategy; + basic_auth_negotiator->allocator = allocator; + basic_auth_negotiator->connect_state = AWS_PNCS_READY; + basic_auth_negotiator->negotiator_base.impl = basic_auth_negotiator; aws_ref_count_init( - &basic_auth_strategy->strategy_base.ref_count, - &basic_auth_strategy->strategy_base, - (aws_simple_completion_callback *)s_destroy_basic_auth_strategy); + &basic_auth_negotiator->negotiator_base.ref_count, + &basic_auth_negotiator->negotiator_base, + (aws_simple_completion_callback *)s_destroy_basic_auth_negotiator); - if (proxy_strategy_factory->proxy_connection_type == AWS_HPCT_HTTP_FORWARD) { - basic_auth_strategy->strategy_base.strategy_vtable.forwarding_vtable = &s_basic_auth_proxy_forwarding_vtable; + if (proxy_strategy->proxy_connection_type == AWS_HPCT_HTTP_FORWARD) { + basic_auth_negotiator->negotiator_base.strategy_vtable.forwarding_vtable = + &s_basic_auth_proxy_negotiator_forwarding_vtable; } else { - basic_auth_strategy->strategy_base.strategy_vtable.tunnelling_vtable = &s_basic_auth_proxy_tunneling_vtable; + basic_auth_negotiator->negotiator_base.strategy_vtable.tunnelling_vtable = + &s_basic_auth_proxy_negotiator_tunneling_vtable; } - basic_auth_strategy->factory = aws_ref_count_acquire(&proxy_strategy_factory->ref_count); + basic_auth_negotiator->strategy = aws_http_proxy_strategy_acquire(proxy_strategy); - return &basic_auth_strategy->strategy_base; + return &basic_auth_negotiator->negotiator_base; } -static struct aws_http_proxy_strategy_factory_vtable s_basic_auth_factory_vtable = { - .create_strategy = s_create_basic_auth_strategy, +static struct aws_http_proxy_strategy_vtable s_basic_auth_proxy_strategy_vtable = { + .create_negotiator = s_create_basic_auth_negotiator, }; -struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_basic_auth( +struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_basic_auth( struct aws_allocator *allocator, - struct aws_http_proxy_strategy_factory_basic_auth_config *config) { + struct aws_http_proxy_strategy_basic_auth_options *config) { if (config == NULL || allocator == NULL) { aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); return NULL; @@ -287,286 +294,284 @@ struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_basi return NULL; } - struct aws_http_proxy_strategy_factory_basic_auth *basic_auth_factory = - aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_strategy_factory_basic_auth)); - if (basic_auth_factory == NULL) { + struct aws_http_proxy_strategy_basic_auth *basic_auth_strategy = + aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_strategy_basic_auth)); + if (basic_auth_strategy == NULL) { return NULL; } - basic_auth_factory->factory_base.impl = basic_auth_factory; - basic_auth_factory->factory_base.vtable = &s_basic_auth_factory_vtable; - basic_auth_factory->allocator = allocator; - basic_auth_factory->factory_base.proxy_connection_type = config->proxy_connection_type; + basic_auth_strategy->strategy_base.impl = basic_auth_strategy; + basic_auth_strategy->strategy_base.vtable = &s_basic_auth_proxy_strategy_vtable; + basic_auth_strategy->allocator = allocator; + basic_auth_strategy->strategy_base.proxy_connection_type = config->proxy_connection_type; aws_ref_count_init( - &basic_auth_factory->factory_base.ref_count, - &basic_auth_factory->factory_base, - (aws_simple_completion_callback *)s_destroy_basic_auth_factory); + &basic_auth_strategy->strategy_base.ref_count, + &basic_auth_strategy->strategy_base, + (aws_simple_completion_callback *)s_destroy_basic_auth_strategy); - basic_auth_factory->user_name = aws_string_new_from_cursor(allocator, &config->user_name); - if (basic_auth_factory->user_name == NULL) { + basic_auth_strategy->user_name = aws_string_new_from_cursor(allocator, &config->user_name); + if (basic_auth_strategy->user_name == NULL) { goto on_error; } - basic_auth_factory->password = aws_string_new_from_cursor(allocator, &config->password); - if (basic_auth_factory->password == NULL) { + basic_auth_strategy->password = aws_string_new_from_cursor(allocator, &config->password); + if (basic_auth_strategy->password == NULL) { goto on_error; } - return &basic_auth_factory->factory_base; + return &basic_auth_strategy->strategy_base; on_error: - aws_http_proxy_strategy_factory_release(&basic_auth_factory->factory_base); + aws_http_proxy_strategy_release(&basic_auth_strategy->strategy_base); return NULL; } -/******************************************************************************************************************/ +/*****************************************************************************************************************/ -struct aws_http_proxy_strategy_factory_one_time_identity { +struct aws_http_proxy_strategy_one_time_identity { struct aws_allocator *allocator; - struct aws_http_proxy_strategy_factory factory_base; + struct aws_http_proxy_strategy strategy_base; }; -struct aws_http_proxy_strategy_one_time_identity { +struct aws_http_proxy_negotiator_one_time_identity { struct aws_allocator *allocator; - enum proxy_strategy_connect_state connect_state; + enum proxy_negotiator_connect_state connect_state; - struct aws_http_proxy_strategy strategy_base; + struct aws_http_proxy_negotiator negotiator_base; }; -static void s_destroy_one_time_identity_strategy(struct aws_http_proxy_strategy *proxy_strategy) { - struct aws_http_proxy_strategy_one_time_identity *identity_strategy = proxy_strategy->impl; +static void s_destroy_one_time_identity_negotiator(struct aws_http_proxy_negotiator *proxy_negotiator) { + struct aws_http_proxy_negotiator_one_time_identity *identity_negotiator = proxy_negotiator->impl; - aws_mem_release(identity_strategy->allocator, identity_strategy); + aws_mem_release(identity_negotiator->allocator, identity_negotiator); } void s_one_time_identity_connect_transform( - struct aws_http_proxy_strategy *proxy_strategy, + struct aws_http_proxy_negotiator *proxy_negotiator, struct aws_http_message *message, - aws_http_proxy_strategy_terminate_fn *strategy_termination_callback, - aws_http_proxy_strategy_http_request_forward_fn *strategy_http_request_forward_callback, + aws_http_proxy_negotiation_terminate_fn *negotiation_termination_callback, + aws_http_proxy_negotiation_http_request_forward_fn *negotiation_http_request_forward_callback, void *internal_proxy_user_data) { - struct aws_http_proxy_strategy_one_time_identity *one_time_identity_strategy = proxy_strategy->impl; - if (one_time_identity_strategy->connect_state != AWS_PSCS_READY) { - strategy_termination_callback(message, AWS_ERROR_INVALID_STATE, internal_proxy_user_data); + struct aws_http_proxy_negotiator_one_time_identity *one_time_identity_negotiator = proxy_negotiator->impl; + if (one_time_identity_negotiator->connect_state != AWS_PNCS_READY) { + negotiation_termination_callback(message, AWS_ERROR_INVALID_STATE, internal_proxy_user_data); return; } - one_time_identity_strategy->connect_state = AWS_PSCS_IN_PROGRESS; - strategy_http_request_forward_callback(message, internal_proxy_user_data); + one_time_identity_negotiator->connect_state = AWS_PNCS_IN_PROGRESS; + negotiation_http_request_forward_callback(message, internal_proxy_user_data); } static int s_one_time_identity_on_connect_status( - struct aws_http_proxy_strategy *proxy_strategy, + struct aws_http_proxy_negotiator *proxy_negotiator, enum aws_http_status_code status_code) { - struct aws_http_proxy_strategy_one_time_identity *one_time_identity_strategy = proxy_strategy->impl; + struct aws_http_proxy_negotiator_one_time_identity *one_time_identity_negotiator = proxy_negotiator->impl; - if (one_time_identity_strategy->connect_state == AWS_PSCS_IN_PROGRESS) { + if (one_time_identity_negotiator->connect_state == AWS_PNCS_IN_PROGRESS) { if (AWS_HTTP_STATUS_CODE_200_OK != status_code) { - one_time_identity_strategy->connect_state = AWS_PSCS_FAILURE; + one_time_identity_negotiator->connect_state = AWS_PNCS_FAILURE; } else { - one_time_identity_strategy->connect_state = AWS_PSCS_SUCCESS; + one_time_identity_negotiator->connect_state = AWS_PNCS_SUCCESS; } } return AWS_OP_SUCCESS; } -static struct aws_http_proxy_strategy_tunnelling_vtable s_one_time_identity_proxy_tunneling_vtable = { +static struct aws_http_proxy_negotiator_tunnelling_vtable s_one_time_identity_proxy_negotiator_tunneling_vtable = { .on_status_callback = s_one_time_identity_on_connect_status, .connect_request_transform = s_one_time_identity_connect_transform, }; -static struct aws_http_proxy_strategy *s_create_one_time_identity_strategy( - struct aws_http_proxy_strategy_factory *proxy_strategy_factory, +static struct aws_http_proxy_negotiator *s_create_one_time_identity_negotiator( + struct aws_http_proxy_strategy *proxy_strategy, struct aws_allocator *allocator) { - if (proxy_strategy_factory == NULL || allocator == NULL) { + if (proxy_strategy == NULL || allocator == NULL) { aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); return NULL; } - struct aws_http_proxy_strategy_one_time_identity *identity_strategy = - aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_strategy_one_time_identity)); - if (identity_strategy == NULL) { + struct aws_http_proxy_negotiator_one_time_identity *identity_negotiator = + aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_negotiator_one_time_identity)); + if (identity_negotiator == NULL) { return NULL; } - identity_strategy->allocator = allocator; - identity_strategy->connect_state = AWS_PSCS_READY; - identity_strategy->strategy_base.impl = identity_strategy; + identity_negotiator->allocator = allocator; + identity_negotiator->connect_state = AWS_PNCS_READY; + identity_negotiator->negotiator_base.impl = identity_negotiator; aws_ref_count_init( - &identity_strategy->strategy_base.ref_count, - &identity_strategy->strategy_base, - (aws_simple_completion_callback *)s_destroy_one_time_identity_strategy); + &identity_negotiator->negotiator_base.ref_count, + &identity_negotiator->negotiator_base, + (aws_simple_completion_callback *)s_destroy_one_time_identity_negotiator); - identity_strategy->strategy_base.strategy_vtable.tunnelling_vtable = &s_one_time_identity_proxy_tunneling_vtable; + identity_negotiator->negotiator_base.strategy_vtable.tunnelling_vtable = + &s_one_time_identity_proxy_negotiator_tunneling_vtable; - return &identity_strategy->strategy_base; + return &identity_negotiator->negotiator_base; } -static struct aws_http_proxy_strategy_factory_vtable s_one_time_identity_factory_vtable = { - .create_strategy = s_create_one_time_identity_strategy, +static struct aws_http_proxy_strategy_vtable s_one_time_identity_proxy_strategy_vtable = { + .create_negotiator = s_create_one_time_identity_negotiator, }; -static void s_destroy_one_time_identity_factory(struct aws_http_proxy_strategy_factory *proxy_strategy_factory) { - struct aws_http_proxy_strategy_factory_one_time_identity *identity_factory = proxy_strategy_factory->impl; +static void s_destroy_one_time_identity_strategy(struct aws_http_proxy_strategy *proxy_strategy) { + struct aws_http_proxy_strategy_one_time_identity *identity_strategy = proxy_strategy->impl; - aws_mem_release(identity_factory->allocator, identity_factory); + aws_mem_release(identity_strategy->allocator, identity_strategy); } -struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunneling_one_time_identity( +struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_tunneling_one_time_identity( struct aws_allocator *allocator) { if (allocator == NULL) { aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); return NULL; } - struct aws_http_proxy_strategy_factory_one_time_identity *identity_factory = - aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_strategy_factory_one_time_identity)); - if (identity_factory == NULL) { + struct aws_http_proxy_strategy_one_time_identity *identity_strategy = + aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_strategy_one_time_identity)); + if (identity_strategy == NULL) { return NULL; } - identity_factory->factory_base.impl = identity_factory; - identity_factory->factory_base.vtable = &s_one_time_identity_factory_vtable; - identity_factory->factory_base.proxy_connection_type = AWS_HPCT_HTTP_TUNNEL; - identity_factory->allocator = allocator; + identity_strategy->strategy_base.impl = identity_strategy; + identity_strategy->strategy_base.vtable = &s_one_time_identity_proxy_strategy_vtable; + identity_strategy->strategy_base.proxy_connection_type = AWS_HPCT_HTTP_TUNNEL; + identity_strategy->allocator = allocator; aws_ref_count_init( - &identity_factory->factory_base.ref_count, - &identity_factory->factory_base, - (aws_simple_completion_callback *)s_destroy_one_time_identity_factory); + &identity_strategy->strategy_base.ref_count, + &identity_strategy->strategy_base, + (aws_simple_completion_callback *)s_destroy_one_time_identity_strategy); - return &identity_factory->factory_base; + return &identity_strategy->strategy_base; } /******************************************************************************************************************/ -struct aws_http_proxy_strategy_factory_forwarding_identity { +struct aws_http_proxy_strategy_forwarding_identity { struct aws_allocator *allocator; - struct aws_http_proxy_strategy_factory factory_base; + struct aws_http_proxy_strategy strategy_base; }; -struct aws_http_proxy_strategy_forwarding_identity { +struct aws_http_proxy_negotiator_forwarding_identity { struct aws_allocator *allocator; - enum proxy_strategy_connect_state connect_state; - - struct aws_http_proxy_strategy strategy_base; + struct aws_http_proxy_negotiator negotiator_base; }; -static void s_destroy_forwarding_identity_strategy(struct aws_http_proxy_strategy *proxy_strategy) { - struct aws_http_proxy_strategy_forwarding_identity *identity_strategy = proxy_strategy->impl; +static void s_destroy_forwarding_identity_negotiator(struct aws_http_proxy_negotiator *proxy_negotiator) { + struct aws_http_proxy_negotiator_forwarding_identity *identity_negotiator = proxy_negotiator->impl; - aws_mem_release(identity_strategy->allocator, identity_strategy); + aws_mem_release(identity_negotiator->allocator, identity_negotiator); } int s_forwarding_identity_connect_transform( - struct aws_http_proxy_strategy *proxy_strategy, + struct aws_http_proxy_negotiator *proxy_negotiator, struct aws_http_message *message) { (void)message; - (void)proxy_strategy; + (void)proxy_negotiator; return AWS_OP_SUCCESS; } -static struct aws_http_proxy_strategy_forwarding_vtable s_forwarding_identity_proxy_tunneling_vtable = { +static struct aws_http_proxy_negotiator_forwarding_vtable s_forwarding_identity_proxy_negotiator_tunneling_vtable = { .forward_request_transform = s_forwarding_identity_connect_transform, }; -static struct aws_http_proxy_strategy *s_create_forwarding_identity_strategy( - struct aws_http_proxy_strategy_factory *proxy_strategy_factory, +static struct aws_http_proxy_negotiator *s_create_forwarding_identity_negotiator( + struct aws_http_proxy_strategy *proxy_strategy, struct aws_allocator *allocator) { - if (proxy_strategy_factory == NULL || allocator == NULL) { + if (proxy_strategy == NULL || allocator == NULL) { aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); return NULL; } - struct aws_http_proxy_strategy_forwarding_identity *identity_strategy = - aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_strategy_forwarding_identity)); - if (identity_strategy == NULL) { + struct aws_http_proxy_negotiator_forwarding_identity *identity_negotiator = + aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_negotiator_forwarding_identity)); + if (identity_negotiator == NULL) { return NULL; } - identity_strategy->allocator = allocator; - identity_strategy->connect_state = AWS_PSCS_READY; - identity_strategy->strategy_base.impl = identity_strategy; + identity_negotiator->allocator = allocator; + identity_negotiator->negotiator_base.impl = identity_negotiator; aws_ref_count_init( - &identity_strategy->strategy_base.ref_count, - &identity_strategy->strategy_base, - (aws_simple_completion_callback *)s_destroy_forwarding_identity_strategy); + &identity_negotiator->negotiator_base.ref_count, + &identity_negotiator->negotiator_base, + (aws_simple_completion_callback *)s_destroy_forwarding_identity_negotiator); - identity_strategy->strategy_base.strategy_vtable.forwarding_vtable = &s_forwarding_identity_proxy_tunneling_vtable; + identity_negotiator->negotiator_base.strategy_vtable.forwarding_vtable = + &s_forwarding_identity_proxy_negotiator_tunneling_vtable; - return &identity_strategy->strategy_base; + return &identity_negotiator->negotiator_base; } -static struct aws_http_proxy_strategy_factory_vtable s_forwarding_identity_factory_vtable = { - .create_strategy = s_create_forwarding_identity_strategy, +static struct aws_http_proxy_strategy_vtable s_forwarding_identity_strategy_vtable = { + .create_negotiator = s_create_forwarding_identity_negotiator, }; -static void s_destroy_forwarding_identity_factory(struct aws_http_proxy_strategy_factory *proxy_strategy_factory) { - struct aws_http_proxy_strategy_factory_forwarding_identity *identity_factory = proxy_strategy_factory->impl; +static void s_destroy_forwarding_identity_strategy(struct aws_http_proxy_strategy *proxy_strategy) { + struct aws_http_proxy_strategy_forwarding_identity *identity_strategy = proxy_strategy->impl; - aws_mem_release(identity_factory->allocator, identity_factory); + aws_mem_release(identity_strategy->allocator, identity_strategy); } -struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_forwarding_identity( - struct aws_allocator *allocator) { +struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_forwarding_identity(struct aws_allocator *allocator) { if (allocator == NULL) { aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); return NULL; } - struct aws_http_proxy_strategy_factory_forwarding_identity *identity_factory = - aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_strategy_factory_forwarding_identity)); - if (identity_factory == NULL) { + struct aws_http_proxy_strategy_forwarding_identity *identity_strategy = + aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_strategy_forwarding_identity)); + if (identity_strategy == NULL) { return NULL; } - identity_factory->factory_base.impl = identity_factory; - identity_factory->factory_base.vtable = &s_forwarding_identity_factory_vtable; - identity_factory->factory_base.proxy_connection_type = AWS_HPCT_HTTP_FORWARD; - identity_factory->allocator = allocator; + identity_strategy->strategy_base.impl = identity_strategy; + identity_strategy->strategy_base.vtable = &s_forwarding_identity_strategy_vtable; + identity_strategy->strategy_base.proxy_connection_type = AWS_HPCT_HTTP_FORWARD; + identity_strategy->allocator = allocator; aws_ref_count_init( - &identity_factory->factory_base.ref_count, - &identity_factory->factory_base, - (aws_simple_completion_callback *)s_destroy_forwarding_identity_factory); + &identity_strategy->strategy_base.ref_count, + &identity_strategy->strategy_base, + (aws_simple_completion_callback *)s_destroy_forwarding_identity_strategy); - return &identity_factory->factory_base; + return &identity_strategy->strategy_base; } /******************************************************************************************************************/ -struct aws_http_proxy_strategy_factory_tunneling_chain { +struct aws_http_proxy_strategy_tunneling_chain { struct aws_allocator *allocator; - struct aws_array_list strategy_factories; + struct aws_array_list strategies; - struct aws_http_proxy_strategy_factory factory_base; + struct aws_http_proxy_strategy strategy_base; }; -struct aws_http_proxy_strategy_tunneling_chain { +struct aws_http_proxy_negotiator_tunneling_chain { struct aws_allocator *allocator; - struct aws_array_list strategies; - size_t current_strategy_transform_index; + struct aws_array_list negotiators; + size_t current_negotiator_transform_index; void *original_internal_proxy_user_data; - aws_http_proxy_strategy_terminate_fn *original_strategy_termination_callback; - aws_http_proxy_strategy_http_request_forward_fn *original_strategy_http_request_forward_callback; + aws_http_proxy_negotiation_terminate_fn *original_negotiation_termination_callback; + aws_http_proxy_negotiation_http_request_forward_fn *original_negotiation_http_request_forward_callback; - struct aws_http_proxy_strategy strategy_base; + struct aws_http_proxy_negotiator negotiator_base; }; -static void s_chain_tunnel_try_next_strategy( - struct aws_http_proxy_strategy *proxy_strategy, +static void s_chain_tunnel_try_next_negotiator( + struct aws_http_proxy_negotiator *proxy_negotiator, struct aws_http_message *message); static void s_chain_tunnel_iteration_termination_callback( @@ -576,87 +581,89 @@ static void s_chain_tunnel_iteration_termination_callback( (void)error_code; /* TODO: log */ - struct aws_http_proxy_strategy *proxy_strategy = user_data; - struct aws_http_proxy_strategy_tunneling_chain *chain_strategy = proxy_strategy->impl; + struct aws_http_proxy_negotiator *proxy_negotiator = user_data; + struct aws_http_proxy_negotiator_tunneling_chain *chain_negotiator = proxy_negotiator->impl; - chain_strategy->current_strategy_transform_index++; + chain_negotiator->current_negotiator_transform_index++; - s_chain_tunnel_try_next_strategy(proxy_strategy, message); + s_chain_tunnel_try_next_negotiator(proxy_negotiator, message); } static void s_chain_tunnel_iteration_forward_callback(struct aws_http_message *message, void *user_data) { - struct aws_http_proxy_strategy *proxy_strategy = user_data; - struct aws_http_proxy_strategy_tunneling_chain *chain_strategy = proxy_strategy->impl; + struct aws_http_proxy_negotiator *proxy_negotiator = user_data; + struct aws_http_proxy_negotiator_tunneling_chain *chain_negotiator = proxy_negotiator->impl; - chain_strategy->original_strategy_http_request_forward_callback( - message, chain_strategy->original_internal_proxy_user_data); + chain_negotiator->original_negotiation_http_request_forward_callback( + message, chain_negotiator->original_internal_proxy_user_data); } -static void s_chain_tunnel_try_next_strategy( - struct aws_http_proxy_strategy *proxy_strategy, +static void s_chain_tunnel_try_next_negotiator( + struct aws_http_proxy_negotiator *proxy_negotiator, struct aws_http_message *message) { - struct aws_http_proxy_strategy_tunneling_chain *chain_strategy = proxy_strategy->impl; + struct aws_http_proxy_negotiator_tunneling_chain *chain_negotiator = proxy_negotiator->impl; - size_t strategy_count = aws_array_list_length(&chain_strategy->strategies); - if (chain_strategy->current_strategy_transform_index >= strategy_count) { + size_t negotiator_count = aws_array_list_length(&chain_negotiator->negotiators); + if (chain_negotiator->current_negotiator_transform_index >= negotiator_count) { goto on_error; } - struct aws_http_proxy_strategy *current_strategy = NULL; + struct aws_http_proxy_negotiator *current_negotiator = NULL; if (aws_array_list_get_at( - &chain_strategy->strategies, ¤t_strategy, chain_strategy->current_strategy_transform_index)) { + &chain_negotiator->negotiators, + ¤t_negotiator, + chain_negotiator->current_negotiator_transform_index)) { goto on_error; } - current_strategy->strategy_vtable.tunnelling_vtable->connect_request_transform( - current_strategy, + current_negotiator->strategy_vtable.tunnelling_vtable->connect_request_transform( + current_negotiator, message, s_chain_tunnel_iteration_termination_callback, s_chain_tunnel_iteration_forward_callback, - proxy_strategy); + proxy_negotiator); return; on_error: - chain_strategy->original_strategy_termination_callback( - message, AWS_ERROR_HTTP_PROXY_STRATEGY_TRANSFORM_FAILED, chain_strategy->original_internal_proxy_user_data); + chain_negotiator->original_negotiation_termination_callback( + message, AWS_ERROR_HTTP_PROXY_STRATEGY_TRANSFORM_FAILED, chain_negotiator->original_internal_proxy_user_data); } static void s_chain_tunnel_transform_connect( - struct aws_http_proxy_strategy *proxy_strategy, + struct aws_http_proxy_negotiator *proxy_negotiator, struct aws_http_message *message, - aws_http_proxy_strategy_terminate_fn *strategy_termination_callback, - aws_http_proxy_strategy_http_request_forward_fn *strategy_http_request_forward_callback, + aws_http_proxy_negotiation_terminate_fn *negotiation_termination_callback, + aws_http_proxy_negotiation_http_request_forward_fn *negotiation_http_request_forward_callback, void *internal_proxy_user_data) { - struct aws_http_proxy_strategy_tunneling_chain *chain_strategy = proxy_strategy->impl; + struct aws_http_proxy_negotiator_tunneling_chain *chain_negotiator = proxy_negotiator->impl; - chain_strategy->current_strategy_transform_index = 0; - chain_strategy->original_internal_proxy_user_data = internal_proxy_user_data; - chain_strategy->original_strategy_termination_callback = strategy_termination_callback; - chain_strategy->original_strategy_http_request_forward_callback = strategy_http_request_forward_callback; + chain_negotiator->current_negotiator_transform_index = 0; + chain_negotiator->original_internal_proxy_user_data = internal_proxy_user_data; + chain_negotiator->original_negotiation_termination_callback = negotiation_termination_callback; + chain_negotiator->original_negotiation_http_request_forward_callback = negotiation_http_request_forward_callback; - s_chain_tunnel_try_next_strategy(proxy_strategy, message); + s_chain_tunnel_try_next_negotiator(proxy_negotiator, message); } static int s_chain_on_incoming_headers( - struct aws_http_proxy_strategy *proxy_strategy, + struct aws_http_proxy_negotiator *proxy_negotiator, enum aws_http_header_block header_block, const struct aws_http_header *header_array, size_t num_headers) { - struct aws_http_proxy_strategy_tunneling_chain *chain_strategy = proxy_strategy->impl; - size_t strategy_count = aws_array_list_length(&chain_strategy->strategies); - for (size_t i = 0; i < strategy_count; ++i) { - struct aws_http_proxy_strategy *strategy = NULL; - if (aws_array_list_get_at(&chain_strategy->strategies, &strategy, i)) { + struct aws_http_proxy_negotiator_tunneling_chain *chain_negotiator = proxy_negotiator->impl; + size_t negotiator_count = aws_array_list_length(&chain_negotiator->negotiators); + for (size_t i = 0; i < negotiator_count; ++i) { + struct aws_http_proxy_negotiator *negotiator = NULL; + if (aws_array_list_get_at(&chain_negotiator->negotiators, &negotiator, i)) { continue; } - aws_http_proxy_strategy_connect_on_incoming_headers_fn *on_incoming_headers = - strategy->strategy_vtable.tunnelling_vtable->on_incoming_headers_callback; + aws_http_proxy_negotiation_connect_on_incoming_headers_fn *on_incoming_headers = + negotiator->strategy_vtable.tunnelling_vtable->on_incoming_headers_callback; if (on_incoming_headers != NULL) { - (*on_incoming_headers)(strategy, header_block, header_array, num_headers); + (*on_incoming_headers)(negotiator, header_block, header_array, num_headers); } } @@ -664,21 +671,21 @@ static int s_chain_on_incoming_headers( } static int s_chain_on_connect_status( - struct aws_http_proxy_strategy *proxy_strategy, + struct aws_http_proxy_negotiator *proxy_negotiator, enum aws_http_status_code status_code) { - struct aws_http_proxy_strategy_tunneling_chain *chain_strategy = proxy_strategy->impl; - size_t strategy_count = aws_array_list_length(&chain_strategy->strategies); - for (size_t i = 0; i < strategy_count; ++i) { - struct aws_http_proxy_strategy *strategy = NULL; - if (aws_array_list_get_at(&chain_strategy->strategies, &strategy, i)) { + struct aws_http_proxy_negotiator_tunneling_chain *chain_negotiator = proxy_negotiator->impl; + size_t negotiator_count = aws_array_list_length(&chain_negotiator->negotiators); + for (size_t i = 0; i < negotiator_count; ++i) { + struct aws_http_proxy_negotiator *negotiator = NULL; + if (aws_array_list_get_at(&chain_negotiator->negotiators, &negotiator, i)) { continue; } - aws_http_proxy_strategy_connect_status_fn *on_status = - strategy->strategy_vtable.tunnelling_vtable->on_status_callback; + aws_http_proxy_negotiator_connect_status_fn *on_status = + negotiator->strategy_vtable.tunnelling_vtable->on_status_callback; if (on_status != NULL) { - (*on_status)(strategy, status_code); + (*on_status)(negotiator, status_code); } } @@ -686,357 +693,420 @@ static int s_chain_on_connect_status( } static int s_chain_on_incoming_body( - struct aws_http_proxy_strategy *proxy_strategy, + struct aws_http_proxy_negotiator *proxy_negotiator, const struct aws_byte_cursor *data) { - struct aws_http_proxy_strategy_tunneling_chain *chain_strategy = proxy_strategy->impl; - size_t strategy_count = aws_array_list_length(&chain_strategy->strategies); - for (size_t i = 0; i < strategy_count; ++i) { - struct aws_http_proxy_strategy *strategy = NULL; - if (aws_array_list_get_at(&chain_strategy->strategies, &strategy, i)) { + struct aws_http_proxy_negotiator_tunneling_chain *chain_negotiator = proxy_negotiator->impl; + size_t negotiator_count = aws_array_list_length(&chain_negotiator->negotiators); + for (size_t i = 0; i < negotiator_count; ++i) { + struct aws_http_proxy_negotiator *negotiator = NULL; + if (aws_array_list_get_at(&chain_negotiator->negotiators, &negotiator, i)) { continue; } - aws_http_proxy_strategy_connect_on_incoming_body_fn *on_incoming_body = - strategy->strategy_vtable.tunnelling_vtable->on_incoming_body_callback; + aws_http_proxy_negotiator_connect_on_incoming_body_fn *on_incoming_body = + negotiator->strategy_vtable.tunnelling_vtable->on_incoming_body_callback; if (on_incoming_body != NULL) { - (*on_incoming_body)(strategy, data); + (*on_incoming_body)(negotiator, data); } } return AWS_OP_SUCCESS; } -static struct aws_http_proxy_strategy_tunnelling_vtable s_tunneling_chain_proxy_tunneling_vtable = { +static struct aws_http_proxy_negotiator_tunnelling_vtable s_tunneling_chain_proxy_negotiator_tunneling_vtable = { .on_incoming_body_callback = s_chain_on_incoming_body, .on_incoming_headers_callback = s_chain_on_incoming_headers, .on_status_callback = s_chain_on_connect_status, .connect_request_transform = s_chain_tunnel_transform_connect, }; -static void s_destroy_tunneling_chain_strategy(struct aws_http_proxy_strategy *proxy_strategy) { - struct aws_http_proxy_strategy_tunneling_chain *chain_strategy = proxy_strategy->impl; +static void s_destroy_tunneling_chain_negotiator(struct aws_http_proxy_negotiator *proxy_negotiator) { + struct aws_http_proxy_negotiator_tunneling_chain *chain_negotiator = proxy_negotiator->impl; - size_t strategy_count = aws_array_list_length(&chain_strategy->strategies); - for (size_t i = 0; i < strategy_count; ++i) { - struct aws_http_proxy_strategy *strategy = NULL; - if (aws_array_list_get_at(&chain_strategy->strategies, &strategy, i)) { + size_t negotiator_count = aws_array_list_length(&chain_negotiator->negotiators); + for (size_t i = 0; i < negotiator_count; ++i) { + struct aws_http_proxy_negotiator *negotiator = NULL; + if (aws_array_list_get_at(&chain_negotiator->negotiators, &negotiator, i)) { continue; } - aws_http_proxy_strategy_release(strategy); + aws_http_proxy_negotiator_release(negotiator); } - aws_array_list_clean_up(&chain_strategy->strategies); + aws_array_list_clean_up(&chain_negotiator->negotiators); - aws_mem_release(chain_strategy->allocator, chain_strategy); + aws_mem_release(chain_negotiator->allocator, chain_negotiator); } -static struct aws_http_proxy_strategy *s_create_tunneling_chain_strategy( - struct aws_http_proxy_strategy_factory *proxy_strategy_factory, +static struct aws_http_proxy_negotiator *s_create_tunneling_chain_negotiator( + struct aws_http_proxy_strategy *proxy_strategy, struct aws_allocator *allocator) { - if (proxy_strategy_factory == NULL || allocator == NULL) { + if (proxy_strategy == NULL || allocator == NULL) { aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); return NULL; } - struct aws_http_proxy_strategy_tunneling_chain *chain_strategy = - aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_strategy_tunneling_chain)); - if (chain_strategy == NULL) { + struct aws_http_proxy_negotiator_tunneling_chain *chain_negotiator = + aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_negotiator_tunneling_chain)); + if (chain_negotiator == NULL) { return NULL; } - chain_strategy->allocator = allocator; - chain_strategy->strategy_base.impl = chain_strategy; + chain_negotiator->allocator = allocator; + chain_negotiator->negotiator_base.impl = chain_negotiator; aws_ref_count_init( - &chain_strategy->strategy_base.ref_count, - &chain_strategy->strategy_base, - (aws_simple_completion_callback *)s_destroy_tunneling_chain_strategy); + &chain_negotiator->negotiator_base.ref_count, + &chain_negotiator->negotiator_base, + (aws_simple_completion_callback *)s_destroy_tunneling_chain_negotiator); - chain_strategy->strategy_base.strategy_vtable.tunnelling_vtable = &s_tunneling_chain_proxy_tunneling_vtable; + chain_negotiator->negotiator_base.strategy_vtable.tunnelling_vtable = + &s_tunneling_chain_proxy_negotiator_tunneling_vtable; - struct aws_http_proxy_strategy_factory_tunneling_chain *chain_factory = proxy_strategy_factory->impl; - size_t strategy_count = aws_array_list_length(&chain_factory->strategy_factories); + struct aws_http_proxy_strategy_tunneling_chain *chain_strategy = proxy_strategy->impl; + size_t strategy_count = aws_array_list_length(&chain_strategy->strategies); if (aws_array_list_init_dynamic( - &chain_strategy->strategies, allocator, strategy_count, sizeof(struct aws_http_proxy_strategy *))) { + &chain_negotiator->negotiators, allocator, strategy_count, sizeof(struct aws_http_proxy_negotiator *))) { goto on_error; } for (size_t i = 0; i < strategy_count; ++i) { - struct aws_http_proxy_strategy_factory *factory = NULL; - if (aws_array_list_get_at(&chain_factory->strategy_factories, &factory, i)) { + struct aws_http_proxy_strategy *strategy = NULL; + if (aws_array_list_get_at(&chain_strategy->strategies, &strategy, i)) { goto on_error; } - struct aws_http_proxy_strategy *strategy = aws_http_proxy_strategy_factory_create_strategy(factory, allocator); - if (strategy == NULL) { + struct aws_http_proxy_negotiator *negotiator = aws_http_proxy_strategy_create_negotiator(strategy, allocator); + if (negotiator == NULL) { goto on_error; } - if (aws_array_list_push_back(&chain_strategy->strategies, &strategy)) { - aws_http_proxy_strategy_release(strategy); + if (aws_array_list_push_back(&chain_negotiator->negotiators, &negotiator)) { + aws_http_proxy_negotiator_release(negotiator); goto on_error; } } - return &chain_strategy->strategy_base; + return &chain_negotiator->negotiator_base; on_error: - aws_http_proxy_strategy_release(&chain_strategy->strategy_base); + aws_http_proxy_negotiator_release(&chain_negotiator->negotiator_base); return NULL; } -static struct aws_http_proxy_strategy_factory_vtable s_tunneling_chain_factory_vtable = { - .create_strategy = s_create_tunneling_chain_strategy, +static struct aws_http_proxy_strategy_vtable s_tunneling_chain_strategy_vtable = { + .create_negotiator = s_create_tunneling_chain_negotiator, }; -static void s_destroy_tunneling_chain_factory(struct aws_http_proxy_strategy_factory *proxy_strategy_factory) { - struct aws_http_proxy_strategy_factory_tunneling_chain *chain_factory = proxy_strategy_factory->impl; +static void s_destroy_tunneling_chain_strategy(struct aws_http_proxy_strategy *proxy_strategy) { + struct aws_http_proxy_strategy_tunneling_chain *chain_strategy = proxy_strategy->impl; - size_t factory_count = aws_array_list_length(&chain_factory->strategy_factories); - for (size_t i = 0; i < factory_count; ++i) { - struct aws_http_proxy_strategy_factory *factory = NULL; - if (aws_array_list_get_at(&chain_factory->strategy_factories, &factory, i)) { + size_t strategy_count = aws_array_list_length(&chain_strategy->strategies); + for (size_t i = 0; i < strategy_count; ++i) { + struct aws_http_proxy_strategy *strategy = NULL; + if (aws_array_list_get_at(&chain_strategy->strategies, &strategy, i)) { continue; } - aws_http_proxy_strategy_factory_release(factory); + aws_http_proxy_strategy_release(strategy); } - aws_array_list_clean_up(&chain_factory->strategy_factories); + aws_array_list_clean_up(&chain_strategy->strategies); - aws_mem_release(chain_factory->allocator, chain_factory); + aws_mem_release(chain_strategy->allocator, chain_strategy); } -struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunneling_chain( +struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_tunneling_chain( struct aws_allocator *allocator, - struct aws_http_proxy_strategy_factory_tunneling_chain_options *config) { + struct aws_http_proxy_strategy_tunneling_chain_options *config) { if (allocator == NULL || config == NULL) { aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); return NULL; } - struct aws_http_proxy_strategy_factory_tunneling_chain *chain_factory = - aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_strategy_factory_tunneling_chain)); - if (chain_factory == NULL) { + struct aws_http_proxy_strategy_tunneling_chain *chain_strategy = + aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_strategy_tunneling_chain)); + if (chain_strategy == NULL) { return NULL; } - chain_factory->factory_base.impl = chain_factory; - chain_factory->factory_base.vtable = &s_tunneling_chain_factory_vtable; - chain_factory->factory_base.proxy_connection_type = AWS_HPCT_HTTP_TUNNEL; - chain_factory->allocator = allocator; + chain_strategy->strategy_base.impl = chain_strategy; + chain_strategy->strategy_base.vtable = &s_tunneling_chain_strategy_vtable; + chain_strategy->strategy_base.proxy_connection_type = AWS_HPCT_HTTP_TUNNEL; + chain_strategy->allocator = allocator; aws_ref_count_init( - &chain_factory->factory_base.ref_count, - &chain_factory->factory_base, - (aws_simple_completion_callback *)s_destroy_tunneling_chain_factory); + &chain_strategy->strategy_base.ref_count, + &chain_strategy->strategy_base, + (aws_simple_completion_callback *)s_destroy_tunneling_chain_strategy); if (aws_array_list_init_dynamic( - &chain_factory->strategy_factories, - allocator, - config->factory_count, - sizeof(struct aws_http_proxy_strategy_factory *))) { + &chain_strategy->strategies, allocator, config->strategy_count, sizeof(struct aws_http_proxy_strategy *))) { goto on_error; } - for (size_t i = 0; i < config->factory_count; ++i) { - struct aws_http_proxy_strategy_factory *factory = config->factories[i]; + for (size_t i = 0; i < config->strategy_count; ++i) { + struct aws_http_proxy_strategy *strategy = config->strategies[i]; - if (aws_array_list_push_back(&chain_factory->strategy_factories, &factory)) { + if (aws_array_list_push_back(&chain_strategy->strategies, &strategy)) { goto on_error; } - aws_http_proxy_strategy_factory_acquire(factory); + aws_http_proxy_strategy_acquire(strategy); } - return &chain_factory->factory_base; + return &chain_strategy->strategy_base; on_error: - aws_http_proxy_strategy_factory_release(&chain_factory->factory_base); + aws_http_proxy_strategy_release(&chain_strategy->strategy_base); return NULL; } /******************************************************************************************************************/ +/* kerberos */ -AWS_HTTP_API -struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunneling_adaptive_test( - struct aws_allocator *allocator, - struct aws_http_proxy_strategy_factory_tunneling_adaptive_test_options *config) { +AWS_STATIC_STRING_FROM_LITERAL(s_proxy_authorization_header_kerberos_prefix, "Negotiate "); - if (allocator == NULL || config == NULL) { - aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); - return NULL; - } +struct aws_http_proxy_strategy_tunneling_kerberos { + struct aws_allocator *allocator; - struct aws_http_proxy_strategy_factory *bad_basic_factory = NULL; - struct aws_http_proxy_strategy_factory *good_basic_factory = NULL; - struct aws_http_proxy_strategy_factory *adaptive_factory = NULL; + aws_http_proxy_negotiation_get_token_sync_fn *get_token; - struct aws_http_proxy_strategy_factory_basic_auth_config bad_config = { - .proxy_connection_type = AWS_HPCT_HTTP_TUNNEL, - .password = aws_byte_cursor_from_c_str("NotAUsername"), - .user_name = aws_byte_cursor_from_c_str("NotAPassword"), - }; + void *get_token_user_data; - bad_basic_factory = aws_http_proxy_strategy_factory_new_basic_auth(allocator, &bad_config); - if (bad_basic_factory == NULL) { - goto on_error; - } + struct aws_http_proxy_strategy strategy_base; +}; - struct aws_http_proxy_strategy_factory_basic_auth_config good_config = { - .proxy_connection_type = AWS_HPCT_HTTP_TUNNEL, - .password = config->password, - .user_name = config->user_name, - }; +struct aws_http_proxy_negotiator_tunneling_kerberos { + struct aws_allocator *allocator; - good_basic_factory = aws_http_proxy_strategy_factory_new_basic_auth(allocator, &good_config); - if (good_basic_factory == NULL) { - goto on_error; + struct aws_http_proxy_strategy *strategy; + + enum proxy_negotiator_connect_state connect_state; + + /* + * ToDo: make adaptive and add any state needed here + * + * Likely things include response code (from the vanilla CONNECT) and the appropriate headers in + * the response + */ + + struct aws_http_proxy_negotiator negotiator_base; +}; + +/* + * Adds a proxy authentication header based on the user kerberos authentication token + * This uses a token that is already base64 encoded + */ +static int s_add_kerberos_proxy_usertoken_authentication_header( + struct aws_allocator *allocator, + struct aws_http_message *request, + struct aws_byte_cursor user_token) { + + struct aws_byte_buf header_value; + AWS_ZERO_STRUCT(header_value); + + int result = AWS_OP_ERR; + + if (aws_byte_buf_init( + &header_value, allocator, s_proxy_authorization_header_kerberos_prefix->len + user_token.len)) { + goto done; } - struct aws_http_proxy_strategy_factory *factory_array[2] = { - bad_basic_factory, - good_basic_factory, - }; + /* First append proxy authorization header kerberos prefix */ + struct aws_byte_cursor auth_header_cursor = + aws_byte_cursor_from_string(s_proxy_authorization_header_kerberos_prefix); + if (aws_byte_buf_append(&header_value, &auth_header_cursor)) { + goto done; + } - struct aws_http_proxy_strategy_factory_tunneling_chain_options chain_config = { - .factories = factory_array, - .factory_count = 2, + /* Append token to it */ + if (aws_byte_buf_append(&header_value, &user_token)) { + goto done; + } + + struct aws_http_header header = { + .name = aws_byte_cursor_from_string(s_proxy_authorization_header_name), + .value = aws_byte_cursor_from_array(header_value.buffer, header_value.len), }; - adaptive_factory = aws_http_proxy_strategy_factory_new_tunneling_chain(allocator, &chain_config); - if (adaptive_factory == NULL) { - goto on_error; + if (aws_http_message_add_header(request, header)) { + goto done; } - return adaptive_factory; - -on_error: + result = AWS_OP_SUCCESS; - aws_http_proxy_strategy_factory_release(bad_basic_factory); - aws_http_proxy_strategy_factory_release(good_basic_factory); - aws_http_proxy_strategy_factory_release(adaptive_factory); +done: - return NULL; + aws_byte_buf_clean_up(&header_value); + return result; } -/******************************************************************************************************************/ +static void s_kerberos_tunnel_transform_connect( + struct aws_http_proxy_negotiator *proxy_negotiator, + struct aws_http_message *message, + aws_http_proxy_negotiation_terminate_fn *negotiation_termination_callback, + aws_http_proxy_negotiation_http_request_forward_fn *negotiation_http_request_forward_callback, + void *internal_proxy_user_data) { -struct aws_http_proxy_strategy_factory_tunneling_kerberos { - struct aws_allocator *allocator; + struct aws_http_proxy_negotiator_tunneling_kerberos *kerberos_negotiator = proxy_negotiator->impl; + struct aws_http_proxy_strategy_tunneling_kerberos *kerberos_strategy = kerberos_negotiator->strategy->impl; - struct aws_http_proxy_strategy_factory factory_base; + int result = AWS_OP_ERR; + int error_code = AWS_ERROR_SUCCESS; + struct aws_byte_cursor kerberos_token; + AWS_ZERO_STRUCT(kerberos_token); - /* SA-TBI: add any factory state needed here */ -}; + if (kerberos_negotiator->connect_state == AWS_PNCS_FAILURE) { + error_code = AWS_ERROR_HTTP_PROXY_STRATEGY_FAILED_PREVIOUSLY; + goto done; + } -struct aws_http_proxy_strategy_tunneling_kerberos { - struct aws_allocator *allocator; + if (kerberos_negotiator->connect_state != AWS_PNCS_READY) { + error_code = AWS_ERROR_INVALID_STATE; + goto done; + } - struct aws_http_proxy_strategy strategy_base; + kerberos_negotiator->connect_state = AWS_PNCS_IN_PROGRESS; - enum proxy_strategy_connect_state state; + if (kerberos_strategy->get_token(kerberos_strategy->get_token_user_data, &kerberos_token, &error_code) || + error_code != AWS_ERROR_SUCCESS) { + goto done; + } - /* - * SA-TBI: add any factory state needed here - * - * Likely things include response code (from the vanilla CONNECT) and the appropriate headers in - * the response - */ -}; + /*transform the header with proxy authenticate:Negotiate and kerberos token*/ + if (s_add_kerberos_proxy_usertoken_authentication_header(kerberos_negotiator->allocator, message, kerberos_token)) { + error_code = aws_last_error(); + goto done; + } -static void s_kerberos_tunnel_transform_connect( - struct aws_http_proxy_strategy *proxy_strategy, - struct aws_http_message *message, - aws_http_proxy_strategy_terminate_fn *strategy_termination_callback, - aws_http_proxy_strategy_http_request_forward_fn *strategy_http_request_forward_callback, - void *internal_proxy_user_data) { + kerberos_negotiator->connect_state = AWS_PNCS_IN_PROGRESS; + result = AWS_OP_SUCCESS; - struct aws_http_proxy_strategy_tunneling_kerberos *kerberos_strategy = proxy_strategy->impl; - (void)kerberos_strategy; - (void)message; - (void)strategy_termination_callback; - (void)strategy_http_request_forward_callback; - (void)internal_proxy_user_data; +done: - /* - * SA-TBI: modify message with kerberos auth data and call the request_forward callback or if - * encountering an error, invoke the strategy_termination callback. - * - * As written, a connection attempt using this strategy will hang because neither of these are currently - * invoked. - */ + if (result != AWS_OP_SUCCESS) { + if (error_code == AWS_ERROR_SUCCESS) { + error_code = AWS_ERROR_UNKNOWN; + } + negotiation_termination_callback(message, error_code, internal_proxy_user_data); + } else { + negotiation_http_request_forward_callback(message, internal_proxy_user_data); + } } -static int s_kerberos_on_incoming_headers( - struct aws_http_proxy_strategy *proxy_strategy, +static int s_kerberos_on_incoming_header_adaptive( + struct aws_http_proxy_negotiator *proxy_negotiator, enum aws_http_header_block header_block, const struct aws_http_header *header_array, size_t num_headers) { - struct aws_http_proxy_strategy_tunneling_kerberos *kerberos_strategy = proxy_strategy->impl; - (void)kerberos_strategy; + struct aws_http_proxy_negotiator_tunneling_kerberos *kerberos_negotiator = proxy_negotiator->impl; + (void)kerberos_negotiator; (void)header_block; (void)header_array; (void)num_headers; - /* SA-TBI: process CONNECT response headers here if needed */ + /* TODO: process vanilla CONNECT response headers here to improve usage/application */ return AWS_OP_SUCCESS; } static int s_kerberos_on_connect_status( - struct aws_http_proxy_strategy *proxy_strategy, + struct aws_http_proxy_negotiator *proxy_negotiator, enum aws_http_status_code status_code) { - struct aws_http_proxy_strategy_tunneling_kerberos *kerberos_strategy = proxy_strategy->impl; - (void)kerberos_strategy; - (void)status_code; + struct aws_http_proxy_negotiator_tunneling_kerberos *kerberos_negotiator = proxy_negotiator->impl; - /* SA-TBI: process status code of CONNECT request here if needed */ + /* TODO: process status code of vanilla CONNECT request here to improve usage/application */ + + if (kerberos_negotiator->connect_state == AWS_PNCS_IN_PROGRESS) { + if (AWS_HTTP_STATUS_CODE_200_OK != status_code) { + kerberos_negotiator->connect_state = AWS_PNCS_FAILURE; + } else { + kerberos_negotiator->connect_state = AWS_PNCS_SUCCESS; + } + } return AWS_OP_SUCCESS; } static int s_kerberos_on_incoming_body( - struct aws_http_proxy_strategy *proxy_strategy, + struct aws_http_proxy_negotiator *proxy_negotiator, const struct aws_byte_cursor *data) { - struct aws_http_proxy_strategy_tunneling_kerberos *kerberos_strategy = proxy_strategy->impl; - (void)kerberos_strategy; + struct aws_http_proxy_negotiator_tunneling_kerberos *kerberos_negotiator = proxy_negotiator->impl; + (void)kerberos_negotiator; (void)data; - /* SA-TBI: process body of CONNECT request here if needed */ - return AWS_OP_SUCCESS; } -static struct aws_http_proxy_strategy_tunnelling_vtable s_tunneling_kerberos_proxy_tunneling_vtable = { +static struct aws_http_proxy_negotiator_tunnelling_vtable s_tunneling_kerberos_proxy_negotiator_tunneling_vtable = { .on_incoming_body_callback = s_kerberos_on_incoming_body, - .on_incoming_headers_callback = s_kerberos_on_incoming_headers, + .on_incoming_headers_callback = s_kerberos_on_incoming_header_adaptive, .on_status_callback = s_kerberos_on_connect_status, .connect_request_transform = s_kerberos_tunnel_transform_connect, }; +static void s_destroy_tunneling_kerberos_negotiator(struct aws_http_proxy_negotiator *proxy_negotiator) { + struct aws_http_proxy_negotiator_tunneling_kerberos *kerberos_negotiator = proxy_negotiator->impl; + + aws_http_proxy_strategy_release(kerberos_negotiator->strategy); + + aws_mem_release(kerberos_negotiator->allocator, kerberos_negotiator); +} + +static struct aws_http_proxy_negotiator *s_create_tunneling_kerberos_negotiator( + struct aws_http_proxy_strategy *proxy_strategy, + struct aws_allocator *allocator) { + if (proxy_strategy == NULL || allocator == NULL) { + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + return NULL; + } + + struct aws_http_proxy_negotiator_tunneling_kerberos *kerberos_negotiator = + aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_negotiator_tunneling_kerberos)); + if (kerberos_negotiator == NULL) { + return NULL; + } + + kerberos_negotiator->allocator = allocator; + kerberos_negotiator->negotiator_base.impl = kerberos_negotiator; + aws_ref_count_init( + &kerberos_negotiator->negotiator_base.ref_count, + &kerberos_negotiator->negotiator_base, + (aws_simple_completion_callback *)s_destroy_tunneling_kerberos_negotiator); + + kerberos_negotiator->negotiator_base.strategy_vtable.tunnelling_vtable = + &s_tunneling_kerberos_proxy_negotiator_tunneling_vtable; + + kerberos_negotiator->strategy = aws_http_proxy_strategy_acquire(proxy_strategy); + + return &kerberos_negotiator->negotiator_base; +} + +static struct aws_http_proxy_strategy_vtable s_tunneling_kerberos_strategy_vtable = { + .create_negotiator = s_create_tunneling_kerberos_negotiator, +}; + static void s_destroy_tunneling_kerberos_strategy(struct aws_http_proxy_strategy *proxy_strategy) { struct aws_http_proxy_strategy_tunneling_kerberos *kerberos_strategy = proxy_strategy->impl; - /* SA-TBI: any special kerberos strategy clean up here */ - aws_mem_release(kerberos_strategy->allocator, kerberos_strategy); } -static struct aws_http_proxy_strategy *s_create_tunneling_kerberos_strategy( - struct aws_http_proxy_strategy_factory *proxy_strategy_factory, - struct aws_allocator *allocator) { - if (proxy_strategy_factory == NULL || allocator == NULL) { +struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_tunneling_kerberos( + struct aws_allocator *allocator, + struct aws_http_proxy_strategy_tunneling_kerberos_options *config) { + + if (allocator == NULL || config == NULL || config->get_token == NULL) { aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); return NULL; } @@ -1047,110 +1117,370 @@ static struct aws_http_proxy_strategy *s_create_tunneling_kerberos_strategy( return NULL; } - kerberos_strategy->allocator = allocator; kerberos_strategy->strategy_base.impl = kerberos_strategy; + kerberos_strategy->strategy_base.vtable = &s_tunneling_kerberos_strategy_vtable; + kerberos_strategy->strategy_base.proxy_connection_type = AWS_HPCT_HTTP_TUNNEL; + kerberos_strategy->allocator = allocator; + aws_ref_count_init( &kerberos_strategy->strategy_base.ref_count, &kerberos_strategy->strategy_base, (aws_simple_completion_callback *)s_destroy_tunneling_kerberos_strategy); - kerberos_strategy->strategy_base.strategy_vtable.tunnelling_vtable = &s_tunneling_kerberos_proxy_tunneling_vtable; - - /* SA-TBI: special kerberos strategy init here */ + kerberos_strategy->get_token = config->get_token; + kerberos_strategy->get_token_user_data = config->get_token_user_data; return &kerberos_strategy->strategy_base; } -static struct aws_http_proxy_strategy_factory_vtable s_tunneling_kerberos_factory_vtable = { - .create_strategy = s_create_tunneling_kerberos_strategy, +/******************************************************************************************************************/ + +struct aws_http_proxy_strategy_tunneling_ntlm { + struct aws_allocator *allocator; + + aws_http_proxy_negotiation_get_challenge_token_sync_fn *get_challenge_token; + + void *get_challenge_token_user_data; + + struct aws_http_proxy_strategy strategy_base; }; -static void s_destroy_tunneling_kerberos_factory(struct aws_http_proxy_strategy_factory *proxy_strategy_factory) { - struct aws_http_proxy_strategy_factory_tunneling_kerberos *kerberos_factory = proxy_strategy_factory->impl; +struct aws_http_proxy_negotiator_tunneling_ntlm { + struct aws_allocator *allocator; + + struct aws_http_proxy_strategy *strategy; + + enum proxy_negotiator_connect_state connect_state; + + struct aws_string *challenge_token; + + struct aws_http_proxy_negotiator negotiator_base; +}; + +AWS_STATIC_STRING_FROM_LITERAL(s_proxy_authorization_header_ntlm_prefix, "NTLM "); + +/* + * Adds a proxy authentication header based on ntlm credential or response provided by user + */ +static int s_add_ntlm_proxy_usertoken_authentication_header( + struct aws_allocator *allocator, + struct aws_http_message *request, + struct aws_byte_cursor credential_response) { - /* SA-TBI: any special factory clean up here */ + struct aws_byte_buf header_value; + AWS_ZERO_STRUCT(header_value); + + int result = AWS_OP_ERR; - aws_mem_release(kerberos_factory->allocator, kerberos_factory); + if (aws_byte_buf_init( + &header_value, allocator, s_proxy_authorization_header_ntlm_prefix->len + credential_response.len)) { + goto done; + } + + /* First append proxy authorization header prefix */ + struct aws_byte_cursor auth_header_cursor = aws_byte_cursor_from_string(s_proxy_authorization_header_ntlm_prefix); + if (aws_byte_buf_append(&header_value, &auth_header_cursor)) { + goto done; + } + + /* Append the credential response to it; assumes already encoded properly (base64) */ + if (aws_byte_buf_append(&header_value, &credential_response)) { + goto done; + } + + struct aws_http_header header = { + .name = aws_byte_cursor_from_string(s_proxy_authorization_header_name), + .value = aws_byte_cursor_from_array(header_value.buffer, header_value.len), + }; + + if (aws_http_message_add_header(request, header)) { + goto done; + } + + result = AWS_OP_SUCCESS; + +done: + + aws_byte_buf_clean_up(&header_value); + return result; +} + +static void s_ntlm_tunnel_transform_connect( + struct aws_http_proxy_negotiator *proxy_negotiator, + struct aws_http_message *message, + aws_http_proxy_negotiation_terminate_fn *negotiation_termination_callback, + aws_http_proxy_negotiation_http_request_forward_fn *negotiation_http_request_forward_callback, + void *internal_proxy_user_data) { + + struct aws_http_proxy_negotiator_tunneling_ntlm *ntlm_negotiator = proxy_negotiator->impl; + struct aws_http_proxy_strategy_tunneling_ntlm *ntlm_strategy = ntlm_negotiator->strategy->impl; + + int result = AWS_OP_ERR; + int error_code = AWS_ERROR_SUCCESS; + struct aws_byte_cursor challenge_answer_token; + AWS_ZERO_STRUCT(challenge_answer_token); + struct aws_byte_cursor challenge_token; + AWS_ZERO_STRUCT(challenge_token); + + if (ntlm_negotiator->connect_state == AWS_PNCS_FAILURE) { + error_code = AWS_ERROR_HTTP_PROXY_STRATEGY_FAILED_PREVIOUSLY; + goto done; + } + + if (ntlm_negotiator->connect_state != AWS_PNCS_READY) { + error_code = AWS_ERROR_INVALID_STATE; + goto done; + } + + if (ntlm_negotiator->challenge_token == NULL) { + error_code = AWS_ERROR_HTTP_PROXY_STRATEGY_NTLM_CHALLENGE_TOKEN_MISSING; + goto done; + } + + ntlm_negotiator->connect_state = AWS_PNCS_IN_PROGRESS; + challenge_token = aws_byte_cursor_from_string(ntlm_negotiator->challenge_token); + if (ntlm_strategy->get_challenge_token( + ntlm_strategy->get_challenge_token_user_data, &challenge_token, &challenge_answer_token, &error_code) || + error_code != AWS_ERROR_SUCCESS) { + goto done; + } + + /*transform the header with proxy authenticate:Negotiate and kerberos token*/ + if (s_add_ntlm_proxy_usertoken_authentication_header(ntlm_negotiator->allocator, message, challenge_answer_token)) { + error_code = aws_last_error(); + goto done; + } + + ntlm_negotiator->connect_state = AWS_PNCS_IN_PROGRESS; + result = AWS_OP_SUCCESS; + +done: + + if (result != AWS_OP_SUCCESS) { + if (error_code == AWS_ERROR_SUCCESS) { + error_code = AWS_ERROR_UNKNOWN; + } + negotiation_termination_callback(message, error_code, internal_proxy_user_data); + } else { + negotiation_http_request_forward_callback(message, internal_proxy_user_data); + } } -struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunneling_kerberos( +AWS_STATIC_STRING_FROM_LITERAL(s_ntlm_challenge_token_header, "Proxy-Authenticate"); + +static int s_ntlm_on_incoming_header_adaptive( + struct aws_http_proxy_negotiator *proxy_negotiator, + enum aws_http_header_block header_block, + const struct aws_http_header *header_array, + size_t num_headers) { + + struct aws_http_proxy_negotiator_tunneling_ntlm *ntlm_negotiator = proxy_negotiator->impl; + + /* + * only extract the challenge before we've started our own CONNECT attempt + * + * ToDo: we currently overwrite previous challenge tokens since it is unknown if multiple CONNECT requests + * cause new challenges to be issued such that old challenges become invalid even if successfully computed + */ + if (ntlm_negotiator->connect_state == AWS_PNCS_READY) { + if (header_block == AWS_HTTP_HEADER_BLOCK_MAIN) { + struct aws_byte_cursor proxy_authenticate_header_name = + aws_byte_cursor_from_string(s_ntlm_challenge_token_header); + for (size_t i = 0; i < num_headers; ++i) { + struct aws_byte_cursor header_name_cursor = header_array[i].name; + if (aws_byte_cursor_eq_ignore_case(&proxy_authenticate_header_name, &header_name_cursor)) { + aws_string_destroy(ntlm_negotiator->challenge_token); + + struct aws_byte_cursor challenge_value_cursor = header_array[i].value; + ntlm_negotiator->challenge_token = + aws_string_new_from_cursor(ntlm_negotiator->allocator, &challenge_value_cursor); + break; + } + } + } + } + + return AWS_OP_SUCCESS; +} + +static int s_ntlm_on_connect_status( + struct aws_http_proxy_negotiator *proxy_negotiator, + enum aws_http_status_code status_code) { + + struct aws_http_proxy_negotiator_tunneling_ntlm *ntlm_negotiator = proxy_negotiator->impl; + + if (ntlm_negotiator->connect_state == AWS_PNCS_IN_PROGRESS) { + if (AWS_HTTP_STATUS_CODE_200_OK != status_code) { + ntlm_negotiator->connect_state = AWS_PNCS_FAILURE; + } else { + ntlm_negotiator->connect_state = AWS_PNCS_SUCCESS; + } + } + + return AWS_OP_SUCCESS; +} + +static int s_ntlm_on_incoming_body( + struct aws_http_proxy_negotiator *proxy_negotiator, + const struct aws_byte_cursor *data) { + + struct aws_http_proxy_negotiator_tunneling_ntlm *ntlm_negotiator = proxy_negotiator->impl; + (void)ntlm_negotiator; + (void)data; + + return AWS_OP_SUCCESS; +} + +static struct aws_http_proxy_negotiator_tunnelling_vtable s_tunneling_ntlm_proxy_negotiator_tunneling_vtable = { + .on_incoming_body_callback = s_ntlm_on_incoming_body, + .on_incoming_headers_callback = s_ntlm_on_incoming_header_adaptive, + .on_status_callback = s_ntlm_on_connect_status, + .connect_request_transform = s_ntlm_tunnel_transform_connect, +}; + +static void s_destroy_tunneling_ntlm_negotiator(struct aws_http_proxy_negotiator *proxy_negotiator) { + struct aws_http_proxy_negotiator_tunneling_ntlm *ntlm_negotiator = proxy_negotiator->impl; + + aws_string_destroy(ntlm_negotiator->challenge_token); + aws_http_proxy_strategy_release(ntlm_negotiator->strategy); + + aws_mem_release(ntlm_negotiator->allocator, ntlm_negotiator); +} + +static struct aws_http_proxy_negotiator *s_create_tunneling_ntlm_negotiator( + struct aws_http_proxy_strategy *proxy_strategy, + struct aws_allocator *allocator) { + if (proxy_strategy == NULL || allocator == NULL) { + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + return NULL; + } + + struct aws_http_proxy_negotiator_tunneling_ntlm *ntlm_negotiator = + aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_negotiator_tunneling_ntlm)); + if (ntlm_negotiator == NULL) { + return NULL; + } + + ntlm_negotiator->allocator = allocator; + ntlm_negotiator->negotiator_base.impl = ntlm_negotiator; + aws_ref_count_init( + &ntlm_negotiator->negotiator_base.ref_count, + &ntlm_negotiator->negotiator_base, + (aws_simple_completion_callback *)s_destroy_tunneling_ntlm_negotiator); + + ntlm_negotiator->negotiator_base.strategy_vtable.tunnelling_vtable = + &s_tunneling_ntlm_proxy_negotiator_tunneling_vtable; + + ntlm_negotiator->strategy = aws_http_proxy_strategy_acquire(proxy_strategy); + + return &ntlm_negotiator->negotiator_base; +} + +static struct aws_http_proxy_strategy_vtable s_tunneling_ntlm_strategy_vtable = { + .create_negotiator = s_create_tunneling_ntlm_negotiator, +}; + +static void s_destroy_tunneling_ntlm_strategy(struct aws_http_proxy_strategy *proxy_strategy) { + struct aws_http_proxy_strategy_tunneling_ntlm *ntlm_strategy = proxy_strategy->impl; + + aws_mem_release(ntlm_strategy->allocator, ntlm_strategy); +} + +struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_tunneling_ntlm( struct aws_allocator *allocator, - struct aws_http_proxy_strategy_factory_tunneling_kerberos_options *config) { + struct aws_http_proxy_strategy_tunneling_ntlm_options *config) { - if (allocator == NULL || config == NULL) { + if (allocator == NULL || config == NULL || config->get_challenge_token == NULL) { aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); return NULL; } - struct aws_http_proxy_strategy_factory_tunneling_kerberos *kerberos_factory = - aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_strategy_factory_tunneling_kerberos)); - if (kerberos_factory == NULL) { + struct aws_http_proxy_strategy_tunneling_ntlm *ntlm_strategy = + aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_strategy_tunneling_ntlm)); + if (ntlm_strategy == NULL) { return NULL; } - kerberos_factory->factory_base.impl = kerberos_factory; - kerberos_factory->factory_base.vtable = &s_tunneling_kerberos_factory_vtable; - kerberos_factory->factory_base.proxy_connection_type = AWS_HPCT_HTTP_TUNNEL; - kerberos_factory->allocator = allocator; + ntlm_strategy->strategy_base.impl = ntlm_strategy; + ntlm_strategy->strategy_base.vtable = &s_tunneling_ntlm_strategy_vtable; + ntlm_strategy->strategy_base.proxy_connection_type = AWS_HPCT_HTTP_TUNNEL; + + ntlm_strategy->allocator = allocator; aws_ref_count_init( - &kerberos_factory->factory_base.ref_count, - &kerberos_factory->factory_base, - (aws_simple_completion_callback *)s_destroy_tunneling_kerberos_factory); + &ntlm_strategy->strategy_base.ref_count, + &ntlm_strategy->strategy_base, + (aws_simple_completion_callback *)s_destroy_tunneling_ntlm_strategy); - /* SA-TBI: any other factory init here */ + ntlm_strategy->get_challenge_token = config->get_challenge_token; + ntlm_strategy->get_challenge_token_user_data = config->get_challenge_token_user_data; - return &kerberos_factory->factory_base; + return &ntlm_strategy->strategy_base; } /******************************************************************************************************************/ -AWS_HTTP_API -struct aws_http_proxy_strategy_factory *aws_http_proxy_strategy_factory_new_tunneling_adaptive_kerberos( +#define PROXY_STRATEGY_MAX_ADAPTIVE_STRATEGIES 3 + +struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_tunneling_adaptive( struct aws_allocator *allocator, - struct aws_http_proxy_strategy_factory_tunneling_adaptive_kerberos_options *config) { + struct aws_http_proxy_strategy_tunneling_adaptive_options *config) { if (allocator == NULL || config == NULL) { aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); return NULL; } - struct aws_http_proxy_strategy_factory *identity_factory = NULL; - struct aws_http_proxy_strategy_factory *kerberos_factory = NULL; - struct aws_http_proxy_strategy_factory *adaptive_factory = NULL; + struct aws_http_proxy_strategy *strategies[PROXY_STRATEGY_MAX_ADAPTIVE_STRATEGIES]; + + uint32_t strategy_count = 0; + struct aws_http_proxy_strategy *identity_strategy = NULL; + struct aws_http_proxy_strategy *kerberos_strategy = NULL; + struct aws_http_proxy_strategy *ntlm_strategy = NULL; + struct aws_http_proxy_strategy *adaptive_chain_strategy = NULL; - identity_factory = aws_http_proxy_strategy_factory_new_tunneling_one_time_identity(allocator); - if (identity_factory == NULL) { + identity_strategy = aws_http_proxy_strategy_new_tunneling_one_time_identity(allocator); + if (identity_strategy == NULL) { goto on_error; } + strategies[strategy_count++] = identity_strategy; - kerberos_factory = aws_http_proxy_strategy_factory_new_tunneling_kerberos(allocator, &config->kerberos_options); - if (kerberos_factory == NULL) { - goto on_error; + if (config->kerberos_options != NULL) { + kerberos_strategy = aws_http_proxy_strategy_new_tunneling_kerberos(allocator, config->kerberos_options); + if (kerberos_strategy == NULL) { + goto on_error; + } + + strategies[strategy_count++] = kerberos_strategy; } - struct aws_http_proxy_strategy_factory *factory_array[2] = { - identity_factory, - kerberos_factory, - }; + if (config->ntlm_options != NULL) { + ntlm_strategy = aws_http_proxy_strategy_new_tunneling_ntlm(allocator, config->ntlm_options); + if (ntlm_strategy == NULL) { + goto on_error; + } + + strategies[strategy_count++] = ntlm_strategy; + } - struct aws_http_proxy_strategy_factory_tunneling_chain_options chain_config = { - .factories = factory_array, - .factory_count = 2, + struct aws_http_proxy_strategy_tunneling_chain_options chain_config = { + .strategies = strategies, + .strategy_count = strategy_count, }; - adaptive_factory = aws_http_proxy_strategy_factory_new_tunneling_chain(allocator, &chain_config); - if (adaptive_factory == NULL) { + adaptive_chain_strategy = aws_http_proxy_strategy_new_tunneling_chain(allocator, &chain_config); + if (adaptive_chain_strategy == NULL) { goto on_error; } - return adaptive_factory; + return adaptive_chain_strategy; on_error: - aws_http_proxy_strategy_factory_release(identity_factory); - aws_http_proxy_strategy_factory_release(kerberos_factory); - aws_http_proxy_strategy_factory_release(adaptive_factory); + aws_http_proxy_strategy_release(identity_strategy); + aws_http_proxy_strategy_release(kerberos_strategy); + aws_http_proxy_strategy_release(ntlm_strategy); + aws_http_proxy_strategy_release(adaptive_chain_strategy); return NULL; } diff --git a/tests/proxy_test_helper.c b/tests/proxy_test_helper.c index ffc8149a3..6ffe33b40 100644 --- a/tests/proxy_test_helper.c +++ b/tests/proxy_test_helper.c @@ -199,7 +199,7 @@ int proxy_tester_clean_up(struct proxy_tester *tester) { aws_tls_ctx_options_clean_up(&tester->tls_ctx_options); } - aws_http_proxy_strategy_factory_release(tester->proxy_options.proxy_strategy_factory); + aws_http_proxy_strategy_release(tester->proxy_options.proxy_strategy); aws_http_library_clean_up(); diff --git a/tests/test_proxy.c b/tests/test_proxy.c index 64f446eab..f6e904e2e 100644 --- a/tests/test_proxy.c +++ b/tests/test_proxy.c @@ -186,13 +186,13 @@ static int s_setup_proxy_test( }; if (use_basic_auth) { - struct aws_http_proxy_strategy_factory_basic_auth_config config = { + struct aws_http_proxy_strategy_basic_auth_options config = { .proxy_connection_type = AWS_HPCT_HTTP_FORWARD, .user_name = aws_byte_cursor_from_string(s_mock_request_username), .password = aws_byte_cursor_from_string(s_mock_request_password), }; - proxy_options.proxy_strategy_factory = aws_http_proxy_strategy_factory_new_basic_auth(allocator, &config); + proxy_options.proxy_strategy = aws_http_proxy_strategy_new_basic_auth(allocator, &config); } struct proxy_tester_options options = { From b139f3edd1e4c215c1b1ea77dcc446b5af28882b Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Thu, 21 Jan 2021 16:10:50 -0800 Subject: [PATCH 28/54] Kerberos happy path test; proxy tester updates --- tests/CMakeLists.txt | 1 + tests/proxy_test_helper.c | 118 +++++++++++++++++++++++++++++- tests/proxy_test_helper.h | 8 ++ tests/test_proxy.c | 150 ++++++++++++++++++++++++++++---------- 4 files changed, 237 insertions(+), 40 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 5152b9df5..03d0ee70a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -494,6 +494,7 @@ add_test_case(test_http_forwarding_proxy_connection_channel_failure) add_test_case(test_http_forwarding_proxy_connection_connect_failure) add_test_case(test_http_forwarding_proxy_request_transform) add_test_case(test_http_forwarding_proxy_request_transform_basic_auth) +add_test_case(test_http_forwarding_proxy_request_transform_kerberos) add_test_case(test_http_forwarding_proxy_uri_rewrite) add_test_case(test_http_forwarding_proxy_uri_rewrite_options_star) add_test_case(test_http_tunnel_proxy_connection_success) diff --git a/tests/proxy_test_helper.c b/tests/proxy_test_helper.c index 6ffe33b40..71fe981bc 100644 --- a/tests/proxy_test_helper.c +++ b/tests/proxy_test_helper.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -104,6 +105,22 @@ int proxy_tester_init(struct proxy_tester *tester, const struct proxy_tester_opt ASSERT_SUCCESS(aws_mutex_init(&tester->wait_lock)); ASSERT_SUCCESS(aws_condition_variable_init(&tester->wait_cvar)); + ASSERT_SUCCESS( + aws_array_list_init_dynamic(&tester->connect_requests, tester->alloc, 1, sizeof(struct aws_http_message *))); + + int connect_response_count = 1; + if (options->desired_connect_response_count > connect_response_count) { + connect_response_count = options->desired_connect_response_count; + } + ASSERT_SUCCESS(aws_array_list_init_dynamic( + &tester->desired_connect_responses, tester->alloc, connect_response_count, sizeof(struct aws_string *))); + + for (size_t i = 0; i < options->desired_connect_response_count; ++i) { + struct aws_byte_cursor response_cursor = options->desired_connect_responses[i]; + struct aws_string *response = aws_string_new_from_cursor(tester->alloc, &response_cursor); + ASSERT_SUCCESS(aws_array_list_push_back(&tester->desired_connect_responses, response)); + } + tester->event_loop_group = aws_event_loop_group_new_default(tester->alloc, 1, NULL); struct aws_host_resolver_default_options resolver_options = { @@ -199,7 +216,23 @@ int proxy_tester_clean_up(struct proxy_tester *tester) { aws_tls_ctx_options_clean_up(&tester->tls_ctx_options); } - aws_http_proxy_strategy_release(tester->proxy_options.proxy_strategy); + size_t connect_request_count = aws_array_list_length(&tester->connect_requests); + for (size_t i = 0; i < connect_request_count; ++i) { + struct aws_http_message *request = NULL; + + aws_array_list_get_at(&tester->connect_requests, &request, i); + aws_http_message_release(request); + } + aws_array_list_clean_up(&tester->connect_requests); + + size_t connect_response_count = aws_array_list_length(&tester->desired_connect_responses); + for (size_t i = 0; i < connect_response_count; ++i) { + struct aws_string *response = NULL; + + aws_array_list_get_at(&tester->desired_connect_responses, &response, i); + aws_string_destroy(response); + } + aws_array_list_clean_up(&tester->desired_connect_responses); aws_http_library_clean_up(); @@ -250,6 +283,78 @@ int proxy_tester_create_testing_channel_connection(struct proxy_tester *tester) return AWS_OP_SUCCESS; } +bool s_line_feed_predicate(uint8_t value) { + return value == '\r'; +} + +/* + * A very crude, sloppy http request parser that does just enough to test what we want to test + */ +static int s_record_connect_request(struct aws_byte_buf *request_buffer, struct proxy_tester *tester) { + struct aws_byte_cursor request_cursor = aws_byte_cursor_from_buf(request_buffer); + + struct aws_array_list lines; + ASSERT_SUCCESS(aws_array_list_init_dynamic(&lines, tester->alloc, 10, sizeof(struct aws_byte_cursor))); + aws_byte_cursor_split_on_char(&request_cursor, '\n', &lines); + + size_t line_count = aws_array_list_length(&lines); + ASSERT_TRUE(line_count > 1); + + struct aws_http_message *message = aws_http_message_new_request(tester->alloc); + + struct aws_byte_cursor first_line_cursor; + AWS_ZERO_STRUCT(first_line_cursor); + aws_array_list_get_at(&lines, &first_line_cursor, 0); + first_line_cursor = aws_byte_cursor_trim_pred(&first_line_cursor, s_line_feed_predicate); + + struct aws_byte_cursor method_cursor; + AWS_ZERO_STRUCT(method_cursor); + aws_byte_cursor_next_split(&first_line_cursor, ' ', &method_cursor); + + aws_http_message_set_request_method(message, method_cursor); + + aws_byte_cursor_advance(&first_line_cursor, method_cursor.len + 1); + + struct aws_byte_cursor uri_cursor; + AWS_ZERO_STRUCT(uri_cursor); + aws_byte_cursor_next_split(&first_line_cursor, ' ', &uri_cursor); + + aws_http_message_set_request_path(message, uri_cursor); + + for (size_t i = 1; i < line_count; ++i) { + struct aws_byte_cursor line_cursor; + AWS_ZERO_STRUCT(line_cursor); + aws_array_list_get_at(&lines, &line_cursor, i); + line_cursor = aws_byte_cursor_trim_pred(&line_cursor, s_line_feed_predicate); + + if (line_cursor.len == 0) { + break; + } + + struct aws_byte_cursor name_cursor; + AWS_ZERO_STRUCT(name_cursor); + aws_byte_cursor_next_split(&line_cursor, ':', &name_cursor); + + aws_byte_cursor_advance(&line_cursor, name_cursor.len + 1); + line_cursor = aws_byte_cursor_trim_pred(&line_cursor, aws_isspace); + + struct aws_http_header header = { + .name = name_cursor, + .value = line_cursor, + }; + + aws_http_message_add_header(message, header); + } + + /* we don't care about the body */ + + aws_array_list_push_back(&tester->connect_requests, &message); + + aws_array_list_clean_up(&lines); + + return AWS_OP_SUCCESS; +} + int proxy_tester_verify_connect_request(struct proxy_tester *tester) { struct aws_byte_buf output; ASSERT_SUCCESS(aws_byte_buf_init(&output, tester->alloc, 1024)); @@ -274,6 +379,8 @@ int proxy_tester_verify_connect_request(struct proxy_tester *tester) { ASSERT_TRUE(aws_byte_cursor_eq(&first_line_cursor, &expected_connect_message_first_line_cursor)); + ASSERT_SUCCESS(s_record_connect_request(&output, tester)); + aws_byte_buf_clean_up(&output); return AWS_OP_SUCCESS; @@ -283,7 +390,14 @@ int proxy_tester_send_connect_response(struct proxy_tester *tester) { (void)tester; const char *response_string = NULL; - if (tester->failure_type == PTFT_CONNECT_REQUEST) { + + size_t desired_response_count = aws_array_list_length(&tester->desired_connect_responses); + if (desired_response_count > 0) { + struct aws_string *response = NULL; + aws_array_list_get_at(&tester->desired_connect_responses, &response, tester->current_response_index++); + response_string = (const char *)response->bytes; + + } else if (tester->failure_type == PTFT_CONNECT_REQUEST) { response_string = "HTTP/1.0 401 Unauthorized\r\n\r\n"; } else { /* adding close here because it's an edge case we need to exercise. The desired behavior is that it has diff --git a/tests/proxy_test_helper.h b/tests/proxy_test_helper.h index fbbb7faeb..a512d0f2e 100644 --- a/tests/proxy_test_helper.h +++ b/tests/proxy_test_helper.h @@ -39,6 +39,9 @@ struct proxy_tester_options { uint16_t port; enum proxy_tester_test_mode test_mode; enum proxy_tester_failure_type failure_type; + + uint32_t desired_connect_response_count; + struct aws_byte_cursor *desired_connect_responses; }; struct proxy_tester { @@ -77,6 +80,11 @@ struct proxy_tester { struct aws_byte_buf connection_host_name; uint16_t connection_port; + + struct aws_array_list connect_requests; + + uint32_t current_response_index; + struct aws_array_list desired_connect_responses; }; int proxy_tester_wait(struct proxy_tester *tester, bool (*pred)(void *user_data)); diff --git a/tests/test_proxy.c b/tests/test_proxy.c index f6e904e2e..12c639b2a 100644 --- a/tests/test_proxy.c +++ b/tests/test_proxy.c @@ -29,8 +29,8 @@ static uint16_t s_proxy_port = 777; AWS_STATIC_STRING_FROM_LITERAL(s_mock_request_method, "GET"); AWS_STATIC_STRING_FROM_LITERAL(s_mock_request_path, "/"); AWS_STATIC_STRING_FROM_LITERAL(s_mock_request_host, "aws.amazon.com"); -AWS_STATIC_STRING_FROM_LITERAL(s_expected_auth_header_name, "Proxy-Authorization"); -AWS_STATIC_STRING_FROM_LITERAL(s_expected_auth_header_value, "Basic U29tZVVzZXI6U3VwZXJTZWNyZXQ="); +AWS_STATIC_STRING_FROM_LITERAL(s_expected_basic_auth_header_name, "Proxy-Authorization"); +AWS_STATIC_STRING_FROM_LITERAL(s_expected_basic_auth_header_value, "Basic U29tZVVzZXI6U3VwZXJTZWNyZXQ="); AWS_STATIC_STRING_FROM_LITERAL(s_mock_request_username, "SomeUser"); AWS_STATIC_STRING_FROM_LITERAL(s_mock_request_password, "SuperSecret"); @@ -174,7 +174,7 @@ static int s_setup_proxy_test( struct aws_allocator *allocator, enum proxy_tester_test_mode test_mode, enum proxy_tester_failure_type failure_type, - bool use_basic_auth) { + struct aws_http_proxy_strategy *proxy_strategy) { aws_http_connection_set_system_vtable(&s_proxy_connection_system_vtable); aws_http_proxy_system_set_vtable(&s_proxy_table_for_tls); @@ -183,18 +183,9 @@ static int s_setup_proxy_test( .connection_type = (test_mode == PTTM_HTTP_FORWARD) ? AWS_HPCT_HTTP_FORWARD : AWS_HPCT_HTTP_TUNNEL, .host = aws_byte_cursor_from_c_str(s_proxy_host_name), .port = s_proxy_port, + .proxy_strategy = proxy_strategy, }; - if (use_basic_auth) { - struct aws_http_proxy_strategy_basic_auth_options config = { - .proxy_connection_type = AWS_HPCT_HTTP_FORWARD, - .user_name = aws_byte_cursor_from_string(s_mock_request_username), - .password = aws_byte_cursor_from_string(s_mock_request_password), - }; - - proxy_options.proxy_strategy = aws_http_proxy_strategy_new_basic_auth(allocator, &config); - } - struct proxy_tester_options options = { .alloc = allocator, .proxy_options = &proxy_options, @@ -218,7 +209,7 @@ static int s_setup_proxy_test( static int s_test_http_forwarding_proxy_connection_proxy_target(struct aws_allocator *allocator, void *ctx) { (void)ctx; - ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTP_FORWARD, PTFT_NONE, false)); + ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTP_FORWARD, PTFT_NONE, NULL)); ASSERT_SUCCESS(proxy_tester_verify_connection_attempt_was_to_proxy( &tester, aws_byte_cursor_from_c_str(s_proxy_host_name), s_proxy_port)); @@ -236,7 +227,7 @@ AWS_TEST_CASE(test_http_forwarding_proxy_connection_proxy_target, s_test_http_fo static int s_test_http_forwarding_proxy_connection_channel_failure(struct aws_allocator *allocator, void *ctx) { (void)ctx; - ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTP_FORWARD, PTFT_CHANNEL, false)); + ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTP_FORWARD, PTFT_CHANNEL, NULL)); ASSERT_SUCCESS(proxy_tester_verify_connection_attempt_was_to_proxy( &tester, aws_byte_cursor_from_c_str(s_proxy_host_name), s_proxy_port)); @@ -258,7 +249,7 @@ AWS_TEST_CASE( static int s_test_http_forwarding_proxy_connection_connect_failure(struct aws_allocator *allocator, void *ctx) { (void)ctx; - ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTP_FORWARD, PTFT_CONNECTION, false)); + ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTP_FORWARD, PTFT_CONNECTION, NULL)); ASSERT_SUCCESS(proxy_tester_verify_connection_attempt_was_to_proxy( &tester, aws_byte_cursor_from_c_str(s_proxy_host_name), s_proxy_port)); @@ -280,7 +271,7 @@ AWS_TEST_CASE( static int s_test_https_tunnel_proxy_connection_success(struct aws_allocator *allocator, void *ctx) { (void)ctx; - ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTPS_TUNNEL, PTFT_NONE, false)); + ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTPS_TUNNEL, PTFT_NONE, NULL)); ASSERT_SUCCESS(proxy_tester_verify_connection_attempt_was_to_proxy( &tester, aws_byte_cursor_from_c_str(s_proxy_host_name), s_proxy_port)); @@ -300,7 +291,7 @@ AWS_TEST_CASE(test_https_tunnel_proxy_connection_success, s_test_https_tunnel_pr static int s_test_http_tunnel_proxy_connection_success(struct aws_allocator *allocator, void *ctx) { (void)ctx; - ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTP_TUNNEL, PTFT_NONE, false)); + ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTP_TUNNEL, PTFT_NONE, NULL)); ASSERT_SUCCESS(proxy_tester_verify_connection_attempt_was_to_proxy( &tester, aws_byte_cursor_from_c_str(s_proxy_host_name), s_proxy_port)); @@ -320,7 +311,7 @@ AWS_TEST_CASE(test_http_tunnel_proxy_connection_success, s_test_http_tunnel_prox static int s_test_https_tunnel_proxy_connection_failure_connect(struct aws_allocator *allocator, void *ctx) { (void)ctx; - ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTPS_TUNNEL, PTFT_CONNECT_REQUEST, false)); + ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTPS_TUNNEL, PTFT_CONNECT_REQUEST, NULL)); ASSERT_SUCCESS(proxy_tester_verify_connection_attempt_was_to_proxy( &tester, aws_byte_cursor_from_c_str(s_proxy_host_name), s_proxy_port)); @@ -340,7 +331,7 @@ AWS_TEST_CASE(test_https_tunnel_proxy_connection_failure_connect, s_test_https_t static int s_test_http_tunnel_proxy_connection_failure_connect(struct aws_allocator *allocator, void *ctx) { (void)ctx; - ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTP_TUNNEL, PTFT_CONNECT_REQUEST, false)); + ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTP_TUNNEL, PTFT_CONNECT_REQUEST, NULL)); ASSERT_SUCCESS(proxy_tester_verify_connection_attempt_was_to_proxy( &tester, aws_byte_cursor_from_c_str(s_proxy_host_name), s_proxy_port)); @@ -360,7 +351,7 @@ AWS_TEST_CASE(test_http_tunnel_proxy_connection_failure_connect, s_test_http_tun static int s_test_https_tunnel_proxy_connection_failure_tls(struct aws_allocator *allocator, void *ctx) { (void)ctx; - ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTPS_TUNNEL, PTFT_TLS_NEGOTIATION, false)); + ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTPS_TUNNEL, PTFT_TLS_NEGOTIATION, NULL)); ASSERT_SUCCESS(proxy_tester_verify_connection_attempt_was_to_proxy( &tester, aws_byte_cursor_from_c_str(s_proxy_host_name), s_proxy_port)); @@ -376,7 +367,6 @@ AWS_TEST_CASE(test_https_tunnel_proxy_connection_failure_tls, s_test_https_tunne static int s_verify_transformed_request( struct aws_http_message *untransformed_request, struct aws_http_message *transformed_request, - bool used_basic_auth, struct aws_allocator *allocator) { /* method shouldn't change */ @@ -409,31 +399,23 @@ static int s_verify_transformed_request( /* all old headers should still be present */ size_t untransformed_header_count = aws_http_message_get_header_count(untransformed_request); - ASSERT_TRUE( - untransformed_header_count + (used_basic_auth ? 1 : 0) == - aws_http_message_get_header_count(transformed_request)); for (size_t i = 0; i < untransformed_header_count; ++i) { struct aws_http_header header; ASSERT_SUCCESS(aws_http_message_get_header(untransformed_request, &header, i)); ASSERT_TRUE(s_is_header_in_request(transformed_request, &header)); } - /* auth header should be present if basic auth used */ - if (used_basic_auth) { - struct aws_http_header auth_header; - auth_header.name = aws_byte_cursor_from_string(s_expected_auth_header_name); - auth_header.value = aws_byte_cursor_from_string(s_expected_auth_header_value); - ASSERT_TRUE(s_is_header_in_request(transformed_request, &auth_header)); - } - aws_uri_clean_up(&uri); return AWS_OP_SUCCESS; } -static int s_do_http_forwarding_proxy_request_transform_test(struct aws_allocator *allocator, bool use_basic_auth) { +static int s_do_http_forwarding_proxy_request_transform_test( + struct aws_allocator *allocator, + struct aws_http_proxy_strategy *proxy_strategy, + int (*transformed_request_verifier_fn)(struct aws_http_message *)) { - ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTP_FORWARD, PTFT_NONE, use_basic_auth)); + ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTP_FORWARD, PTFT_NONE, proxy_strategy)); struct aws_http_message *untransformed_request = s_build_http_request(allocator); struct aws_http_message *request = s_build_http_request(allocator); @@ -450,7 +432,11 @@ static int s_do_http_forwarding_proxy_request_transform_test(struct aws_allocato testing_channel_run_currently_queued_tasks(tester.testing_channel); - s_verify_transformed_request(untransformed_request, request, use_basic_auth, allocator); + s_verify_transformed_request(untransformed_request, request, allocator); + + if (transformed_request_verifier_fn != NULL) { + ASSERT_SUCCESS(transformed_request_verifier_fn(request)); + } /* double release the stream because the dummy connection doesn't actually process (and release) it */ aws_http_stream_release(stream); @@ -469,19 +455,40 @@ static int s_do_http_forwarding_proxy_request_transform_test(struct aws_allocato static int s_test_http_forwarding_proxy_request_transform(struct aws_allocator *allocator, void *ctx) { (void)ctx; - ASSERT_SUCCESS(s_do_http_forwarding_proxy_request_transform_test(allocator, false)); + ASSERT_SUCCESS(s_do_http_forwarding_proxy_request_transform_test(allocator, NULL, NULL)); return AWS_OP_SUCCESS; } AWS_TEST_CASE(test_http_forwarding_proxy_request_transform, s_test_http_forwarding_proxy_request_transform); +static int s_check_for_basic_auth_header(struct aws_http_message *transformed_request) { + /* Check for basic auth header */ + struct aws_http_header auth_header; + auth_header.name = aws_byte_cursor_from_string(s_expected_basic_auth_header_name); + auth_header.value = aws_byte_cursor_from_string(s_expected_basic_auth_header_value); + ASSERT_TRUE(s_is_header_in_request(transformed_request, &auth_header)); + + return AWS_OP_SUCCESS; +} + /* * If we do pass in proxy options, verify requests get properly transformed with basic authentication */ static int s_test_http_forwarding_proxy_request_transform_basic_auth(struct aws_allocator *allocator, void *ctx) { (void)ctx; - ASSERT_SUCCESS(s_do_http_forwarding_proxy_request_transform_test(allocator, true)); + struct aws_http_proxy_strategy_basic_auth_options config = { + .proxy_connection_type = AWS_HPCT_HTTP_FORWARD, + .user_name = aws_byte_cursor_from_string(s_mock_request_username), + .password = aws_byte_cursor_from_string(s_mock_request_password), + }; + + struct aws_http_proxy_strategy *proxy_strategy = aws_http_proxy_strategy_new_basic_auth(allocator, &config); + + ASSERT_SUCCESS( + s_do_http_forwarding_proxy_request_transform_test(allocator, proxy_strategy, s_check_for_basic_auth_header)); + + aws_http_proxy_strategy_release(proxy_strategy); return AWS_OP_SUCCESS; } @@ -489,6 +496,73 @@ AWS_TEST_CASE( test_http_forwarding_proxy_request_transform_basic_auth, s_test_http_forwarding_proxy_request_transform_basic_auth); +AWS_STATIC_STRING_FROM_LITERAL(s_mock_kerberos_token_value, "abcdefABCDEF123"); + +static int s_mock_aws_http_proxy_negotiation_kerberos_get_token_sync_fn( + void *user_data, + struct aws_byte_cursor *out_token_value, + int *out_error_code) { + + (void)user_data; + + *out_error_code = AWS_ERROR_SUCCESS; + *out_token_value = aws_byte_cursor_from_string(s_mock_kerberos_token_value); + + return AWS_OP_SUCCESS; +} + +AWS_STATIC_STRING_FROM_LITERAL(s_expected_kerberos_auth_header_name, "Proxy-Authorization"); +AWS_STATIC_STRING_FROM_LITERAL(s_expected_kerberos_auth_header_value, "Negotiate abcdefABCDEF123"); + +static int s_verify_kerberos_connect_request(struct aws_http_message *request) { + /* Check for basic auth header */ + struct aws_http_header auth_header; + auth_header.name = aws_byte_cursor_from_string(s_expected_kerberos_auth_header_name); + auth_header.value = aws_byte_cursor_from_string(s_expected_kerberos_auth_header_value); + ASSERT_TRUE(s_is_header_in_request(request, &auth_header)); + + return AWS_OP_SUCCESS; +} + +/* + * Verify requests get properly transformed with kerberos strategy + */ +static int s_test_http_forwarding_proxy_request_transform_kerberos(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + struct aws_http_proxy_strategy_tunneling_kerberos_options config = { + .get_token = s_mock_aws_http_proxy_negotiation_kerberos_get_token_sync_fn, + .get_token_user_data = NULL, + }; + + struct aws_http_proxy_strategy *kerberos_strategy = + aws_http_proxy_strategy_new_tunneling_kerberos(allocator, &config); + + ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTP_TUNNEL, PTFT_NONE, kerberos_strategy)); + + ASSERT_SUCCESS(proxy_tester_verify_connection_attempt_was_to_proxy( + &tester, aws_byte_cursor_from_c_str(s_proxy_host_name), s_proxy_port)); + ASSERT_TRUE(tester.client_connection != NULL); + ASSERT_TRUE(tester.wait_result == AWS_ERROR_SUCCESS); + + ASSERT_INT_EQUALS(1, aws_array_list_length(&tester.connect_requests)); + + struct aws_http_message *connect_request = NULL; + aws_array_list_get_at(&tester.connect_requests, &connect_request, 0); + + ASSERT_SUCCESS(s_verify_kerberos_connect_request(connect_request)); + + aws_http_proxy_strategy_release(kerberos_strategy); + + ASSERT_SUCCESS(proxy_tester_clean_up(&tester)); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE( + test_http_forwarding_proxy_request_transform_kerberos, + s_test_http_forwarding_proxy_request_transform_kerberos); + AWS_STATIC_STRING_FROM_LITERAL(s_rewrite_host, "www.uri.com"); AWS_STATIC_STRING_FROM_LITERAL(s_rewrite_path, "/main/index.html?foo=bar"); AWS_STATIC_STRING_FROM_LITERAL(s_expected_rewritten_path, "http://www.uri.com:80/main/index.html?foo=bar"); From 42f3493266923536366475fe90f3c91e9b006192 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Thu, 21 Jan 2021 19:17:12 -0800 Subject: [PATCH 29/54] kerberos failure tests --- include/aws/http/http.h | 3 +- source/http.c | 5 +-- source/proxy_connection.c | 7 ++- source/proxy_strategy.c | 4 +- tests/CMakeLists.txt | 4 +- tests/proxy_test_helper.h | 1 + tests/test_proxy.c | 89 +++++++++++++++++++++++++++++++++++---- 7 files changed, 93 insertions(+), 20 deletions(-) diff --git a/include/aws/http/http.h b/include/aws/http/http.h index fc7b9dd55..c95748757 100644 --- a/include/aws/http/http.h +++ b/include/aws/http/http.h @@ -36,7 +36,7 @@ enum aws_http_errors { AWS_ERROR_HTTP_CONNECTION_MANAGER_INVALID_STATE_FOR_ACQUIRE, AWS_ERROR_HTTP_CONNECTION_MANAGER_VENDED_CONNECTION_UNDERFLOW, AWS_ERROR_HTTP_SERVER_CLOSED, - AWS_ERROR_HTTP_PROXY_TLS_CONNECT_FAILED, + AWS_ERROR_HTTP_PROXY_CONNECT_FAILED, AWS_ERROR_HTTP_CONNECTION_MANAGER_SHUTTING_DOWN, AWS_ERROR_HTTP_CHANNEL_THROUGHPUT_FAILURE, AWS_ERROR_HTTP_PROTOCOL_ERROR, @@ -47,7 +47,6 @@ enum aws_http_errors { AWS_ERROR_HTTP_STREAM_NOT_ACTIVATED, AWS_ERROR_HTTP_STREAM_HAS_COMPLETED, AWS_ERROR_HTTP_PROXY_STRATEGY_TRANSFORM_FAILED, - AWS_ERROR_HTTP_PROXY_STRATEGY_FAILED_PREVIOUSLY, AWS_ERROR_HTTP_PROXY_STRATEGY_NTLM_CHALLENGE_TOKEN_MISSING, AWS_ERROR_HTTP_END_RANGE = AWS_ERROR_ENUM_END_RANGE(AWS_C_HTTP_PACKAGE_ID) diff --git a/source/http.c b/source/http.c index ba0053c51..ae7a5b65a 100644 --- a/source/http.c +++ b/source/http.c @@ -86,7 +86,7 @@ static struct aws_error_info s_errors[] = { AWS_ERROR_HTTP_SERVER_CLOSED, "The http server is closed, no more connections will be accepted"), AWS_DEFINE_ERROR_INFO_HTTP( - AWS_ERROR_HTTP_PROXY_TLS_CONNECT_FAILED, + AWS_ERROR_HTTP_PROXY_CONNECT_FAILED, "Proxy tls connection establishment failed because the CONNECT call failed"), AWS_DEFINE_ERROR_INFO_HTTP( AWS_ERROR_HTTP_CONNECTION_MANAGER_SHUTTING_DOWN, @@ -118,9 +118,6 @@ static struct aws_error_info s_errors[] = { AWS_DEFINE_ERROR_INFO_HTTP( AWS_ERROR_HTTP_PROXY_STRATEGY_TRANSFORM_FAILED, "Proxy strategy transform has completely failed."), - AWS_DEFINE_ERROR_INFO_HTTP( - AWS_ERROR_HTTP_PROXY_STRATEGY_FAILED_PREVIOUSLY, - "Proxy strategy was previously tried and failed"), AWS_DEFINE_ERROR_INFO_HTTP( AWS_ERROR_HTTP_PROXY_STRATEGY_NTLM_CHALLENGE_TOKEN_MISSING, "NTLM Proxy strategy was initiated without a challenge token"), diff --git a/source/proxy_connection.c b/source/proxy_connection.c index e3183e83e..08457ea05 100644 --- a/source/proxy_connection.c +++ b/source/proxy_connection.c @@ -200,8 +200,11 @@ static void s_aws_http_proxy_user_data_shutdown(struct aws_http_proxy_user_data user_data->connect_request = NULL; } - aws_http_connection_release(user_data->connection); + struct aws_http_connection *http_connection = user_data->connection; user_data->connection = NULL; + + aws_channel_shutdown(http_connection->channel_slot->channel, user_data->error_code); + aws_http_connection_release(http_connection); } /* @@ -336,7 +339,7 @@ static int s_aws_http_on_incoming_header_block_done_tunnel_proxy( "(%p) Proxy CONNECT request failed with status code %d", (void *)context->connection, status); - context->error_code = AWS_ERROR_HTTP_PROXY_TLS_CONNECT_FAILED; + context->error_code = AWS_ERROR_HTTP_PROXY_CONNECT_FAILED; } aws_http_proxy_negotiator_connect_status_fn *on_status = diff --git a/source/proxy_strategy.c b/source/proxy_strategy.c index fee53b1b0..7763efd5b 100644 --- a/source/proxy_strategy.c +++ b/source/proxy_strategy.c @@ -964,7 +964,7 @@ static void s_kerberos_tunnel_transform_connect( AWS_ZERO_STRUCT(kerberos_token); if (kerberos_negotiator->connect_state == AWS_PNCS_FAILURE) { - error_code = AWS_ERROR_HTTP_PROXY_STRATEGY_FAILED_PREVIOUSLY; + error_code = AWS_ERROR_HTTP_PROXY_CONNECT_FAILED; goto done; } @@ -1223,7 +1223,7 @@ static void s_ntlm_tunnel_transform_connect( AWS_ZERO_STRUCT(challenge_token); if (ntlm_negotiator->connect_state == AWS_PNCS_FAILURE) { - error_code = AWS_ERROR_HTTP_PROXY_STRATEGY_FAILED_PREVIOUSLY; + error_code = AWS_ERROR_HTTP_PROXY_CONNECT_FAILED; goto done; } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 03d0ee70a..e60423db9 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -494,7 +494,9 @@ add_test_case(test_http_forwarding_proxy_connection_channel_failure) add_test_case(test_http_forwarding_proxy_connection_connect_failure) add_test_case(test_http_forwarding_proxy_request_transform) add_test_case(test_http_forwarding_proxy_request_transform_basic_auth) -add_test_case(test_http_forwarding_proxy_request_transform_kerberos) +add_test_case(test_http_proxy_request_transform_kerberos) +add_test_case(test_http_proxy_kerberos_token_failure) +add_test_case(test_http_proxy_kerberos_connect_failure) add_test_case(test_http_forwarding_proxy_uri_rewrite) add_test_case(test_http_forwarding_proxy_uri_rewrite_options_star) add_test_case(test_http_tunnel_proxy_connection_success) diff --git a/tests/proxy_test_helper.h b/tests/proxy_test_helper.h index a512d0f2e..38a4e45b5 100644 --- a/tests/proxy_test_helper.h +++ b/tests/proxy_test_helper.h @@ -30,6 +30,7 @@ enum proxy_tester_failure_type { PTFT_TLS_NEGOTIATION, PTFT_CHANNEL, PTFT_CONNECTION, + PTFT_PROXY_STRATEGY, }; struct proxy_tester_options { diff --git a/tests/test_proxy.c b/tests/test_proxy.c index 12c639b2a..47b3fdf42 100644 --- a/tests/test_proxy.c +++ b/tests/test_proxy.c @@ -152,12 +152,19 @@ static int s_test_aws_proxy_new_socket_channel(struct aws_socket_channel_bootstr struct aws_http_client_bootstrap *http_bootstrap = channel_options->user_data; http_bootstrap->on_setup(tester.client_connection, AWS_ERROR_SUCCESS, http_bootstrap->user_data); - testing_channel_run_currently_queued_tasks(tester.testing_channel); + if (tester.failure_type == PTFT_PROXY_STRATEGY) { + testing_channel_drain_queued_tasks(tester.testing_channel); + } else { + testing_channel_run_currently_queued_tasks(tester.testing_channel); + } - if (tester.proxy_options.connection_type == AWS_HPCT_HTTP_TUNNEL) { - /* For tunnel proxies, send the CONNECT request and response */ - ASSERT_SUCCESS(proxy_tester_verify_connect_request(&tester)); - ASSERT_SUCCESS(proxy_tester_send_connect_response(&tester)); + if (tester.failure_type == PTFT_NONE || tester.failure_type == PTFT_CONNECT_REQUEST || + tester.failure_type == PTFT_TLS_NEGOTIATION) { + if (tester.proxy_options.connection_type == AWS_HPCT_HTTP_TUNNEL) { + /* For tunnel proxies, send the CONNECT request and response */ + ASSERT_SUCCESS(proxy_tester_verify_connect_request(&tester)); + ASSERT_SUCCESS(proxy_tester_send_connect_response(&tester)); + } } return AWS_OP_SUCCESS; @@ -527,7 +534,7 @@ static int s_verify_kerberos_connect_request(struct aws_http_message *request) { /* * Verify requests get properly transformed with kerberos strategy */ -static int s_test_http_forwarding_proxy_request_transform_kerberos(struct aws_allocator *allocator, void *ctx) { +static int s_test_http_proxy_request_transform_kerberos(struct aws_allocator *allocator, void *ctx) { (void)ctx; struct aws_http_proxy_strategy_tunneling_kerberos_options config = { @@ -559,9 +566,73 @@ static int s_test_http_forwarding_proxy_request_transform_kerberos(struct aws_al return AWS_OP_SUCCESS; } -AWS_TEST_CASE( - test_http_forwarding_proxy_request_transform_kerberos, - s_test_http_forwarding_proxy_request_transform_kerberos); +AWS_TEST_CASE(test_http_proxy_request_transform_kerberos, s_test_http_proxy_request_transform_kerberos); + +static int s_mock_aws_http_proxy_negotiation_kerberos_get_token_sync_failure_fn( + void *user_data, + struct aws_byte_cursor *out_token_value, + int *out_error_code) { + + (void)user_data; + + *out_error_code = AWS_ERROR_UNKNOWN; + + return AWS_OP_ERR; +} + +static int s_test_http_proxy_kerberos_token_failure(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + struct aws_http_proxy_strategy_tunneling_kerberos_options config = { + .get_token = s_mock_aws_http_proxy_negotiation_kerberos_get_token_sync_failure_fn, + .get_token_user_data = NULL, + }; + + struct aws_http_proxy_strategy *kerberos_strategy = + aws_http_proxy_strategy_new_tunneling_kerberos(allocator, &config); + + ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTP_TUNNEL, PTFT_PROXY_STRATEGY, kerberos_strategy)); + + ASSERT_SUCCESS(proxy_tester_verify_connection_attempt_was_to_proxy( + &tester, aws_byte_cursor_from_c_str(s_proxy_host_name), s_proxy_port)); + ASSERT_TRUE(tester.client_connection == NULL); + ASSERT_TRUE(tester.wait_result == AWS_ERROR_UNKNOWN); + + aws_http_proxy_strategy_release(kerberos_strategy); + + ASSERT_SUCCESS(proxy_tester_clean_up(&tester)); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(test_http_proxy_kerberos_token_failure, s_test_http_proxy_kerberos_token_failure); + +static int s_test_http_proxy_kerberos_connect_failure(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + struct aws_http_proxy_strategy_tunneling_kerberos_options config = { + .get_token = s_mock_aws_http_proxy_negotiation_kerberos_get_token_sync_fn, + .get_token_user_data = NULL, + }; + + struct aws_http_proxy_strategy *kerberos_strategy = + aws_http_proxy_strategy_new_tunneling_kerberos(allocator, &config); + + ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTP_TUNNEL, PTFT_CONNECT_REQUEST, kerberos_strategy)); + + ASSERT_SUCCESS(proxy_tester_verify_connection_attempt_was_to_proxy( + &tester, aws_byte_cursor_from_c_str(s_proxy_host_name), s_proxy_port)); + ASSERT_TRUE(tester.client_connection == NULL); + ASSERT_TRUE(tester.wait_result == AWS_ERROR_HTTP_PROXY_CONNECT_FAILED); + + aws_http_proxy_strategy_release(kerberos_strategy); + + ASSERT_SUCCESS(proxy_tester_clean_up(&tester)); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(test_http_proxy_kerberos_connect_failure, s_test_http_proxy_kerberos_connect_failure); AWS_STATIC_STRING_FROM_LITERAL(s_rewrite_host, "www.uri.com"); AWS_STATIC_STRING_FROM_LITERAL(s_rewrite_path, "/main/index.html?foo=bar"); From a26ff593cc61723864ba6577125e3f439288b805 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Fri, 22 Jan 2021 13:04:25 -0800 Subject: [PATCH 30/54] Adaptive proxy strategy tests --- include/aws/http/http.h | 1 - source/http.c | 3 - source/proxy_connection.c | 4 + source/proxy_strategy.c | 17 +- tests/CMakeLists.txt | 4 + tests/proxy_test_helper.c | 13 +- tests/proxy_test_helper.h | 2 + tests/test_proxy.c | 395 +++++++++++++++++++++++++++++++++++--- 8 files changed, 395 insertions(+), 44 deletions(-) diff --git a/include/aws/http/http.h b/include/aws/http/http.h index c95748757..095333625 100644 --- a/include/aws/http/http.h +++ b/include/aws/http/http.h @@ -46,7 +46,6 @@ enum aws_http_errors { AWS_ERROR_HTTP_RST_STREAM_SENT, AWS_ERROR_HTTP_STREAM_NOT_ACTIVATED, AWS_ERROR_HTTP_STREAM_HAS_COMPLETED, - AWS_ERROR_HTTP_PROXY_STRATEGY_TRANSFORM_FAILED, AWS_ERROR_HTTP_PROXY_STRATEGY_NTLM_CHALLENGE_TOKEN_MISSING, AWS_ERROR_HTTP_END_RANGE = AWS_ERROR_ENUM_END_RANGE(AWS_C_HTTP_PACKAGE_ID) diff --git a/source/http.c b/source/http.c index ae7a5b65a..971da349b 100644 --- a/source/http.c +++ b/source/http.c @@ -115,9 +115,6 @@ static struct aws_error_info s_errors[] = { AWS_DEFINE_ERROR_INFO_HTTP( AWS_ERROR_HTTP_STREAM_HAS_COMPLETED, "HTTP-stream has completed, action cannot be performed."), - AWS_DEFINE_ERROR_INFO_HTTP( - AWS_ERROR_HTTP_PROXY_STRATEGY_TRANSFORM_FAILED, - "Proxy strategy transform has completely failed."), AWS_DEFINE_ERROR_INFO_HTTP( AWS_ERROR_HTTP_PROXY_STRATEGY_NTLM_CHALLENGE_TOKEN_MISSING, "NTLM Proxy strategy was initiated without a challenge token"), diff --git a/source/proxy_connection.c b/source/proxy_connection.c index 08457ea05..49ad4a1f4 100644 --- a/source/proxy_connection.c +++ b/source/proxy_connection.c @@ -492,6 +492,10 @@ static void s_continue_tunneling_connect(struct aws_http_message *message, void .on_complete = s_aws_http_on_stream_complete_tunnel_proxy, }; + if (proxy_ud->connect_stream != NULL) { + aws_http_stream_release(proxy_ud->connect_stream); + } + proxy_ud->connect_stream = aws_http_connection_make_request(proxy_ud->connection, &request_options); if (proxy_ud->connect_stream == NULL) { goto on_error; diff --git a/source/proxy_strategy.c b/source/proxy_strategy.c index 7763efd5b..5ac956d8e 100644 --- a/source/proxy_strategy.c +++ b/source/proxy_strategy.c @@ -626,7 +626,7 @@ static void s_chain_tunnel_try_next_negotiator( on_error: chain_negotiator->original_negotiation_termination_callback( - message, AWS_ERROR_HTTP_PROXY_STRATEGY_TRANSFORM_FAILED, chain_negotiator->original_internal_proxy_user_data); + message, AWS_ERROR_HTTP_PROXY_CONNECT_FAILED, chain_negotiator->original_internal_proxy_user_data); } static void s_chain_tunnel_transform_connect( @@ -1441,14 +1441,14 @@ struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_tunneling_adaptive( identity_strategy = aws_http_proxy_strategy_new_tunneling_one_time_identity(allocator); if (identity_strategy == NULL) { - goto on_error; + goto done; } strategies[strategy_count++] = identity_strategy; if (config->kerberos_options != NULL) { kerberos_strategy = aws_http_proxy_strategy_new_tunneling_kerberos(allocator, config->kerberos_options); if (kerberos_strategy == NULL) { - goto on_error; + goto done; } strategies[strategy_count++] = kerberos_strategy; @@ -1457,7 +1457,7 @@ struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_tunneling_adaptive( if (config->ntlm_options != NULL) { ntlm_strategy = aws_http_proxy_strategy_new_tunneling_ntlm(allocator, config->ntlm_options); if (ntlm_strategy == NULL) { - goto on_error; + goto done; } strategies[strategy_count++] = ntlm_strategy; @@ -1470,19 +1470,16 @@ struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_tunneling_adaptive( adaptive_chain_strategy = aws_http_proxy_strategy_new_tunneling_chain(allocator, &chain_config); if (adaptive_chain_strategy == NULL) { - goto on_error; + goto done; } - return adaptive_chain_strategy; - -on_error: +done: aws_http_proxy_strategy_release(identity_strategy); aws_http_proxy_strategy_release(kerberos_strategy); aws_http_proxy_strategy_release(ntlm_strategy); - aws_http_proxy_strategy_release(adaptive_chain_strategy); - return NULL; + return adaptive_chain_strategy; } #if defined(_MSC_VER) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index e60423db9..d3d394104 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -497,6 +497,10 @@ add_test_case(test_http_forwarding_proxy_request_transform_basic_auth) add_test_case(test_http_proxy_request_transform_kerberos) add_test_case(test_http_proxy_kerberos_token_failure) add_test_case(test_http_proxy_kerberos_connect_failure) +add_test_case(test_http_proxy_adaptive_identity_success) +add_test_case(test_http_proxy_adaptive_kerberos_success) +add_test_case(test_http_proxy_adaptive_ntlm_success) +add_test_case(test_http_proxy_adaptive_failure) add_test_case(test_http_forwarding_proxy_uri_rewrite) add_test_case(test_http_forwarding_proxy_uri_rewrite_options_star) add_test_case(test_http_tunnel_proxy_connection_success) diff --git a/tests/proxy_test_helper.c b/tests/proxy_test_helper.c index 71fe981bc..30fa72030 100644 --- a/tests/proxy_test_helper.c +++ b/tests/proxy_test_helper.c @@ -35,6 +35,8 @@ void proxy_tester_on_client_connection_setup(struct aws_http_connection *connect struct proxy_tester *tester = user_data; AWS_FATAL_ASSERT(aws_mutex_lock(&tester->wait_lock) == AWS_OP_SUCCESS); + tester->client_connection_is_setup = true; + if (error_code) { tester->client_connection = NULL; tester->wait_result = error_code; @@ -77,6 +79,11 @@ bool proxy_tester_connection_setup_pred(void *user_data) { return tester->wait_result || tester->client_connection; } +bool proxy_tester_connection_complete_pred(void *user_data) { + struct proxy_tester *tester = user_data; + return tester->client_connection_is_setup; +} + bool proxy_tester_connection_shutdown_pred(void *user_data) { struct proxy_tester *tester = user_data; return tester->wait_result || tester->client_connection_is_shutdown; @@ -118,7 +125,7 @@ int proxy_tester_init(struct proxy_tester *tester, const struct proxy_tester_opt for (size_t i = 0; i < options->desired_connect_response_count; ++i) { struct aws_byte_cursor response_cursor = options->desired_connect_responses[i]; struct aws_string *response = aws_string_new_from_cursor(tester->alloc, &response_cursor); - ASSERT_SUCCESS(aws_array_list_push_back(&tester->desired_connect_responses, response)); + ASSERT_SUCCESS(aws_array_list_push_back(&tester->desired_connect_responses, &response)); } tester->event_loop_group = aws_event_loop_group_new_default(tester->alloc, 1, NULL); @@ -181,9 +188,7 @@ int proxy_tester_init(struct proxy_tester *tester, const struct proxy_tester_opt int proxy_tester_clean_up(struct proxy_tester *tester) { if (tester->client_connection) { - if (tester->client_connection) { - aws_http_connection_release(tester->client_connection); - } + aws_http_connection_release(tester->client_connection); } if (tester->testing_channel) { diff --git a/tests/proxy_test_helper.h b/tests/proxy_test_helper.h index 38a4e45b5..c1803d910 100644 --- a/tests/proxy_test_helper.h +++ b/tests/proxy_test_helper.h @@ -67,6 +67,7 @@ struct proxy_tester { struct aws_http_client_bootstrap *http_bootstrap; struct testing_channel *testing_channel; + bool client_connection_is_setup; bool client_connection_is_shutdown; /* If we need to wait for some async process*/ @@ -91,6 +92,7 @@ struct proxy_tester { int proxy_tester_wait(struct proxy_tester *tester, bool (*pred)(void *user_data)); bool proxy_tester_connection_setup_pred(void *user_data); +bool proxy_tester_connection_complete_pred(void *user_data); bool proxy_tester_connection_shutdown_pred(void *user_data); bool proxy_tester_request_complete_pred_fn(void *user_data); diff --git a/tests/test_proxy.c b/tests/test_proxy.c index 47b3fdf42..c39a2ae79 100644 --- a/tests/test_proxy.c +++ b/tests/test_proxy.c @@ -70,7 +70,21 @@ static struct aws_http_message *s_build_http_request(struct aws_allocator *alloc aws_byte_cursor_from_string(s_mock_request_host)); } -static bool s_is_header_in_request(struct aws_http_message *request, struct aws_http_header *header) { +static bool s_is_header_in_request(struct aws_http_message *request, struct aws_byte_cursor header_name) { + size_t header_count = aws_http_message_get_header_count(request); + for (size_t i = 0; i < header_count; ++i) { + struct aws_http_header current_header; + ASSERT_SUCCESS(aws_http_message_get_header(request, ¤t_header, i)); + + if (aws_byte_cursor_eq_ignore_case(¤t_header.name, &header_name)) { + return true; + } + } + + return false; +} + +static bool s_is_header_and_value_in_request(struct aws_http_message *request, struct aws_http_header *header) { size_t header_count = aws_http_message_get_header_count(request); for (size_t i = 0; i < header_count; ++i) { struct aws_http_header current_header; @@ -174,23 +188,28 @@ struct aws_http_connection_system_vtable s_proxy_connection_system_vtable = { .new_socket_channel = s_test_aws_proxy_new_socket_channel, }; +struct mocked_proxy_test_options { + enum proxy_tester_test_mode test_mode; + enum proxy_tester_failure_type failure_type; + struct aws_http_proxy_strategy *proxy_strategy; + + uint32_t mocked_response_count; + struct aws_byte_cursor *mocked_responses; +}; + /* * Basic setup common to all mocked proxy tests - set vtables, options, call init, wait for setup completion */ -static int s_setup_proxy_test( - struct aws_allocator *allocator, - enum proxy_tester_test_mode test_mode, - enum proxy_tester_failure_type failure_type, - struct aws_http_proxy_strategy *proxy_strategy) { +static int s_setup_proxy_test(struct aws_allocator *allocator, struct mocked_proxy_test_options *config) { aws_http_connection_set_system_vtable(&s_proxy_connection_system_vtable); aws_http_proxy_system_set_vtable(&s_proxy_table_for_tls); struct aws_http_proxy_options proxy_options = { - .connection_type = (test_mode == PTTM_HTTP_FORWARD) ? AWS_HPCT_HTTP_FORWARD : AWS_HPCT_HTTP_TUNNEL, + .connection_type = (config->test_mode == PTTM_HTTP_FORWARD) ? AWS_HPCT_HTTP_FORWARD : AWS_HPCT_HTTP_TUNNEL, .host = aws_byte_cursor_from_c_str(s_proxy_host_name), .port = s_proxy_port, - .proxy_strategy = proxy_strategy, + .proxy_strategy = config->proxy_strategy, }; struct proxy_tester_options options = { @@ -198,8 +217,10 @@ static int s_setup_proxy_test( .proxy_options = &proxy_options, .host = aws_byte_cursor_from_c_str(s_host_name), .port = s_port, - .test_mode = test_mode, - .failure_type = failure_type, + .test_mode = config->test_mode, + .failure_type = config->failure_type, + .desired_connect_response_count = config->mocked_response_count, + .desired_connect_responses = config->mocked_responses, }; ASSERT_SUCCESS(proxy_tester_init(&tester, &options)); @@ -216,7 +237,12 @@ static int s_setup_proxy_test( static int s_test_http_forwarding_proxy_connection_proxy_target(struct aws_allocator *allocator, void *ctx) { (void)ctx; - ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTP_FORWARD, PTFT_NONE, NULL)); + struct mocked_proxy_test_options options = { + .test_mode = PTTM_HTTP_FORWARD, + .failure_type = PTFT_NONE, + }; + + ASSERT_SUCCESS(s_setup_proxy_test(allocator, &options)); ASSERT_SUCCESS(proxy_tester_verify_connection_attempt_was_to_proxy( &tester, aws_byte_cursor_from_c_str(s_proxy_host_name), s_proxy_port)); @@ -234,7 +260,12 @@ AWS_TEST_CASE(test_http_forwarding_proxy_connection_proxy_target, s_test_http_fo static int s_test_http_forwarding_proxy_connection_channel_failure(struct aws_allocator *allocator, void *ctx) { (void)ctx; - ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTP_FORWARD, PTFT_CHANNEL, NULL)); + struct mocked_proxy_test_options options = { + .test_mode = PTTM_HTTP_FORWARD, + .failure_type = PTFT_CHANNEL, + }; + + ASSERT_SUCCESS(s_setup_proxy_test(allocator, &options)); ASSERT_SUCCESS(proxy_tester_verify_connection_attempt_was_to_proxy( &tester, aws_byte_cursor_from_c_str(s_proxy_host_name), s_proxy_port)); @@ -256,7 +287,12 @@ AWS_TEST_CASE( static int s_test_http_forwarding_proxy_connection_connect_failure(struct aws_allocator *allocator, void *ctx) { (void)ctx; - ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTP_FORWARD, PTFT_CONNECTION, NULL)); + struct mocked_proxy_test_options options = { + .test_mode = PTTM_HTTP_FORWARD, + .failure_type = PTFT_CONNECTION, + }; + + ASSERT_SUCCESS(s_setup_proxy_test(allocator, &options)); ASSERT_SUCCESS(proxy_tester_verify_connection_attempt_was_to_proxy( &tester, aws_byte_cursor_from_c_str(s_proxy_host_name), s_proxy_port)); @@ -278,7 +314,12 @@ AWS_TEST_CASE( static int s_test_https_tunnel_proxy_connection_success(struct aws_allocator *allocator, void *ctx) { (void)ctx; - ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTPS_TUNNEL, PTFT_NONE, NULL)); + struct mocked_proxy_test_options options = { + .test_mode = PTTM_HTTPS_TUNNEL, + .failure_type = PTFT_NONE, + }; + + ASSERT_SUCCESS(s_setup_proxy_test(allocator, &options)); ASSERT_SUCCESS(proxy_tester_verify_connection_attempt_was_to_proxy( &tester, aws_byte_cursor_from_c_str(s_proxy_host_name), s_proxy_port)); @@ -298,7 +339,12 @@ AWS_TEST_CASE(test_https_tunnel_proxy_connection_success, s_test_https_tunnel_pr static int s_test_http_tunnel_proxy_connection_success(struct aws_allocator *allocator, void *ctx) { (void)ctx; - ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTP_TUNNEL, PTFT_NONE, NULL)); + struct mocked_proxy_test_options options = { + .test_mode = PTTM_HTTP_TUNNEL, + .failure_type = PTFT_NONE, + }; + + ASSERT_SUCCESS(s_setup_proxy_test(allocator, &options)); ASSERT_SUCCESS(proxy_tester_verify_connection_attempt_was_to_proxy( &tester, aws_byte_cursor_from_c_str(s_proxy_host_name), s_proxy_port)); @@ -318,7 +364,12 @@ AWS_TEST_CASE(test_http_tunnel_proxy_connection_success, s_test_http_tunnel_prox static int s_test_https_tunnel_proxy_connection_failure_connect(struct aws_allocator *allocator, void *ctx) { (void)ctx; - ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTPS_TUNNEL, PTFT_CONNECT_REQUEST, NULL)); + struct mocked_proxy_test_options options = { + .test_mode = PTTM_HTTPS_TUNNEL, + .failure_type = PTFT_CONNECT_REQUEST, + }; + + ASSERT_SUCCESS(s_setup_proxy_test(allocator, &options)); ASSERT_SUCCESS(proxy_tester_verify_connection_attempt_was_to_proxy( &tester, aws_byte_cursor_from_c_str(s_proxy_host_name), s_proxy_port)); @@ -338,7 +389,12 @@ AWS_TEST_CASE(test_https_tunnel_proxy_connection_failure_connect, s_test_https_t static int s_test_http_tunnel_proxy_connection_failure_connect(struct aws_allocator *allocator, void *ctx) { (void)ctx; - ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTP_TUNNEL, PTFT_CONNECT_REQUEST, NULL)); + struct mocked_proxy_test_options options = { + .test_mode = PTTM_HTTP_TUNNEL, + .failure_type = PTFT_CONNECT_REQUEST, + }; + + ASSERT_SUCCESS(s_setup_proxy_test(allocator, &options)); ASSERT_SUCCESS(proxy_tester_verify_connection_attempt_was_to_proxy( &tester, aws_byte_cursor_from_c_str(s_proxy_host_name), s_proxy_port)); @@ -358,7 +414,12 @@ AWS_TEST_CASE(test_http_tunnel_proxy_connection_failure_connect, s_test_http_tun static int s_test_https_tunnel_proxy_connection_failure_tls(struct aws_allocator *allocator, void *ctx) { (void)ctx; - ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTPS_TUNNEL, PTFT_TLS_NEGOTIATION, NULL)); + struct mocked_proxy_test_options options = { + .test_mode = PTTM_HTTPS_TUNNEL, + .failure_type = PTFT_TLS_NEGOTIATION, + }; + + ASSERT_SUCCESS(s_setup_proxy_test(allocator, &options)); ASSERT_SUCCESS(proxy_tester_verify_connection_attempt_was_to_proxy( &tester, aws_byte_cursor_from_c_str(s_proxy_host_name), s_proxy_port)); @@ -409,7 +470,7 @@ static int s_verify_transformed_request( for (size_t i = 0; i < untransformed_header_count; ++i) { struct aws_http_header header; ASSERT_SUCCESS(aws_http_message_get_header(untransformed_request, &header, i)); - ASSERT_TRUE(s_is_header_in_request(transformed_request, &header)); + ASSERT_TRUE(s_is_header_and_value_in_request(transformed_request, &header)); } aws_uri_clean_up(&uri); @@ -422,7 +483,13 @@ static int s_do_http_forwarding_proxy_request_transform_test( struct aws_http_proxy_strategy *proxy_strategy, int (*transformed_request_verifier_fn)(struct aws_http_message *)) { - ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTP_FORWARD, PTFT_NONE, proxy_strategy)); + struct mocked_proxy_test_options options = { + .test_mode = PTTM_HTTP_FORWARD, + .failure_type = PTFT_NONE, + .proxy_strategy = proxy_strategy, + }; + + ASSERT_SUCCESS(s_setup_proxy_test(allocator, &options)); struct aws_http_message *untransformed_request = s_build_http_request(allocator); struct aws_http_message *request = s_build_http_request(allocator); @@ -473,7 +540,7 @@ static int s_check_for_basic_auth_header(struct aws_http_message *transformed_re struct aws_http_header auth_header; auth_header.name = aws_byte_cursor_from_string(s_expected_basic_auth_header_name); auth_header.value = aws_byte_cursor_from_string(s_expected_basic_auth_header_value); - ASSERT_TRUE(s_is_header_in_request(transformed_request, &auth_header)); + ASSERT_TRUE(s_is_header_and_value_in_request(transformed_request, &auth_header)); return AWS_OP_SUCCESS; } @@ -522,11 +589,11 @@ AWS_STATIC_STRING_FROM_LITERAL(s_expected_kerberos_auth_header_name, "Proxy-Auth AWS_STATIC_STRING_FROM_LITERAL(s_expected_kerberos_auth_header_value, "Negotiate abcdefABCDEF123"); static int s_verify_kerberos_connect_request(struct aws_http_message *request) { - /* Check for basic auth header */ + /* Check for auth header */ struct aws_http_header auth_header; auth_header.name = aws_byte_cursor_from_string(s_expected_kerberos_auth_header_name); auth_header.value = aws_byte_cursor_from_string(s_expected_kerberos_auth_header_value); - ASSERT_TRUE(s_is_header_in_request(request, &auth_header)); + ASSERT_TRUE(s_is_header_and_value_in_request(request, &auth_header)); return AWS_OP_SUCCESS; } @@ -545,7 +612,13 @@ static int s_test_http_proxy_request_transform_kerberos(struct aws_allocator *al struct aws_http_proxy_strategy *kerberos_strategy = aws_http_proxy_strategy_new_tunneling_kerberos(allocator, &config); - ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTP_TUNNEL, PTFT_NONE, kerberos_strategy)); + struct mocked_proxy_test_options options = { + .test_mode = PTTM_HTTP_TUNNEL, + .failure_type = PTFT_NONE, + .proxy_strategy = kerberos_strategy, + }; + + ASSERT_SUCCESS(s_setup_proxy_test(allocator, &options)); ASSERT_SUCCESS(proxy_tester_verify_connection_attempt_was_to_proxy( &tester, aws_byte_cursor_from_c_str(s_proxy_host_name), s_proxy_port)); @@ -591,7 +664,13 @@ static int s_test_http_proxy_kerberos_token_failure(struct aws_allocator *alloca struct aws_http_proxy_strategy *kerberos_strategy = aws_http_proxy_strategy_new_tunneling_kerberos(allocator, &config); - ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTP_TUNNEL, PTFT_PROXY_STRATEGY, kerberos_strategy)); + struct mocked_proxy_test_options options = { + .test_mode = PTTM_HTTP_TUNNEL, + .failure_type = PTFT_PROXY_STRATEGY, + .proxy_strategy = kerberos_strategy, + }; + + ASSERT_SUCCESS(s_setup_proxy_test(allocator, &options)); ASSERT_SUCCESS(proxy_tester_verify_connection_attempt_was_to_proxy( &tester, aws_byte_cursor_from_c_str(s_proxy_host_name), s_proxy_port)); @@ -618,7 +697,13 @@ static int s_test_http_proxy_kerberos_connect_failure(struct aws_allocator *allo struct aws_http_proxy_strategy *kerberos_strategy = aws_http_proxy_strategy_new_tunneling_kerberos(allocator, &config); - ASSERT_SUCCESS(s_setup_proxy_test(allocator, PTTM_HTTP_TUNNEL, PTFT_CONNECT_REQUEST, kerberos_strategy)); + struct mocked_proxy_test_options options = { + .test_mode = PTTM_HTTP_TUNNEL, + .failure_type = PTFT_CONNECT_REQUEST, + .proxy_strategy = kerberos_strategy, + }; + + ASSERT_SUCCESS(s_setup_proxy_test(allocator, &options)); ASSERT_SUCCESS(proxy_tester_verify_connection_attempt_was_to_proxy( &tester, aws_byte_cursor_from_c_str(s_proxy_host_name), s_proxy_port)); @@ -634,6 +719,264 @@ static int s_test_http_proxy_kerberos_connect_failure(struct aws_allocator *allo AWS_TEST_CASE(test_http_proxy_kerberos_connect_failure, s_test_http_proxy_kerberos_connect_failure); +AWS_STATIC_STRING_FROM_LITERAL(s_mock_ntlm_token_value, "NTLM_RESPONSE"); + +static int s_mock_aws_http_proxy_negotiation_ntlm_get_challenge_token_sync_fn( + void *user_data, + const struct aws_byte_cursor *challenge_value, + struct aws_byte_cursor *out_token_value, + int *out_error_code) { + + (void)user_data; + + *out_error_code = AWS_ERROR_SUCCESS; + *out_token_value = aws_byte_cursor_from_string(s_mock_ntlm_token_value); + + return AWS_OP_SUCCESS; +} + +static int s_verify_identity_connect_request(struct aws_http_message *request) { + ASSERT_FALSE(s_is_header_in_request(request, aws_byte_cursor_from_string(s_expected_kerberos_auth_header_name))); + + return AWS_OP_SUCCESS; +} + +static struct aws_http_proxy_strategy *s_create_adaptive_strategy(struct aws_allocator *allocator) { + struct aws_http_proxy_strategy_tunneling_kerberos_options kerberos_config = { + .get_token = s_mock_aws_http_proxy_negotiation_kerberos_get_token_sync_fn, + .get_token_user_data = NULL, + }; + + struct aws_http_proxy_strategy_tunneling_ntlm_options ntlm_config = { + .get_challenge_token = s_mock_aws_http_proxy_negotiation_ntlm_get_challenge_token_sync_fn, + .get_challenge_token_user_data = NULL, + }; + + struct aws_http_proxy_strategy_tunneling_adaptive_options config = { + .ntlm_options = &ntlm_config, + .kerberos_options = &kerberos_config, + }; + + return aws_http_proxy_strategy_new_tunneling_adaptive(allocator, &config); +} + +static int s_test_http_proxy_adaptive_identity_success(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + struct aws_http_proxy_strategy *adaptive_strategy = s_create_adaptive_strategy(allocator); + + struct mocked_proxy_test_options options = { + .test_mode = PTTM_HTTP_TUNNEL, + .failure_type = PTFT_NONE, + .proxy_strategy = adaptive_strategy, + }; + + ASSERT_SUCCESS(s_setup_proxy_test(allocator, &options)); + + ASSERT_SUCCESS(proxy_tester_verify_connection_attempt_was_to_proxy( + &tester, aws_byte_cursor_from_c_str(s_proxy_host_name), s_proxy_port)); + ASSERT_TRUE(tester.client_connection != NULL); + ASSERT_TRUE(tester.wait_result == AWS_ERROR_SUCCESS); + + ASSERT_INT_EQUALS(1, aws_array_list_length(&tester.connect_requests)); + + struct aws_http_message *connect_request = NULL; + aws_array_list_get_at(&tester.connect_requests, &connect_request, 0); + + ASSERT_SUCCESS(s_verify_identity_connect_request(connect_request)); + + aws_http_proxy_strategy_release(adaptive_strategy); + + ASSERT_SUCCESS(proxy_tester_clean_up(&tester)); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(test_http_proxy_adaptive_identity_success, s_test_http_proxy_adaptive_identity_success); + +AWS_STATIC_STRING_FROM_LITERAL(s_unauthorized_response, "HTTP/1.0 401 Unauthorized\r\n\r\n"); +AWS_STATIC_STRING_FROM_LITERAL(s_good_response, "HTTP/1.0 200 Connection established\r\nconnection: close\r\n\r\n"); + +static int s_test_http_proxy_adaptive_kerberos_success(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + struct aws_http_proxy_strategy *adaptive_strategy = s_create_adaptive_strategy(allocator); + + struct aws_byte_cursor first_response = aws_byte_cursor_from_string(s_unauthorized_response); + struct aws_byte_cursor second_response = aws_byte_cursor_from_string(s_good_response); + + struct aws_byte_cursor connect_responses[] = { + first_response, + second_response, + }; + + struct mocked_proxy_test_options options = { + .test_mode = PTTM_HTTP_TUNNEL, + .failure_type = PTFT_NONE, + .proxy_strategy = adaptive_strategy, + .mocked_response_count = 2, + .mocked_responses = connect_responses, + }; + + ASSERT_SUCCESS(s_setup_proxy_test(allocator, &options)); + + ASSERT_SUCCESS(proxy_tester_verify_connect_request(&tester)); + ASSERT_SUCCESS(proxy_tester_send_connect_response(&tester)); + + ASSERT_SUCCESS(proxy_tester_wait(&tester, proxy_tester_connection_setup_pred)); + + ASSERT_TRUE(tester.client_connection != NULL); + ASSERT_TRUE(tester.wait_result == AWS_ERROR_SUCCESS); + + ASSERT_INT_EQUALS(2, aws_array_list_length(&tester.connect_requests)); + + struct aws_http_message *first_connect_request = NULL; + aws_array_list_get_at(&tester.connect_requests, &first_connect_request, 0); + + ASSERT_SUCCESS(s_verify_identity_connect_request(first_connect_request)); + + struct aws_http_message *second_connect_request = NULL; + aws_array_list_get_at(&tester.connect_requests, &second_connect_request, 1); + + ASSERT_SUCCESS(s_verify_kerberos_connect_request(second_connect_request)); + + aws_http_proxy_strategy_release(adaptive_strategy); + + ASSERT_SUCCESS(proxy_tester_clean_up(&tester)); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(test_http_proxy_adaptive_kerberos_success, s_test_http_proxy_adaptive_kerberos_success); + +static int s_verify_ntlm_connect_request(struct aws_http_message *request) { + (void)request; + + return AWS_OP_SUCCESS; +} + +AWS_STATIC_STRING_FROM_LITERAL(s_ntlm_response, "HTTP/1.0 407 Bad\r\nProxy-Authenticate: TestChallenge\r\n\r\n"); + +static int s_test_http_proxy_adaptive_ntlm_success(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + struct aws_http_proxy_strategy *adaptive_strategy = s_create_adaptive_strategy(allocator); + + struct aws_byte_cursor bad_response = aws_byte_cursor_from_string(s_ntlm_response); + struct aws_byte_cursor good_response = aws_byte_cursor_from_string(s_good_response); + + struct aws_byte_cursor connect_responses[] = { + bad_response, + bad_response, + good_response, + }; + + struct mocked_proxy_test_options options = { + .test_mode = PTTM_HTTP_TUNNEL, + .failure_type = PTFT_NONE, + .proxy_strategy = adaptive_strategy, + .mocked_response_count = 3, + .mocked_responses = connect_responses, + }; + + ASSERT_SUCCESS(s_setup_proxy_test(allocator, &options)); + + ASSERT_SUCCESS(proxy_tester_verify_connect_request(&tester)); + ASSERT_SUCCESS(proxy_tester_send_connect_response(&tester)); + + ASSERT_SUCCESS(proxy_tester_verify_connect_request(&tester)); + ASSERT_SUCCESS(proxy_tester_send_connect_response(&tester)); + + ASSERT_SUCCESS(proxy_tester_wait(&tester, proxy_tester_connection_setup_pred)); + + ASSERT_TRUE(tester.client_connection != NULL); + ASSERT_TRUE(tester.wait_result == AWS_ERROR_SUCCESS); + + ASSERT_INT_EQUALS(3, aws_array_list_length(&tester.connect_requests)); + + struct aws_http_message *first_connect_request = NULL; + aws_array_list_get_at(&tester.connect_requests, &first_connect_request, 0); + + ASSERT_SUCCESS(s_verify_identity_connect_request(first_connect_request)); + + struct aws_http_message *second_connect_request = NULL; + aws_array_list_get_at(&tester.connect_requests, &second_connect_request, 1); + + ASSERT_SUCCESS(s_verify_kerberos_connect_request(second_connect_request)); + + struct aws_http_message *third_connect_request = NULL; + aws_array_list_get_at(&tester.connect_requests, &third_connect_request, 2); + + ASSERT_SUCCESS(s_verify_ntlm_connect_request(third_connect_request)); + + aws_http_proxy_strategy_release(adaptive_strategy); + + ASSERT_SUCCESS(proxy_tester_clean_up(&tester)); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(test_http_proxy_adaptive_ntlm_success, s_test_http_proxy_adaptive_ntlm_success); + +static int s_test_http_proxy_adaptive_failure(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + struct aws_http_proxy_strategy *adaptive_strategy = s_create_adaptive_strategy(allocator); + + struct aws_byte_cursor bad_response = aws_byte_cursor_from_string(s_ntlm_response); + + struct aws_byte_cursor connect_responses[] = { + bad_response, + bad_response, + bad_response, + }; + + struct mocked_proxy_test_options options = { + .test_mode = PTTM_HTTP_TUNNEL, + .failure_type = PTFT_NONE, + .proxy_strategy = adaptive_strategy, + .mocked_response_count = 3, + .mocked_responses = connect_responses, + }; + + ASSERT_SUCCESS(s_setup_proxy_test(allocator, &options)); + + ASSERT_SUCCESS(proxy_tester_verify_connect_request(&tester)); + ASSERT_SUCCESS(proxy_tester_send_connect_response(&tester)); + + ASSERT_SUCCESS(proxy_tester_verify_connect_request(&tester)); + ASSERT_SUCCESS(proxy_tester_send_connect_response(&tester)); + + ASSERT_SUCCESS(proxy_tester_wait(&tester, proxy_tester_connection_setup_pred)); + + ASSERT_TRUE(tester.wait_result == AWS_ERROR_HTTP_PROXY_CONNECT_FAILED); + + ASSERT_INT_EQUALS(3, aws_array_list_length(&tester.connect_requests)); + + struct aws_http_message *first_connect_request = NULL; + aws_array_list_get_at(&tester.connect_requests, &first_connect_request, 0); + + ASSERT_SUCCESS(s_verify_identity_connect_request(first_connect_request)); + + struct aws_http_message *second_connect_request = NULL; + aws_array_list_get_at(&tester.connect_requests, &second_connect_request, 1); + + ASSERT_SUCCESS(s_verify_kerberos_connect_request(second_connect_request)); + + struct aws_http_message *third_connect_request = NULL; + aws_array_list_get_at(&tester.connect_requests, &third_connect_request, 2); + + ASSERT_SUCCESS(s_verify_ntlm_connect_request(third_connect_request)); + + aws_http_proxy_strategy_release(adaptive_strategy); + + ASSERT_SUCCESS(proxy_tester_clean_up(&tester)); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(test_http_proxy_adaptive_failure, s_test_http_proxy_adaptive_failure); + AWS_STATIC_STRING_FROM_LITERAL(s_rewrite_host, "www.uri.com"); AWS_STATIC_STRING_FROM_LITERAL(s_rewrite_path, "/main/index.html?foo=bar"); AWS_STATIC_STRING_FROM_LITERAL(s_expected_rewritten_path, "http://www.uri.com:80/main/index.html?foo=bar"); From d7c829a5171765494fc9e7988c03850fe6ff2006 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Sun, 24 Jan 2021 12:13:39 -0800 Subject: [PATCH 31/54] Update token retrieval function signatures to handle persistence properly --- include/aws/http/proxy_strategy.h | 8 ++------ source/proxy_strategy.c | 27 ++++++++++++++++----------- tests/test_proxy.c | 31 ++++++++++++------------------- 3 files changed, 30 insertions(+), 36 deletions(-) diff --git a/include/aws/http/proxy_strategy.h b/include/aws/http/proxy_strategy.h index 34a929580..5dca15653 100644 --- a/include/aws/http/proxy_strategy.h +++ b/include/aws/http/proxy_strategy.h @@ -21,19 +21,15 @@ struct aws_http_proxy_strategy; /** * Synchronous (for now) callback function to fetch a token used in modifying CONNECT requests */ -typedef int(aws_http_proxy_negotiation_get_token_sync_fn)( - void *user_data, - struct aws_byte_cursor *out_token_value, - int *out_error_code); +typedef struct aws_string *(aws_http_proxy_negotiation_get_token_sync_fn)(void *user_data, int *out_error_code); /** * Synchronous (for now) callback function to fetch a token used in modifying CONNECT request. Includes a (byte string) * context intended to be used as part of a challenge-response flow. */ -typedef int(aws_http_proxy_negotiation_get_challenge_token_sync_fn)( +typedef struct aws_string *(aws_http_proxy_negotiation_get_challenge_token_sync_fn)( void *user_data, const struct aws_byte_cursor *challenge_context, - struct aws_byte_cursor *out_token_value, int *out_error_code); /** diff --git a/source/proxy_strategy.c b/source/proxy_strategy.c index 5ac956d8e..63bee3b24 100644 --- a/source/proxy_strategy.c +++ b/source/proxy_strategy.c @@ -960,8 +960,7 @@ static void s_kerberos_tunnel_transform_connect( int result = AWS_OP_ERR; int error_code = AWS_ERROR_SUCCESS; - struct aws_byte_cursor kerberos_token; - AWS_ZERO_STRUCT(kerberos_token); + struct aws_string *kerberos_token = NULL; if (kerberos_negotiator->connect_state == AWS_PNCS_FAILURE) { error_code = AWS_ERROR_HTTP_PROXY_CONNECT_FAILED; @@ -975,13 +974,14 @@ static void s_kerberos_tunnel_transform_connect( kerberos_negotiator->connect_state = AWS_PNCS_IN_PROGRESS; - if (kerberos_strategy->get_token(kerberos_strategy->get_token_user_data, &kerberos_token, &error_code) || - error_code != AWS_ERROR_SUCCESS) { + kerberos_token = kerberos_strategy->get_token(kerberos_strategy->get_token_user_data, &error_code); + if (kerberos_token == NULL || error_code != AWS_ERROR_SUCCESS) { goto done; } /*transform the header with proxy authenticate:Negotiate and kerberos token*/ - if (s_add_kerberos_proxy_usertoken_authentication_header(kerberos_negotiator->allocator, message, kerberos_token)) { + if (s_add_kerberos_proxy_usertoken_authentication_header( + kerberos_negotiator->allocator, message, aws_byte_cursor_from_string(kerberos_token))) { error_code = aws_last_error(); goto done; } @@ -999,6 +999,8 @@ static void s_kerberos_tunnel_transform_connect( } else { negotiation_http_request_forward_callback(message, internal_proxy_user_data); } + + aws_string_destroy(kerberos_token); } static int s_kerberos_on_incoming_header_adaptive( @@ -1217,8 +1219,7 @@ static void s_ntlm_tunnel_transform_connect( int result = AWS_OP_ERR; int error_code = AWS_ERROR_SUCCESS; - struct aws_byte_cursor challenge_answer_token; - AWS_ZERO_STRUCT(challenge_answer_token); + struct aws_string *challenge_answer_token = NULL; struct aws_byte_cursor challenge_token; AWS_ZERO_STRUCT(challenge_token); @@ -1239,14 +1240,16 @@ static void s_ntlm_tunnel_transform_connect( ntlm_negotiator->connect_state = AWS_PNCS_IN_PROGRESS; challenge_token = aws_byte_cursor_from_string(ntlm_negotiator->challenge_token); - if (ntlm_strategy->get_challenge_token( - ntlm_strategy->get_challenge_token_user_data, &challenge_token, &challenge_answer_token, &error_code) || - error_code != AWS_ERROR_SUCCESS) { + challenge_answer_token = + ntlm_strategy->get_challenge_token(ntlm_strategy->get_challenge_token_user_data, &challenge_token, &error_code); + + if (challenge_answer_token == NULL || error_code != AWS_ERROR_SUCCESS) { goto done; } /*transform the header with proxy authenticate:Negotiate and kerberos token*/ - if (s_add_ntlm_proxy_usertoken_authentication_header(ntlm_negotiator->allocator, message, challenge_answer_token)) { + if (s_add_ntlm_proxy_usertoken_authentication_header( + ntlm_negotiator->allocator, message, aws_byte_cursor_from_string(challenge_answer_token))) { error_code = aws_last_error(); goto done; } @@ -1264,6 +1267,8 @@ static void s_ntlm_tunnel_transform_connect( } else { negotiation_http_request_forward_callback(message, internal_proxy_user_data); } + + aws_string_destroy(challenge_answer_token); } AWS_STATIC_STRING_FROM_LITERAL(s_ntlm_challenge_token_header, "Proxy-Authenticate"); diff --git a/tests/test_proxy.c b/tests/test_proxy.c index c39a2ae79..44231d8ba 100644 --- a/tests/test_proxy.c +++ b/tests/test_proxy.c @@ -572,17 +572,14 @@ AWS_TEST_CASE( AWS_STATIC_STRING_FROM_LITERAL(s_mock_kerberos_token_value, "abcdefABCDEF123"); -static int s_mock_aws_http_proxy_negotiation_kerberos_get_token_sync_fn( +static struct aws_string *s_mock_aws_http_proxy_negotiation_kerberos_get_token_sync_fn( void *user_data, - struct aws_byte_cursor *out_token_value, int *out_error_code) { - (void)user_data; + struct aws_allocator *allocator = user_data; *out_error_code = AWS_ERROR_SUCCESS; - *out_token_value = aws_byte_cursor_from_string(s_mock_kerberos_token_value); - - return AWS_OP_SUCCESS; + return aws_string_new_from_string(allocator, s_mock_kerberos_token_value); } AWS_STATIC_STRING_FROM_LITERAL(s_expected_kerberos_auth_header_name, "Proxy-Authorization"); @@ -606,7 +603,7 @@ static int s_test_http_proxy_request_transform_kerberos(struct aws_allocator *al struct aws_http_proxy_strategy_tunneling_kerberos_options config = { .get_token = s_mock_aws_http_proxy_negotiation_kerberos_get_token_sync_fn, - .get_token_user_data = NULL, + .get_token_user_data = allocator, }; struct aws_http_proxy_strategy *kerberos_strategy = @@ -641,16 +638,15 @@ static int s_test_http_proxy_request_transform_kerberos(struct aws_allocator *al AWS_TEST_CASE(test_http_proxy_request_transform_kerberos, s_test_http_proxy_request_transform_kerberos); -static int s_mock_aws_http_proxy_negotiation_kerberos_get_token_sync_failure_fn( +static struct aws_string *s_mock_aws_http_proxy_negotiation_kerberos_get_token_sync_failure_fn( void *user_data, - struct aws_byte_cursor *out_token_value, int *out_error_code) { (void)user_data; *out_error_code = AWS_ERROR_UNKNOWN; - return AWS_OP_ERR; + return NULL; } static int s_test_http_proxy_kerberos_token_failure(struct aws_allocator *allocator, void *ctx) { @@ -691,7 +687,7 @@ static int s_test_http_proxy_kerberos_connect_failure(struct aws_allocator *allo struct aws_http_proxy_strategy_tunneling_kerberos_options config = { .get_token = s_mock_aws_http_proxy_negotiation_kerberos_get_token_sync_fn, - .get_token_user_data = NULL, + .get_token_user_data = allocator, }; struct aws_http_proxy_strategy *kerberos_strategy = @@ -721,18 +717,15 @@ AWS_TEST_CASE(test_http_proxy_kerberos_connect_failure, s_test_http_proxy_kerber AWS_STATIC_STRING_FROM_LITERAL(s_mock_ntlm_token_value, "NTLM_RESPONSE"); -static int s_mock_aws_http_proxy_negotiation_ntlm_get_challenge_token_sync_fn( +static struct aws_string *s_mock_aws_http_proxy_negotiation_ntlm_get_challenge_token_sync_fn( void *user_data, const struct aws_byte_cursor *challenge_value, - struct aws_byte_cursor *out_token_value, int *out_error_code) { - (void)user_data; + struct aws_allocator *allocator = user_data; *out_error_code = AWS_ERROR_SUCCESS; - *out_token_value = aws_byte_cursor_from_string(s_mock_ntlm_token_value); - - return AWS_OP_SUCCESS; + return aws_string_new_from_string(allocator, s_mock_ntlm_token_value); } static int s_verify_identity_connect_request(struct aws_http_message *request) { @@ -744,12 +737,12 @@ static int s_verify_identity_connect_request(struct aws_http_message *request) { static struct aws_http_proxy_strategy *s_create_adaptive_strategy(struct aws_allocator *allocator) { struct aws_http_proxy_strategy_tunneling_kerberos_options kerberos_config = { .get_token = s_mock_aws_http_proxy_negotiation_kerberos_get_token_sync_fn, - .get_token_user_data = NULL, + .get_token_user_data = allocator, }; struct aws_http_proxy_strategy_tunneling_ntlm_options ntlm_config = { .get_challenge_token = s_mock_aws_http_proxy_negotiation_ntlm_get_challenge_token_sync_fn, - .get_challenge_token_user_data = NULL, + .get_challenge_token_user_data = allocator, }; struct aws_http_proxy_strategy_tunneling_adaptive_options config = { From 6d9d4ae1c80776d3d1759daef9c7504422c63829 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Sun, 24 Jan 2021 13:17:32 -0800 Subject: [PATCH 32/54] User token retrieval error code --- include/aws/http/http.h | 1 + source/http.c | 3 +++ 2 files changed, 4 insertions(+) diff --git a/include/aws/http/http.h b/include/aws/http/http.h index 095333625..21970f177 100644 --- a/include/aws/http/http.h +++ b/include/aws/http/http.h @@ -47,6 +47,7 @@ enum aws_http_errors { AWS_ERROR_HTTP_STREAM_NOT_ACTIVATED, AWS_ERROR_HTTP_STREAM_HAS_COMPLETED, AWS_ERROR_HTTP_PROXY_STRATEGY_NTLM_CHALLENGE_TOKEN_MISSING, + AWS_ERROR_HTTP_PROXY_STRATEGY_TOKEN_RETRIEVAL_FAILURE, AWS_ERROR_HTTP_END_RANGE = AWS_ERROR_ENUM_END_RANGE(AWS_C_HTTP_PACKAGE_ID) }; diff --git a/source/http.c b/source/http.c index 971da349b..3e96cb05d 100644 --- a/source/http.c +++ b/source/http.c @@ -118,6 +118,9 @@ static struct aws_error_info s_errors[] = { AWS_DEFINE_ERROR_INFO_HTTP( AWS_ERROR_HTTP_PROXY_STRATEGY_NTLM_CHALLENGE_TOKEN_MISSING, "NTLM Proxy strategy was initiated without a challenge token"), + AWS_DEFINE_ERROR_INFO_HTTP( + AWS_ERROR_HTTP_PROXY_STRATEGY_TOKEN_RETRIEVAL_FAILURE, + "Failure in user code while retrieving proxy auth token"), }; /* clang-format on */ From c6825dc319fac29b40af479e4f86aca1c114d142 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Thu, 28 Jan 2021 11:02:08 -0800 Subject: [PATCH 33/54] First attempt at iterating connections per CONNECT attempt with proxies --- include/aws/http/http.h | 2 + include/aws/http/private/proxy_impl.h | 20 +- include/aws/http/proxy_strategy.h | 28 +- source/http.c | 7 + source/proxy_connection.c | 199 ++++++-- source/proxy_strategy.c | 654 +++++++++++++------------- tests/CMakeLists.txt | 8 +- tests/test_proxy.c | 5 + 8 files changed, 546 insertions(+), 377 deletions(-) diff --git a/include/aws/http/http.h b/include/aws/http/http.h index 21970f177..e6dfd90d9 100644 --- a/include/aws/http/http.h +++ b/include/aws/http/http.h @@ -48,6 +48,7 @@ enum aws_http_errors { AWS_ERROR_HTTP_STREAM_HAS_COMPLETED, AWS_ERROR_HTTP_PROXY_STRATEGY_NTLM_CHALLENGE_TOKEN_MISSING, AWS_ERROR_HTTP_PROXY_STRATEGY_TOKEN_RETRIEVAL_FAILURE, + AWS_ERROR_HTTP_PROXY_CONNECT_FAILED_RETRYABLE, AWS_ERROR_HTTP_END_RANGE = AWS_ERROR_ENUM_END_RANGE(AWS_C_HTTP_PACKAGE_ID) }; @@ -81,6 +82,7 @@ enum aws_http_log_subject { AWS_LS_HTTP_CONNECTION_MANAGER, AWS_LS_HTTP_WEBSOCKET, AWS_LS_HTTP_WEBSOCKET_SETUP, + AWS_LS_HTTP_PROXY_NEGOTIATION, }; enum aws_http_version { diff --git a/include/aws/http/private/proxy_impl.h b/include/aws/http/private/proxy_impl.h index 395b3e47c..80fdf4b71 100644 --- a/include/aws/http/private/proxy_impl.h +++ b/include/aws/http/private/proxy_impl.h @@ -9,6 +9,8 @@ #include #include +#include +#include struct aws_http_connection_manager_options; struct aws_http_message; @@ -60,12 +62,20 @@ struct aws_http_proxy_config { struct aws_http_proxy_user_data { struct aws_allocator *allocator; + /* + * dynamic proxy connection resolution state + */ enum aws_proxy_bootstrap_state state; int error_code; + enum aws_http_status_code connect_status_code; struct aws_http_connection *connection; struct aws_http_message *connect_request; struct aws_http_stream *connect_stream; + struct aws_http_proxy_negotiator *proxy_negotiator; + /* + * Cached original connect options + */ struct aws_string *original_host; uint16_t original_port; aws_http_on_client_connection_setup_fn *original_on_setup; @@ -73,9 +83,12 @@ struct aws_http_proxy_user_data { void *original_user_data; struct aws_tls_connection_options *tls_options; + struct aws_client_bootstrap *bootstrap; + struct aws_socket_options socket_options; + bool manual_window_management; + size_t initial_window_size; struct aws_http_proxy_config *proxy_config; - struct aws_http_proxy_negotiator *proxy_negotiator; }; struct aws_http_proxy_system_vtable { @@ -113,6 +126,11 @@ struct aws_http_proxy_config *aws_http_proxy_config_new_from_manager_options( struct aws_allocator *allocator, const struct aws_http_connection_manager_options *options); +AWS_HTTP_API +struct aws_http_proxy_config *aws_http_proxy_config_new_clone( + struct aws_allocator *allocator, + const struct aws_http_proxy_config *proxy_config); + AWS_HTTP_API void aws_http_proxy_config_destroy(struct aws_http_proxy_config *config); diff --git a/include/aws/http/proxy_strategy.h b/include/aws/http/proxy_strategy.h index 5dca15653..59833116d 100644 --- a/include/aws/http/proxy_strategy.h +++ b/include/aws/http/proxy_strategy.h @@ -176,12 +176,6 @@ struct aws_http_proxy_strategy_basic_auth_options { struct aws_byte_cursor password; }; -struct aws_http_proxy_strategy_tunneling_chain_options { - struct aws_http_proxy_strategy **strategies; - - uint32_t strategy_count; -}; - struct aws_http_proxy_strategy_tunneling_kerberos_options { aws_http_proxy_negotiation_get_token_sync_fn *get_token; @@ -198,16 +192,22 @@ struct aws_http_proxy_strategy_tunneling_ntlm_options { struct aws_http_proxy_strategy_tunneling_adaptive_options { /* - * If non-null, will insert a kerberos proxy strategy into the adaptive chain + * If non-null, will insert a kerberos proxy strategy into the adaptive sequence */ struct aws_http_proxy_strategy_tunneling_kerberos_options *kerberos_options; /* - * If non-null will insert an ntlm proxy strategy into the adaptive chain + * If non-null will insert an ntlm proxy strategy into the adaptive sequence */ struct aws_http_proxy_strategy_tunneling_ntlm_options *ntlm_options; }; +struct aws_http_proxy_strategy_tunneling_sequence_options { + struct aws_http_proxy_strategy **strategies; + + uint32_t strategy_count; +}; + AWS_EXTERN_C_BEGIN /** @@ -266,7 +266,7 @@ struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_basic_auth( /** * Constructor for a tunnel-only proxy strategy that applies no changes to outbound CONNECT requests. Intended to be - * the first link in an adaptive chain for a tunneling proxy: first try a basic CONNECT, then based on the response, + * the first link in an adaptive sequence for a tunneling proxy: first try a basic CONNECT, then based on the response, * later links are allowed to make attempts. * * @param allocator memory allocator to use @@ -287,17 +287,17 @@ AWS_HTTP_API struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_forwarding_identity(struct aws_allocator *allocator); /** - * Constructor for a tunneling proxy strategy that contains a chain of sub-strategies which are tried - * sequentially in order. + * Constructor for a tunneling proxy strategy that contains a set of sub-strategies which are tried + * sequentially in order. Each strategy is tried against a new, fresh connection. * * @param allocator memory allocator to use - * @param config chain configuration options + * @param config sequence configuration options * @return a new proxy strategy if successfully constructed, otherwise NULL */ AWS_HTTP_API -struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_tunneling_chain( +struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_tunneling_sequence( struct aws_allocator *allocator, - struct aws_http_proxy_strategy_tunneling_chain_options *config); + struct aws_http_proxy_strategy_tunneling_sequence_options *config); /** * A constructor for a proxy strategy that performs kerberos authentication by adding the appropriate diff --git a/source/http.c b/source/http.c index 3e96cb05d..d823cb77d 100644 --- a/source/http.c +++ b/source/http.c @@ -121,6 +121,9 @@ static struct aws_error_info s_errors[] = { AWS_DEFINE_ERROR_INFO_HTTP( AWS_ERROR_HTTP_PROXY_STRATEGY_TOKEN_RETRIEVAL_FAILURE, "Failure in user code while retrieving proxy auth token"), + AWS_DEFINE_ERROR_INFO_HTTP( + AWS_ERROR_HTTP_PROXY_CONNECT_FAILED_RETRYABLE, + "Proxy connection attempt failed but the negotiation could be continued on a new connection"), }; /* clang-format on */ @@ -139,6 +142,10 @@ static struct aws_log_subject_info s_log_subject_infos[] = { DEFINE_LOG_SUBJECT_INFO(AWS_LS_HTTP_CONNECTION_MANAGER, "connection-manager", "HTTP connection manager"), DEFINE_LOG_SUBJECT_INFO(AWS_LS_HTTP_WEBSOCKET, "websocket", "Websocket"), DEFINE_LOG_SUBJECT_INFO(AWS_LS_HTTP_WEBSOCKET_SETUP, "websocket-setup", "Websocket setup"), + DEFINE_LOG_SUBJECT_INFO( + AWS_LS_HTTP_PROXY_NEGOTIATION, + "proxy-negotiation", + "Negotiating an http connection with a proxy server"), }; static struct aws_log_subject_info_list s_log_subject_list = { diff --git a/source/proxy_connection.c b/source/proxy_connection.c index 49ad4a1f4..353deb20c 100644 --- a/source/proxy_connection.c +++ b/source/proxy_connection.c @@ -55,6 +55,8 @@ void aws_http_proxy_user_data_destroy(struct aws_http_proxy_user_data *user_data aws_http_proxy_negotiator_release(user_data->proxy_negotiator); + aws_client_bootstrap_release(user_data->bootstrap); + aws_mem_release(user_data->allocator, user_data); } @@ -72,6 +74,13 @@ struct aws_http_proxy_user_data *aws_http_proxy_user_data_new( user_data->allocator = allocator; user_data->state = AWS_PBS_SOCKET_CONNECT; user_data->error_code = AWS_ERROR_SUCCESS; + user_data->connect_status_code = AWS_HTTP_STATUS_CODE_UNKNOWN; + user_data->bootstrap = aws_client_bootstrap_acquire(options->bootstrap); + if (options->socket_options != NULL) { + user_data->socket_options = *options->socket_options; + } + user_data->manual_window_management = options->manual_window_management; + user_data->initial_window_size = options->initial_window_size; user_data->original_host = aws_string_new_from_cursor(allocator, &options->host_name); if (user_data->original_host == NULL) { @@ -121,6 +130,73 @@ struct aws_http_proxy_user_data *aws_http_proxy_user_data_new( return NULL; } +struct aws_http_proxy_user_data *aws_http_proxy_user_data_new_reset_clone( + struct aws_allocator *allocator, + struct aws_http_proxy_user_data *old_user_data) { + + AWS_FATAL_ASSERT(old_user_data != NULL); + + struct aws_http_proxy_user_data *user_data = aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_user_data)); + if (user_data == NULL) { + return NULL; + } + + user_data->allocator = allocator; + user_data->state = AWS_PBS_SOCKET_CONNECT; + user_data->error_code = AWS_ERROR_SUCCESS; + user_data->connect_status_code = AWS_HTTP_STATUS_CODE_UNKNOWN; + user_data->bootstrap = aws_client_bootstrap_acquire(old_user_data->bootstrap); + user_data->socket_options = old_user_data->socket_options; + user_data->manual_window_management = old_user_data->manual_window_management; + user_data->initial_window_size = old_user_data->initial_window_size; + + user_data->original_host = aws_string_new_from_string(allocator, old_user_data->original_host); + if (user_data->original_host == NULL) { + goto on_error; + } + + user_data->original_port = old_user_data->original_port; + + user_data->proxy_config = aws_http_proxy_config_new_clone(allocator, old_user_data->proxy_config); + if (user_data->proxy_config == NULL) { + goto on_error; + } + + user_data->proxy_negotiator = aws_http_proxy_negotiator_acquire(old_user_data->proxy_negotiator); + if (user_data->proxy_negotiator == NULL) { + goto on_error; + } + + if (old_user_data->tls_options) { + /* clone tls options, but redirect user data to what we're creating */ + user_data->tls_options = aws_mem_calloc(allocator, 1, sizeof(struct aws_tls_connection_options)); + if (user_data->tls_options == NULL || + aws_tls_connection_options_copy(user_data->tls_options, old_user_data->tls_options)) { + goto on_error; + } + + user_data->tls_options->user_data = user_data; + } + + user_data->original_on_setup = old_user_data->original_on_setup; + user_data->original_on_shutdown = old_user_data->original_on_shutdown; + user_data->original_user_data = old_user_data->original_user_data; + + return user_data; + +on_error: + + AWS_LOGF_ERROR( + AWS_LS_HTTP_CONNECTION, + "(STATIC) Proxy connection failed to create user data with error %d(%s)", + aws_last_error(), + aws_error_str(aws_last_error())); + + aws_http_proxy_user_data_destroy(user_data); + + return NULL; +} + /* * Connection callback used ONLY by http proxy connections. After this, * the connection is live and the user is notified @@ -164,14 +240,16 @@ static void s_aws_http_on_client_connection_http_proxy_shutdown_fn( ec = AWS_ERROR_UNKNOWN; } - AWS_LOGF_ERROR( + AWS_LOGF_WARN( AWS_LS_HTTP_CONNECTION, "(%p) Error %d while connecting to \"%s\" via proxy.", (void *)connection, ec, (char *)proxy_ud->original_host->bytes); - proxy_ud->original_on_setup(NULL, ec, proxy_ud->original_user_data); + if (proxy_ud->original_on_setup != NULL) { + proxy_ud->original_on_setup(NULL, ec, proxy_ud->original_user_data); + } } aws_http_proxy_user_data_destroy(user_data); @@ -332,20 +410,20 @@ static int s_aws_http_on_incoming_header_block_done_tunnel_proxy( struct aws_http_proxy_user_data *context = user_data; if (header_block == AWS_HTTP_HEADER_BLOCK_MAIN) { - int status = 0; - if (aws_http_stream_get_incoming_response_status(stream, &status) || status != 200) { + aws_http_stream_get_incoming_response_status(stream, &context->connect_status_code); + if (context->connect_status_code != 200) { AWS_LOGF_ERROR( AWS_LS_HTTP_CONNECTION, "(%p) Proxy CONNECT request failed with status code %d", (void *)context->connection, - status); + context->connect_status_code); context->error_code = AWS_ERROR_HTTP_PROXY_CONNECT_FAILED; } aws_http_proxy_negotiator_connect_status_fn *on_status = context->proxy_negotiator->strategy_vtable.tunnelling_vtable->on_status_callback; if (on_status != NULL) { - (*on_status)(context->proxy_negotiator, status); + (*on_status)(context->proxy_negotiator, context->connect_status_code); } } @@ -381,6 +459,7 @@ static void s_on_origin_server_tls_negotation_result( context->original_on_setup(context->connection, AWS_ERROR_SUCCESS, context->original_user_data); } +static int s_create_tunneling_connection(struct aws_http_proxy_user_data *user_data); static int s_make_proxy_connect_request(struct aws_http_proxy_user_data *user_data); /* @@ -398,9 +477,18 @@ static void s_aws_http_on_stream_complete_tunnel_proxy( } if (context->error_code != AWS_ERROR_SUCCESS) { - /* retry until strategy terminates */ - context->error_code = 0; - if (s_make_proxy_connect_request(context)) { + if (context->connect_status_code == AWS_HTTP_STATUS_CODE_407_PROXY_AUTHENTICATION_REQUIRED) { + struct aws_http_proxy_user_data *new_context = + aws_http_proxy_user_data_new_reset_clone(context->allocator, context); + if (new_context == NULL || s_create_tunneling_connection(new_context)) { + context->error_code = AWS_ERROR_HTTP_PROXY_CONNECT_FAILED; + s_aws_http_proxy_user_data_shutdown(context); + } else { + context->original_on_shutdown = NULL; + context->original_on_setup = NULL; + } + } else { + context->error_code = AWS_ERROR_HTTP_PROXY_CONNECT_FAILED; s_aws_http_proxy_user_data_shutdown(context); } return; @@ -737,6 +825,39 @@ static int s_aws_http_client_connect_via_forwarding_proxy(const struct aws_http_ return result; } +static int s_create_tunneling_connection(struct aws_http_proxy_user_data *user_data) { + struct aws_http_client_connection_options connect_options; + AWS_ZERO_STRUCT(connect_options); + + connect_options.self_size = sizeof(struct aws_http_client_connection_options); + connect_options.allocator = user_data->allocator; + connect_options.bootstrap = user_data->bootstrap; + connect_options.host_name = aws_byte_cursor_from_buf(&user_data->proxy_config->host); + connect_options.port = user_data->proxy_config->port; + connect_options.socket_options = &user_data->socket_options; + connect_options.tls_options = user_data->proxy_config->tls_options; + connect_options.monitoring_options = NULL; /* ToDo */ + connect_options.manual_window_management = user_data->manual_window_management; + connect_options.initial_window_size = user_data->initial_window_size; + connect_options.user_data = user_data; + connect_options.on_setup = s_aws_http_on_client_connection_http_tunnel_proxy_setup_fn; + connect_options.on_shutdown = s_aws_http_on_client_connection_http_proxy_shutdown_fn; + connect_options.http1_options = NULL; /* ToDo */ + connect_options.http2_options = NULL; /* ToDo */ + + int result = aws_http_client_connect(&connect_options); + if (result == AWS_OP_ERR) { + AWS_LOGF_ERROR( + AWS_LS_HTTP_CONNECTION, + "(STATIC) Proxy tunnel connection failed client connect with error %d(%s)", + aws_last_error(), + aws_error_str(aws_last_error())); + aws_http_proxy_user_data_destroy(user_data); + } + + return result; +} + /* * Top-level function to route a connection through a proxy server via a CONNECT request */ @@ -755,28 +876,7 @@ static int s_aws_http_client_connect_via_tunneling_proxy(const struct aws_http_c return AWS_OP_ERR; } - /* Fill in a new connection options pointing at the proxy */ - struct aws_http_client_connection_options options_copy = *options; - - options_copy.proxy_options = NULL; - options_copy.host_name = options->proxy_options->host; - options_copy.port = options->proxy_options->port; - options_copy.user_data = user_data; - options_copy.on_setup = s_aws_http_on_client_connection_http_tunnel_proxy_setup_fn; - options_copy.on_shutdown = s_aws_http_on_client_connection_http_proxy_shutdown_fn; - options_copy.tls_options = options->proxy_options->tls_options; - - int result = aws_http_client_connect(&options_copy); - if (result == AWS_OP_ERR) { - AWS_LOGF_ERROR( - AWS_LS_HTTP_CONNECTION, - "(STATIC) Proxy tunnel connection failed client connect with error %d(%s)", - aws_last_error(), - aws_error_str(aws_last_error())); - aws_http_proxy_user_data_destroy(user_data); - } - - return result; + return s_create_tunneling_connection(user_data); } static enum aws_http_proxy_connection_type s_determine_proxy_connection_type( @@ -897,6 +997,43 @@ struct aws_http_proxy_config *aws_http_proxy_config_new_from_manager_options( s_determine_proxy_connection_type(options->proxy_options->connection_type, options->tls_connection_options)); } +struct aws_http_proxy_config *aws_http_proxy_config_new_clone( + struct aws_allocator *allocator, + const struct aws_http_proxy_config *proxy_config) { + + AWS_FATAL_ASSERT(proxy_config != NULL); + + struct aws_http_proxy_config *config = aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_config)); + if (config == NULL) { + return NULL; + } + + config->connection_type = proxy_config->connection_type; + + if (aws_byte_buf_init_copy_from_cursor(&config->host, allocator, aws_byte_cursor_from_buf(&proxy_config->host))) { + goto on_error; + } + + if (proxy_config->tls_options) { + config->tls_options = aws_mem_calloc(allocator, 1, sizeof(struct aws_tls_connection_options)); + if (aws_tls_connection_options_copy(config->tls_options, proxy_config->tls_options)) { + goto on_error; + } + } + + config->allocator = allocator; + config->port = proxy_config->port; + config->proxy_strategy = aws_http_proxy_strategy_acquire(proxy_config->proxy_strategy); + + return config; + +on_error: + + aws_http_proxy_config_destroy(config); + + return NULL; +} + void aws_http_proxy_config_destroy(struct aws_http_proxy_config *config) { if (config == NULL) { return; diff --git a/source/proxy_strategy.c b/source/proxy_strategy.c index 63bee3b24..4b7ae33ca 100644 --- a/source/proxy_strategy.c +++ b/source/proxy_strategy.c @@ -202,7 +202,7 @@ void s_basic_auth_tunnel_add_header( struct aws_http_proxy_negotiator_basic_auth *basic_auth_negotiator = proxy_negotiator->impl; if (basic_auth_negotiator->connect_state != AWS_PNCS_READY) { - negotiation_termination_callback(message, AWS_ERROR_INVALID_STATE, internal_proxy_user_data); + negotiation_termination_callback(message, AWS_ERROR_HTTP_PROXY_CONNECT_FAILED, internal_proxy_user_data); return; } @@ -359,7 +359,7 @@ void s_one_time_identity_connect_transform( struct aws_http_proxy_negotiator_one_time_identity *one_time_identity_negotiator = proxy_negotiator->impl; if (one_time_identity_negotiator->connect_state != AWS_PNCS_READY) { - negotiation_termination_callback(message, AWS_ERROR_INVALID_STATE, internal_proxy_user_data); + negotiation_termination_callback(message, AWS_ERROR_HTTP_PROXY_CONNECT_FAILED, internal_proxy_user_data); return; } @@ -548,326 +548,6 @@ struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_forwarding_identity( return &identity_strategy->strategy_base; } -/******************************************************************************************************************/ - -struct aws_http_proxy_strategy_tunneling_chain { - struct aws_allocator *allocator; - - struct aws_array_list strategies; - - struct aws_http_proxy_strategy strategy_base; -}; - -struct aws_http_proxy_negotiator_tunneling_chain { - struct aws_allocator *allocator; - - struct aws_array_list negotiators; - size_t current_negotiator_transform_index; - void *original_internal_proxy_user_data; - aws_http_proxy_negotiation_terminate_fn *original_negotiation_termination_callback; - aws_http_proxy_negotiation_http_request_forward_fn *original_negotiation_http_request_forward_callback; - - struct aws_http_proxy_negotiator negotiator_base; -}; - -static void s_chain_tunnel_try_next_negotiator( - struct aws_http_proxy_negotiator *proxy_negotiator, - struct aws_http_message *message); - -static void s_chain_tunnel_iteration_termination_callback( - struct aws_http_message *message, - int error_code, - void *user_data) { - - (void)error_code; /* TODO: log */ - - struct aws_http_proxy_negotiator *proxy_negotiator = user_data; - struct aws_http_proxy_negotiator_tunneling_chain *chain_negotiator = proxy_negotiator->impl; - - chain_negotiator->current_negotiator_transform_index++; - - s_chain_tunnel_try_next_negotiator(proxy_negotiator, message); -} - -static void s_chain_tunnel_iteration_forward_callback(struct aws_http_message *message, void *user_data) { - struct aws_http_proxy_negotiator *proxy_negotiator = user_data; - struct aws_http_proxy_negotiator_tunneling_chain *chain_negotiator = proxy_negotiator->impl; - - chain_negotiator->original_negotiation_http_request_forward_callback( - message, chain_negotiator->original_internal_proxy_user_data); -} - -static void s_chain_tunnel_try_next_negotiator( - struct aws_http_proxy_negotiator *proxy_negotiator, - struct aws_http_message *message) { - struct aws_http_proxy_negotiator_tunneling_chain *chain_negotiator = proxy_negotiator->impl; - - size_t negotiator_count = aws_array_list_length(&chain_negotiator->negotiators); - if (chain_negotiator->current_negotiator_transform_index >= negotiator_count) { - goto on_error; - } - - struct aws_http_proxy_negotiator *current_negotiator = NULL; - if (aws_array_list_get_at( - &chain_negotiator->negotiators, - ¤t_negotiator, - chain_negotiator->current_negotiator_transform_index)) { - goto on_error; - } - - current_negotiator->strategy_vtable.tunnelling_vtable->connect_request_transform( - current_negotiator, - message, - s_chain_tunnel_iteration_termination_callback, - s_chain_tunnel_iteration_forward_callback, - proxy_negotiator); - return; - -on_error: - - chain_negotiator->original_negotiation_termination_callback( - message, AWS_ERROR_HTTP_PROXY_CONNECT_FAILED, chain_negotiator->original_internal_proxy_user_data); -} - -static void s_chain_tunnel_transform_connect( - struct aws_http_proxy_negotiator *proxy_negotiator, - struct aws_http_message *message, - aws_http_proxy_negotiation_terminate_fn *negotiation_termination_callback, - aws_http_proxy_negotiation_http_request_forward_fn *negotiation_http_request_forward_callback, - void *internal_proxy_user_data) { - - struct aws_http_proxy_negotiator_tunneling_chain *chain_negotiator = proxy_negotiator->impl; - - chain_negotiator->current_negotiator_transform_index = 0; - chain_negotiator->original_internal_proxy_user_data = internal_proxy_user_data; - chain_negotiator->original_negotiation_termination_callback = negotiation_termination_callback; - chain_negotiator->original_negotiation_http_request_forward_callback = negotiation_http_request_forward_callback; - - s_chain_tunnel_try_next_negotiator(proxy_negotiator, message); -} - -static int s_chain_on_incoming_headers( - struct aws_http_proxy_negotiator *proxy_negotiator, - enum aws_http_header_block header_block, - const struct aws_http_header *header_array, - size_t num_headers) { - - struct aws_http_proxy_negotiator_tunneling_chain *chain_negotiator = proxy_negotiator->impl; - size_t negotiator_count = aws_array_list_length(&chain_negotiator->negotiators); - for (size_t i = 0; i < negotiator_count; ++i) { - struct aws_http_proxy_negotiator *negotiator = NULL; - if (aws_array_list_get_at(&chain_negotiator->negotiators, &negotiator, i)) { - continue; - } - - aws_http_proxy_negotiation_connect_on_incoming_headers_fn *on_incoming_headers = - negotiator->strategy_vtable.tunnelling_vtable->on_incoming_headers_callback; - if (on_incoming_headers != NULL) { - (*on_incoming_headers)(negotiator, header_block, header_array, num_headers); - } - } - - return AWS_OP_SUCCESS; -} - -static int s_chain_on_connect_status( - struct aws_http_proxy_negotiator *proxy_negotiator, - enum aws_http_status_code status_code) { - - struct aws_http_proxy_negotiator_tunneling_chain *chain_negotiator = proxy_negotiator->impl; - size_t negotiator_count = aws_array_list_length(&chain_negotiator->negotiators); - for (size_t i = 0; i < negotiator_count; ++i) { - struct aws_http_proxy_negotiator *negotiator = NULL; - if (aws_array_list_get_at(&chain_negotiator->negotiators, &negotiator, i)) { - continue; - } - - aws_http_proxy_negotiator_connect_status_fn *on_status = - negotiator->strategy_vtable.tunnelling_vtable->on_status_callback; - if (on_status != NULL) { - (*on_status)(negotiator, status_code); - } - } - - return AWS_OP_SUCCESS; -} - -static int s_chain_on_incoming_body( - struct aws_http_proxy_negotiator *proxy_negotiator, - const struct aws_byte_cursor *data) { - - struct aws_http_proxy_negotiator_tunneling_chain *chain_negotiator = proxy_negotiator->impl; - size_t negotiator_count = aws_array_list_length(&chain_negotiator->negotiators); - for (size_t i = 0; i < negotiator_count; ++i) { - struct aws_http_proxy_negotiator *negotiator = NULL; - if (aws_array_list_get_at(&chain_negotiator->negotiators, &negotiator, i)) { - continue; - } - - aws_http_proxy_negotiator_connect_on_incoming_body_fn *on_incoming_body = - negotiator->strategy_vtable.tunnelling_vtable->on_incoming_body_callback; - if (on_incoming_body != NULL) { - (*on_incoming_body)(negotiator, data); - } - } - - return AWS_OP_SUCCESS; -} - -static struct aws_http_proxy_negotiator_tunnelling_vtable s_tunneling_chain_proxy_negotiator_tunneling_vtable = { - .on_incoming_body_callback = s_chain_on_incoming_body, - .on_incoming_headers_callback = s_chain_on_incoming_headers, - .on_status_callback = s_chain_on_connect_status, - .connect_request_transform = s_chain_tunnel_transform_connect, -}; - -static void s_destroy_tunneling_chain_negotiator(struct aws_http_proxy_negotiator *proxy_negotiator) { - struct aws_http_proxy_negotiator_tunneling_chain *chain_negotiator = proxy_negotiator->impl; - - size_t negotiator_count = aws_array_list_length(&chain_negotiator->negotiators); - for (size_t i = 0; i < negotiator_count; ++i) { - struct aws_http_proxy_negotiator *negotiator = NULL; - if (aws_array_list_get_at(&chain_negotiator->negotiators, &negotiator, i)) { - continue; - } - - aws_http_proxy_negotiator_release(negotiator); - } - - aws_array_list_clean_up(&chain_negotiator->negotiators); - - aws_mem_release(chain_negotiator->allocator, chain_negotiator); -} - -static struct aws_http_proxy_negotiator *s_create_tunneling_chain_negotiator( - struct aws_http_proxy_strategy *proxy_strategy, - struct aws_allocator *allocator) { - if (proxy_strategy == NULL || allocator == NULL) { - aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); - return NULL; - } - - struct aws_http_proxy_negotiator_tunneling_chain *chain_negotiator = - aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_negotiator_tunneling_chain)); - if (chain_negotiator == NULL) { - return NULL; - } - - chain_negotiator->allocator = allocator; - chain_negotiator->negotiator_base.impl = chain_negotiator; - aws_ref_count_init( - &chain_negotiator->negotiator_base.ref_count, - &chain_negotiator->negotiator_base, - (aws_simple_completion_callback *)s_destroy_tunneling_chain_negotiator); - - chain_negotiator->negotiator_base.strategy_vtable.tunnelling_vtable = - &s_tunneling_chain_proxy_negotiator_tunneling_vtable; - - struct aws_http_proxy_strategy_tunneling_chain *chain_strategy = proxy_strategy->impl; - size_t strategy_count = aws_array_list_length(&chain_strategy->strategies); - - if (aws_array_list_init_dynamic( - &chain_negotiator->negotiators, allocator, strategy_count, sizeof(struct aws_http_proxy_negotiator *))) { - goto on_error; - } - - for (size_t i = 0; i < strategy_count; ++i) { - struct aws_http_proxy_strategy *strategy = NULL; - if (aws_array_list_get_at(&chain_strategy->strategies, &strategy, i)) { - goto on_error; - } - - struct aws_http_proxy_negotiator *negotiator = aws_http_proxy_strategy_create_negotiator(strategy, allocator); - if (negotiator == NULL) { - goto on_error; - } - - if (aws_array_list_push_back(&chain_negotiator->negotiators, &negotiator)) { - aws_http_proxy_negotiator_release(negotiator); - goto on_error; - } - } - - return &chain_negotiator->negotiator_base; - -on_error: - - aws_http_proxy_negotiator_release(&chain_negotiator->negotiator_base); - - return NULL; -} - -static struct aws_http_proxy_strategy_vtable s_tunneling_chain_strategy_vtable = { - .create_negotiator = s_create_tunneling_chain_negotiator, -}; - -static void s_destroy_tunneling_chain_strategy(struct aws_http_proxy_strategy *proxy_strategy) { - struct aws_http_proxy_strategy_tunneling_chain *chain_strategy = proxy_strategy->impl; - - size_t strategy_count = aws_array_list_length(&chain_strategy->strategies); - for (size_t i = 0; i < strategy_count; ++i) { - struct aws_http_proxy_strategy *strategy = NULL; - if (aws_array_list_get_at(&chain_strategy->strategies, &strategy, i)) { - continue; - } - - aws_http_proxy_strategy_release(strategy); - } - - aws_array_list_clean_up(&chain_strategy->strategies); - - aws_mem_release(chain_strategy->allocator, chain_strategy); -} - -struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_tunneling_chain( - struct aws_allocator *allocator, - struct aws_http_proxy_strategy_tunneling_chain_options *config) { - - if (allocator == NULL || config == NULL) { - aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); - return NULL; - } - - struct aws_http_proxy_strategy_tunneling_chain *chain_strategy = - aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_strategy_tunneling_chain)); - if (chain_strategy == NULL) { - return NULL; - } - - chain_strategy->strategy_base.impl = chain_strategy; - chain_strategy->strategy_base.vtable = &s_tunneling_chain_strategy_vtable; - chain_strategy->strategy_base.proxy_connection_type = AWS_HPCT_HTTP_TUNNEL; - chain_strategy->allocator = allocator; - - aws_ref_count_init( - &chain_strategy->strategy_base.ref_count, - &chain_strategy->strategy_base, - (aws_simple_completion_callback *)s_destroy_tunneling_chain_strategy); - - if (aws_array_list_init_dynamic( - &chain_strategy->strategies, allocator, config->strategy_count, sizeof(struct aws_http_proxy_strategy *))) { - goto on_error; - } - - for (size_t i = 0; i < config->strategy_count; ++i) { - struct aws_http_proxy_strategy *strategy = config->strategies[i]; - - if (aws_array_list_push_back(&chain_strategy->strategies, &strategy)) { - goto on_error; - } - - aws_http_proxy_strategy_acquire(strategy); - } - - return &chain_strategy->strategy_base; - -on_error: - - aws_http_proxy_strategy_release(&chain_strategy->strategy_base); - - return NULL; -} - /******************************************************************************************************************/ /* kerberos */ @@ -1442,7 +1122,7 @@ struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_tunneling_adaptive( struct aws_http_proxy_strategy *identity_strategy = NULL; struct aws_http_proxy_strategy *kerberos_strategy = NULL; struct aws_http_proxy_strategy *ntlm_strategy = NULL; - struct aws_http_proxy_strategy *adaptive_chain_strategy = NULL; + struct aws_http_proxy_strategy *adaptive_sequence_strategy = NULL; identity_strategy = aws_http_proxy_strategy_new_tunneling_one_time_identity(allocator); if (identity_strategy == NULL) { @@ -1468,13 +1148,13 @@ struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_tunneling_adaptive( strategies[strategy_count++] = ntlm_strategy; } - struct aws_http_proxy_strategy_tunneling_chain_options chain_config = { + struct aws_http_proxy_strategy_tunneling_sequence_options sequence_config = { .strategies = strategies, .strategy_count = strategy_count, }; - adaptive_chain_strategy = aws_http_proxy_strategy_new_tunneling_chain(allocator, &chain_config); - if (adaptive_chain_strategy == NULL) { + adaptive_sequence_strategy = aws_http_proxy_strategy_new_tunneling_sequence(allocator, &sequence_config); + if (adaptive_sequence_strategy == NULL) { goto done; } @@ -1484,7 +1164,327 @@ struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_tunneling_adaptive( aws_http_proxy_strategy_release(kerberos_strategy); aws_http_proxy_strategy_release(ntlm_strategy); - return adaptive_chain_strategy; + return adaptive_sequence_strategy; +} + +/******************************************************************************************************************/ + +struct aws_http_proxy_strategy_tunneling_sequence { + struct aws_allocator *allocator; + + struct aws_array_list strategies; + + struct aws_http_proxy_strategy strategy_base; +}; + +struct aws_http_proxy_negotiator_tunneling_sequence { + struct aws_allocator *allocator; + + struct aws_array_list negotiators; + size_t current_negotiator_transform_index; + void *original_internal_proxy_user_data; + aws_http_proxy_negotiation_terminate_fn *original_negotiation_termination_callback; + aws_http_proxy_negotiation_http_request_forward_fn *original_negotiation_http_request_forward_callback; + + struct aws_http_proxy_negotiator negotiator_base; +}; + +static void s_sequence_tunnel_iteration_termination_callback( + struct aws_http_message *message, + int error_code, + void *user_data) { + + struct aws_http_proxy_negotiator *proxy_negotiator = user_data; + struct aws_http_proxy_negotiator_tunneling_sequence *sequence_negotiator = proxy_negotiator->impl; + + AWS_LOGF_WARN(AWS_LS_HTTP_PROXY_NEGOTIATION, "(id=%p) Proxy negotiation step failed with error %d", (void *)proxy_negotiator, error_code); + + int connection_error_code = AWS_ERROR_HTTP_PROXY_CONNECT_FAILED_RETRYABLE; + if (sequence_negotiator->current_negotiator_transform_index >= aws_array_list_length(&sequence_negotiator->negotiators)) { + connection_error_code = AWS_ERROR_HTTP_PROXY_CONNECT_FAILED; + } + + sequence_negotiator->original_negotiation_termination_callback( + message, connection_error_code, sequence_negotiator->original_internal_proxy_user_data); +} + +static void s_sequence_tunnel_iteration_forward_callback(struct aws_http_message *message, void *user_data) { + struct aws_http_proxy_negotiator *proxy_negotiator = user_data; + struct aws_http_proxy_negotiator_tunneling_sequence *sequence_negotiator = proxy_negotiator->impl; + + sequence_negotiator->original_negotiation_http_request_forward_callback( + message, sequence_negotiator->original_internal_proxy_user_data); +} + +static void s_sequence_tunnel_try_next_negotiator( + struct aws_http_proxy_negotiator *proxy_negotiator, + struct aws_http_message *message) { + struct aws_http_proxy_negotiator_tunneling_sequence *sequence_negotiator = proxy_negotiator->impl; + + size_t negotiator_count = aws_array_list_length(&sequence_negotiator->negotiators); + if (sequence_negotiator->current_negotiator_transform_index >= negotiator_count) { + goto on_error; + } + + struct aws_http_proxy_negotiator *current_negotiator = NULL; + if (aws_array_list_get_at( + &sequence_negotiator->negotiators, + ¤t_negotiator, + sequence_negotiator->current_negotiator_transform_index++)) { + goto on_error; + } + + current_negotiator->strategy_vtable.tunnelling_vtable->connect_request_transform( + current_negotiator, + message, + s_sequence_tunnel_iteration_termination_callback, + s_sequence_tunnel_iteration_forward_callback, + proxy_negotiator); + + return; + +on_error: + + sequence_negotiator->original_negotiation_termination_callback( + message, AWS_ERROR_HTTP_PROXY_CONNECT_FAILED, sequence_negotiator->original_internal_proxy_user_data); +} + +static void s_sequence_tunnel_transform_connect( + struct aws_http_proxy_negotiator *proxy_negotiator, + struct aws_http_message *message, + aws_http_proxy_negotiation_terminate_fn *negotiation_termination_callback, + aws_http_proxy_negotiation_http_request_forward_fn *negotiation_http_request_forward_callback, + void *internal_proxy_user_data) { + + struct aws_http_proxy_negotiator_tunneling_sequence *sequence_negotiator = proxy_negotiator->impl; + + sequence_negotiator->original_internal_proxy_user_data = internal_proxy_user_data; + sequence_negotiator->original_negotiation_termination_callback = negotiation_termination_callback; + sequence_negotiator->original_negotiation_http_request_forward_callback = negotiation_http_request_forward_callback; + + s_sequence_tunnel_try_next_negotiator(proxy_negotiator, message); +} + +static int s_sequence_on_incoming_headers( + struct aws_http_proxy_negotiator *proxy_negotiator, + enum aws_http_header_block header_block, + const struct aws_http_header *header_array, + size_t num_headers) { + + struct aws_http_proxy_negotiator_tunneling_sequence *sequence_negotiator = proxy_negotiator->impl; + size_t negotiator_count = aws_array_list_length(&sequence_negotiator->negotiators); + for (size_t i = 0; i < negotiator_count; ++i) { + struct aws_http_proxy_negotiator *negotiator = NULL; + if (aws_array_list_get_at(&sequence_negotiator->negotiators, &negotiator, i)) { + continue; + } + + aws_http_proxy_negotiation_connect_on_incoming_headers_fn *on_incoming_headers = + negotiator->strategy_vtable.tunnelling_vtable->on_incoming_headers_callback; + if (on_incoming_headers != NULL) { + (*on_incoming_headers)(negotiator, header_block, header_array, num_headers); + } + } + + return AWS_OP_SUCCESS; +} + +static int s_sequence_on_connect_status( + struct aws_http_proxy_negotiator *proxy_negotiator, + enum aws_http_status_code status_code) { + + struct aws_http_proxy_negotiator_tunneling_sequence *sequence_negotiator = proxy_negotiator->impl; + size_t negotiator_count = aws_array_list_length(&sequence_negotiator->negotiators); + for (size_t i = 0; i < negotiator_count; ++i) { + struct aws_http_proxy_negotiator *negotiator = NULL; + if (aws_array_list_get_at(&sequence_negotiator->negotiators, &negotiator, i)) { + continue; + } + + aws_http_proxy_negotiator_connect_status_fn *on_status = + negotiator->strategy_vtable.tunnelling_vtable->on_status_callback; + if (on_status != NULL) { + (*on_status)(negotiator, status_code); + } + } + + return AWS_OP_SUCCESS; +} + +static int s_sequence_on_incoming_body( + struct aws_http_proxy_negotiator *proxy_negotiator, + const struct aws_byte_cursor *data) { + + struct aws_http_proxy_negotiator_tunneling_sequence *sequence_negotiator = proxy_negotiator->impl; + size_t negotiator_count = aws_array_list_length(&sequence_negotiator->negotiators); + for (size_t i = 0; i < negotiator_count; ++i) { + struct aws_http_proxy_negotiator *negotiator = NULL; + if (aws_array_list_get_at(&sequence_negotiator->negotiators, &negotiator, i)) { + continue; + } + + aws_http_proxy_negotiator_connect_on_incoming_body_fn *on_incoming_body = + negotiator->strategy_vtable.tunnelling_vtable->on_incoming_body_callback; + if (on_incoming_body != NULL) { + (*on_incoming_body)(negotiator, data); + } + } + + return AWS_OP_SUCCESS; +} + +static struct aws_http_proxy_negotiator_tunnelling_vtable s_tunneling_sequence_proxy_negotiator_tunneling_vtable = { + .on_incoming_body_callback = s_sequence_on_incoming_body, + .on_incoming_headers_callback = s_sequence_on_incoming_headers, + .on_status_callback = s_sequence_on_connect_status, + .connect_request_transform = s_sequence_tunnel_transform_connect, +}; + +static void s_destroy_tunneling_sequence_negotiator(struct aws_http_proxy_negotiator *proxy_negotiator) { + struct aws_http_proxy_negotiator_tunneling_sequence *sequence_negotiator = proxy_negotiator->impl; + + size_t negotiator_count = aws_array_list_length(&sequence_negotiator->negotiators); + for (size_t i = 0; i < negotiator_count; ++i) { + struct aws_http_proxy_negotiator *negotiator = NULL; + if (aws_array_list_get_at(&sequence_negotiator->negotiators, &negotiator, i)) { + continue; + } + + aws_http_proxy_negotiator_release(negotiator); + } + + aws_array_list_clean_up(&sequence_negotiator->negotiators); + + aws_mem_release(sequence_negotiator->allocator, sequence_negotiator); +} + +static struct aws_http_proxy_negotiator *s_create_tunneling_sequence_negotiator( + struct aws_http_proxy_strategy *proxy_strategy, + struct aws_allocator *allocator) { + if (proxy_strategy == NULL || allocator == NULL) { + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + return NULL; + } + + struct aws_http_proxy_negotiator_tunneling_sequence *sequence_negotiator = + aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_negotiator_tunneling_sequence)); + if (sequence_negotiator == NULL) { + return NULL; + } + + sequence_negotiator->allocator = allocator; + sequence_negotiator->negotiator_base.impl = sequence_negotiator; + aws_ref_count_init( + &sequence_negotiator->negotiator_base.ref_count, + &sequence_negotiator->negotiator_base, + (aws_simple_completion_callback *)s_destroy_tunneling_sequence_negotiator); + + sequence_negotiator->negotiator_base.strategy_vtable.tunnelling_vtable = + &s_tunneling_sequence_proxy_negotiator_tunneling_vtable; + + struct aws_http_proxy_strategy_tunneling_sequence *sequence_strategy = proxy_strategy->impl; + size_t strategy_count = aws_array_list_length(&sequence_strategy->strategies); + + if (aws_array_list_init_dynamic( + &sequence_negotiator->negotiators, allocator, strategy_count, sizeof(struct aws_http_proxy_negotiator *))) { + goto on_error; + } + + for (size_t i = 0; i < strategy_count; ++i) { + struct aws_http_proxy_strategy *strategy = NULL; + if (aws_array_list_get_at(&sequence_strategy->strategies, &strategy, i)) { + goto on_error; + } + + struct aws_http_proxy_negotiator *negotiator = aws_http_proxy_strategy_create_negotiator(strategy, allocator); + if (negotiator == NULL) { + goto on_error; + } + + if (aws_array_list_push_back(&sequence_negotiator->negotiators, &negotiator)) { + aws_http_proxy_negotiator_release(negotiator); + goto on_error; + } + } + + return &sequence_negotiator->negotiator_base; + +on_error: + + aws_http_proxy_negotiator_release(&sequence_negotiator->negotiator_base); + + return NULL; +} + +static struct aws_http_proxy_strategy_vtable s_tunneling_sequence_strategy_vtable = { + .create_negotiator = s_create_tunneling_sequence_negotiator, +}; + +static void s_destroy_tunneling_sequence_strategy(struct aws_http_proxy_strategy *proxy_strategy) { + struct aws_http_proxy_strategy_tunneling_sequence *sequence_strategy = proxy_strategy->impl; + + size_t strategy_count = aws_array_list_length(&sequence_strategy->strategies); + for (size_t i = 0; i < strategy_count; ++i) { + struct aws_http_proxy_strategy *strategy = NULL; + if (aws_array_list_get_at(&sequence_strategy->strategies, &strategy, i)) { + continue; + } + + aws_http_proxy_strategy_release(strategy); + } + + aws_array_list_clean_up(&sequence_strategy->strategies); + + aws_mem_release(sequence_strategy->allocator, sequence_strategy); +} + +struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_tunneling_sequence( + struct aws_allocator *allocator, + struct aws_http_proxy_strategy_tunneling_sequence_options *config) { + + if (allocator == NULL || config == NULL) { + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + return NULL; + } + + struct aws_http_proxy_strategy_tunneling_sequence *sequence_strategy = + aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_strategy_tunneling_sequence)); + if (sequence_strategy == NULL) { + return NULL; + } + + sequence_strategy->strategy_base.impl = sequence_strategy; + sequence_strategy->strategy_base.vtable = &s_tunneling_sequence_strategy_vtable; + sequence_strategy->strategy_base.proxy_connection_type = AWS_HPCT_HTTP_TUNNEL; + sequence_strategy->allocator = allocator; + + aws_ref_count_init( + &sequence_strategy->strategy_base.ref_count, + &sequence_strategy->strategy_base, + (aws_simple_completion_callback *)s_destroy_tunneling_sequence_strategy); + + if (aws_array_list_init_dynamic( + &sequence_strategy->strategies, allocator, config->strategy_count, sizeof(struct aws_http_proxy_strategy *))) { + goto on_error; + } + + for (size_t i = 0; i < config->strategy_count; ++i) { + struct aws_http_proxy_strategy *strategy = config->strategies[i]; + + if (aws_array_list_push_back(&sequence_strategy->strategies, &strategy)) { + goto on_error; + } + + aws_http_proxy_strategy_acquire(strategy); + } + + return &sequence_strategy->strategy_base; + +on_error: + + aws_http_proxy_strategy_release(&sequence_strategy->strategy_base); + + return NULL; } #if defined(_MSC_VER) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d3d394104..b61feb496 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -497,10 +497,10 @@ add_test_case(test_http_forwarding_proxy_request_transform_basic_auth) add_test_case(test_http_proxy_request_transform_kerberos) add_test_case(test_http_proxy_kerberos_token_failure) add_test_case(test_http_proxy_kerberos_connect_failure) -add_test_case(test_http_proxy_adaptive_identity_success) -add_test_case(test_http_proxy_adaptive_kerberos_success) -add_test_case(test_http_proxy_adaptive_ntlm_success) -add_test_case(test_http_proxy_adaptive_failure) +#add_test_case(test_http_proxy_adaptive_identity_success) +#add_test_case(test_http_proxy_adaptive_kerberos_success) +#add_test_case(test_http_proxy_adaptive_ntlm_success) +#add_test_case(test_http_proxy_adaptive_failure) add_test_case(test_http_forwarding_proxy_uri_rewrite) add_test_case(test_http_forwarding_proxy_uri_rewrite_options_star) add_test_case(test_http_tunnel_proxy_connection_success) diff --git a/tests/test_proxy.c b/tests/test_proxy.c index 44231d8ba..72fe91613 100644 --- a/tests/test_proxy.c +++ b/tests/test_proxy.c @@ -70,6 +70,7 @@ static struct aws_http_message *s_build_http_request(struct aws_allocator *alloc aws_byte_cursor_from_string(s_mock_request_host)); } +#ifdef NEVER static bool s_is_header_in_request(struct aws_http_message *request, struct aws_byte_cursor header_name) { size_t header_count = aws_http_message_get_header_count(request); for (size_t i = 0; i < header_count; ++i) { @@ -83,6 +84,7 @@ static bool s_is_header_in_request(struct aws_http_message *request, struct aws_ return false; } +#endif static bool s_is_header_and_value_in_request(struct aws_http_message *request, struct aws_http_header *header) { size_t header_count = aws_http_message_get_header_count(request); @@ -715,6 +717,8 @@ static int s_test_http_proxy_kerberos_connect_failure(struct aws_allocator *allo AWS_TEST_CASE(test_http_proxy_kerberos_connect_failure, s_test_http_proxy_kerberos_connect_failure); +#ifdef NEVER + AWS_STATIC_STRING_FROM_LITERAL(s_mock_ntlm_token_value, "NTLM_RESPONSE"); static struct aws_string *s_mock_aws_http_proxy_negotiation_ntlm_get_challenge_token_sync_fn( @@ -969,6 +973,7 @@ static int s_test_http_proxy_adaptive_failure(struct aws_allocator *allocator, v } AWS_TEST_CASE(test_http_proxy_adaptive_failure, s_test_http_proxy_adaptive_failure); +#endif AWS_STATIC_STRING_FROM_LITERAL(s_rewrite_host, "www.uri.com"); AWS_STATIC_STRING_FROM_LITERAL(s_rewrite_path, "/main/index.html?foo=bar"); From 9014f909255c7a59f0d07524677140b4ab9d3bb3 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Thu, 28 Jan 2021 12:08:49 -0800 Subject: [PATCH 34/54] Basic failure sequence testing --- source/proxy_connection.c | 27 +++++------- source/proxy_strategy.c | 22 +++++++--- tests/CMakeLists.txt | 1 + tests/integration_test_proxy.c | 79 ++++++++++++++++++++++++++++++++++ 4 files changed, 105 insertions(+), 24 deletions(-) diff --git a/source/proxy_connection.c b/source/proxy_connection.c index 353deb20c..1dca2ed06 100644 --- a/source/proxy_connection.c +++ b/source/proxy_connection.c @@ -201,7 +201,7 @@ struct aws_http_proxy_user_data *aws_http_proxy_user_data_new_reset_clone( * Connection callback used ONLY by http proxy connections. After this, * the connection is live and the user is notified */ -static void s_aws_http_on_client_connection_http_proxy_setup_fn( +static void s_aws_http_on_client_connection_http_forwarding_proxy_setup_fn( struct aws_http_connection *connection, int error_code, void *user_data) { @@ -477,20 +477,18 @@ static void s_aws_http_on_stream_complete_tunnel_proxy( } if (context->error_code != AWS_ERROR_SUCCESS) { - if (context->connect_status_code == AWS_HTTP_STATUS_CODE_407_PROXY_AUTHENTICATION_REQUIRED) { + context->error_code = AWS_ERROR_HTTP_PROXY_CONNECT_FAILED; + if (context->connect_status_code != AWS_HTTP_STATUS_CODE_407_PROXY_AUTHENTICATION_REQUIRED) { struct aws_http_proxy_user_data *new_context = aws_http_proxy_user_data_new_reset_clone(context->allocator, context); - if (new_context == NULL || s_create_tunneling_connection(new_context)) { - context->error_code = AWS_ERROR_HTTP_PROXY_CONNECT_FAILED; - s_aws_http_proxy_user_data_shutdown(context); - } else { + if (new_context != NULL && s_create_tunneling_connection(new_context) == AWS_OP_SUCCESS) { context->original_on_shutdown = NULL; context->original_on_setup = NULL; + context->error_code = AWS_ERROR_HTTP_PROXY_CONNECT_FAILED_RETRYABLE; } - } else { - context->error_code = AWS_ERROR_HTTP_PROXY_CONNECT_FAILED; - s_aws_http_proxy_user_data_shutdown(context); } + + s_aws_http_proxy_user_data_shutdown(context); return; } @@ -603,11 +601,6 @@ static void s_continue_tunneling_connect(struct aws_http_message *message, void * of upgrading with TLS on success */ static int s_make_proxy_connect_request(struct aws_http_proxy_user_data *user_data) { - if (user_data->connect_request != NULL) { - aws_http_message_destroy(user_data->connect_request); - user_data->connect_request = NULL; - } - user_data->connect_request = s_build_proxy_connect_request(user_data); if (user_data->connect_request == NULL) { return AWS_OP_ERR; @@ -627,7 +620,7 @@ static int s_make_proxy_connect_request(struct aws_http_proxy_user_data *user_da * Connection setup callback for tls-based proxy connections. * Could be unified with non-tls version by checking tls options and branching post-success */ -static void s_aws_http_on_client_connection_http_tunnel_proxy_setup_fn( +static void s_aws_http_on_client_connection_http_tunneling_proxy_setup_fn( struct aws_http_connection *connection, int error_code, void *user_data) { @@ -807,7 +800,7 @@ static int s_aws_http_client_connect_via_forwarding_proxy(const struct aws_http_ options_copy.host_name = options->proxy_options->host; options_copy.port = options->proxy_options->port; options_copy.user_data = proxy_user_data; - options_copy.on_setup = s_aws_http_on_client_connection_http_proxy_setup_fn; + options_copy.on_setup = s_aws_http_on_client_connection_http_forwarding_proxy_setup_fn; options_copy.on_shutdown = s_aws_http_on_client_connection_http_proxy_shutdown_fn; options_copy.tls_options = options->proxy_options->tls_options; @@ -840,7 +833,7 @@ static int s_create_tunneling_connection(struct aws_http_proxy_user_data *user_d connect_options.manual_window_management = user_data->manual_window_management; connect_options.initial_window_size = user_data->initial_window_size; connect_options.user_data = user_data; - connect_options.on_setup = s_aws_http_on_client_connection_http_tunnel_proxy_setup_fn; + connect_options.on_setup = s_aws_http_on_client_connection_http_tunneling_proxy_setup_fn; connect_options.on_shutdown = s_aws_http_on_client_connection_http_proxy_shutdown_fn; connect_options.http1_options = NULL; /* ToDo */ connect_options.http2_options = NULL; /* ToDo */ diff --git a/source/proxy_strategy.c b/source/proxy_strategy.c index 4b7ae33ca..fd29c1d9d 100644 --- a/source/proxy_strategy.c +++ b/source/proxy_strategy.c @@ -1197,10 +1197,15 @@ static void s_sequence_tunnel_iteration_termination_callback( struct aws_http_proxy_negotiator *proxy_negotiator = user_data; struct aws_http_proxy_negotiator_tunneling_sequence *sequence_negotiator = proxy_negotiator->impl; - AWS_LOGF_WARN(AWS_LS_HTTP_PROXY_NEGOTIATION, "(id=%p) Proxy negotiation step failed with error %d", (void *)proxy_negotiator, error_code); + AWS_LOGF_WARN( + AWS_LS_HTTP_PROXY_NEGOTIATION, + "(id=%p) Proxy negotiation step failed with error %d", + (void *)proxy_negotiator, + error_code); int connection_error_code = AWS_ERROR_HTTP_PROXY_CONNECT_FAILED_RETRYABLE; - if (sequence_negotiator->current_negotiator_transform_index >= aws_array_list_length(&sequence_negotiator->negotiators)) { + if (sequence_negotiator->current_negotiator_transform_index >= + aws_array_list_length(&sequence_negotiator->negotiators)) { connection_error_code = AWS_ERROR_HTTP_PROXY_CONNECT_FAILED; } @@ -1228,9 +1233,9 @@ static void s_sequence_tunnel_try_next_negotiator( struct aws_http_proxy_negotiator *current_negotiator = NULL; if (aws_array_list_get_at( - &sequence_negotiator->negotiators, - ¤t_negotiator, - sequence_negotiator->current_negotiator_transform_index++)) { + &sequence_negotiator->negotiators, + ¤t_negotiator, + sequence_negotiator->current_negotiator_transform_index++)) { goto on_error; } @@ -1386,7 +1391,7 @@ static struct aws_http_proxy_negotiator *s_create_tunneling_sequence_negotiator( size_t strategy_count = aws_array_list_length(&sequence_strategy->strategies); if (aws_array_list_init_dynamic( - &sequence_negotiator->negotiators, allocator, strategy_count, sizeof(struct aws_http_proxy_negotiator *))) { + &sequence_negotiator->negotiators, allocator, strategy_count, sizeof(struct aws_http_proxy_negotiator *))) { goto on_error; } @@ -1464,7 +1469,10 @@ struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_tunneling_sequence( (aws_simple_completion_callback *)s_destroy_tunneling_sequence_strategy); if (aws_array_list_init_dynamic( - &sequence_strategy->strategies, allocator, config->strategy_count, sizeof(struct aws_http_proxy_strategy *))) { + &sequence_strategy->strategies, + allocator, + config->strategy_count, + sizeof(struct aws_http_proxy_strategy *))) { goto on_error; } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index b61feb496..e8f84eda8 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -517,6 +517,7 @@ if (ENABLE_PROXY_INTEGRATION_TESTS) add_test_case(test_https_proxy_connection_new_destroy) add_test_case(test_https_proxy_connection_get) add_test_case(test_nested_https_proxy_connection_get) + add_test_case(test_proxy_sequential_negotiation) endif() add_test_case(test_http_connection_monitor_options_is_valid) diff --git a/tests/integration_test_proxy.c b/tests/integration_test_proxy.c index 5b33bbab5..db8b9d460 100644 --- a/tests/integration_test_proxy.c +++ b/tests/integration_test_proxy.c @@ -271,3 +271,82 @@ static int s_test_nested_https_proxy_connection_get(struct aws_allocator *alloca return AWS_OP_SUCCESS; } AWS_TEST_CASE(test_nested_https_proxy_connection_get, s_test_nested_https_proxy_connection_get); + +#include + +AWS_STATIC_STRING_FROM_LITERAL(s_mock_kerberos_token_value, "abcdefABCDEF123"); + +static struct aws_string *s_mock_aws_http_proxy_negotiation_kerberos_get_token_sync_fn( + void *user_data, + int *out_error_code) { + + struct aws_allocator *allocator = user_data; + + *out_error_code = AWS_ERROR_SUCCESS; + return aws_string_new_from_string(allocator, s_mock_kerberos_token_value); +} + +AWS_STATIC_STRING_FROM_LITERAL(s_mock_ntlm_token_value, "NTLM_RESPONSE"); + +static struct aws_string *s_mock_aws_http_proxy_negotiation_ntlm_get_challenge_token_sync_fn( + void *user_data, + const struct aws_byte_cursor *challenge_value, + int *out_error_code) { + + struct aws_allocator *allocator = user_data; + + *out_error_code = AWS_ERROR_SUCCESS; + return aws_string_new_from_string(allocator, s_mock_ntlm_token_value); +} + +static int s_test_proxy_sequential_negotiation(struct aws_allocator *allocator, void *ctx) { + + struct aws_http_proxy_strategy_tunneling_kerberos_options kerberos_config = { + .get_token = s_mock_aws_http_proxy_negotiation_kerberos_get_token_sync_fn, + .get_token_user_data = allocator, + }; + + struct aws_http_proxy_strategy_tunneling_ntlm_options ntlm_config = { + .get_challenge_token = s_mock_aws_http_proxy_negotiation_ntlm_get_challenge_token_sync_fn, + .get_challenge_token_user_data = allocator, + }; + + (void)ntlm_config; + + struct aws_http_proxy_strategy_tunneling_adaptive_options adaptive_config = { + .kerberos_options = &kerberos_config, .ntlm_options = NULL, /*&ntlm_config,*/ + }; + + struct aws_http_proxy_strategy *proxy_strategy = + aws_http_proxy_strategy_new_tunneling_adaptive(allocator, &adaptive_config); + + struct aws_http_proxy_options proxy_options = { + .connection_type = AWS_HPCT_HTTP_TUNNEL, + .host = aws_byte_cursor_from_string(s_proxy_host_name), + .port = HTTPS_PROXY_PORT, + .tls_options = NULL, + .proxy_strategy = proxy_strategy, + }; + + struct proxy_tester_options options = { + .alloc = allocator, + .proxy_options = &proxy_options, + .host = aws_byte_cursor_from_c_str("www.amazon.com"), + .port = 443, + .test_mode = PTTM_HTTP_TUNNEL, + .failure_type = PTFT_NONE, + }; + + ASSERT_SUCCESS(proxy_tester_init(&tester, &options)); + ASSERT_SUCCESS(proxy_tester_wait(&tester, proxy_tester_connection_setup_pred)); + + ASSERT_TRUE(tester.wait_result != AWS_ERROR_SUCCESS); + + ASSERT_SUCCESS(proxy_tester_clean_up(&tester)); + + aws_http_proxy_strategy_release(proxy_strategy); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(test_proxy_sequential_negotiation, s_test_proxy_sequential_negotiation); From d441a2d86d05f99a605bffbaec79b342aedf186b Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Thu, 28 Jan 2021 12:13:11 -0800 Subject: [PATCH 35/54] Use full adapative in failure test --- tests/integration_test_proxy.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/integration_test_proxy.c b/tests/integration_test_proxy.c index db8b9d460..3be7a6989 100644 --- a/tests/integration_test_proxy.c +++ b/tests/integration_test_proxy.c @@ -311,10 +311,10 @@ static int s_test_proxy_sequential_negotiation(struct aws_allocator *allocator, .get_challenge_token_user_data = allocator, }; - (void)ntlm_config; struct aws_http_proxy_strategy_tunneling_adaptive_options adaptive_config = { - .kerberos_options = &kerberos_config, .ntlm_options = NULL, /*&ntlm_config,*/ + .kerberos_options = &kerberos_config, + .ntlm_options = &ntlm_config, }; struct aws_http_proxy_strategy *proxy_strategy = From c042ee192d1b5ba6e9848abb54cd69f8fee78534 Mon Sep 17 00:00:00 2001 From: Abhishek Jain <61713632+ajainaus@users.noreply.github.com> Date: Thu, 28 Jan 2021 14:30:36 -0600 Subject: [PATCH 36/54] Proxy request flow no one time strategy (#303) * ntlm fixes --- include/aws/http/proxy_strategy.h | 3 +- source/proxy_connection.c | 1 + source/proxy_strategy.c | 178 ++++++++++++++++++++++++++++-- 3 files changed, 174 insertions(+), 8 deletions(-) diff --git a/include/aws/http/proxy_strategy.h b/include/aws/http/proxy_strategy.h index 5dca15653..2fdd24913 100644 --- a/include/aws/http/proxy_strategy.h +++ b/include/aws/http/proxy_strategy.h @@ -191,6 +191,8 @@ struct aws_http_proxy_strategy_tunneling_kerberos_options { struct aws_http_proxy_strategy_tunneling_ntlm_options { + aws_http_proxy_negotiation_get_token_sync_fn *get_token; + aws_http_proxy_negotiation_get_challenge_token_sync_fn *get_challenge_token; void *get_challenge_token_user_data; @@ -339,7 +341,6 @@ AWS_HTTP_API struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_tunneling_adaptive( struct aws_allocator *allocator, struct aws_http_proxy_strategy_tunneling_adaptive_options *config); - AWS_EXTERN_C_END #endif /* AWS_PROXY_STRATEGY_H */ diff --git a/source/proxy_connection.c b/source/proxy_connection.c index 49ad4a1f4..78d26ad54 100644 --- a/source/proxy_connection.c +++ b/source/proxy_connection.c @@ -830,6 +830,7 @@ static struct aws_http_proxy_config *s_aws_http_proxy_config_new( config->connection_type = override_proxy_connection_type; if (aws_byte_buf_init_copy_from_cursor(&config->host, allocator, proxy_options->host)) { + goto on_error; } diff --git a/source/proxy_strategy.c b/source/proxy_strategy.c index 63bee3b24..0cf142b71 100644 --- a/source/proxy_strategy.c +++ b/source/proxy_strategy.c @@ -566,7 +566,6 @@ struct aws_http_proxy_negotiator_tunneling_chain { void *original_internal_proxy_user_data; aws_http_proxy_negotiation_terminate_fn *original_negotiation_termination_callback; aws_http_proxy_negotiation_http_request_forward_fn *original_negotiation_http_request_forward_callback; - struct aws_http_proxy_negotiator negotiator_base; }; @@ -954,7 +953,6 @@ static void s_kerberos_tunnel_transform_connect( aws_http_proxy_negotiation_terminate_fn *negotiation_termination_callback, aws_http_proxy_negotiation_http_request_forward_fn *negotiation_http_request_forward_callback, void *internal_proxy_user_data) { - struct aws_http_proxy_negotiator_tunneling_kerberos *kerberos_negotiator = proxy_negotiator->impl; struct aws_http_proxy_strategy_tunneling_kerberos *kerberos_strategy = kerberos_negotiator->strategy->impl; @@ -1129,6 +1127,7 @@ struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_tunneling_kerberos( &kerberos_strategy->strategy_base, (aws_simple_completion_callback *)s_destroy_tunneling_kerberos_strategy); + kerberos_strategy->get_token = config->get_token; kerberos_strategy->get_token_user_data = config->get_token_user_data; @@ -1140,6 +1139,8 @@ struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_tunneling_kerberos( struct aws_http_proxy_strategy_tunneling_ntlm { struct aws_allocator *allocator; + aws_http_proxy_negotiation_get_token_sync_fn *get_token; + aws_http_proxy_negotiation_get_challenge_token_sync_fn *get_challenge_token; void *get_challenge_token_user_data; @@ -1154,6 +1155,8 @@ struct aws_http_proxy_negotiator_tunneling_ntlm { enum proxy_negotiator_connect_state connect_state; + struct aws_string *token; + struct aws_string *challenge_token; struct aws_http_proxy_negotiator negotiator_base; @@ -1422,10 +1425,156 @@ struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_tunneling_ntlm( return &ntlm_strategy->strategy_base; } +/******************************************************************************************************/ + +static void s_ntlm_credential_tunnel_transform_connect( + struct aws_http_proxy_negotiator *proxy_negotiator, + struct aws_http_message *message, + aws_http_proxy_negotiation_terminate_fn *negotiation_termination_callback, + aws_http_proxy_negotiation_http_request_forward_fn *negotiation_http_request_forward_callback, + void *internal_proxy_user_data) { + + struct aws_http_proxy_negotiator_tunneling_ntlm *ntlm_credential_negotiator = proxy_negotiator->impl; + struct aws_http_proxy_strategy_tunneling_ntlm *ntlm_credential_strategy = + ntlm_credential_negotiator->strategy->impl; + + int result = AWS_OP_ERR; + int error_code = AWS_ERROR_SUCCESS; + struct aws_string *token = NULL; + + if (ntlm_credential_negotiator->connect_state == AWS_PNCS_FAILURE) { + error_code = AWS_ERROR_HTTP_PROXY_CONNECT_FAILED; + goto done; + } + + if (ntlm_credential_negotiator->connect_state != AWS_PNCS_READY) { + error_code = AWS_ERROR_INVALID_STATE; + goto done; + } + + ntlm_credential_negotiator->connect_state = AWS_PNCS_IN_PROGRESS; + token = ntlm_credential_strategy->get_token(ntlm_credential_strategy->get_challenge_token_user_data, &error_code); + + if (token == NULL || error_code != AWS_ERROR_SUCCESS) { + goto done; + } + + /*transform the header with proxy authenticate:Negotiate and kerberos token*/ + if (s_add_ntlm_proxy_usertoken_authentication_header( + ntlm_credential_negotiator->allocator, message, aws_byte_cursor_from_string(token))) { + error_code = aws_last_error(); + goto done; + } + + ntlm_credential_negotiator->connect_state = AWS_PNCS_IN_PROGRESS; + result = AWS_OP_SUCCESS; + +done: + + if (result != AWS_OP_SUCCESS) { + if (error_code == AWS_ERROR_SUCCESS) { + error_code = AWS_ERROR_UNKNOWN; + } + negotiation_termination_callback(message, error_code, internal_proxy_user_data); + } else { + negotiation_http_request_forward_callback(message, internal_proxy_user_data); + } + + aws_string_destroy(token); +} + +static struct aws_http_proxy_negotiator_tunnelling_vtable + s_tunneling_ntlm_proxy_credential_negotiator_tunneling_vtable = { + .on_incoming_body_callback = s_ntlm_on_incoming_body, + .on_incoming_headers_callback = s_ntlm_on_incoming_header_adaptive, + .on_status_callback = s_ntlm_on_connect_status, + .connect_request_transform = s_ntlm_credential_tunnel_transform_connect, +}; + +static void s_destroy_tunneling_ntlm_credential_negotiator(struct aws_http_proxy_negotiator *proxy_negotiator) { + struct aws_http_proxy_negotiator_tunneling_ntlm *ntlm_credential_negotiator = proxy_negotiator->impl; + + aws_string_destroy(ntlm_credential_negotiator->challenge_token); + aws_http_proxy_strategy_release(ntlm_credential_negotiator->strategy); + + aws_mem_release(ntlm_credential_negotiator->allocator, ntlm_credential_negotiator); +} + +static struct aws_http_proxy_negotiator *s_create_tunneling_ntlm_credential_negotiator( + struct aws_http_proxy_strategy *proxy_strategy, + struct aws_allocator *allocator) { + if (proxy_strategy == NULL || allocator == NULL) { + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + return NULL; + } + + struct aws_http_proxy_negotiator_tunneling_ntlm *ntlm_credential_negotiator = + aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_negotiator_tunneling_ntlm)); + if (ntlm_credential_negotiator == NULL) { + return NULL; + } + + ntlm_credential_negotiator->allocator = allocator; + ntlm_credential_negotiator->negotiator_base.impl = ntlm_credential_negotiator; + aws_ref_count_init( + &ntlm_credential_negotiator->negotiator_base.ref_count, + &ntlm_credential_negotiator->negotiator_base, + (aws_simple_completion_callback *)s_destroy_tunneling_ntlm_credential_negotiator); + + ntlm_credential_negotiator->negotiator_base.strategy_vtable.tunnelling_vtable = + &s_tunneling_ntlm_proxy_credential_negotiator_tunneling_vtable; + + ntlm_credential_negotiator->strategy = aws_http_proxy_strategy_acquire(proxy_strategy); + + return &ntlm_credential_negotiator->negotiator_base; +} + +static struct aws_http_proxy_strategy_vtable s_tunneling_ntlm_credential_strategy_vtable = { + .create_negotiator = s_create_tunneling_ntlm_credential_negotiator, +}; + +static void s_destroy_tunneling_ntlm_credential_strategy(struct aws_http_proxy_strategy *proxy_strategy) { + struct aws_http_proxy_strategy_tunneling_ntlm *ntlm_credential_strategy = proxy_strategy->impl; + + aws_mem_release(ntlm_credential_strategy->allocator, ntlm_credential_strategy); +} + +struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_tunneling_ntlm_credential( + struct aws_allocator *allocator, + struct aws_http_proxy_strategy_tunneling_ntlm_options *config) { + + if (allocator == NULL || config == NULL || config->get_token == NULL) { + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + return NULL; + } + + struct aws_http_proxy_strategy_tunneling_ntlm *ntlm_credential_strategy = + aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_strategy_tunneling_ntlm)); + if (ntlm_credential_strategy == NULL) { + return NULL; + } + + ntlm_credential_strategy->strategy_base.impl = ntlm_credential_strategy; + ntlm_credential_strategy->strategy_base.vtable = &s_tunneling_ntlm_credential_strategy_vtable; + ntlm_credential_strategy->strategy_base.proxy_connection_type = AWS_HPCT_HTTP_TUNNEL; + + ntlm_credential_strategy->allocator = allocator; + + aws_ref_count_init( + &ntlm_credential_strategy->strategy_base.ref_count, + &ntlm_credential_strategy->strategy_base, + (aws_simple_completion_callback *)s_destroy_tunneling_ntlm_credential_strategy); + + ntlm_credential_strategy->get_token = config->get_token; + ntlm_credential_strategy->get_challenge_token_user_data = config->get_challenge_token_user_data; + + return &ntlm_credential_strategy->strategy_base; +} + /******************************************************************************************************************/ -#define PROXY_STRATEGY_MAX_ADAPTIVE_STRATEGIES 3 +#define PROXY_STRATEGY_MAX_ADAPTIVE_STRATEGIES 4 struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_tunneling_adaptive( struct aws_allocator *allocator, @@ -1439,16 +1588,17 @@ struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_tunneling_adaptive( struct aws_http_proxy_strategy *strategies[PROXY_STRATEGY_MAX_ADAPTIVE_STRATEGIES]; uint32_t strategy_count = 0; - struct aws_http_proxy_strategy *identity_strategy = NULL; + /*struct aws_http_proxy_strategy *identity_strategy = NULL;*/ struct aws_http_proxy_strategy *kerberos_strategy = NULL; + struct aws_http_proxy_strategy *ntlm_credential_strategy = NULL; struct aws_http_proxy_strategy *ntlm_strategy = NULL; struct aws_http_proxy_strategy *adaptive_chain_strategy = NULL; - identity_strategy = aws_http_proxy_strategy_new_tunneling_one_time_identity(allocator); + /*identity_strategy = aws_http_proxy_strategy_new_tunneling_one_time_identity(allocator); if (identity_strategy == NULL) { goto done; } - strategies[strategy_count++] = identity_strategy; + strategies[strategy_count++] = identity_strategy;*/ if (config->kerberos_options != NULL) { kerberos_strategy = aws_http_proxy_strategy_new_tunneling_kerberos(allocator, config->kerberos_options); @@ -1459,6 +1609,18 @@ struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_tunneling_adaptive( strategies[strategy_count++] = kerberos_strategy; } + if (config->ntlm_options != NULL) { + ntlm_credential_strategy = + aws_http_proxy_strategy_new_tunneling_ntlm_credential(allocator, config->ntlm_options); + if (ntlm_credential_strategy == NULL) { + goto done; + } + + strategies[strategy_count++] = ntlm_credential_strategy; + } + + + if (config->ntlm_options != NULL) { ntlm_strategy = aws_http_proxy_strategy_new_tunneling_ntlm(allocator, config->ntlm_options); if (ntlm_strategy == NULL) { @@ -1480,13 +1642,15 @@ struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_tunneling_adaptive( done: - aws_http_proxy_strategy_release(identity_strategy); + /* aws_http_proxy_strategy_release(identity_strategy);*/ aws_http_proxy_strategy_release(kerberos_strategy); + aws_http_proxy_strategy_release(ntlm_credential_strategy); aws_http_proxy_strategy_release(ntlm_strategy); return adaptive_chain_strategy; } + #if defined(_MSC_VER) # pragma warning(pop) #endif /* _MSC_VER */ From 1d9ff1dc4277e5c00cefe06973130ced2e8aaca8 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Thu, 28 Jan 2021 12:36:29 -0800 Subject: [PATCH 37/54] Restore identity in prep for sequence merge --- source/proxy_strategy.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/source/proxy_strategy.c b/source/proxy_strategy.c index 0cf142b71..9d66eedaf 100644 --- a/source/proxy_strategy.c +++ b/source/proxy_strategy.c @@ -1155,8 +1155,6 @@ struct aws_http_proxy_negotiator_tunneling_ntlm { enum proxy_negotiator_connect_state connect_state; - struct aws_string *token; - struct aws_string *challenge_token; struct aws_http_proxy_negotiator negotiator_base; @@ -1588,17 +1586,17 @@ struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_tunneling_adaptive( struct aws_http_proxy_strategy *strategies[PROXY_STRATEGY_MAX_ADAPTIVE_STRATEGIES]; uint32_t strategy_count = 0; - /*struct aws_http_proxy_strategy *identity_strategy = NULL;*/ + struct aws_http_proxy_strategy *identity_strategy = NULL; struct aws_http_proxy_strategy *kerberos_strategy = NULL; struct aws_http_proxy_strategy *ntlm_credential_strategy = NULL; struct aws_http_proxy_strategy *ntlm_strategy = NULL; struct aws_http_proxy_strategy *adaptive_chain_strategy = NULL; - /*identity_strategy = aws_http_proxy_strategy_new_tunneling_one_time_identity(allocator); + identity_strategy = aws_http_proxy_strategy_new_tunneling_one_time_identity(allocator); if (identity_strategy == NULL) { goto done; } - strategies[strategy_count++] = identity_strategy;*/ + strategies[strategy_count++] = identity_strategy; if (config->kerberos_options != NULL) { kerberos_strategy = aws_http_proxy_strategy_new_tunneling_kerberos(allocator, config->kerberos_options); @@ -1617,11 +1615,7 @@ struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_tunneling_adaptive( } strategies[strategy_count++] = ntlm_credential_strategy; - } - - - if (config->ntlm_options != NULL) { ntlm_strategy = aws_http_proxy_strategy_new_tunneling_ntlm(allocator, config->ntlm_options); if (ntlm_strategy == NULL) { goto done; @@ -1642,7 +1636,7 @@ struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_tunneling_adaptive( done: - /* aws_http_proxy_strategy_release(identity_strategy);*/ + aws_http_proxy_strategy_release(identity_strategy); aws_http_proxy_strategy_release(kerberos_strategy); aws_http_proxy_strategy_release(ntlm_credential_strategy); aws_http_proxy_strategy_release(ntlm_strategy); From ca9ea62ac9073875f052d86bca26e0ca32515be9 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Thu, 28 Jan 2021 13:35:52 -0800 Subject: [PATCH 38/54] Updates and fixes, real retry loop --- include/aws/http/private/proxy_impl.h | 8 ++++++++ include/aws/http/proxy_strategy.h | 4 ++++ source/proxy_connection.c | 3 ++- source/proxy_strategy.c | 18 ++++++++++++++++++ tests/integration_test_proxy.c | 15 ++++++++++++++- tests/proxy_test_helper.c | 2 +- tests/test_proxy.c | 2 +- 7 files changed, 48 insertions(+), 4 deletions(-) diff --git a/include/aws/http/private/proxy_impl.h b/include/aws/http/private/proxy_impl.h index 437f305ad..c7c763b07 100644 --- a/include/aws/http/private/proxy_impl.h +++ b/include/aws/http/private/proxy_impl.h @@ -143,6 +143,14 @@ void aws_http_proxy_options_init_from_config( struct aws_http_proxy_options *options, const struct aws_http_proxy_config *config); +/** + * Checks if tunneling proxy negotiation should continue to try and connect + * @param proxy_negotiator negotiator to query + * @return true if another connect request should be attempted, false otherwise + */ +AWS_HTTP_API +bool aws_http_proxy_negotiator_should_retry(struct aws_http_proxy_negotiator *proxy_negotiator); + /** * Constructor for a tunnel-only proxy strategy that applies no changes to outbound CONNECT requests. Intended to be * the first link in an adaptive sequence for a tunneling proxy: first try a basic CONNECT, then based on the response, diff --git a/include/aws/http/proxy_strategy.h b/include/aws/http/proxy_strategy.h index 2f812ce83..f46dabb97 100644 --- a/include/aws/http/proxy_strategy.h +++ b/include/aws/http/proxy_strategy.h @@ -101,6 +101,8 @@ typedef int(aws_http_proxy_negotiator_connect_on_incoming_body_fn)( struct aws_http_proxy_negotiator *proxy_negotiator, const struct aws_byte_cursor *data); +typedef bool(aws_http_proxy_negotiator_should_retry_fn)(struct aws_http_proxy_negotiator *proxy_negotiator); + /** * Vtable for forwarding-based proxy negotiators */ @@ -117,6 +119,8 @@ struct aws_http_proxy_negotiator_tunnelling_vtable { aws_http_proxy_negotiation_connect_on_incoming_headers_fn *on_incoming_headers_callback; aws_http_proxy_negotiator_connect_status_fn *on_status_callback; aws_http_proxy_negotiator_connect_on_incoming_body_fn *on_incoming_body_callback; + + aws_http_proxy_negotiator_should_retry_fn *should_retry; }; /* diff --git a/source/proxy_connection.c b/source/proxy_connection.c index 1824c3c89..cdb80974e 100644 --- a/source/proxy_connection.c +++ b/source/proxy_connection.c @@ -478,7 +478,8 @@ static void s_aws_http_on_stream_complete_tunnel_proxy( if (context->error_code != AWS_ERROR_SUCCESS) { context->error_code = AWS_ERROR_HTTP_PROXY_CONNECT_FAILED; - if (context->connect_status_code != AWS_HTTP_STATUS_CODE_407_PROXY_AUTHENTICATION_REQUIRED) { + if (context->connect_status_code == AWS_HTTP_STATUS_CODE_407_PROXY_AUTHENTICATION_REQUIRED && + aws_http_proxy_negotiator_should_retry(context->proxy_negotiator)) { struct aws_http_proxy_user_data *new_context = aws_http_proxy_user_data_new_reset_clone(context->allocator, context); if (new_context != NULL && s_create_tunneling_connection(new_context) == AWS_OP_SUCCESS) { diff --git a/source/proxy_strategy.c b/source/proxy_strategy.c index fe28710ad..e03add79e 100644 --- a/source/proxy_strategy.c +++ b/source/proxy_strategy.c @@ -40,6 +40,16 @@ struct aws_http_proxy_negotiator *aws_http_proxy_strategy_create_negotiator( return strategy->vtable->create_negotiator(strategy, allocator); } +bool aws_http_proxy_negotiator_should_retry(struct aws_http_proxy_negotiator *proxy_negotiator) { + if (proxy_negotiator != NULL) { + if (proxy_negotiator->strategy_vtable.tunnelling_vtable->should_retry != NULL) { + return proxy_negotiator->strategy_vtable.tunnelling_vtable->should_retry(proxy_negotiator); + } + } + + return false; +} + struct aws_http_proxy_strategy *aws_http_proxy_strategy_acquire(struct aws_http_proxy_strategy *proxy_strategy) { if (proxy_strategy != NULL) { aws_ref_count_acquire(&proxy_strategy->ref_count); @@ -1495,11 +1505,19 @@ static int s_sequence_on_incoming_body( return AWS_OP_SUCCESS; } +static bool s_sequence_should_retry(struct aws_http_proxy_negotiator *proxy_negotiator) { + struct aws_http_proxy_negotiator_tunneling_sequence *sequence_negotiator = proxy_negotiator->impl; + + return sequence_negotiator->current_negotiator_transform_index < + aws_array_list_length(&sequence_negotiator->negotiators); +} + static struct aws_http_proxy_negotiator_tunnelling_vtable s_tunneling_sequence_proxy_negotiator_tunneling_vtable = { .on_incoming_body_callback = s_sequence_on_incoming_body, .on_incoming_headers_callback = s_sequence_on_incoming_headers, .on_status_callback = s_sequence_on_connect_status, .connect_request_transform = s_sequence_tunnel_transform_connect, + .should_retry = s_sequence_should_retry, }; static void s_destroy_tunneling_sequence_negotiator(struct aws_http_proxy_negotiator *proxy_negotiator) { diff --git a/tests/integration_test_proxy.c b/tests/integration_test_proxy.c index 512ec6020..f7d27bc0e 100644 --- a/tests/integration_test_proxy.c +++ b/tests/integration_test_proxy.c @@ -286,7 +286,7 @@ static struct aws_string *s_mock_aws_http_proxy_negotiation_kerberos_get_token_s return aws_string_new_from_string(allocator, s_mock_kerberos_token_value); } -AWS_STATIC_STRING_FROM_LITERAL(s_mock_ntlm_token_value, "NTLM_RESPONSE"); +AWS_STATIC_STRING_FROM_LITERAL(s_mock_ntlm_challenge_token_value, "NTLM_RESPONSE"); static struct aws_string *s_mock_aws_http_proxy_negotiation_ntlm_get_challenge_token_sync_fn( void *user_data, @@ -295,6 +295,18 @@ static struct aws_string *s_mock_aws_http_proxy_negotiation_ntlm_get_challenge_t struct aws_allocator *allocator = user_data; + *out_error_code = AWS_ERROR_SUCCESS; + return aws_string_new_from_string(allocator, s_mock_ntlm_challenge_token_value); +} + +AWS_STATIC_STRING_FROM_LITERAL(s_mock_ntlm_token_value, "NTLM_TOKEN"); + +static struct aws_string *s_mock_aws_http_proxy_negotiation_ntlm_get_token_sync_fn( + void *user_data, + int *out_error_code) { + + struct aws_allocator *allocator = user_data; + *out_error_code = AWS_ERROR_SUCCESS; return aws_string_new_from_string(allocator, s_mock_ntlm_token_value); } @@ -309,6 +321,7 @@ static int s_test_proxy_sequential_negotiation(struct aws_allocator *allocator, struct aws_http_proxy_strategy_tunneling_ntlm_options ntlm_config = { .get_challenge_token = s_mock_aws_http_proxy_negotiation_ntlm_get_challenge_token_sync_fn, .get_challenge_token_user_data = allocator, + .get_token = s_mock_aws_http_proxy_negotiation_ntlm_get_token_sync_fn, }; struct aws_http_proxy_strategy_tunneling_adaptive_options adaptive_config = { diff --git a/tests/proxy_test_helper.c b/tests/proxy_test_helper.c index 30fa72030..682050042 100644 --- a/tests/proxy_test_helper.c +++ b/tests/proxy_test_helper.c @@ -403,7 +403,7 @@ int proxy_tester_send_connect_response(struct proxy_tester *tester) { response_string = (const char *)response->bytes; } else if (tester->failure_type == PTFT_CONNECT_REQUEST) { - response_string = "HTTP/1.0 401 Unauthorized\r\n\r\n"; + response_string = "HTTP/1.0 407 Unauthorized\r\n\r\n"; } else { /* adding close here because it's an edge case we need to exercise. The desired behavior is that it has * absolutely no effect. */ diff --git a/tests/test_proxy.c b/tests/test_proxy.c index 72fe91613..9d12ff30d 100644 --- a/tests/test_proxy.c +++ b/tests/test_proxy.c @@ -791,7 +791,7 @@ static int s_test_http_proxy_adaptive_identity_success(struct aws_allocator *all AWS_TEST_CASE(test_http_proxy_adaptive_identity_success, s_test_http_proxy_adaptive_identity_success); -AWS_STATIC_STRING_FROM_LITERAL(s_unauthorized_response, "HTTP/1.0 401 Unauthorized\r\n\r\n"); +AWS_STATIC_STRING_FROM_LITERAL(s_unauthorized_response, "HTTP/1.0 407 Unauthorized\r\n\r\n"); AWS_STATIC_STRING_FROM_LITERAL(s_good_response, "HTTP/1.0 200 Connection established\r\nconnection: close\r\n\r\n"); static int s_test_http_proxy_adaptive_kerberos_success(struct aws_allocator *allocator, void *ctx) { From 1bdee3e4c708f4ab0fb7f85c2214fc4dffef0180 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Thu, 28 Jan 2021 17:15:35 -0800 Subject: [PATCH 39/54] Allow proxy sequence strategy to dynamically choose whether or not to use the next strategy on a new connection or the existing connection --- include/aws/http/private/proxy_impl.h | 4 ++- include/aws/http/proxy_strategy.h | 10 +++++-- source/proxy_connection.c | 24 ++++++++++----- source/proxy_strategy.c | 42 ++++++++++++++++++++++----- 4 files changed, 61 insertions(+), 19 deletions(-) diff --git a/include/aws/http/private/proxy_impl.h b/include/aws/http/private/proxy_impl.h index c7c763b07..e115490b2 100644 --- a/include/aws/http/private/proxy_impl.h +++ b/include/aws/http/private/proxy_impl.h @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -149,7 +150,8 @@ void aws_http_proxy_options_init_from_config( * @return true if another connect request should be attempted, false otherwise */ AWS_HTTP_API -bool aws_http_proxy_negotiator_should_retry(struct aws_http_proxy_negotiator *proxy_negotiator); +enum aws_http_proxy_negotiation_retry_directive aws_http_proxy_negotiator_get_retry_directive( + struct aws_http_proxy_negotiator *proxy_negotiator); /** * Constructor for a tunnel-only proxy strategy that applies no changes to outbound CONNECT requests. Intended to be diff --git a/include/aws/http/proxy_strategy.h b/include/aws/http/proxy_strategy.h index f46dabb97..d7b13da0e 100644 --- a/include/aws/http/proxy_strategy.h +++ b/include/aws/http/proxy_strategy.h @@ -101,7 +101,13 @@ typedef int(aws_http_proxy_negotiator_connect_on_incoming_body_fn)( struct aws_http_proxy_negotiator *proxy_negotiator, const struct aws_byte_cursor *data); -typedef bool(aws_http_proxy_negotiator_should_retry_fn)(struct aws_http_proxy_negotiator *proxy_negotiator); +enum aws_http_proxy_negotiation_retry_directive { + AWS_HPNRD_STOP, + AWS_HPNRD_NEW_CONNECTION, + AWS_HPNRD_CURRENT_CONNECTION, +}; + +typedef enum aws_http_proxy_negotiation_retry_directive(aws_http_proxy_negotiator_get_retry_directive_fn)(struct aws_http_proxy_negotiator *proxy_negotiator); /** * Vtable for forwarding-based proxy negotiators @@ -120,7 +126,7 @@ struct aws_http_proxy_negotiator_tunnelling_vtable { aws_http_proxy_negotiator_connect_status_fn *on_status_callback; aws_http_proxy_negotiator_connect_on_incoming_body_fn *on_incoming_body_callback; - aws_http_proxy_negotiator_should_retry_fn *should_retry; + aws_http_proxy_negotiator_get_retry_directive_fn *get_retry_directive; }; /* diff --git a/source/proxy_connection.c b/source/proxy_connection.c index cdb80974e..f521d5b50 100644 --- a/source/proxy_connection.c +++ b/source/proxy_connection.c @@ -478,14 +478,22 @@ static void s_aws_http_on_stream_complete_tunnel_proxy( if (context->error_code != AWS_ERROR_SUCCESS) { context->error_code = AWS_ERROR_HTTP_PROXY_CONNECT_FAILED; - if (context->connect_status_code == AWS_HTTP_STATUS_CODE_407_PROXY_AUTHENTICATION_REQUIRED && - aws_http_proxy_negotiator_should_retry(context->proxy_negotiator)) { - struct aws_http_proxy_user_data *new_context = - aws_http_proxy_user_data_new_reset_clone(context->allocator, context); - if (new_context != NULL && s_create_tunneling_connection(new_context) == AWS_OP_SUCCESS) { - context->original_on_shutdown = NULL; - context->original_on_setup = NULL; - context->error_code = AWS_ERROR_HTTP_PROXY_CONNECT_FAILED_RETRYABLE; + if (context->connect_status_code == AWS_HTTP_STATUS_CODE_407_PROXY_AUTHENTICATION_REQUIRED) { + enum aws_http_proxy_negotiation_retry_directive retry_directive = + aws_http_proxy_negotiator_get_retry_directive(context->proxy_negotiator); + + if (retry_directive == AWS_HPNRD_NEW_CONNECTION) { + struct aws_http_proxy_user_data *new_context = + aws_http_proxy_user_data_new_reset_clone(context->allocator, context); + if (new_context != NULL && s_create_tunneling_connection(new_context) == AWS_OP_SUCCESS) { + context->original_on_shutdown = NULL; + context->original_on_setup = NULL; + context->error_code = AWS_ERROR_HTTP_PROXY_CONNECT_FAILED_RETRYABLE; + } + } else if (retry_directive == AWS_HPNRD_CURRENT_CONNECTION) { + if (s_make_proxy_connect_request(context) == AWS_OP_SUCCESS) { + return; + } } } diff --git a/source/proxy_strategy.c b/source/proxy_strategy.c index e03add79e..dcd42e488 100644 --- a/source/proxy_strategy.c +++ b/source/proxy_strategy.c @@ -40,14 +40,15 @@ struct aws_http_proxy_negotiator *aws_http_proxy_strategy_create_negotiator( return strategy->vtable->create_negotiator(strategy, allocator); } -bool aws_http_proxy_negotiator_should_retry(struct aws_http_proxy_negotiator *proxy_negotiator) { +enum aws_http_proxy_negotiation_retry_directive aws_http_proxy_negotiator_get_retry_directive( + struct aws_http_proxy_negotiator *proxy_negotiator) { if (proxy_negotiator != NULL) { - if (proxy_negotiator->strategy_vtable.tunnelling_vtable->should_retry != NULL) { - return proxy_negotiator->strategy_vtable.tunnelling_vtable->should_retry(proxy_negotiator); + if (proxy_negotiator->strategy_vtable.tunnelling_vtable->get_retry_directive != NULL) { + return proxy_negotiator->strategy_vtable.tunnelling_vtable->get_retry_directive(proxy_negotiator); } } - return false; + return AWS_HPNRD_STOP; } struct aws_http_proxy_strategy *aws_http_proxy_strategy_acquire(struct aws_http_proxy_strategy *proxy_strategy) { @@ -1028,11 +1029,19 @@ static int s_ntlm_on_incoming_body( return AWS_OP_SUCCESS; } +static enum aws_http_proxy_negotiation_retry_directive s_ntlm_tunnel_get_retry_directive( + struct aws_http_proxy_negotiator *proxy_negotiator) { + (void)proxy_negotiator; + + return AWS_HPNRD_CURRENT_CONNECTION; +} + static struct aws_http_proxy_negotiator_tunnelling_vtable s_tunneling_ntlm_proxy_negotiator_tunneling_vtable = { .on_incoming_body_callback = s_ntlm_on_incoming_body, .on_incoming_headers_callback = s_ntlm_on_incoming_header_adaptive, .on_status_callback = s_ntlm_on_connect_status, .connect_request_transform = s_ntlm_tunnel_transform_connect, + .get_retry_directive = s_ntlm_tunnel_get_retry_directive, }; static void s_destroy_tunneling_ntlm_negotiator(struct aws_http_proxy_negotiator *proxy_negotiator) { @@ -1505,11 +1514,28 @@ static int s_sequence_on_incoming_body( return AWS_OP_SUCCESS; } -static bool s_sequence_should_retry(struct aws_http_proxy_negotiator *proxy_negotiator) { +static enum aws_http_proxy_negotiation_retry_directive s_sequence_get_retry_directive( + struct aws_http_proxy_negotiator *proxy_negotiator) { struct aws_http_proxy_negotiator_tunneling_sequence *sequence_negotiator = proxy_negotiator->impl; - return sequence_negotiator->current_negotiator_transform_index < - aws_array_list_length(&sequence_negotiator->negotiators); + if (sequence_negotiator->current_negotiator_transform_index < + aws_array_list_length(&sequence_negotiator->negotiators)) { + struct aws_http_proxy_negotiator *next_negotiator = NULL; + aws_array_list_get_at( + &sequence_negotiator->negotiators, + &next_negotiator, + sequence_negotiator->current_negotiator_transform_index); + + enum aws_http_proxy_negotiation_retry_directive next_negotiator_directive = + aws_http_proxy_negotiator_get_retry_directive(next_negotiator); + if (next_negotiator_directive == AWS_HPNRD_CURRENT_CONNECTION) { + return AWS_HPNRD_CURRENT_CONNECTION; + } else { + return AWS_HPNRD_NEW_CONNECTION; + } + } + + return AWS_HPNRD_STOP; } static struct aws_http_proxy_negotiator_tunnelling_vtable s_tunneling_sequence_proxy_negotiator_tunneling_vtable = { @@ -1517,7 +1543,7 @@ static struct aws_http_proxy_negotiator_tunnelling_vtable s_tunneling_sequence_p .on_incoming_headers_callback = s_sequence_on_incoming_headers, .on_status_callback = s_sequence_on_connect_status, .connect_request_transform = s_sequence_tunnel_transform_connect, - .should_retry = s_sequence_should_retry, + .get_retry_directive = s_sequence_get_retry_directive, }; static void s_destroy_tunneling_sequence_negotiator(struct aws_http_proxy_negotiator *proxy_negotiator) { From 034d1376111d61c813f46aabcdb0dd22d26a5afd Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Thu, 28 Jan 2021 18:18:51 -0800 Subject: [PATCH 40/54] Clear error code before attempting anoter CONNECT on a persistent connection --- include/aws/http/proxy_strategy.h | 3 ++- source/proxy_connection.c | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/include/aws/http/proxy_strategy.h b/include/aws/http/proxy_strategy.h index d7b13da0e..b97be39fb 100644 --- a/include/aws/http/proxy_strategy.h +++ b/include/aws/http/proxy_strategy.h @@ -107,7 +107,8 @@ enum aws_http_proxy_negotiation_retry_directive { AWS_HPNRD_CURRENT_CONNECTION, }; -typedef enum aws_http_proxy_negotiation_retry_directive(aws_http_proxy_negotiator_get_retry_directive_fn)(struct aws_http_proxy_negotiator *proxy_negotiator); +typedef enum aws_http_proxy_negotiation_retry_directive(aws_http_proxy_negotiator_get_retry_directive_fn)( + struct aws_http_proxy_negotiator *proxy_negotiator); /** * Vtable for forwarding-based proxy negotiators diff --git a/source/proxy_connection.c b/source/proxy_connection.c index f521d5b50..54599f29f 100644 --- a/source/proxy_connection.c +++ b/source/proxy_connection.c @@ -491,6 +491,7 @@ static void s_aws_http_on_stream_complete_tunnel_proxy( context->error_code = AWS_ERROR_HTTP_PROXY_CONNECT_FAILED_RETRYABLE; } } else if (retry_directive == AWS_HPNRD_CURRENT_CONNECTION) { + context->error_code = AWS_ERROR_SUCCESS; if (s_make_proxy_connect_request(context) == AWS_OP_SUCCESS) { return; } From ffabc431a81c5427cd82f8fd8eaefbc540f23711 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Thu, 28 Jan 2021 18:24:05 -0800 Subject: [PATCH 41/54] Wipe previous connect request before attempting a new one on the same connection --- source/proxy_connection.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/source/proxy_connection.c b/source/proxy_connection.c index 54599f29f..1ecfb2b83 100644 --- a/source/proxy_connection.c +++ b/source/proxy_connection.c @@ -611,6 +611,11 @@ static void s_continue_tunneling_connect(struct aws_http_message *message, void * of upgrading with TLS on success */ static int s_make_proxy_connect_request(struct aws_http_proxy_user_data *user_data) { + if (user_data->connect_request != NULL) { + aws_http_message_destroy(user_data->connect_request); + user_data->connect_request = NULL; + } + user_data->connect_request = s_build_proxy_connect_request(user_data); if (user_data->connect_request == NULL) { return AWS_OP_ERR; From 2169056445d41530ba63d54dd5898b1aa84572bf Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Fri, 29 Jan 2021 02:52:06 -0800 Subject: [PATCH 42/54] Windows warning fix --- source/proxy_connection.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/source/proxy_connection.c b/source/proxy_connection.c index 1ecfb2b83..82854f9f6 100644 --- a/source/proxy_connection.c +++ b/source/proxy_connection.c @@ -410,8 +410,10 @@ static int s_aws_http_on_incoming_header_block_done_tunnel_proxy( struct aws_http_proxy_user_data *context = user_data; if (header_block == AWS_HTTP_HEADER_BLOCK_MAIN) { - aws_http_stream_get_incoming_response_status(stream, &context->connect_status_code); - if (context->connect_status_code != 200) { + int status_code = AWS_HTTP_STATUS_CODE_UNKNOWN; + aws_http_stream_get_incoming_response_status(stream, &status_code); + context->connect_status_code = (enum aws_http_status_code)status_code; + if (context->connect_status_code != AWS_HTTP_STATUS_CODE_200_OK) { AWS_LOGF_ERROR( AWS_LS_HTTP_CONNECTION, "(%p) Proxy CONNECT request failed with status code %d", From d789f31915b908f6dc0b7fabe518fd4aeeaa2ab1 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Fri, 19 Feb 2021 13:04:57 -0800 Subject: [PATCH 43/54] Empty change --- source/proxy_connection.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/proxy_connection.c b/source/proxy_connection.c index c6c1e6794..e34da2e3c 100644 --- a/source/proxy_connection.c +++ b/source/proxy_connection.c @@ -536,7 +536,7 @@ static void s_aws_http_on_stream_complete_tunnel_proxy( */ left_of_tls_slot = left_of_tls_slot->adj_right; } - + if (s_vtable->setup_client_tls(left_of_tls_slot, context->tls_options)) { AWS_LOGF_ERROR( AWS_LS_HTTP_CONNECTION, From 200c575858053be6f4a368283e7f43d688dfec53 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Sat, 20 Feb 2021 10:51:39 -0800 Subject: [PATCH 44/54] Legacy backwards compatibility --- include/aws/http/connection.h | 34 +++++++++++++++++----- source/proxy_connection.c | 17 +++++++---- tests/CMakeLists.txt | 1 + tests/test_proxy.c | 55 ++++++++++++++++++++++++++++------- 4 files changed, 85 insertions(+), 22 deletions(-) diff --git a/include/aws/http/connection.h b/include/aws/http/connection.h index 2bfacaa24..d6d245ab1 100644 --- a/include/aws/http/connection.h +++ b/include/aws/http/connection.h @@ -121,6 +121,14 @@ struct aws_http_connection_monitoring_options { uint32_t allowable_throughput_failure_interval_seconds; }; +/** + * @Deprecated - Supported proxy authentication modes. Superceded by proxy strategy. + */ +enum aws_http_proxy_authentication_type { + AWS_HPAT_NONE = 0, + AWS_HPAT_BASIC, +}; + /** * Supported proxy connection types */ @@ -129,7 +137,7 @@ enum aws_http_proxy_connection_type { * Deprecated, but 0-valued for backwards compatibility * * If tls options are provided (for the main connection) then treat the proxy as a tunneling proxy - * If tls options are not provided (for the main connection), then treate the proxy as a forwarding proxy + * If tls options are not provided (for the main connection), then treat the proxy as a forwarding proxy */ AWS_HPCT_HTTP_LEGACY = 0, @@ -144,12 +152,6 @@ enum aws_http_proxy_connection_type { * tls connections. */ AWS_HPCT_HTTP_TUNNEL, - - /** - * Establish an http(s) connection through a socks5 proxy. - * Socks5 proxies are not yet supported - */ - AWS_HPCT_SOCKS5, }; struct aws_http_proxy_strategy; @@ -191,6 +193,24 @@ struct aws_http_proxy_options { * Other proxy connection types TBD. */ struct aws_http_proxy_strategy *proxy_strategy; + + /** + * @Deprecated - What type of proxy authentication to use, if any. + * Replaced by proxy_strategy + */ + enum aws_http_proxy_authentication_type auth_type; + + /** + * @Deprecated - Optional user name to use for basic authentication + * Replaced by proxy_strategy, using an instantiation from aws_http_proxy_strategy_new_basic_auth() + */ + struct aws_byte_cursor auth_username; + + /** + * @Deprecated - Optional password to use for basic authentication + * Replaced by proxy_strategy, using an instantiation from aws_http_proxy_strategy_new_basic_auth() + */ + struct aws_byte_cursor auth_password; }; /** diff --git a/source/proxy_connection.c b/source/proxy_connection.c index a10dbd6c3..d0b2d2dcf 100644 --- a/source/proxy_connection.c +++ b/source/proxy_connection.c @@ -956,7 +956,18 @@ static struct aws_http_proxy_config *s_aws_http_proxy_config_new( if (proxy_options->proxy_strategy != NULL) { config->proxy_strategy = aws_http_proxy_strategy_acquire(proxy_options->proxy_strategy); - } else { + } else if (proxy_options->auth_type == AWS_HPAT_BASIC) { + struct aws_http_proxy_strategy_basic_auth_options basic_config; + AWS_ZERO_STRUCT(basic_config); + + basic_config.proxy_connection_type = proxy_options->connection_type; + basic_config.user_name = proxy_options->auth_username; + basic_config.password = proxy_options->auth_password; + + config->proxy_strategy = aws_http_proxy_strategy_new_basic_auth(allocator, &basic_config); + } + + if (config->proxy_strategy == NULL) { switch (override_proxy_connection_type) { case AWS_HPCT_HTTP_FORWARD: config->proxy_strategy = aws_http_proxy_strategy_new_forwarding_identity(allocator); @@ -1080,10 +1091,6 @@ int aws_http_options_validate_proxy_configuration(const struct aws_http_client_c } enum aws_http_proxy_connection_type proxy_type = options->proxy_options->connection_type; - if (proxy_type == AWS_HPCT_SOCKS5) { - return aws_raise_error(AWS_ERROR_UNIMPLEMENTED); - } - if (proxy_type == AWS_HPCT_HTTP_FORWARD && options->tls_options != NULL) { return aws_raise_error(AWS_ERROR_INVALID_STATE); } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index e8f84eda8..72127eac1 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -494,6 +494,7 @@ add_test_case(test_http_forwarding_proxy_connection_channel_failure) add_test_case(test_http_forwarding_proxy_connection_connect_failure) add_test_case(test_http_forwarding_proxy_request_transform) add_test_case(test_http_forwarding_proxy_request_transform_basic_auth) +add_test_case(test_http_forwarding_proxy_request_transform_legacy_basic_auth) add_test_case(test_http_proxy_request_transform_kerberos) add_test_case(test_http_proxy_kerberos_token_failure) add_test_case(test_http_proxy_kerberos_connect_failure) diff --git a/tests/test_proxy.c b/tests/test_proxy.c index 9d12ff30d..198f85387 100644 --- a/tests/test_proxy.c +++ b/tests/test_proxy.c @@ -195,6 +195,10 @@ struct mocked_proxy_test_options { enum proxy_tester_failure_type failure_type; struct aws_http_proxy_strategy *proxy_strategy; + enum aws_http_proxy_authentication_type auth_type; + struct aws_byte_cursor legacy_basic_username; + struct aws_byte_cursor legacy_basic_password; + uint32_t mocked_response_count; struct aws_byte_cursor *mocked_responses; }; @@ -212,6 +216,9 @@ static int s_setup_proxy_test(struct aws_allocator *allocator, struct mocked_pro .host = aws_byte_cursor_from_c_str(s_proxy_host_name), .port = s_proxy_port, .proxy_strategy = config->proxy_strategy, + .auth_type = config->auth_type, + .auth_username = config->legacy_basic_username, + .auth_password = config->legacy_basic_password, }; struct proxy_tester_options options = { @@ -482,16 +489,10 @@ static int s_verify_transformed_request( static int s_do_http_forwarding_proxy_request_transform_test( struct aws_allocator *allocator, - struct aws_http_proxy_strategy *proxy_strategy, + struct mocked_proxy_test_options *test_options, int (*transformed_request_verifier_fn)(struct aws_http_message *)) { - struct mocked_proxy_test_options options = { - .test_mode = PTTM_HTTP_FORWARD, - .failure_type = PTFT_NONE, - .proxy_strategy = proxy_strategy, - }; - - ASSERT_SUCCESS(s_setup_proxy_test(allocator, &options)); + ASSERT_SUCCESS(s_setup_proxy_test(allocator, test_options)); struct aws_http_message *untransformed_request = s_build_http_request(allocator); struct aws_http_message *request = s_build_http_request(allocator); @@ -531,7 +532,13 @@ static int s_do_http_forwarding_proxy_request_transform_test( static int s_test_http_forwarding_proxy_request_transform(struct aws_allocator *allocator, void *ctx) { (void)ctx; - ASSERT_SUCCESS(s_do_http_forwarding_proxy_request_transform_test(allocator, NULL, NULL)); + struct mocked_proxy_test_options options = { + .test_mode = PTTM_HTTP_FORWARD, + .failure_type = PTFT_NONE, + .proxy_strategy = NULL, + }; + + ASSERT_SUCCESS(s_do_http_forwarding_proxy_request_transform_test(allocator, &options, NULL)); return AWS_OP_SUCCESS; } @@ -561,8 +568,14 @@ static int s_test_http_forwarding_proxy_request_transform_basic_auth(struct aws_ struct aws_http_proxy_strategy *proxy_strategy = aws_http_proxy_strategy_new_basic_auth(allocator, &config); + struct mocked_proxy_test_options options = { + .test_mode = PTTM_HTTP_FORWARD, + .failure_type = PTFT_NONE, + .proxy_strategy = proxy_strategy, + }; + ASSERT_SUCCESS( - s_do_http_forwarding_proxy_request_transform_test(allocator, proxy_strategy, s_check_for_basic_auth_header)); + s_do_http_forwarding_proxy_request_transform_test(allocator, &options, s_check_for_basic_auth_header)); aws_http_proxy_strategy_release(proxy_strategy); @@ -572,6 +585,28 @@ AWS_TEST_CASE( test_http_forwarding_proxy_request_transform_basic_auth, s_test_http_forwarding_proxy_request_transform_basic_auth); +static int s_test_http_forwarding_proxy_request_transform_legacy_basic_auth( + struct aws_allocator *allocator, + void *ctx) { + (void)ctx; + + struct mocked_proxy_test_options options = { + .test_mode = PTTM_HTTP_FORWARD, + .failure_type = PTFT_NONE, + .auth_type = AWS_HPAT_BASIC, + .legacy_basic_username = aws_byte_cursor_from_string(s_mock_request_username), + .legacy_basic_password = aws_byte_cursor_from_string(s_mock_request_password), + }; + + ASSERT_SUCCESS( + s_do_http_forwarding_proxy_request_transform_test(allocator, &options, s_check_for_basic_auth_header)); + + return AWS_OP_SUCCESS; +} +AWS_TEST_CASE( + test_http_forwarding_proxy_request_transform_legacy_basic_auth, + s_test_http_forwarding_proxy_request_transform_legacy_basic_auth); + AWS_STATIC_STRING_FROM_LITERAL(s_mock_kerberos_token_value, "abcdefABCDEF123"); static struct aws_string *s_mock_aws_http_proxy_negotiation_kerberos_get_token_sync_fn( From 58f8be776dcbe2735d5a03e9f49c2c46436fc74a Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Tue, 23 Feb 2021 17:01:39 -0800 Subject: [PATCH 45/54] Override proxy type when using legacy basic authentication --- source/proxy_connection.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/proxy_connection.c b/source/proxy_connection.c index d0b2d2dcf..7f7ee49cf 100644 --- a/source/proxy_connection.c +++ b/source/proxy_connection.c @@ -960,7 +960,7 @@ static struct aws_http_proxy_config *s_aws_http_proxy_config_new( struct aws_http_proxy_strategy_basic_auth_options basic_config; AWS_ZERO_STRUCT(basic_config); - basic_config.proxy_connection_type = proxy_options->connection_type; + basic_config.proxy_connection_type = override_proxy_connection_type; basic_config.user_name = proxy_options->auth_username; basic_config.password = proxy_options->auth_password; From 910846e99ce127d8a31a98396f9483bb5ced3a43 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Mon, 1 Mar 2021 12:28:19 -0800 Subject: [PATCH 46/54] Refactor proxy config/options in anticipation of mqtt refactor --- include/aws/http/connection.h | 92 ------------- include/aws/http/private/proxy_impl.h | 25 +--- .../aws/http/{proxy_strategy.h => proxy.h} | 126 +++++++++++++++++- source/proxy_connection.c | 12 +- source/proxy_strategy.c | 2 +- tests/integration_test_proxy.c | 2 +- tests/proxy_test_helper.c | 2 +- tests/proxy_test_helper.h | 1 + tests/test_connection_manager.c | 1 + tests/test_proxy.c | 2 +- 10 files changed, 143 insertions(+), 122 deletions(-) rename include/aws/http/{proxy_strategy.h => proxy.h} (73%) diff --git a/include/aws/http/connection.h b/include/aws/http/connection.h index d6d245ab1..3901ee5cd 100644 --- a/include/aws/http/connection.h +++ b/include/aws/http/connection.h @@ -121,98 +121,6 @@ struct aws_http_connection_monitoring_options { uint32_t allowable_throughput_failure_interval_seconds; }; -/** - * @Deprecated - Supported proxy authentication modes. Superceded by proxy strategy. - */ -enum aws_http_proxy_authentication_type { - AWS_HPAT_NONE = 0, - AWS_HPAT_BASIC, -}; - -/** - * Supported proxy connection types - */ -enum aws_http_proxy_connection_type { - /** - * Deprecated, but 0-valued for backwards compatibility - * - * If tls options are provided (for the main connection) then treat the proxy as a tunneling proxy - * If tls options are not provided (for the main connection), then treat the proxy as a forwarding proxy - */ - AWS_HPCT_HTTP_LEGACY = 0, - - /** - * Use the proxy to forward http requests. Attempting to use both this mode and TLS on the tunnel destination - * is a configuration error. - */ - AWS_HPCT_HTTP_FORWARD, - - /** - * Use the proxy to establish an http connection via a CONNECT request to the proxy. Works for both plaintext and - * tls connections. - */ - AWS_HPCT_HTTP_TUNNEL, -}; - -struct aws_http_proxy_strategy; - -/** - * Options for http proxy server usage - */ -struct aws_http_proxy_options { - - /** - * Type of proxy connection to make - */ - enum aws_http_proxy_connection_type connection_type; - - /** - * Proxy host to connect to - */ - struct aws_byte_cursor host; - - /** - * Port to make the proxy connection to - */ - uint16_t port; - - /** - * Optional. - * TLS configuration for the Local <-> Proxy connection - * Must be distinct from the the TLS options in the parent aws_http_connection_options struct - */ - const struct aws_tls_connection_options *tls_options; - - /** - * Optional - * Advanced option that allows the user to create a custom strategy that gives low-level control of - * certain logical flows within the proxy logic. - * - * For tunneling proxies it allows custom retry and adaptive negotiation of CONNECT requests. - * For forwarding proxies it allows custom request transformations. - * Other proxy connection types TBD. - */ - struct aws_http_proxy_strategy *proxy_strategy; - - /** - * @Deprecated - What type of proxy authentication to use, if any. - * Replaced by proxy_strategy - */ - enum aws_http_proxy_authentication_type auth_type; - - /** - * @Deprecated - Optional user name to use for basic authentication - * Replaced by proxy_strategy, using an instantiation from aws_http_proxy_strategy_new_basic_auth() - */ - struct aws_byte_cursor auth_username; - - /** - * @Deprecated - Optional password to use for basic authentication - * Replaced by proxy_strategy, using an instantiation from aws_http_proxy_strategy_new_basic_auth() - */ - struct aws_byte_cursor auth_password; -}; - /** * Options specific to HTTP/1.x connections. * Initialize with AWS_HTTP1_CONNECTION_OPTIONS_INIT to set default values. diff --git a/include/aws/http/private/proxy_impl.h b/include/aws/http/private/proxy_impl.h index e115490b2..ab8ab0964 100644 --- a/include/aws/http/private/proxy_impl.h +++ b/include/aws/http/private/proxy_impl.h @@ -9,7 +9,7 @@ #include #include -#include +#include #include #include @@ -121,29 +121,6 @@ int aws_http_rewrite_uri_for_proxy_request( AWS_HTTP_API void aws_http_proxy_system_set_vtable(struct aws_http_proxy_system_vtable *vtable); -AWS_HTTP_API -struct aws_http_proxy_config *aws_http_proxy_config_new_from_connection_options( - struct aws_allocator *allocator, - const struct aws_http_client_connection_options *options); - -AWS_HTTP_API -struct aws_http_proxy_config *aws_http_proxy_config_new_from_manager_options( - struct aws_allocator *allocator, - const struct aws_http_connection_manager_options *options); - -AWS_HTTP_API -struct aws_http_proxy_config *aws_http_proxy_config_new_clone( - struct aws_allocator *allocator, - const struct aws_http_proxy_config *proxy_config); - -AWS_HTTP_API -void aws_http_proxy_config_destroy(struct aws_http_proxy_config *config); - -AWS_HTTP_API -void aws_http_proxy_options_init_from_config( - struct aws_http_proxy_options *options, - const struct aws_http_proxy_config *config); - /** * Checks if tunneling proxy negotiation should continue to try and connect * @param proxy_negotiator negotiator to query diff --git a/include/aws/http/proxy_strategy.h b/include/aws/http/proxy.h similarity index 73% rename from include/aws/http/proxy_strategy.h rename to include/aws/http/proxy.h index b97be39fb..acc737f7a 100644 --- a/include/aws/http/proxy_strategy.h +++ b/include/aws/http/proxy.h @@ -7,17 +7,112 @@ */ #include -#include #include #include #include +struct aws_http_client_connection_options; +struct aws_http_connection_manager_options; + struct aws_http_message; struct aws_http_header; +struct aws_http_proxy_config; struct aws_http_proxy_negotiator; struct aws_http_proxy_strategy; +/** + * @Deprecated - Supported proxy authentication modes. Superceded by proxy strategy. + */ +enum aws_http_proxy_authentication_type { + AWS_HPAT_NONE = 0, + AWS_HPAT_BASIC, +}; + +/** + * Supported proxy connection types + */ +enum aws_http_proxy_connection_type { + /** + * Deprecated, but 0-valued for backwards compatibility + * + * If tls options are provided (for the main connection) then treat the proxy as a tunneling proxy + * If tls options are not provided (for the main connection), then treat the proxy as a forwarding proxy + */ + AWS_HPCT_HTTP_LEGACY = 0, + + /** + * Use the proxy to forward http requests. Attempting to use both this mode and TLS on the tunnel destination + * is a configuration error. + */ + AWS_HPCT_HTTP_FORWARD, + + /** + * Use the proxy to establish an http connection via a CONNECT request to the proxy. Works for both plaintext and + * tls connections. + */ + AWS_HPCT_HTTP_TUNNEL, +}; + +struct aws_http_proxy_strategy; + +/** + * Options for http proxy server usage + */ +struct aws_http_proxy_options { + + /** + * Type of proxy connection to make + */ + enum aws_http_proxy_connection_type connection_type; + + /** + * Proxy host to connect to + */ + struct aws_byte_cursor host; + + /** + * Port to make the proxy connection to + */ + uint16_t port; + + /** + * Optional. + * TLS configuration for the Local <-> Proxy connection + * Must be distinct from the the TLS options in the parent aws_http_connection_options struct + */ + const struct aws_tls_connection_options *tls_options; + + /** + * Optional + * Advanced option that allows the user to create a custom strategy that gives low-level control of + * certain logical flows within the proxy logic. + * + * For tunneling proxies it allows custom retry and adaptive negotiation of CONNECT requests. + * For forwarding proxies it allows custom request transformations. + * Other proxy connection types TBD. + */ + struct aws_http_proxy_strategy *proxy_strategy; + + /** + * @Deprecated - What type of proxy authentication to use, if any. + * Replaced by proxy_strategy + */ + enum aws_http_proxy_authentication_type auth_type; + + /** + * @Deprecated - Optional user name to use for basic authentication + * Replaced by proxy_strategy, using an instantiation from aws_http_proxy_strategy_new_basic_auth() + */ + struct aws_byte_cursor auth_username; + + /** + * @Deprecated - Optional password to use for basic authentication + * Replaced by proxy_strategy, using an instantiation from aws_http_proxy_strategy_new_basic_auth() + */ + struct aws_byte_cursor auth_password; +}; + /** * Synchronous (for now) callback function to fetch a token used in modifying CONNECT requests */ @@ -290,6 +385,35 @@ AWS_HTTP_API struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_tunneling_adaptive( struct aws_allocator *allocator, struct aws_http_proxy_strategy_tunneling_adaptive_options *config); + +AWS_HTTP_API +struct aws_http_proxy_config *aws_http_proxy_config_new_from_connection_options( + struct aws_allocator *allocator, + const struct aws_http_client_connection_options *options); + +AWS_HTTP_API +struct aws_http_proxy_config *aws_http_proxy_config_new_from_manager_options( + struct aws_allocator *allocator, + const struct aws_http_connection_manager_options *options); + +AWS_HTTP_API +struct aws_http_proxy_config *aws_http_proxy_config_new_tunneling_from_proxy_options( + struct aws_allocator *allocator, + const struct aws_http_proxy_options *options); + +AWS_HTTP_API +struct aws_http_proxy_config *aws_http_proxy_config_new_clone( + struct aws_allocator *allocator, + const struct aws_http_proxy_config *proxy_config); + +AWS_HTTP_API +void aws_http_proxy_config_destroy(struct aws_http_proxy_config *config); + +AWS_HTTP_API +void aws_http_proxy_options_init_from_config( + struct aws_http_proxy_options *options, + const struct aws_http_proxy_config *config); + AWS_EXTERN_C_END #endif /* AWS_PROXY_STRATEGY_H */ diff --git a/source/proxy_connection.c b/source/proxy_connection.c index 7f7ee49cf..db798e349 100644 --- a/source/proxy_connection.c +++ b/source/proxy_connection.c @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include #include #include @@ -1019,6 +1019,16 @@ struct aws_http_proxy_config *aws_http_proxy_config_new_from_manager_options( s_determine_proxy_connection_type(options->proxy_options->connection_type, options->tls_connection_options)); } +struct aws_http_proxy_config *aws_http_proxy_config_new_tunneling_from_proxy_options( + struct aws_allocator *allocator, + const struct aws_http_proxy_options *proxy_options) { + + return s_aws_http_proxy_config_new( + allocator, + proxy_options, + AWS_HPCT_HTTP_TUNNEL); +} + struct aws_http_proxy_config *aws_http_proxy_config_new_clone( struct aws_allocator *allocator, const struct aws_http_proxy_config *proxy_config) { diff --git a/source/proxy_strategy.c b/source/proxy_strategy.c index dcd42e488..eda510baf 100644 --- a/source/proxy_strategy.c +++ b/source/proxy_strategy.c @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0. */ -#include +#include #include #include diff --git a/tests/integration_test_proxy.c b/tests/integration_test_proxy.c index f7d27bc0e..35343d0fa 100644 --- a/tests/integration_test_proxy.c +++ b/tests/integration_test_proxy.c @@ -272,7 +272,7 @@ static int s_test_nested_https_proxy_connection_get(struct aws_allocator *alloca } AWS_TEST_CASE(test_nested_https_proxy_connection_get, s_test_nested_https_proxy_connection_get); -#include +#include AWS_STATIC_STRING_FROM_LITERAL(s_mock_kerberos_token_value, "abcdefABCDEF123"); diff --git a/tests/proxy_test_helper.c b/tests/proxy_test_helper.c index 7b9175085..8816d4bbf 100644 --- a/tests/proxy_test_helper.c +++ b/tests/proxy_test_helper.c @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include #include diff --git a/tests/proxy_test_helper.h b/tests/proxy_test_helper.h index c1803d910..afff11061 100644 --- a/tests/proxy_test_helper.h +++ b/tests/proxy_test_helper.h @@ -8,6 +8,7 @@ #include #include +#include #include #include #include diff --git a/tests/test_connection_manager.c b/tests/test_connection_manager.c index 04f8195d3..8fab7fe16 100644 --- a/tests/test_connection_manager.c +++ b/tests/test_connection_manager.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include diff --git a/tests/test_proxy.c b/tests/test_proxy.c index 198f85387..1c7cd9377 100644 --- a/tests/test_proxy.c +++ b/tests/test_proxy.c @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include From 55f36201375c420419d9fe3f5f2fea393774ba35 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Mon, 1 Mar 2021 12:29:04 -0800 Subject: [PATCH 47/54] Format --- source/proxy_connection.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/source/proxy_connection.c b/source/proxy_connection.c index db798e349..665f2b7ea 100644 --- a/source/proxy_connection.c +++ b/source/proxy_connection.c @@ -1023,10 +1023,7 @@ struct aws_http_proxy_config *aws_http_proxy_config_new_tunneling_from_proxy_opt struct aws_allocator *allocator, const struct aws_http_proxy_options *proxy_options) { - return s_aws_http_proxy_config_new( - allocator, - proxy_options, - AWS_HPCT_HTTP_TUNNEL); + return s_aws_http_proxy_config_new(allocator, proxy_options, AWS_HPCT_HTTP_TUNNEL); } struct aws_http_proxy_config *aws_http_proxy_config_new_clone( From de9b506433cd33d5c02fc8b6529b321e64e9df75 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Mon, 1 Mar 2021 12:49:22 -0800 Subject: [PATCH 48/54] Documentation --- include/aws/http/proxy.h | 44 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/include/aws/http/proxy.h b/include/aws/http/proxy.h index acc737f7a..263a5f54d 100644 --- a/include/aws/http/proxy.h +++ b/include/aws/http/proxy.h @@ -386,29 +386,73 @@ struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_tunneling_adaptive( struct aws_allocator *allocator, struct aws_http_proxy_strategy_tunneling_adaptive_options *config); +/* + * aws_http_proxy_config is the persistent version of aws_http_proxy_options + * + * This is a set of APIs for creating, destroying and converting between them + */ + +/** + * Create a persistent proxy configuration from http connection options + * @param allocator memory allocator to use + * @param options http connection options to source proxy configuration from + * @return + */ AWS_HTTP_API struct aws_http_proxy_config *aws_http_proxy_config_new_from_connection_options( struct aws_allocator *allocator, const struct aws_http_client_connection_options *options); +/** + * Create a persistent proxy configuration from http connection manager options + * @param allocator memory allocator to use + * @param options http connection manager options to source proxy configuration from + * @return + */ AWS_HTTP_API struct aws_http_proxy_config *aws_http_proxy_config_new_from_manager_options( struct aws_allocator *allocator, const struct aws_http_connection_manager_options *options); +/** + * Create a persistent proxy configuration from non-persistent proxy options. The resulting + * proxy configuration assumes a tunneling connection type. + * + * @param allocator memory allocator to use + * @param options http proxy options to source proxy configuration from + * @return + */ AWS_HTTP_API struct aws_http_proxy_config *aws_http_proxy_config_new_tunneling_from_proxy_options( struct aws_allocator *allocator, const struct aws_http_proxy_options *options); +/** + * Clones an existing proxy configuration. A refactor could remove this (do a "move" between the old and new user + * data in the one spot it's used) but that should wait until we have better test cases for the logic where this + * gets invoked (ntlm/kerberos chains). + * + * @param allocator memory allocator to use + * @param proxy_config http proxy configuration to clone + * @return + */ AWS_HTTP_API struct aws_http_proxy_config *aws_http_proxy_config_new_clone( struct aws_allocator *allocator, const struct aws_http_proxy_config *proxy_config); +/** + * Destroys an http proxy configuration + * @param config http proxy configuration to destroy + */ AWS_HTTP_API void aws_http_proxy_config_destroy(struct aws_http_proxy_config *config); +/** + * Initializes non-persistent http proxy options from a persistent http proxy configuration + * @param options http proxy options to initialize + * @param config the http proxy config to use as an initialization source + */ AWS_HTTP_API void aws_http_proxy_options_init_from_config( struct aws_http_proxy_options *options, From 4a7da73a973b91bfc45c8a7cfefa397a436915a4 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Mon, 1 Mar 2021 14:20:22 -0800 Subject: [PATCH 49/54] Placeholder for raw channel creation through an http proxy --- include/aws/http/proxy.h | 6 ++++++ source/proxy_connection.c | 9 +++++++++ 2 files changed, 15 insertions(+) diff --git a/include/aws/http/proxy.h b/include/aws/http/proxy.h index 263a5f54d..380916968 100644 --- a/include/aws/http/proxy.h +++ b/include/aws/http/proxy.h @@ -21,6 +21,8 @@ struct aws_http_proxy_config; struct aws_http_proxy_negotiator; struct aws_http_proxy_strategy; +struct aws_socket_channel_bootstrap_options; + /** * @Deprecated - Supported proxy authentication modes. Superceded by proxy strategy. */ @@ -458,6 +460,10 @@ void aws_http_proxy_options_init_from_config( struct aws_http_proxy_options *options, const struct aws_http_proxy_config *config); +AWS_HTTP_API int aws_http_proxy_new_socket_channel( + struct aws_socket_channel_bootstrap_options *channel_options, + struct aws_http_proxy_options *proxy_options); + AWS_EXTERN_C_END #endif /* AWS_PROXY_STRATEGY_H */ diff --git a/source/proxy_connection.c b/source/proxy_connection.c index 665f2b7ea..fddbf5815 100644 --- a/source/proxy_connection.c +++ b/source/proxy_connection.c @@ -1111,3 +1111,12 @@ int aws_http_options_validate_proxy_configuration(const struct aws_http_client_c return AWS_OP_SUCCESS; } + +int aws_http_proxy_new_socket_channel( + struct aws_socket_channel_bootstrap_options *channel_options, + struct aws_http_proxy_options *proxy_options) { + (void)channel_options; + (void)proxy_options; + + return AWS_OP_ERR; +} From fd4a1a414924b0c371af30b8044b6190014a7625 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Wed, 10 Mar 2021 17:04:21 -0800 Subject: [PATCH 50/54] Windows fixes to trigger clang CI --- tests/integration_test_proxy.c | 3 +++ tests/proxy_test_helper.c | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/integration_test_proxy.c b/tests/integration_test_proxy.c index 35343d0fa..7b1f8be92 100644 --- a/tests/integration_test_proxy.c +++ b/tests/integration_test_proxy.c @@ -293,6 +293,8 @@ static struct aws_string *s_mock_aws_http_proxy_negotiation_ntlm_get_challenge_t const struct aws_byte_cursor *challenge_value, int *out_error_code) { + (void)challenge_value; + struct aws_allocator *allocator = user_data; *out_error_code = AWS_ERROR_SUCCESS; @@ -312,6 +314,7 @@ static struct aws_string *s_mock_aws_http_proxy_negotiation_ntlm_get_token_sync_ } static int s_test_proxy_sequential_negotiation(struct aws_allocator *allocator, void *ctx) { + (void)ctx; struct aws_http_proxy_strategy_tunneling_kerberos_options kerberos_config = { .get_token = s_mock_aws_http_proxy_negotiation_kerberos_get_token_sync_fn, diff --git a/tests/proxy_test_helper.c b/tests/proxy_test_helper.c index 8816d4bbf..cf865f82c 100644 --- a/tests/proxy_test_helper.c +++ b/tests/proxy_test_helper.c @@ -115,7 +115,7 @@ int proxy_tester_init(struct proxy_tester *tester, const struct proxy_tester_opt ASSERT_SUCCESS( aws_array_list_init_dynamic(&tester->connect_requests, tester->alloc, 1, sizeof(struct aws_http_message *))); - int connect_response_count = 1; + uint32_t connect_response_count = 1; if (options->desired_connect_response_count > connect_response_count) { connect_response_count = options->desired_connect_response_count; } From 1f9e53dbf276fc0dfbf39b118292db95886af785 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Mon, 22 Mar 2021 11:13:27 -0700 Subject: [PATCH 51/54] correct include guard --- include/aws/http/proxy.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/aws/http/proxy.h b/include/aws/http/proxy.h index 380916968..50871368c 100644 --- a/include/aws/http/proxy.h +++ b/include/aws/http/proxy.h @@ -1,5 +1,5 @@ -#ifndef AWS_PROXY_STRATEGY_H -#define AWS_PROXY_STRATEGY_H +#ifndef AWS_PROXY_H +#define AWS_PROXY_H /** * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. @@ -466,4 +466,4 @@ AWS_HTTP_API int aws_http_proxy_new_socket_channel( AWS_EXTERN_C_END -#endif /* AWS_PROXY_STRATEGY_H */ +#endif /* AWS_PROXY_H */ From 18522411282d326a7e7f40dbb0bb094f1af4d10c Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Mon, 22 Mar 2021 19:14:12 -0700 Subject: [PATCH 52/54] Reenable and fix adaptive proxy negotiation unit tests --- include/aws/http/proxy.h | 2 - tests/CMakeLists.txt | 8 +- tests/proxy_test_helper.c | 116 ++++++++++++++++++++++------- tests/proxy_test_helper.h | 9 ++- tests/test_proxy.c | 151 +++++++++++++++++++++----------------- 5 files changed, 183 insertions(+), 103 deletions(-) diff --git a/include/aws/http/proxy.h b/include/aws/http/proxy.h index 50871368c..864a406f5 100644 --- a/include/aws/http/proxy.h +++ b/include/aws/http/proxy.h @@ -241,8 +241,6 @@ struct aws_http_proxy_negotiator_tunnelling_vtable { * * (2) Forwarding - In a forwarding proxy connection, the forward_request_transform is invoked on every request sent out * on the connection. - * - * (3) Socks5 - not yet supported */ struct aws_http_proxy_negotiator { struct aws_ref_count ref_count; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 72127eac1..2636f7cc2 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -498,10 +498,10 @@ add_test_case(test_http_forwarding_proxy_request_transform_legacy_basic_auth) add_test_case(test_http_proxy_request_transform_kerberos) add_test_case(test_http_proxy_kerberos_token_failure) add_test_case(test_http_proxy_kerberos_connect_failure) -#add_test_case(test_http_proxy_adaptive_identity_success) -#add_test_case(test_http_proxy_adaptive_kerberos_success) -#add_test_case(test_http_proxy_adaptive_ntlm_success) -#add_test_case(test_http_proxy_adaptive_failure) +add_test_case(test_http_proxy_adaptive_identity_success) +add_test_case(test_http_proxy_adaptive_kerberos_success) +add_test_case(test_http_proxy_adaptive_ntlm_success) +add_test_case(test_http_proxy_adaptive_failure) add_test_case(test_http_forwarding_proxy_uri_rewrite) add_test_case(test_http_forwarding_proxy_uri_rewrite_options_star) add_test_case(test_http_tunnel_proxy_connection_success) diff --git a/tests/proxy_test_helper.c b/tests/proxy_test_helper.c index cf865f82c..0a2dfad07 100644 --- a/tests/proxy_test_helper.c +++ b/tests/proxy_test_helper.c @@ -30,6 +30,20 @@ enum { TESTER_TIMEOUT_SEC = 60, /* Give enough time for non-sudo users to enter password */ }; +struct testing_channel_bootstrap_wrapper { + struct testing_channel *channel; + struct aws_http_client_bootstrap *bootstrap; +}; + +static struct testing_channel_bootstrap_wrapper *s_get_current_channel_bootstrap_wrapper(struct proxy_tester *tester) { + struct testing_channel_bootstrap_wrapper *wrapper = NULL; + + size_t count = aws_array_list_length(&tester->testing_channels); + aws_array_list_get_at_ptr(&tester->testing_channels, (void **)&wrapper, count - 1); + + return wrapper; +} + void proxy_tester_on_client_connection_setup(struct aws_http_connection *connection, int error_code, void *user_data) { struct proxy_tester *tester = user_data; @@ -101,6 +115,9 @@ int proxy_tester_init(struct proxy_tester *tester, const struct proxy_tester_opt aws_http_library_init(options->alloc); + ASSERT_SUCCESS(aws_array_list_init_dynamic( + &tester->testing_channels, options->alloc, 1, sizeof(struct testing_channel_bootstrap_wrapper))); + tester->host = options->host; tester->port = options->port; tester->proxy_options = *options->proxy_options; @@ -191,24 +208,39 @@ int proxy_tester_clean_up(struct proxy_tester *tester) { aws_http_connection_release(tester->client_connection); } - if (tester->testing_channel) { - ASSERT_SUCCESS(testing_channel_clean_up(tester->testing_channel)); - while (!testing_channel_is_shutdown_completed(tester->testing_channel)) { - aws_thread_current_sleep(1000000000); + size_t channel_count = aws_array_list_length(&tester->testing_channels); + for (size_t i = 0; i < channel_count; ++i) { + struct testing_channel_bootstrap_wrapper wrapper; + aws_array_list_get_at(&tester->testing_channels, &wrapper, i); + struct testing_channel *channel = wrapper.channel; + if (channel) { + ASSERT_SUCCESS(testing_channel_clean_up(channel)); + while (!testing_channel_is_shutdown_completed(channel)) { + aws_thread_current_sleep(1000000000); + } + + aws_mem_release(tester->alloc, channel); } - - aws_mem_release(tester->alloc, tester->testing_channel); } ASSERT_SUCCESS(proxy_tester_wait(tester, proxy_tester_connection_shutdown_pred)); - if (tester->http_bootstrap != NULL) { - if (tester->testing_channel == NULL && tester->http_bootstrap->user_data) { - aws_http_proxy_user_data_destroy(tester->http_bootstrap->user_data); + for (size_t i = 0; i < channel_count; ++i) { + struct testing_channel_bootstrap_wrapper wrapper; + aws_array_list_get_at(&tester->testing_channels, &wrapper, i); + if (wrapper.bootstrap != NULL) { + if (channel_count == 0 && wrapper.bootstrap->user_data) { + aws_http_proxy_user_data_destroy(wrapper.bootstrap->user_data); + } + if (i + 1 < channel_count) { + wrapper.bootstrap->on_shutdown(tester->client_connection, 0, wrapper.bootstrap->user_data); + } + aws_mem_release(tester->alloc, wrapper.bootstrap); } - aws_mem_release(tester->alloc, tester->http_bootstrap); } + aws_array_list_clean_up(&tester->testing_channels); + aws_client_bootstrap_release(tester->client_bootstrap); aws_host_resolver_release(tester->host_resolver); @@ -252,17 +284,26 @@ static void s_testing_channel_shutdown_callback(int error_code, void *user_data) tester->wait_result = error_code; } - tester->http_bootstrap->on_shutdown( - tester->client_connection, tester->wait_result, tester->http_bootstrap->user_data); + struct testing_channel_bootstrap_wrapper *wrapper = s_get_current_channel_bootstrap_wrapper(tester); + + wrapper->bootstrap->on_shutdown(tester->client_connection, tester->wait_result, wrapper->bootstrap->user_data); } -int proxy_tester_create_testing_channel_connection(struct proxy_tester *tester) { - tester->testing_channel = aws_mem_calloc(tester->alloc, 1, sizeof(struct testing_channel)); +int proxy_tester_create_testing_channel_connection( + struct proxy_tester *tester, + struct aws_http_client_bootstrap *http_bootstrap) { + + struct testing_channel_bootstrap_wrapper *old_wrapper = s_get_current_channel_bootstrap_wrapper(tester); + if (old_wrapper != NULL) { + old_wrapper->channel->channel_shutdown = NULL; + } + + struct testing_channel *testing_channel = aws_mem_calloc(tester->alloc, 1, sizeof(struct testing_channel)); struct aws_testing_channel_options test_channel_options = {.clock_fn = aws_high_res_clock_get_ticks}; - ASSERT_SUCCESS(testing_channel_init(tester->testing_channel, tester->alloc, &test_channel_options)); - tester->testing_channel->channel_shutdown = s_testing_channel_shutdown_callback; - tester->testing_channel->channel_shutdown_user_data = tester; + ASSERT_SUCCESS(testing_channel_init(testing_channel, tester->alloc, &test_channel_options)); + testing_channel->channel_shutdown = s_testing_channel_shutdown_callback; + testing_channel->channel_shutdown_user_data = tester; /* Use small window so that we can observe it opening in tests. * Channel may wait until the window is small before issuing the increment command. */ @@ -271,19 +312,29 @@ int proxy_tester_create_testing_channel_connection(struct proxy_tester *tester) aws_http_connection_new_http1_1_client(tester->alloc, true, 256, &http1_options); ASSERT_NOT_NULL(connection); - connection->user_data = tester->http_bootstrap->user_data; + if (tester->client_connection != NULL) { + aws_http_connection_release(tester->client_connection); + tester->client_connection = NULL; + } + + connection->user_data = http_bootstrap->user_data; connection->client_data = &connection->client_or_server_data.client; - connection->proxy_request_transform = tester->http_bootstrap->proxy_request_transform; + connection->proxy_request_transform = http_bootstrap->proxy_request_transform; - struct aws_channel_slot *slot = aws_channel_slot_new(tester->testing_channel->channel); + struct aws_channel_slot *slot = aws_channel_slot_new(testing_channel->channel); ASSERT_NOT_NULL(slot); - ASSERT_SUCCESS(aws_channel_slot_insert_end(tester->testing_channel->channel, slot)); + ASSERT_SUCCESS(aws_channel_slot_insert_end(testing_channel->channel, slot)); ASSERT_SUCCESS(aws_channel_slot_set_handler(slot, &connection->channel_handler)); connection->vtable->on_channel_handler_installed(&connection->channel_handler, slot); - testing_channel_drain_queued_tasks(tester->testing_channel); + testing_channel_drain_queued_tasks(testing_channel); tester->client_connection = connection; + struct testing_channel_bootstrap_wrapper wrapper; + wrapper.channel = testing_channel; + wrapper.bootstrap = http_bootstrap; + aws_array_list_push_back(&tester->testing_channels, &wrapper); + return AWS_OP_SUCCESS; } @@ -362,7 +413,11 @@ static int s_record_connect_request(struct aws_byte_buf *request_buffer, struct int proxy_tester_verify_connect_request(struct proxy_tester *tester) { struct aws_byte_buf output; ASSERT_SUCCESS(aws_byte_buf_init(&output, tester->alloc, 1024)); - ASSERT_SUCCESS(testing_channel_drain_written_messages(tester->testing_channel, &output)); + + struct testing_channel *testing_channel = proxy_tester_get_current_channel(tester); + ASSERT_NOT_NULL(testing_channel); + + ASSERT_SUCCESS(testing_channel_drain_written_messages(testing_channel, &output)); char connect_request_buffer[1024]; snprintf( @@ -409,10 +464,12 @@ int proxy_tester_send_connect_response(struct proxy_tester *tester) { response_string = "HTTP/1.0 200 Connection established\r\nconnection: close\r\n\r\n"; } + struct testing_channel *channel = proxy_tester_get_current_channel(tester); + /* send response */ - ASSERT_SUCCESS(testing_channel_push_read_str(tester->testing_channel, response_string)); + ASSERT_SUCCESS(testing_channel_push_read_str(channel, response_string)); - testing_channel_drain_queued_tasks(tester->testing_channel); + testing_channel_drain_queued_tasks(channel); return AWS_OP_SUCCESS; } @@ -434,3 +491,12 @@ int proxy_tester_verify_connection_attempt_was_to_proxy( return AWS_OP_SUCCESS; } + +struct testing_channel *proxy_tester_get_current_channel(struct proxy_tester *tester) { + struct testing_channel_bootstrap_wrapper *wrapper = s_get_current_channel_bootstrap_wrapper(tester); + if (wrapper == NULL) { + return NULL; + } + + return wrapper->channel; +} diff --git a/tests/proxy_test_helper.h b/tests/proxy_test_helper.h index afff11061..64f507df5 100644 --- a/tests/proxy_test_helper.h +++ b/tests/proxy_test_helper.h @@ -65,8 +65,7 @@ struct proxy_tester { enum proxy_tester_failure_type failure_type; struct aws_http_connection *client_connection; - struct aws_http_client_bootstrap *http_bootstrap; - struct testing_channel *testing_channel; + struct aws_array_list testing_channels; bool client_connection_is_setup; bool client_connection_is_shutdown; @@ -110,7 +109,9 @@ void proxy_tester_on_client_connection_shutdown( void proxy_tester_on_client_bootstrap_shutdown(void *user_data); -int proxy_tester_create_testing_channel_connection(struct proxy_tester *tester); +int proxy_tester_create_testing_channel_connection( + struct proxy_tester *tester, + struct aws_http_client_bootstrap *http_bootstrap); int proxy_tester_verify_connect_request(struct proxy_tester *tester); @@ -121,4 +122,6 @@ int proxy_tester_verify_connection_attempt_was_to_proxy( struct aws_byte_cursor expected_host, uint16_t expected_port); +struct testing_channel *proxy_tester_get_current_channel(struct proxy_tester *tester); + #endif /* AWS_HTTP_PROXY_TEST_HELPER_H */ diff --git a/tests/test_proxy.c b/tests/test_proxy.c index 1c7cd9377..ea3ce1aab 100644 --- a/tests/test_proxy.c +++ b/tests/test_proxy.c @@ -70,7 +70,6 @@ static struct aws_http_message *s_build_http_request(struct aws_allocator *alloc aws_byte_cursor_from_string(s_mock_request_host)); } -#ifdef NEVER static bool s_is_header_in_request(struct aws_http_message *request, struct aws_byte_cursor header_name) { size_t header_count = aws_http_message_get_header_count(request); for (size_t i = 0; i < header_count; ++i) { @@ -84,7 +83,6 @@ static bool s_is_header_in_request(struct aws_http_message *request, struct aws_ return false; } -#endif static bool s_is_header_and_value_in_request(struct aws_http_message *request, struct aws_http_header *header) { size_t header_count = aws_http_message_get_header_count(request); @@ -144,8 +142,7 @@ static int s_test_aws_proxy_new_socket_channel(struct aws_socket_channel_bootstr if (tester.failure_type == PTFT_CHANNEL) { tester.wait_result = AWS_ERROR_UNKNOWN; } else if (tester.failure_type != PTFT_CONNECTION) { - tester.http_bootstrap = channel_options->user_data; - ASSERT_SUCCESS(proxy_tester_create_testing_channel_connection(&tester)); + ASSERT_SUCCESS(proxy_tester_create_testing_channel_connection(&tester, channel_options->user_data)); } aws_mutex_unlock(&tester.wait_lock); @@ -168,10 +165,11 @@ static int s_test_aws_proxy_new_socket_channel(struct aws_socket_channel_bootstr struct aws_http_client_bootstrap *http_bootstrap = channel_options->user_data; http_bootstrap->on_setup(tester.client_connection, AWS_ERROR_SUCCESS, http_bootstrap->user_data); + struct testing_channel *channel = proxy_tester_get_current_channel(&tester); if (tester.failure_type == PTFT_PROXY_STRATEGY) { - testing_channel_drain_queued_tasks(tester.testing_channel); + testing_channel_drain_queued_tasks(channel); } else { - testing_channel_run_currently_queued_tasks(tester.testing_channel); + testing_channel_run_currently_queued_tasks(channel); } if (tester.failure_type == PTFT_NONE || tester.failure_type == PTFT_CONNECT_REQUEST || @@ -507,7 +505,8 @@ static int s_do_http_forwarding_proxy_request_transform_test( ASSERT_NOT_NULL(stream); aws_http_stream_activate(stream); - testing_channel_run_currently_queued_tasks(tester.testing_channel); + struct testing_channel *channel = proxy_tester_get_current_channel(&tester); + testing_channel_run_currently_queued_tasks(channel); s_verify_transformed_request(untransformed_request, request, allocator); @@ -619,13 +618,13 @@ static struct aws_string *s_mock_aws_http_proxy_negotiation_kerberos_get_token_s return aws_string_new_from_string(allocator, s_mock_kerberos_token_value); } -AWS_STATIC_STRING_FROM_LITERAL(s_expected_kerberos_auth_header_name, "Proxy-Authorization"); +AWS_STATIC_STRING_FROM_LITERAL(s_expected_auth_header_name, "Proxy-Authorization"); AWS_STATIC_STRING_FROM_LITERAL(s_expected_kerberos_auth_header_value, "Negotiate abcdefABCDEF123"); static int s_verify_kerberos_connect_request(struct aws_http_message *request) { /* Check for auth header */ struct aws_http_header auth_header; - auth_header.name = aws_byte_cursor_from_string(s_expected_kerberos_auth_header_name); + auth_header.name = aws_byte_cursor_from_string(s_expected_auth_header_name); auth_header.value = aws_byte_cursor_from_string(s_expected_kerberos_auth_header_value); ASSERT_TRUE(s_is_header_and_value_in_request(request, &auth_header)); @@ -752,9 +751,8 @@ static int s_test_http_proxy_kerberos_connect_failure(struct aws_allocator *allo AWS_TEST_CASE(test_http_proxy_kerberos_connect_failure, s_test_http_proxy_kerberos_connect_failure); -#ifdef NEVER - -AWS_STATIC_STRING_FROM_LITERAL(s_mock_ntlm_token_value, "NTLM_RESPONSE"); +AWS_STATIC_STRING_FROM_LITERAL(s_mock_ntlm_token_value, "NTLM_TOKEN"); +AWS_STATIC_STRING_FROM_LITERAL(s_mock_ntlm_challenge_token_value, "NTLM_CHALLENGE_TOKEN"); static struct aws_string *s_mock_aws_http_proxy_negotiation_ntlm_get_challenge_token_sync_fn( void *user_data, @@ -763,12 +761,21 @@ static struct aws_string *s_mock_aws_http_proxy_negotiation_ntlm_get_challenge_t struct aws_allocator *allocator = user_data; + *out_error_code = AWS_ERROR_SUCCESS; + return aws_string_new_from_string(allocator, s_mock_ntlm_challenge_token_value); +} + +static struct aws_string *s_mock_aws_http_proxy_negotiation_ntlm_get_token_sync_fn( + void *user_data, + int *out_error_code) { + struct aws_allocator *allocator = user_data; + *out_error_code = AWS_ERROR_SUCCESS; return aws_string_new_from_string(allocator, s_mock_ntlm_token_value); } static int s_verify_identity_connect_request(struct aws_http_message *request) { - ASSERT_FALSE(s_is_header_in_request(request, aws_byte_cursor_from_string(s_expected_kerberos_auth_header_name))); + ASSERT_FALSE(s_is_header_in_request(request, aws_byte_cursor_from_string(s_expected_auth_header_name))); return AWS_OP_SUCCESS; } @@ -780,6 +787,7 @@ static struct aws_http_proxy_strategy *s_create_adaptive_strategy(struct aws_all }; struct aws_http_proxy_strategy_tunneling_ntlm_options ntlm_config = { + .get_token = s_mock_aws_http_proxy_negotiation_ntlm_get_token_sync_fn, .get_challenge_token = s_mock_aws_http_proxy_negotiation_ntlm_get_challenge_token_sync_fn, .get_challenge_token_user_data = allocator, }; @@ -829,6 +837,22 @@ AWS_TEST_CASE(test_http_proxy_adaptive_identity_success, s_test_http_proxy_adapt AWS_STATIC_STRING_FROM_LITERAL(s_unauthorized_response, "HTTP/1.0 407 Unauthorized\r\n\r\n"); AWS_STATIC_STRING_FROM_LITERAL(s_good_response, "HTTP/1.0 200 Connection established\r\nconnection: close\r\n\r\n"); +typedef int (*aws_proxy_test_verify_connect_fn)(struct aws_http_message *); + +static int s_verify_connect_requests(aws_proxy_test_verify_connect_fn verify_functions[], size_t function_count) { + size_t connect_requests = aws_array_list_length(&tester.connect_requests); + ASSERT_INT_EQUALS(function_count, connect_requests); + + for (size_t i = 0; i < connect_requests; ++i) { + struct aws_http_message *request = NULL; + aws_array_list_get_at(&tester.connect_requests, &request, i); + + ASSERT_SUCCESS(verify_functions[i](request)); + } + + return AWS_OP_SUCCESS; +} + static int s_test_http_proxy_adaptive_kerberos_success(struct aws_allocator *allocator, void *ctx) { (void)ctx; @@ -842,6 +866,11 @@ static int s_test_http_proxy_adaptive_kerberos_success(struct aws_allocator *all second_response, }; + aws_proxy_test_verify_connect_fn verifiers[] = { + s_verify_identity_connect_request, + s_verify_kerberos_connect_request, + }; + struct mocked_proxy_test_options options = { .test_mode = PTTM_HTTP_TUNNEL, .failure_type = PTFT_NONE, @@ -852,37 +881,42 @@ static int s_test_http_proxy_adaptive_kerberos_success(struct aws_allocator *all ASSERT_SUCCESS(s_setup_proxy_test(allocator, &options)); - ASSERT_SUCCESS(proxy_tester_verify_connect_request(&tester)); - ASSERT_SUCCESS(proxy_tester_send_connect_response(&tester)); - ASSERT_SUCCESS(proxy_tester_wait(&tester, proxy_tester_connection_setup_pred)); ASSERT_TRUE(tester.client_connection != NULL); ASSERT_TRUE(tester.wait_result == AWS_ERROR_SUCCESS); - ASSERT_INT_EQUALS(2, aws_array_list_length(&tester.connect_requests)); + s_verify_connect_requests(verifiers, 2); - struct aws_http_message *first_connect_request = NULL; - aws_array_list_get_at(&tester.connect_requests, &first_connect_request, 0); + aws_http_proxy_strategy_release(adaptive_strategy); - ASSERT_SUCCESS(s_verify_identity_connect_request(first_connect_request)); + ASSERT_SUCCESS(proxy_tester_clean_up(&tester)); - struct aws_http_message *second_connect_request = NULL; - aws_array_list_get_at(&tester.connect_requests, &second_connect_request, 1); + return AWS_OP_SUCCESS; +} - ASSERT_SUCCESS(s_verify_kerberos_connect_request(second_connect_request)); +AWS_TEST_CASE(test_http_proxy_adaptive_kerberos_success, s_test_http_proxy_adaptive_kerberos_success); - aws_http_proxy_strategy_release(adaptive_strategy); +AWS_STATIC_STRING_FROM_LITERAL(s_expected_ntlm_token_auth_header_value, "NTLM NTLM_TOKEN"); - ASSERT_SUCCESS(proxy_tester_clean_up(&tester)); +static int s_verify_ntlm_connect_token_request(struct aws_http_message *request) { + /* Check for auth header */ + struct aws_http_header auth_header; + auth_header.name = aws_byte_cursor_from_string(s_expected_auth_header_name); + auth_header.value = aws_byte_cursor_from_string(s_expected_ntlm_token_auth_header_value); + ASSERT_TRUE(s_is_header_and_value_in_request(request, &auth_header)); return AWS_OP_SUCCESS; } -AWS_TEST_CASE(test_http_proxy_adaptive_kerberos_success, s_test_http_proxy_adaptive_kerberos_success); +AWS_STATIC_STRING_FROM_LITERAL(s_expected_ntlm_challenge_token_auth_header_value, "NTLM NTLM_CHALLENGE_TOKEN"); -static int s_verify_ntlm_connect_request(struct aws_http_message *request) { - (void)request; +static int s_verify_ntlm_connect_challenge_token_request(struct aws_http_message *request) { + /* Check for auth header */ + struct aws_http_header auth_header; + auth_header.name = aws_byte_cursor_from_string(s_expected_auth_header_name); + auth_header.value = aws_byte_cursor_from_string(s_expected_ntlm_challenge_token_auth_header_value); + ASSERT_TRUE(s_is_header_and_value_in_request(request, &auth_header)); return AWS_OP_SUCCESS; } @@ -898,16 +932,24 @@ static int s_test_http_proxy_adaptive_ntlm_success(struct aws_allocator *allocat struct aws_byte_cursor good_response = aws_byte_cursor_from_string(s_good_response); struct aws_byte_cursor connect_responses[] = { + bad_response, bad_response, bad_response, good_response, }; + aws_proxy_test_verify_connect_fn verifiers[] = { + s_verify_identity_connect_request, + s_verify_kerberos_connect_request, + s_verify_ntlm_connect_token_request, + s_verify_ntlm_connect_challenge_token_request, + }; + struct mocked_proxy_test_options options = { .test_mode = PTTM_HTTP_TUNNEL, .failure_type = PTFT_NONE, .proxy_strategy = adaptive_strategy, - .mocked_response_count = 3, + .mocked_response_count = 4, .mocked_responses = connect_responses, }; @@ -916,30 +958,12 @@ static int s_test_http_proxy_adaptive_ntlm_success(struct aws_allocator *allocat ASSERT_SUCCESS(proxy_tester_verify_connect_request(&tester)); ASSERT_SUCCESS(proxy_tester_send_connect_response(&tester)); - ASSERT_SUCCESS(proxy_tester_verify_connect_request(&tester)); - ASSERT_SUCCESS(proxy_tester_send_connect_response(&tester)); - ASSERT_SUCCESS(proxy_tester_wait(&tester, proxy_tester_connection_setup_pred)); ASSERT_TRUE(tester.client_connection != NULL); ASSERT_TRUE(tester.wait_result == AWS_ERROR_SUCCESS); - ASSERT_INT_EQUALS(3, aws_array_list_length(&tester.connect_requests)); - - struct aws_http_message *first_connect_request = NULL; - aws_array_list_get_at(&tester.connect_requests, &first_connect_request, 0); - - ASSERT_SUCCESS(s_verify_identity_connect_request(first_connect_request)); - - struct aws_http_message *second_connect_request = NULL; - aws_array_list_get_at(&tester.connect_requests, &second_connect_request, 1); - - ASSERT_SUCCESS(s_verify_kerberos_connect_request(second_connect_request)); - - struct aws_http_message *third_connect_request = NULL; - aws_array_list_get_at(&tester.connect_requests, &third_connect_request, 2); - - ASSERT_SUCCESS(s_verify_ntlm_connect_request(third_connect_request)); + ASSERT_SUCCESS(s_verify_connect_requests(verifiers, 4)); aws_http_proxy_strategy_release(adaptive_strategy); @@ -961,13 +985,21 @@ static int s_test_http_proxy_adaptive_failure(struct aws_allocator *allocator, v bad_response, bad_response, bad_response, + bad_response, + }; + + aws_proxy_test_verify_connect_fn verifiers[] = { + s_verify_identity_connect_request, + s_verify_kerberos_connect_request, + s_verify_ntlm_connect_token_request, + s_verify_ntlm_connect_challenge_token_request, }; struct mocked_proxy_test_options options = { .test_mode = PTTM_HTTP_TUNNEL, .failure_type = PTFT_NONE, .proxy_strategy = adaptive_strategy, - .mocked_response_count = 3, + .mocked_response_count = 4, .mocked_responses = connect_responses, }; @@ -976,29 +1008,11 @@ static int s_test_http_proxy_adaptive_failure(struct aws_allocator *allocator, v ASSERT_SUCCESS(proxy_tester_verify_connect_request(&tester)); ASSERT_SUCCESS(proxy_tester_send_connect_response(&tester)); - ASSERT_SUCCESS(proxy_tester_verify_connect_request(&tester)); - ASSERT_SUCCESS(proxy_tester_send_connect_response(&tester)); - ASSERT_SUCCESS(proxy_tester_wait(&tester, proxy_tester_connection_setup_pred)); ASSERT_TRUE(tester.wait_result == AWS_ERROR_HTTP_PROXY_CONNECT_FAILED); - ASSERT_INT_EQUALS(3, aws_array_list_length(&tester.connect_requests)); - - struct aws_http_message *first_connect_request = NULL; - aws_array_list_get_at(&tester.connect_requests, &first_connect_request, 0); - - ASSERT_SUCCESS(s_verify_identity_connect_request(first_connect_request)); - - struct aws_http_message *second_connect_request = NULL; - aws_array_list_get_at(&tester.connect_requests, &second_connect_request, 1); - - ASSERT_SUCCESS(s_verify_kerberos_connect_request(second_connect_request)); - - struct aws_http_message *third_connect_request = NULL; - aws_array_list_get_at(&tester.connect_requests, &third_connect_request, 2); - - ASSERT_SUCCESS(s_verify_ntlm_connect_request(third_connect_request)); + ASSERT_SUCCESS(s_verify_connect_requests(verifiers, 4)); aws_http_proxy_strategy_release(adaptive_strategy); @@ -1008,7 +1022,6 @@ static int s_test_http_proxy_adaptive_failure(struct aws_allocator *allocator, v } AWS_TEST_CASE(test_http_proxy_adaptive_failure, s_test_http_proxy_adaptive_failure); -#endif AWS_STATIC_STRING_FROM_LITERAL(s_rewrite_host, "www.uri.com"); AWS_STATIC_STRING_FROM_LITERAL(s_rewrite_path, "/main/index.html?foo=bar"); From e5431c4157f840cc432d5659b6b199f69efebbed Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Tue, 23 Mar 2021 09:30:06 -0700 Subject: [PATCH 53/54] Test fixes --- tests/proxy_test_helper.c | 5 ----- tests/test_proxy.c | 2 ++ 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/tests/proxy_test_helper.c b/tests/proxy_test_helper.c index 0a2dfad07..e299234c9 100644 --- a/tests/proxy_test_helper.c +++ b/tests/proxy_test_helper.c @@ -312,11 +312,6 @@ int proxy_tester_create_testing_channel_connection( aws_http_connection_new_http1_1_client(tester->alloc, true, 256, &http1_options); ASSERT_NOT_NULL(connection); - if (tester->client_connection != NULL) { - aws_http_connection_release(tester->client_connection); - tester->client_connection = NULL; - } - connection->user_data = http_bootstrap->user_data; connection->client_data = &connection->client_or_server_data.client; connection->proxy_request_transform = http_bootstrap->proxy_request_transform; diff --git a/tests/test_proxy.c b/tests/test_proxy.c index ea3ce1aab..828302ae2 100644 --- a/tests/test_proxy.c +++ b/tests/test_proxy.c @@ -759,6 +759,8 @@ static struct aws_string *s_mock_aws_http_proxy_negotiation_ntlm_get_challenge_t const struct aws_byte_cursor *challenge_value, int *out_error_code) { + (void)challenge_value; + struct aws_allocator *allocator = user_data; *out_error_code = AWS_ERROR_SUCCESS; From 0ca610f109f892985f5155fa522b9c05e273c31c Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Tue, 23 Mar 2021 10:08:09 -0700 Subject: [PATCH 54/54] Documentation and invariants --- include/aws/http/private/proxy_impl.h | 8 ++++- include/aws/http/proxy.h | 48 +++++++++++++++++++++------ source/http.c | 2 +- source/proxy_connection.c | 20 ++++------- source/proxy_strategy.c | 2 ++ 5 files changed, 54 insertions(+), 26 deletions(-) diff --git a/include/aws/http/private/proxy_impl.h b/include/aws/http/private/proxy_impl.h index ab8ab0964..352a2f390 100644 --- a/include/aws/http/private/proxy_impl.h +++ b/include/aws/http/private/proxy_impl.h @@ -18,6 +18,7 @@ struct aws_http_message; struct aws_channel_slot; struct aws_string; struct aws_tls_connection_options; +struct aws_http_proxy_negotiator; struct aws_http_proxy_strategy; struct aws_http_proxy_strategy_tunneling_sequence_options; struct aws_http_proxy_strategy_tunneling_kerberos_options; @@ -154,7 +155,8 @@ struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_forwarding_identity( /** * Constructor for a tunneling proxy strategy that contains a set of sub-strategies which are tried - * sequentially in order. Each strategy is tried against a new, fresh connection. + * sequentially in order. Each strategy has the choice to either proceed on a fresh connection or + * reuse the current one. * * @param allocator memory allocator to use * @param config sequence configuration options @@ -169,6 +171,8 @@ struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_tunneling_sequence( * A constructor for a proxy strategy that performs kerberos authentication by adding the appropriate * header and header value to CONNECT requests. * + * Currently only supports synchronous fetch of kerberos token values. + * * @param allocator memory allocator to use * @param config kerberos authentication configuration info * @return a new proxy strategy if successfully constructed, otherwise NULL @@ -183,6 +187,8 @@ struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_tunneling_kerberos( * strategy will only succeed in a chain in a non-leading position. The strategy extracts the challenge from the * proxy's response to a previous CONNECT request in the chain. * + * Currently only supports synchronous fetch of token values. + * * @param allocator memory allocator to use * @param config configuration options for the strategy * @return a new proxy strategy if successfully constructed, otherwise NULL diff --git a/include/aws/http/proxy.h b/include/aws/http/proxy.h index 864a406f5..97dfb9a0a 100644 --- a/include/aws/http/proxy.h +++ b/include/aws/http/proxy.h @@ -50,8 +50,8 @@ enum aws_http_proxy_connection_type { AWS_HPCT_HTTP_FORWARD, /** - * Use the proxy to establish an http connection via a CONNECT request to the proxy. Works for both plaintext and - * tls connections. + * Use the proxy to establish a connection to a remote endpoint via a CONNECT request through the proxy. + * Works for both plaintext and tls connections. */ AWS_HPCT_HTTP_TUNNEL, }; @@ -92,25 +92,24 @@ struct aws_http_proxy_options { * * For tunneling proxies it allows custom retry and adaptive negotiation of CONNECT requests. * For forwarding proxies it allows custom request transformations. - * Other proxy connection types TBD. */ struct aws_http_proxy_strategy *proxy_strategy; /** * @Deprecated - What type of proxy authentication to use, if any. - * Replaced by proxy_strategy + * Replaced by instantiating a proxy_strategy */ enum aws_http_proxy_authentication_type auth_type; /** * @Deprecated - Optional user name to use for basic authentication - * Replaced by proxy_strategy, using an instantiation from aws_http_proxy_strategy_new_basic_auth() + * Replaced by instantiating a proxy_strategy via aws_http_proxy_strategy_new_basic_auth() */ struct aws_byte_cursor auth_username; /** * @Deprecated - Optional password to use for basic authentication - * Replaced by proxy_strategy, using an instantiation from aws_http_proxy_strategy_new_basic_auth() + * Replaced by instantiating a proxy_strategy via aws_http_proxy_strategy_new_basic_auth() */ struct aws_byte_cursor auth_password; }; @@ -198,9 +197,24 @@ typedef int(aws_http_proxy_negotiator_connect_on_incoming_body_fn)( struct aws_http_proxy_negotiator *proxy_negotiator, const struct aws_byte_cursor *data); +/* + * Control value that lets the http proxy implementation know if and how to retry a CONNECT request based on + * the proxy negotiator's state. + */ enum aws_http_proxy_negotiation_retry_directive { + /* + * Stop trying to connect through the proxy and give up. + */ AWS_HPNRD_STOP, + + /* + * Establish a new connection to the proxy before making the next CONNECT request + */ AWS_HPNRD_NEW_CONNECTION, + + /* + * Reuse the existing connection to make the next CONNECT request + */ AWS_HPNRD_CURRENT_CONNECTION, }; @@ -270,6 +284,9 @@ struct aws_http_proxy_strategy { enum aws_http_proxy_connection_type proxy_connection_type; }; +/* + * Options necessary to create a basic authentication proxy strategy + */ struct aws_http_proxy_strategy_basic_auth_options { /* type of proxy connection being established, must be forwarding or tunnel */ @@ -282,6 +299,9 @@ struct aws_http_proxy_strategy_basic_auth_options { struct aws_byte_cursor password; }; +/* + * Options necessary to create a (synchronous) kerberos authentication proxy strategy + */ struct aws_http_proxy_strategy_tunneling_kerberos_options { aws_http_proxy_negotiation_get_token_sync_fn *get_token; @@ -289,6 +309,9 @@ struct aws_http_proxy_strategy_tunneling_kerberos_options { void *get_token_user_data; }; +/* + * Options necessary to create a (synchronous) ntlm authentication proxy strategy + */ struct aws_http_proxy_strategy_tunneling_ntlm_options { aws_http_proxy_negotiation_get_token_sync_fn *get_token; @@ -298,6 +321,10 @@ struct aws_http_proxy_strategy_tunneling_ntlm_options { void *get_challenge_token_user_data; }; +/* + * Options necessary to create an adaptive sequential strategy that tries one or more of kerberos and ntlm (in that + * order, if both are active). If an options struct is NULL, then that strategy will not be used. + */ struct aws_http_proxy_strategy_tunneling_adaptive_options { /* * If non-null, will insert a kerberos proxy strategy into the adaptive sequence @@ -310,6 +337,9 @@ struct aws_http_proxy_strategy_tunneling_adaptive_options { struct aws_http_proxy_strategy_tunneling_ntlm_options *ntlm_options; }; +/* + * Options necessary to create a sequential proxy strategy. + */ struct aws_http_proxy_strategy_tunneling_sequence_options { struct aws_http_proxy_strategy **strategies; @@ -387,7 +417,7 @@ struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_tunneling_adaptive( struct aws_http_proxy_strategy_tunneling_adaptive_options *config); /* - * aws_http_proxy_config is the persistent version of aws_http_proxy_options + * aws_http_proxy_config is the persistent, memory-managed version of aws_http_proxy_options * * This is a set of APIs for creating, destroying and converting between them */ @@ -458,10 +488,6 @@ void aws_http_proxy_options_init_from_config( struct aws_http_proxy_options *options, const struct aws_http_proxy_config *config); -AWS_HTTP_API int aws_http_proxy_new_socket_channel( - struct aws_socket_channel_bootstrap_options *channel_options, - struct aws_http_proxy_options *proxy_options); - AWS_EXTERN_C_END #endif /* AWS_PROXY_H */ diff --git a/source/http.c b/source/http.c index abf43a02c..86100866d 100644 --- a/source/http.c +++ b/source/http.c @@ -87,7 +87,7 @@ static struct aws_error_info s_errors[] = { "The http server is closed, no more connections will be accepted"), AWS_DEFINE_ERROR_INFO_HTTP( AWS_ERROR_HTTP_PROXY_CONNECT_FAILED, - "Proxy tls connection establishment failed because the CONNECT call failed"), + "Proxy-based connection establishment failed because the CONNECT call failed"), AWS_DEFINE_ERROR_INFO_HTTP( AWS_ERROR_HTTP_CONNECTION_MANAGER_SHUTTING_DOWN, "Connection acquisition failed because connection manager is shutting down"), diff --git a/source/proxy_connection.c b/source/proxy_connection.c index fddbf5815..cf44efc34 100644 --- a/source/proxy_connection.c +++ b/source/proxy_connection.c @@ -488,6 +488,11 @@ static void s_aws_http_on_stream_complete_tunnel_proxy( struct aws_http_proxy_user_data *new_context = aws_http_proxy_user_data_new_reset_clone(context->allocator, context); if (new_context != NULL && s_create_tunneling_connection(new_context) == AWS_OP_SUCCESS) { + /* + * We successfully kicked off a new connection. By NULLing the callbacks on the old one, we can + * shut it down quietly without the user being notified. The new connection will notify the user + * based on its success or failure. + */ context->original_on_shutdown = NULL; context->original_on_setup = NULL; context->error_code = AWS_ERROR_HTTP_PROXY_CONNECT_FAILED_RETRYABLE; @@ -609,8 +614,7 @@ static void s_continue_tunneling_connect(struct aws_http_message *message, void } /* - * Issues a CONNECT request on a newly-established proxy connection with the intent - * of upgrading with TLS on success + * Issues a CONNECT request on an http connection */ static int s_make_proxy_connect_request(struct aws_http_proxy_user_data *user_data) { if (user_data->connect_request != NULL) { @@ -634,8 +638,7 @@ static int s_make_proxy_connect_request(struct aws_http_proxy_user_data *user_da } /* - * Connection setup callback for tls-based proxy connections. - * Could be unified with non-tls version by checking tls options and branching post-success + * Connection setup callback for tunneling proxy connections. */ static void s_aws_http_on_client_connection_http_tunneling_proxy_setup_fn( struct aws_http_connection *connection, @@ -1111,12 +1114,3 @@ int aws_http_options_validate_proxy_configuration(const struct aws_http_client_c return AWS_OP_SUCCESS; } - -int aws_http_proxy_new_socket_channel( - struct aws_socket_channel_bootstrap_options *channel_options, - struct aws_http_proxy_options *proxy_options) { - (void)channel_options; - (void)proxy_options; - - return AWS_OP_ERR; -} diff --git a/source/proxy_strategy.c b/source/proxy_strategy.c index eda510baf..3130d91cc 100644 --- a/source/proxy_strategy.c +++ b/source/proxy_strategy.c @@ -1323,6 +1323,8 @@ struct aws_http_proxy_strategy *aws_http_proxy_strategy_new_tunneling_adaptive( strategies[strategy_count++] = ntlm_strategy; } + AWS_FATAL_ASSERT(strategy_count <= PROXY_STRATEGY_MAX_ADAPTIVE_STRATEGIES); + struct aws_http_proxy_strategy_tunneling_sequence_options sequence_config = { .strategies = strategies, .strategy_count = strategy_count,