1 /**
2  * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3  * SPDX-License-Identifier: Apache-2.0.
4  */
5 
6 #include <aws/http/private/proxy_impl.h>
7 
8 #include <aws/common/encoding.h>
9 #include <aws/common/environment.h>
10 #include <aws/common/string.h>
11 #include <aws/http/connection_manager.h>
12 #include <aws/http/private/connection_impl.h>
13 #include <aws/http/proxy.h>
14 #include <aws/http/request_response.h>
15 #include <aws/io/channel.h>
16 #include <aws/io/logging.h>
17 #include <aws/io/tls_channel_handler.h>
18 #include <aws/io/uri.h>
19 
20 #if _MSC_VER
21 #    pragma warning(disable : 4204) /* non-constant aggregate initializer */
22 #    pragma warning(disable : 4232) /* function pointer to dll symbol */
23 #endif
24 
25 AWS_STATIC_STRING_FROM_LITERAL(s_host_header_name, "Host");
26 AWS_STATIC_STRING_FROM_LITERAL(s_proxy_connection_header_name, "Proxy-Connection");
27 AWS_STATIC_STRING_FROM_LITERAL(s_proxy_connection_header_value, "Keep-Alive");
28 AWS_STATIC_STRING_FROM_LITERAL(s_options_method, "OPTIONS");
29 AWS_STATIC_STRING_FROM_LITERAL(s_star_path, "*");
30 AWS_STATIC_STRING_FROM_LITERAL(s_http_scheme, "http");
31 
32 AWS_STATIC_STRING_FROM_LITERAL(s_http_proxy_env_var, "HTTP_PROXY");
33 AWS_STATIC_STRING_FROM_LITERAL(s_http_proxy_env_var_low, "http_proxy");
34 AWS_STATIC_STRING_FROM_LITERAL(s_https_proxy_env_var, "HTTPS_PROXY");
35 AWS_STATIC_STRING_FROM_LITERAL(s_https_proxy_env_var_low, "https_proxy");
36 
37 #ifndef BYO_CRYPTO
38 AWS_STATIC_STRING_FROM_LITERAL(s_proxy_no_verify_peer_env_var, "AWS_PROXY_NO_VERIFY_PEER");
39 #endif
40 
41 static struct aws_http_proxy_system_vtable s_default_vtable = {
42     .setup_client_tls = &aws_channel_setup_client_tls,
43 };
44 
45 static struct aws_http_proxy_system_vtable *s_vtable = &s_default_vtable;
46 
aws_http_proxy_system_set_vtable(struct aws_http_proxy_system_vtable * vtable)47 void aws_http_proxy_system_set_vtable(struct aws_http_proxy_system_vtable *vtable) {
48     s_vtable = vtable;
49 }
50 
aws_http_proxy_user_data_destroy(struct aws_http_proxy_user_data * user_data)51 void aws_http_proxy_user_data_destroy(struct aws_http_proxy_user_data *user_data) {
52     if (user_data == NULL) {
53         return;
54     }
55 
56     /*
57      * For tunneling connections, this is now internal and never surfaced to the user, so it's our responsibility
58      * to clean up the last reference.
59      */
60     if (user_data->proxy_connection != NULL && user_data->proxy_config->connection_type == AWS_HPCT_HTTP_TUNNEL) {
61         aws_http_connection_release(user_data->proxy_connection);
62         user_data->proxy_connection = NULL;
63     }
64 
65     aws_string_destroy(user_data->original_host);
66     if (user_data->proxy_config) {
67         aws_http_proxy_config_destroy(user_data->proxy_config);
68     }
69 
70     if (user_data->original_tls_options) {
71         aws_tls_connection_options_clean_up(user_data->original_tls_options);
72         aws_mem_release(user_data->allocator, user_data->original_tls_options);
73     }
74 
75     aws_http_proxy_negotiator_release(user_data->proxy_negotiator);
76 
77     aws_client_bootstrap_release(user_data->original_bootstrap);
78 
79     aws_mem_release(user_data->allocator, user_data);
80 }
81 
aws_http_proxy_user_data_new(struct aws_allocator * allocator,const struct aws_http_client_connection_options * options,aws_client_bootstrap_on_channel_event_fn * on_channel_setup,aws_client_bootstrap_on_channel_event_fn * on_channel_shutdown)82 struct aws_http_proxy_user_data *aws_http_proxy_user_data_new(
83     struct aws_allocator *allocator,
84     const struct aws_http_client_connection_options *options,
85     aws_client_bootstrap_on_channel_event_fn *on_channel_setup,
86     aws_client_bootstrap_on_channel_event_fn *on_channel_shutdown) {
87 
88     AWS_FATAL_ASSERT(options->proxy_options != NULL);
89 
90     struct aws_http_proxy_user_data *user_data = aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_user_data));
91     if (user_data == NULL) {
92         return NULL;
93     }
94 
95     user_data->allocator = allocator;
96     user_data->state = AWS_PBS_SOCKET_CONNECT;
97     user_data->error_code = AWS_ERROR_SUCCESS;
98     user_data->connect_status_code = AWS_HTTP_STATUS_CODE_UNKNOWN;
99     user_data->original_bootstrap = aws_client_bootstrap_acquire(options->bootstrap);
100     if (options->socket_options != NULL) {
101         user_data->original_socket_options = *options->socket_options;
102     }
103     user_data->original_manual_window_management = options->manual_window_management;
104     user_data->original_initial_window_size = options->initial_window_size;
105 
106     user_data->original_host = aws_string_new_from_cursor(allocator, &options->host_name);
107     if (user_data->original_host == NULL) {
108         goto on_error;
109     }
110 
111     user_data->original_port = options->port;
112 
113     user_data->proxy_config = aws_http_proxy_config_new_from_connection_options(allocator, options);
114     if (user_data->proxy_config == NULL) {
115         goto on_error;
116     }
117 
118     user_data->proxy_negotiator =
119         aws_http_proxy_strategy_create_negotiator(user_data->proxy_config->proxy_strategy, allocator);
120     if (user_data->proxy_negotiator == NULL) {
121         goto on_error;
122     }
123 
124     if (options->tls_options) {
125         /* clone tls options, but redirect user data to what we're creating */
126         user_data->original_tls_options = aws_mem_calloc(allocator, 1, sizeof(struct aws_tls_connection_options));
127         if (user_data->original_tls_options == NULL ||
128             aws_tls_connection_options_copy(user_data->original_tls_options, options->tls_options)) {
129             goto on_error;
130         }
131 
132         user_data->original_tls_options->user_data = user_data;
133     }
134 
135     user_data->original_http_on_setup = options->on_setup;
136     user_data->original_http_on_shutdown = options->on_shutdown;
137     user_data->original_channel_on_setup = on_channel_setup;
138     user_data->original_channel_on_shutdown = on_channel_shutdown;
139 
140     /* one and only one setup callback must be valid */
141     AWS_FATAL_ASSERT((user_data->original_http_on_setup == NULL) != (user_data->original_channel_on_setup == NULL));
142 
143     /* one and only one shutdown callback must be valid */
144     AWS_FATAL_ASSERT(
145         (user_data->original_http_on_shutdown == NULL) != (user_data->original_channel_on_shutdown == NULL));
146 
147     /* callback set must be self-consistent.  Technically the second check is redundant given the previous checks */
148     AWS_FATAL_ASSERT((user_data->original_http_on_setup == NULL) == (user_data->original_http_on_shutdown == NULL));
149     AWS_FATAL_ASSERT(
150         (user_data->original_channel_on_setup == NULL) == (user_data->original_channel_on_shutdown == NULL));
151 
152     user_data->original_user_data = options->user_data;
153 
154     struct aws_http1_connection_options default_options;
155     AWS_ZERO_STRUCT(default_options);
156     if (options->http1_options) {
157         user_data->original_http1_options = *options->http1_options;
158     } else {
159         user_data->original_http1_options = default_options;
160     }
161 
162     return user_data;
163 
164 on_error:
165 
166     AWS_LOGF_ERROR(
167         AWS_LS_HTTP_CONNECTION,
168         "(STATIC) Proxy connection failed to create user data with error %d(%s)",
169         aws_last_error(),
170         aws_error_str(aws_last_error()));
171 
172     aws_http_proxy_user_data_destroy(user_data);
173 
174     return NULL;
175 }
176 
aws_http_proxy_user_data_new_reset_clone(struct aws_allocator * allocator,struct aws_http_proxy_user_data * old_user_data)177 struct aws_http_proxy_user_data *aws_http_proxy_user_data_new_reset_clone(
178     struct aws_allocator *allocator,
179     struct aws_http_proxy_user_data *old_user_data) {
180 
181     AWS_FATAL_ASSERT(old_user_data != NULL);
182 
183     struct aws_http_proxy_user_data *user_data = aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_user_data));
184     if (user_data == NULL) {
185         return NULL;
186     }
187 
188     user_data->allocator = allocator;
189     user_data->state = AWS_PBS_SOCKET_CONNECT;
190     user_data->error_code = AWS_ERROR_SUCCESS;
191     user_data->connect_status_code = AWS_HTTP_STATUS_CODE_UNKNOWN;
192     user_data->original_bootstrap = aws_client_bootstrap_acquire(old_user_data->original_bootstrap);
193     user_data->original_socket_options = old_user_data->original_socket_options;
194     user_data->original_manual_window_management = old_user_data->original_manual_window_management;
195     user_data->original_initial_window_size = old_user_data->original_initial_window_size;
196 
197     user_data->original_host = aws_string_new_from_string(allocator, old_user_data->original_host);
198     if (user_data->original_host == NULL) {
199         goto on_error;
200     }
201 
202     user_data->original_port = old_user_data->original_port;
203 
204     user_data->proxy_config = aws_http_proxy_config_new_clone(allocator, old_user_data->proxy_config);
205     if (user_data->proxy_config == NULL) {
206         goto on_error;
207     }
208 
209     user_data->proxy_negotiator = aws_http_proxy_negotiator_acquire(old_user_data->proxy_negotiator);
210     if (user_data->proxy_negotiator == NULL) {
211         goto on_error;
212     }
213 
214     if (old_user_data->original_tls_options) {
215         /* clone tls options, but redirect user data to what we're creating */
216         user_data->original_tls_options = aws_mem_calloc(allocator, 1, sizeof(struct aws_tls_connection_options));
217         if (user_data->original_tls_options == NULL ||
218             aws_tls_connection_options_copy(user_data->original_tls_options, old_user_data->original_tls_options)) {
219             goto on_error;
220         }
221 
222         user_data->original_tls_options->user_data = user_data;
223     }
224 
225     user_data->original_http_on_setup = old_user_data->original_http_on_setup;
226     user_data->original_http_on_shutdown = old_user_data->original_http_on_shutdown;
227     user_data->original_channel_on_setup = old_user_data->original_channel_on_setup;
228     user_data->original_channel_on_shutdown = old_user_data->original_channel_on_shutdown;
229     user_data->original_user_data = old_user_data->original_user_data;
230 
231     return user_data;
232 
233 on_error:
234 
235     AWS_LOGF_ERROR(
236         AWS_LS_HTTP_CONNECTION,
237         "(STATIC) Proxy connection failed to create user data with error %d(%s)",
238         aws_last_error(),
239         aws_error_str(aws_last_error()));
240 
241     aws_http_proxy_user_data_destroy(user_data);
242 
243     return NULL;
244 }
245 
246 /*
247  * Examines the proxy user data state and determines whether to make an http-interface setup callback
248  * or a raw channel setup callback
249  */
s_do_on_setup_callback(struct aws_http_proxy_user_data * proxy_ud,struct aws_http_connection * connection,int error_code)250 static void s_do_on_setup_callback(
251     struct aws_http_proxy_user_data *proxy_ud,
252     struct aws_http_connection *connection,
253     int error_code) {
254     if (proxy_ud->original_http_on_setup) {
255         proxy_ud->original_http_on_setup(connection, error_code, proxy_ud->original_user_data);
256         proxy_ud->original_http_on_setup = NULL;
257     }
258 
259     if (proxy_ud->original_channel_on_setup) {
260         struct aws_channel *channel = NULL;
261         if (connection != NULL) {
262             channel = aws_http_connection_get_channel(connection);
263         }
264         proxy_ud->original_channel_on_setup(
265             proxy_ud->original_bootstrap, error_code, channel, proxy_ud->original_user_data);
266         proxy_ud->original_channel_on_setup = NULL;
267     }
268 }
269 
270 /*
271  * Examines the proxy user data state and determines whether to make an http-interface shutdown callback
272  * or a raw channel shutdown callback
273  */
s_do_on_shutdown_callback(struct aws_http_proxy_user_data * proxy_ud,int error_code)274 static void s_do_on_shutdown_callback(struct aws_http_proxy_user_data *proxy_ud, int error_code) {
275     AWS_FATAL_ASSERT(proxy_ud->proxy_connection);
276 
277     if (proxy_ud->original_http_on_shutdown) {
278         AWS_FATAL_ASSERT(proxy_ud->final_connection);
279         proxy_ud->original_http_on_shutdown(proxy_ud->final_connection, error_code, proxy_ud->original_user_data);
280         proxy_ud->original_http_on_shutdown = NULL;
281     }
282 
283     if (proxy_ud->original_channel_on_shutdown) {
284         struct aws_channel *channel = aws_http_connection_get_channel(proxy_ud->proxy_connection);
285         proxy_ud->original_channel_on_shutdown(
286             proxy_ud->original_bootstrap, error_code, channel, proxy_ud->original_user_data);
287         proxy_ud->original_channel_on_shutdown = NULL;
288     }
289 }
290 
291 /*
292  * Connection callback used ONLY by forwarding http proxy connections.  After this,
293  * the connection is live and the user is notified
294  */
s_aws_http_on_client_connection_http_forwarding_proxy_setup_fn(struct aws_http_connection * connection,int error_code,void * user_data)295 static void s_aws_http_on_client_connection_http_forwarding_proxy_setup_fn(
296     struct aws_http_connection *connection,
297     int error_code,
298     void *user_data) {
299     struct aws_http_proxy_user_data *proxy_ud = user_data;
300 
301     s_do_on_setup_callback(proxy_ud, connection, error_code);
302 
303     if (error_code != AWS_ERROR_SUCCESS) {
304         aws_http_proxy_user_data_destroy(user_data);
305     } else {
306         /*
307          * The proxy connection and final connection are the same in forwarding proxy connections.  This lets
308          * us unconditionally use fatal asserts on these being non-null regardless of proxy configuration.
309          */
310         proxy_ud->proxy_connection = connection;
311         proxy_ud->final_connection = connection;
312         proxy_ud->state = AWS_PBS_SUCCESS;
313     }
314 }
315 
316 /*
317  * Connection shutdown callback used by both http and https proxy connections.  Only invokes
318  * user shutdown if the connection was successfully established.  Otherwise, it invokes
319  * the user setup function with an error.
320  */
s_aws_http_on_client_connection_http_proxy_shutdown_fn(struct aws_http_connection * connection,int error_code,void * user_data)321 static void s_aws_http_on_client_connection_http_proxy_shutdown_fn(
322     struct aws_http_connection *connection,
323     int error_code,
324     void *user_data) {
325 
326     struct aws_http_proxy_user_data *proxy_ud = user_data;
327 
328     if (proxy_ud->state == AWS_PBS_SUCCESS) {
329         AWS_LOGF_INFO(
330             AWS_LS_HTTP_CONNECTION,
331             "(%p) Proxy connection (channel %p) shutting down.",
332             (void *)connection,
333             (void *)aws_http_connection_get_channel(connection));
334         s_do_on_shutdown_callback(proxy_ud, error_code);
335     } else {
336         int ec = error_code;
337         if (ec == AWS_ERROR_SUCCESS) {
338             ec = proxy_ud->error_code;
339         }
340         if (ec == AWS_ERROR_SUCCESS) {
341             ec = AWS_ERROR_UNKNOWN;
342         }
343 
344         AWS_LOGF_WARN(
345             AWS_LS_HTTP_CONNECTION,
346             "(%p) Error %d while connecting to \"%s\" via proxy.",
347             (void *)connection,
348             ec,
349             (char *)proxy_ud->original_host->bytes);
350 
351         s_do_on_setup_callback(proxy_ud, NULL, ec);
352     }
353 
354     aws_http_proxy_user_data_destroy(user_data);
355 }
356 
357 /*
358  * On-any-error entry point that releases all resources involved in establishing the proxy connection.
359  * This must not be invoked any time after a successful setup callback.
360  */
s_aws_http_proxy_user_data_shutdown(struct aws_http_proxy_user_data * user_data)361 static void s_aws_http_proxy_user_data_shutdown(struct aws_http_proxy_user_data *user_data) {
362 
363     user_data->state = AWS_PBS_FAILURE;
364 
365     if (user_data->proxy_connection == NULL) {
366         s_do_on_setup_callback(user_data, NULL, user_data->error_code);
367         aws_http_proxy_user_data_destroy(user_data);
368         return;
369     }
370 
371     if (user_data->connect_stream) {
372         aws_http_stream_release(user_data->connect_stream);
373         user_data->connect_stream = NULL;
374     }
375 
376     if (user_data->connect_request) {
377         aws_http_message_destroy(user_data->connect_request);
378         user_data->connect_request = NULL;
379     }
380 
381     struct aws_http_connection *http_connection = user_data->proxy_connection;
382     user_data->proxy_connection = NULL;
383 
384     aws_channel_shutdown(http_connection->channel_slot->channel, user_data->error_code);
385     aws_http_connection_release(http_connection);
386 }
387 
388 /*
389  * Builds the CONNECT request issued after proxy connection establishment, during the creation of
390  * tls-enabled proxy connections.
391  */
s_build_proxy_connect_request(struct aws_http_proxy_user_data * user_data)392 static struct aws_http_message *s_build_proxy_connect_request(struct aws_http_proxy_user_data *user_data) {
393     struct aws_http_message *request = aws_http_message_new_request(user_data->allocator);
394     if (request == NULL) {
395         return NULL;
396     }
397 
398     struct aws_byte_buf path_buffer;
399     AWS_ZERO_STRUCT(path_buffer);
400 
401     if (aws_http_message_set_request_method(request, aws_byte_cursor_from_c_str("CONNECT"))) {
402         goto on_error;
403     }
404 
405     if (aws_byte_buf_init(&path_buffer, user_data->allocator, user_data->original_host->len + 10)) {
406         goto on_error;
407     }
408 
409     struct aws_byte_cursor host_cursor = aws_byte_cursor_from_string(user_data->original_host);
410     if (aws_byte_buf_append(&path_buffer, &host_cursor)) {
411         goto on_error;
412     }
413 
414     struct aws_byte_cursor colon_cursor = aws_byte_cursor_from_c_str(":");
415     if (aws_byte_buf_append(&path_buffer, &colon_cursor)) {
416         goto on_error;
417     }
418 
419     char port_str[20] = "\0";
420     snprintf(port_str, sizeof(port_str), "%d", (int)user_data->original_port);
421     struct aws_byte_cursor port_cursor = aws_byte_cursor_from_c_str(port_str);
422     if (aws_byte_buf_append(&path_buffer, &port_cursor)) {
423         goto on_error;
424     }
425 
426     struct aws_byte_cursor path_cursor = aws_byte_cursor_from_array(path_buffer.buffer, path_buffer.len);
427     if (aws_http_message_set_request_path(request, path_cursor)) {
428         goto on_error;
429     }
430 
431     struct aws_http_header host_header = {
432         .name = aws_byte_cursor_from_string(s_host_header_name),
433         .value = aws_byte_cursor_from_array(path_buffer.buffer, path_buffer.len),
434     };
435     if (aws_http_message_add_header(request, host_header)) {
436         goto on_error;
437     }
438 
439     struct aws_http_header keep_alive_header = {
440         .name = aws_byte_cursor_from_string(s_proxy_connection_header_name),
441         .value = aws_byte_cursor_from_string(s_proxy_connection_header_value),
442     };
443     if (aws_http_message_add_header(request, keep_alive_header)) {
444         goto on_error;
445     }
446 
447     aws_byte_buf_clean_up(&path_buffer);
448 
449     return request;
450 
451 on_error:
452 
453     AWS_LOGF_ERROR(
454         AWS_LS_HTTP_CONNECTION,
455         "(%p) TLS proxy connection failed to build CONNECT request with error %d(%s)",
456         (void *)user_data->proxy_connection,
457         aws_last_error(),
458         aws_error_str(aws_last_error()));
459 
460     aws_byte_buf_clean_up(&path_buffer);
461     aws_http_message_destroy(request);
462 
463     return NULL;
464 }
465 
s_aws_http_on_incoming_body_tunnel_proxy(struct aws_http_stream * stream,const struct aws_byte_cursor * data,void * user_data)466 static int s_aws_http_on_incoming_body_tunnel_proxy(
467     struct aws_http_stream *stream,
468     const struct aws_byte_cursor *data,
469     void *user_data) {
470     (void)stream;
471 
472     struct aws_http_proxy_user_data *context = user_data;
473     aws_http_proxy_negotiator_connect_on_incoming_body_fn *on_incoming_body =
474         context->proxy_negotiator->strategy_vtable.tunnelling_vtable->on_incoming_body_callback;
475     if (on_incoming_body != NULL) {
476         (*on_incoming_body)(context->proxy_negotiator, data);
477     }
478 
479     aws_http_stream_update_window(stream, data->len);
480 
481     return AWS_OP_SUCCESS;
482 }
483 
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)484 static int s_aws_http_on_response_headers_tunnel_proxy(
485     struct aws_http_stream *stream,
486     enum aws_http_header_block header_block,
487     const struct aws_http_header *header_array,
488     size_t num_headers,
489     void *user_data) {
490     (void)stream;
491 
492     struct aws_http_proxy_user_data *context = user_data;
493     aws_http_proxy_negotiation_connect_on_incoming_headers_fn *on_incoming_headers =
494         context->proxy_negotiator->strategy_vtable.tunnelling_vtable->on_incoming_headers_callback;
495     if (on_incoming_headers != NULL) {
496         (*on_incoming_headers)(context->proxy_negotiator, header_block, header_array, num_headers);
497     }
498 
499     return AWS_OP_SUCCESS;
500 }
501 
502 /*
503  * Headers done callback for the CONNECT request made during tls proxy connections
504  */
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)505 static int s_aws_http_on_incoming_header_block_done_tunnel_proxy(
506     struct aws_http_stream *stream,
507     enum aws_http_header_block header_block,
508     void *user_data) {
509 
510     struct aws_http_proxy_user_data *context = user_data;
511 
512     if (header_block == AWS_HTTP_HEADER_BLOCK_MAIN) {
513         int status_code = AWS_HTTP_STATUS_CODE_UNKNOWN;
514         aws_http_stream_get_incoming_response_status(stream, &status_code);
515         context->connect_status_code = (enum aws_http_status_code)status_code;
516         if (context->connect_status_code != AWS_HTTP_STATUS_CODE_200_OK) {
517             AWS_LOGF_ERROR(
518                 AWS_LS_HTTP_CONNECTION,
519                 "(%p) Proxy CONNECT request failed with status code %d",
520                 (void *)context->proxy_connection,
521                 context->connect_status_code);
522             context->error_code = AWS_ERROR_HTTP_PROXY_CONNECT_FAILED;
523         }
524 
525         aws_http_proxy_negotiator_connect_status_fn *on_status =
526             context->proxy_negotiator->strategy_vtable.tunnelling_vtable->on_status_callback;
527         if (on_status != NULL) {
528             (*on_status)(context->proxy_negotiator, context->connect_status_code);
529         }
530     }
531 
532     return AWS_OP_SUCCESS;
533 }
534 
s_aws_http_apply_http_connection_to_proxied_channel(struct aws_http_proxy_user_data * context)535 static int s_aws_http_apply_http_connection_to_proxied_channel(struct aws_http_proxy_user_data *context) {
536     AWS_FATAL_ASSERT(context->proxy_connection != NULL);
537     AWS_FATAL_ASSERT(context->original_http_on_setup != NULL);
538 
539     struct aws_channel *channel = aws_http_connection_get_channel(context->proxy_connection);
540 
541     struct aws_http_connection *connection = aws_http_connection_new_channel_handler(
542         context->allocator,
543         channel,
544         false,
545         context->original_tls_options != NULL,
546         context->original_manual_window_management,
547         false, /* prior_knowledge_http2 */
548         context->original_initial_window_size,
549         NULL, /* alpn_string_map */
550         &context->original_http1_options,
551         NULL); /* TODO: support http2 options */
552     if (connection == NULL) {
553         AWS_LOGF_ERROR(
554             AWS_LS_HTTP_CONNECTION,
555             "static: Failed to create the client connection object, error %d (%s).",
556             aws_last_error(),
557             aws_error_name(aws_last_error()));
558 
559         return AWS_OP_ERR;
560     }
561 
562     connection->user_data = context->original_user_data;
563 
564     AWS_LOGF_INFO(
565         AWS_LS_HTTP_CONNECTION,
566         "id=%p: " PRInSTR " client connection established.",
567         (void *)connection,
568         AWS_BYTE_CURSOR_PRI(aws_http_version_to_str(connection->http_version)));
569 
570     context->final_connection = connection;
571 
572     return AWS_OP_SUCCESS;
573 }
574 
s_do_final_proxied_channel_setup(struct aws_http_proxy_user_data * proxy_ud)575 static void s_do_final_proxied_channel_setup(struct aws_http_proxy_user_data *proxy_ud) {
576     if (proxy_ud->original_http_on_setup != NULL) {
577         /*
578          * If we're transitioning to http with http setup/shutdown callbacks, try to apply a new http connection to
579          * the channel
580          */
581         if (s_aws_http_apply_http_connection_to_proxied_channel(proxy_ud)) {
582             proxy_ud->error_code = aws_last_error();
583             s_aws_http_proxy_user_data_shutdown(proxy_ud);
584             return;
585         }
586 
587         s_do_on_setup_callback(proxy_ud, proxy_ud->final_connection, AWS_ERROR_SUCCESS);
588     } else {
589         /*
590          * Otherwise invoke setup directly (which will end up being channel setup)
591          */
592         s_do_on_setup_callback(proxy_ud, proxy_ud->proxy_connection, AWS_ERROR_SUCCESS);
593     }
594 
595     /* Tell user of successful connection. */
596     proxy_ud->state = AWS_PBS_SUCCESS;
597 }
598 
599 /*
600  * Tls negotiation callback for tls proxy connections
601  */
s_on_origin_server_tls_negotation_result(struct aws_channel_handler * handler,struct aws_channel_slot * slot,int error_code,void * user_data)602 static void s_on_origin_server_tls_negotation_result(
603     struct aws_channel_handler *handler,
604     struct aws_channel_slot *slot,
605     int error_code,
606     void *user_data) {
607 
608     (void)handler;
609     (void)slot;
610 
611     struct aws_http_proxy_user_data *context = user_data;
612     if (error_code != AWS_ERROR_SUCCESS) {
613         AWS_LOGF_ERROR(
614             AWS_LS_HTTP_CONNECTION,
615             "(%p) Proxy connection failed origin server TLS negotiation with error %d(%s)",
616             (void *)context->proxy_connection,
617             error_code,
618             aws_error_str(error_code));
619         context->error_code = error_code;
620         s_aws_http_proxy_user_data_shutdown(context);
621         return;
622     }
623 
624     s_do_final_proxied_channel_setup(context);
625 }
626 
627 static int s_create_tunneling_connection(struct aws_http_proxy_user_data *user_data);
628 static int s_make_proxy_connect_request(struct aws_http_proxy_user_data *user_data);
629 
s_zero_callbacks(struct aws_http_proxy_user_data * proxy_ud)630 static void s_zero_callbacks(struct aws_http_proxy_user_data *proxy_ud) {
631     proxy_ud->original_http_on_shutdown = NULL;
632     proxy_ud->original_http_on_setup = NULL;
633     proxy_ud->original_channel_on_shutdown = NULL;
634     proxy_ud->original_channel_on_setup = NULL;
635 }
636 
637 /*
638  * Stream done callback for the CONNECT request made during tls proxy connections
639  */
s_aws_http_on_stream_complete_tunnel_proxy(struct aws_http_stream * stream,int error_code,void * user_data)640 static void s_aws_http_on_stream_complete_tunnel_proxy(
641     struct aws_http_stream *stream,
642     int error_code,
643     void *user_data) {
644     struct aws_http_proxy_user_data *context = user_data;
645     AWS_FATAL_ASSERT(stream == context->connect_stream);
646 
647     if (context->error_code == AWS_ERROR_SUCCESS && error_code != AWS_ERROR_SUCCESS) {
648         context->error_code = error_code;
649     }
650 
651     if (context->error_code != AWS_ERROR_SUCCESS) {
652         context->error_code = AWS_ERROR_HTTP_PROXY_CONNECT_FAILED;
653         if (context->connect_status_code == AWS_HTTP_STATUS_CODE_407_PROXY_AUTHENTICATION_REQUIRED) {
654             enum aws_http_proxy_negotiation_retry_directive retry_directive =
655                 aws_http_proxy_negotiator_get_retry_directive(context->proxy_negotiator);
656 
657             if (retry_directive == AWS_HPNRD_NEW_CONNECTION) {
658                 struct aws_http_proxy_user_data *new_context =
659                     aws_http_proxy_user_data_new_reset_clone(context->allocator, context);
660                 if (new_context != NULL && s_create_tunneling_connection(new_context) == AWS_OP_SUCCESS) {
661                     /*
662                      * We successfully kicked off a new connection.  By NULLing the callbacks on the old one, we can
663                      * shut it down quietly without the user being notified.  The new connection will notify the user
664                      * based on its success or failure.
665                      */
666                     s_zero_callbacks(context);
667                     context->error_code = AWS_ERROR_HTTP_PROXY_CONNECT_FAILED_RETRYABLE;
668                 }
669             } else if (retry_directive == AWS_HPNRD_CURRENT_CONNECTION) {
670                 context->error_code = AWS_ERROR_SUCCESS;
671                 if (s_make_proxy_connect_request(context) == AWS_OP_SUCCESS) {
672                     return;
673                 }
674             }
675         }
676 
677         s_aws_http_proxy_user_data_shutdown(context);
678         return;
679     }
680 
681     AWS_LOGF_INFO(
682         AWS_LS_HTTP_CONNECTION,
683         "(%p) Proxy connection made successful CONNECT request to \"%s\" via proxy",
684         (void *)context->proxy_connection,
685         context->original_host->bytes);
686 
687     /*
688      * We're finished with these, let's release
689      */
690     aws_http_stream_release(stream);
691     context->connect_stream = NULL;
692     aws_http_message_destroy(context->connect_request);
693     context->connect_request = NULL;
694 
695     AWS_LOGF_INFO(
696         AWS_LS_HTTP_CONNECTION, "(%p) Beginning TLS negotiation through proxy", (void *)context->proxy_connection);
697 
698     if (context->original_tls_options != NULL) {
699         /*
700          * Perform TLS negotiation to the origin server through proxy
701          */
702         context->original_tls_options->on_negotiation_result = s_on_origin_server_tls_negotation_result;
703 
704         context->state = AWS_PBS_TLS_NEGOTIATION;
705         struct aws_channel *channel = aws_http_connection_get_channel(context->proxy_connection);
706 
707         struct aws_channel_slot *last_slot = aws_channel_get_first_slot(channel);
708         while (last_slot->adj_right != NULL) {
709             last_slot = last_slot->adj_right;
710         }
711 
712         if (s_vtable->setup_client_tls(last_slot, context->original_tls_options)) {
713             AWS_LOGF_ERROR(
714                 AWS_LS_HTTP_CONNECTION,
715                 "(%p) Proxy connection failed to start TLS negotiation with error %d(%s)",
716                 (void *)context->proxy_connection,
717                 aws_last_error(),
718                 aws_error_str(aws_last_error()));
719             s_aws_http_proxy_user_data_shutdown(context);
720             return;
721         }
722     } else {
723         s_do_final_proxied_channel_setup(context);
724     }
725 }
726 
s_terminate_tunneling_connect(struct aws_http_message * message,int error_code,void * internal_proxy_user_data)727 static void s_terminate_tunneling_connect(
728     struct aws_http_message *message,
729     int error_code,
730     void *internal_proxy_user_data) {
731     (void)message;
732 
733     struct aws_http_proxy_user_data *proxy_ud = internal_proxy_user_data;
734 
735     AWS_LOGF_ERROR(
736         AWS_LS_HTTP_CONNECTION,
737         "(%p) Tunneling proxy connection failed to create request stream for CONNECT request with error %d(%s)",
738         (void *)proxy_ud->proxy_connection,
739         error_code,
740         aws_error_str(error_code));
741 
742     proxy_ud->error_code = error_code;
743     s_aws_http_proxy_user_data_shutdown(proxy_ud);
744 }
745 
s_continue_tunneling_connect(struct aws_http_message * message,void * internal_proxy_user_data)746 static void s_continue_tunneling_connect(struct aws_http_message *message, void *internal_proxy_user_data) {
747     struct aws_http_proxy_user_data *proxy_ud = internal_proxy_user_data;
748 
749     struct aws_http_make_request_options request_options = {
750         .self_size = sizeof(request_options),
751         .request = message,
752         .user_data = proxy_ud,
753         .on_response_headers = s_aws_http_on_response_headers_tunnel_proxy,
754         .on_response_header_block_done = s_aws_http_on_incoming_header_block_done_tunnel_proxy,
755         .on_response_body = s_aws_http_on_incoming_body_tunnel_proxy,
756         .on_complete = s_aws_http_on_stream_complete_tunnel_proxy,
757     };
758 
759     if (proxy_ud->connect_stream != NULL) {
760         aws_http_stream_release(proxy_ud->connect_stream);
761     }
762 
763     proxy_ud->connect_stream = aws_http_connection_make_request(proxy_ud->proxy_connection, &request_options);
764     if (proxy_ud->connect_stream == NULL) {
765         goto on_error;
766     }
767 
768     aws_http_stream_activate(proxy_ud->connect_stream);
769 
770     return;
771 
772 on_error:
773 
774     s_aws_http_proxy_user_data_shutdown(proxy_ud);
775 }
776 
777 /*
778  * Issues a CONNECT request on an http connection
779  */
s_make_proxy_connect_request(struct aws_http_proxy_user_data * user_data)780 static int s_make_proxy_connect_request(struct aws_http_proxy_user_data *user_data) {
781     if (user_data->connect_request != NULL) {
782         aws_http_message_destroy(user_data->connect_request);
783         user_data->connect_request = NULL;
784     }
785 
786     user_data->connect_request = s_build_proxy_connect_request(user_data);
787     if (user_data->connect_request == NULL) {
788         return AWS_OP_ERR;
789     }
790 
791     (*user_data->proxy_negotiator->strategy_vtable.tunnelling_vtable->connect_request_transform)(
792         user_data->proxy_negotiator,
793         user_data->connect_request,
794         s_terminate_tunneling_connect,
795         s_continue_tunneling_connect,
796         user_data);
797 
798     return AWS_OP_SUCCESS;
799 }
800 
801 /*
802  * Connection setup callback for tunneling proxy connections.
803  */
s_aws_http_on_client_connection_http_tunneling_proxy_setup_fn(struct aws_http_connection * connection,int error_code,void * user_data)804 static void s_aws_http_on_client_connection_http_tunneling_proxy_setup_fn(
805     struct aws_http_connection *connection,
806     int error_code,
807     void *user_data) {
808 
809     struct aws_http_proxy_user_data *proxy_ud = user_data;
810 
811     proxy_ud->error_code = error_code;
812     if (error_code != AWS_ERROR_SUCCESS) {
813         goto on_error;
814     }
815 
816     AWS_LOGF_INFO(AWS_LS_HTTP_CONNECTION, "(%p) Making CONNECT request to proxy", (void *)proxy_ud->proxy_connection);
817 
818     proxy_ud->proxy_connection = connection;
819     proxy_ud->state = AWS_PBS_HTTP_CONNECT;
820     if (s_make_proxy_connect_request(proxy_ud)) {
821         goto on_error;
822     }
823 
824     return;
825 
826 on_error:
827 
828     s_aws_http_proxy_user_data_shutdown(proxy_ud);
829 }
830 
831 /*
832  * Checks for the special case when a request is an OPTIONS request with *
833  * path and no query params
834  */
s_is_star_path_options_method(const struct aws_http_message * request)835 static bool s_is_star_path_options_method(const struct aws_http_message *request) {
836     struct aws_byte_cursor method_cursor;
837     if (aws_http_message_get_request_method(request, &method_cursor)) {
838         return false;
839     }
840 
841     struct aws_byte_cursor options_cursor = aws_byte_cursor_from_string(s_options_method);
842     if (!aws_byte_cursor_eq_ignore_case(&method_cursor, &options_cursor)) {
843         return false;
844     }
845 
846     struct aws_byte_cursor path_cursor;
847     if (aws_http_message_get_request_path(request, &path_cursor)) {
848         return false;
849     }
850 
851     struct aws_byte_cursor star_cursor = aws_byte_cursor_from_string(s_star_path);
852     if (!aws_byte_cursor_eq_ignore_case(&path_cursor, &star_cursor)) {
853         return false;
854     }
855 
856     return true;
857 }
858 
859 /*
860  * Modifies a requests uri by transforming it to absolute form according to
861  * section 5.3.2 of rfc 7230
862  *
863  * We do this by parsing the existing uri and then rebuilding it as an
864  * absolute resource path (using the original connection options)
865  */
aws_http_rewrite_uri_for_proxy_request(struct aws_http_message * request,struct aws_http_proxy_user_data * proxy_user_data)866 int aws_http_rewrite_uri_for_proxy_request(
867     struct aws_http_message *request,
868     struct aws_http_proxy_user_data *proxy_user_data) {
869     int result = AWS_OP_ERR;
870 
871     struct aws_uri target_uri;
872     AWS_ZERO_STRUCT(target_uri);
873 
874     struct aws_byte_cursor path_cursor;
875     AWS_ZERO_STRUCT(path_cursor);
876 
877     if (aws_http_message_get_request_path(request, &path_cursor)) {
878         goto done;
879     }
880 
881     /* Pull out the original path/query */
882     struct aws_uri uri;
883     if (aws_uri_init_parse(&uri, proxy_user_data->allocator, &path_cursor)) {
884         goto done;
885     }
886 
887     const struct aws_byte_cursor *actual_path_cursor = aws_uri_path(&uri);
888     const struct aws_byte_cursor *actual_query_cursor = aws_uri_query_string(&uri);
889 
890     /* now rebuild the uri with scheme, host and port subbed in from the original connection options */
891     struct aws_uri_builder_options target_uri_builder;
892     AWS_ZERO_STRUCT(target_uri_builder);
893     target_uri_builder.scheme = aws_byte_cursor_from_string(s_http_scheme);
894     target_uri_builder.path = *actual_path_cursor;
895     target_uri_builder.host_name = aws_byte_cursor_from_string(proxy_user_data->original_host);
896     target_uri_builder.port = proxy_user_data->original_port;
897     target_uri_builder.query_string = *actual_query_cursor;
898 
899     if (aws_uri_init_from_builder_options(&target_uri, proxy_user_data->allocator, &target_uri_builder)) {
900         goto done;
901     }
902 
903     struct aws_byte_cursor full_target_uri =
904         aws_byte_cursor_from_array(target_uri.uri_str.buffer, target_uri.uri_str.len);
905 
906     /*
907      * By rfc 7230, Section 5.3.4, a star-pathed options request made through a proxy MUST be transformed (at the last
908      * proxy) back into a star-pathed request if the proxy request has an empty path and no query string.  This
909      * is behavior we want to support.  So from our side, we need to make sure that star-pathed options requests
910      * get translated into options requests with the authority as the uri and an empty path-query.
911      *
912      * Our URI transform always ends with a '/' which is technically not an empty path. To address this,
913      * the easiest thing to do is just detect if this was originally a star-pathed options request
914      * and drop the final '/' from the path.
915      */
916     if (s_is_star_path_options_method(request)) {
917         if (full_target_uri.len > 0 && *(full_target_uri.ptr + full_target_uri.len - 1) == '/') {
918             full_target_uri.len -= 1;
919         }
920     }
921 
922     /* mutate the request with the new path value */
923     if (aws_http_message_set_request_path(request, full_target_uri)) {
924         goto done;
925     }
926 
927     result = AWS_OP_SUCCESS;
928 
929 done:
930 
931     aws_uri_clean_up(&target_uri);
932     aws_uri_clean_up(&uri);
933 
934     return result;
935 }
936 
937 /*
938  * Plaintext proxy request transformation function
939  *
940  * Rewrites the target uri to absolute form and injects any desired headers
941  */
s_proxy_http_request_transform(struct aws_http_message * request,void * user_data)942 static int s_proxy_http_request_transform(struct aws_http_message *request, void *user_data) {
943     struct aws_http_proxy_user_data *proxy_ud = user_data;
944 
945     if (aws_http_rewrite_uri_for_proxy_request(request, proxy_ud)) {
946         return AWS_OP_ERR;
947     }
948 
949     if ((*proxy_ud->proxy_negotiator->strategy_vtable.forwarding_vtable->forward_request_transform)(
950             proxy_ud->proxy_negotiator, request)) {
951         return AWS_OP_ERR;
952     }
953 
954     return AWS_OP_SUCCESS;
955 }
956 
957 /*
958  * Top-level function to route a connection request through a proxy server, with no channel security
959  */
s_aws_http_client_connect_via_forwarding_proxy(const struct aws_http_client_connection_options * options)960 static int s_aws_http_client_connect_via_forwarding_proxy(const struct aws_http_client_connection_options *options) {
961     AWS_FATAL_ASSERT(options->tls_options == NULL);
962 
963     AWS_LOGF_INFO(
964         AWS_LS_HTTP_CONNECTION,
965         "(STATIC) Connecting to \"" PRInSTR "\" via proxy \"" PRInSTR "\"",
966         AWS_BYTE_CURSOR_PRI(options->host_name),
967         AWS_BYTE_CURSOR_PRI(options->proxy_options->host));
968 
969     /* Create a wrapper user data that contains all proxy-related information, state, and user-facing callbacks */
970     struct aws_http_proxy_user_data *proxy_user_data =
971         aws_http_proxy_user_data_new(options->allocator, options, NULL, NULL);
972     if (proxy_user_data == NULL) {
973         return AWS_OP_ERR;
974     }
975 
976     AWS_FATAL_ASSERT(options->proxy_options != NULL);
977 
978     /* Fill in a new connection options pointing at the proxy */
979     struct aws_http_client_connection_options options_copy = *options;
980 
981     options_copy.proxy_options = NULL;
982     options_copy.host_name = options->proxy_options->host;
983     options_copy.port = options->proxy_options->port;
984     options_copy.user_data = proxy_user_data;
985     options_copy.on_setup = s_aws_http_on_client_connection_http_forwarding_proxy_setup_fn;
986     options_copy.on_shutdown = s_aws_http_on_client_connection_http_proxy_shutdown_fn;
987     options_copy.tls_options = options->proxy_options->tls_options;
988 
989     int result = aws_http_client_connect_internal(&options_copy, s_proxy_http_request_transform);
990     if (result == AWS_OP_ERR) {
991         AWS_LOGF_ERROR(
992             AWS_LS_HTTP_CONNECTION,
993             "(STATIC) Proxy http connection failed client connect with error %d(%s)",
994             aws_last_error(),
995             aws_error_str(aws_last_error()));
996 
997         aws_http_proxy_user_data_destroy(proxy_user_data);
998     }
999 
1000     return result;
1001 }
1002 
s_create_tunneling_connection(struct aws_http_proxy_user_data * user_data)1003 static int s_create_tunneling_connection(struct aws_http_proxy_user_data *user_data) {
1004     struct aws_http_client_connection_options connect_options;
1005     AWS_ZERO_STRUCT(connect_options);
1006 
1007     connect_options.self_size = sizeof(struct aws_http_client_connection_options);
1008     connect_options.allocator = user_data->allocator;
1009     connect_options.bootstrap = user_data->original_bootstrap;
1010     connect_options.host_name = aws_byte_cursor_from_buf(&user_data->proxy_config->host);
1011     connect_options.port = user_data->proxy_config->port;
1012     connect_options.socket_options = &user_data->original_socket_options;
1013     connect_options.tls_options = user_data->proxy_config->tls_options;
1014     connect_options.monitoring_options = NULL; /* ToDo */
1015     connect_options.manual_window_management = user_data->original_manual_window_management;
1016     connect_options.initial_window_size = user_data->original_initial_window_size;
1017     connect_options.user_data = user_data;
1018     connect_options.on_setup = s_aws_http_on_client_connection_http_tunneling_proxy_setup_fn;
1019     connect_options.on_shutdown = s_aws_http_on_client_connection_http_proxy_shutdown_fn;
1020     connect_options.http1_options = NULL; /* ToDo */
1021     connect_options.http2_options = NULL; /* ToDo */
1022 
1023     int result = aws_http_client_connect(&connect_options);
1024     if (result == AWS_OP_ERR) {
1025         AWS_LOGF_ERROR(
1026             AWS_LS_HTTP_CONNECTION,
1027             "(STATIC) Proxy tunnel connection failed client connect with error %d(%s)",
1028             aws_last_error(),
1029             aws_error_str(aws_last_error()));
1030         aws_http_proxy_user_data_destroy(user_data);
1031     }
1032 
1033     return result;
1034 }
1035 
1036 /*
1037  * Top-level function to route a connection through a proxy server via a CONNECT request
1038  */
s_aws_http_client_connect_via_tunneling_proxy(const struct aws_http_client_connection_options * options,aws_client_bootstrap_on_channel_event_fn * on_channel_setup,aws_client_bootstrap_on_channel_event_fn * on_channel_shutdown)1039 static int s_aws_http_client_connect_via_tunneling_proxy(
1040     const struct aws_http_client_connection_options *options,
1041     aws_client_bootstrap_on_channel_event_fn *on_channel_setup,
1042     aws_client_bootstrap_on_channel_event_fn *on_channel_shutdown) {
1043     AWS_FATAL_ASSERT(options->proxy_options != NULL);
1044 
1045     AWS_LOGF_INFO(
1046         AWS_LS_HTTP_CONNECTION,
1047         "(STATIC) Connecting to \"" PRInSTR "\" through a tunnel via proxy \"" PRInSTR "\"",
1048         AWS_BYTE_CURSOR_PRI(options->host_name),
1049         AWS_BYTE_CURSOR_PRI(options->proxy_options->host));
1050 
1051     /* Create a wrapper user data that contains all proxy-related information, state, and user-facing callbacks */
1052     struct aws_http_proxy_user_data *user_data =
1053         aws_http_proxy_user_data_new(options->allocator, options, on_channel_setup, on_channel_shutdown);
1054     if (user_data == NULL) {
1055         return AWS_OP_ERR;
1056     }
1057 
1058     return s_create_tunneling_connection(user_data);
1059 }
1060 
s_determine_proxy_connection_type(enum aws_http_proxy_connection_type proxy_connection_type,const struct aws_tls_connection_options * tls_options)1061 static enum aws_http_proxy_connection_type s_determine_proxy_connection_type(
1062     enum aws_http_proxy_connection_type proxy_connection_type,
1063     const struct aws_tls_connection_options *tls_options) {
1064     if (proxy_connection_type != AWS_HPCT_HTTP_LEGACY) {
1065         return proxy_connection_type;
1066     }
1067 
1068     if (tls_options != NULL) {
1069         return AWS_HPCT_HTTP_TUNNEL;
1070     } else {
1071         return AWS_HPCT_HTTP_FORWARD;
1072     }
1073 }
1074 
s_proxy_uri_init_from_env_variable(struct aws_allocator * allocator,const struct aws_http_client_connection_options * options,struct aws_uri * proxy_uri,bool * found)1075 static int s_proxy_uri_init_from_env_variable(
1076     struct aws_allocator *allocator,
1077     const struct aws_http_client_connection_options *options,
1078     struct aws_uri *proxy_uri,
1079     bool *found) {
1080     struct aws_string *proxy_uri_string = NULL;
1081     *found = false;
1082     if (options->tls_options) {
1083         if (aws_get_environment_value(allocator, s_https_proxy_env_var_low, &proxy_uri_string) == AWS_OP_SUCCESS &&
1084             proxy_uri_string != NULL) {
1085             AWS_LOGF_DEBUG(AWS_LS_HTTP_CONNECTION, "https_proxy environment found");
1086         } else if (
1087             aws_get_environment_value(allocator, s_https_proxy_env_var, &proxy_uri_string) == AWS_OP_SUCCESS &&
1088             proxy_uri_string != NULL) {
1089             AWS_LOGF_DEBUG(AWS_LS_HTTP_CONNECTION, "HTTPS_PROXY environment found");
1090         } else {
1091             return AWS_OP_SUCCESS;
1092         }
1093     } else {
1094         if (aws_get_environment_value(allocator, s_http_proxy_env_var_low, &proxy_uri_string) == AWS_OP_SUCCESS &&
1095             proxy_uri_string != NULL) {
1096             AWS_LOGF_DEBUG(AWS_LS_HTTP_CONNECTION, "http_proxy environment found");
1097         } else if (
1098             aws_get_environment_value(allocator, s_http_proxy_env_var, &proxy_uri_string) == AWS_OP_SUCCESS &&
1099             proxy_uri_string != NULL) {
1100             AWS_LOGF_DEBUG(AWS_LS_HTTP_CONNECTION, "HTTP_PROXY environment found");
1101         } else {
1102             return AWS_OP_SUCCESS;
1103         }
1104     }
1105     struct aws_byte_cursor proxy_uri_cursor = aws_byte_cursor_from_string(proxy_uri_string);
1106     if (aws_uri_init_parse(proxy_uri, allocator, &proxy_uri_cursor)) {
1107         AWS_LOGF_ERROR(AWS_LS_HTTP_CONNECTION, "Could not parse found proxy URI.");
1108         aws_string_destroy(proxy_uri_string);
1109         return AWS_OP_ERR;
1110     }
1111     *found = true;
1112     aws_string_destroy(proxy_uri_string);
1113     return AWS_OP_SUCCESS;
1114 }
1115 
s_connect_proxy(const struct aws_http_client_connection_options * options)1116 static int s_connect_proxy(const struct aws_http_client_connection_options *options) {
1117     if (aws_http_options_validate_proxy_configuration(options)) {
1118         return AWS_OP_ERR;
1119     }
1120 
1121     enum aws_http_proxy_connection_type proxy_connection_type =
1122         s_determine_proxy_connection_type(options->proxy_options->connection_type, options->tls_options);
1123 
1124     switch (proxy_connection_type) {
1125         case AWS_HPCT_HTTP_FORWARD:
1126             return s_aws_http_client_connect_via_forwarding_proxy(options);
1127 
1128         case AWS_HPCT_HTTP_TUNNEL:
1129             return s_aws_http_client_connect_via_tunneling_proxy(options, NULL, NULL);
1130 
1131         default:
1132             return aws_raise_error(AWS_ERROR_UNIMPLEMENTED);
1133     }
1134 }
1135 
s_setup_proxy_tls_env_variable(const struct aws_http_client_connection_options * options,struct aws_tls_connection_options * default_tls_connection_options,struct aws_http_proxy_options * proxy_options,struct aws_uri * proxy_uri)1136 static int s_setup_proxy_tls_env_variable(
1137     const struct aws_http_client_connection_options *options,
1138     struct aws_tls_connection_options *default_tls_connection_options,
1139     struct aws_http_proxy_options *proxy_options,
1140     struct aws_uri *proxy_uri) {
1141     (void)default_tls_connection_options;
1142     (void)proxy_uri;
1143     if (options->proxy_ev_settings->tls_options) {
1144         proxy_options->tls_options = options->proxy_ev_settings->tls_options;
1145     } else {
1146 #ifdef BYO_CRYPTO
1147         AWS_LOGF_ERROR(
1148             AWS_LS_HTTP_CONNECTION,
1149             "Failed making default TLS context because of BYO_CRYPTO, set up the tls_options for proxy_env_settings to "
1150             "make it work.");
1151         return AWS_OP_ERR;
1152 #else
1153         struct aws_tls_ctx *tls_ctx = NULL;
1154         struct aws_tls_ctx_options tls_ctx_options;
1155         AWS_ZERO_STRUCT(tls_ctx_options);
1156         /* create a default tls options */
1157         aws_tls_ctx_options_init_default_client(&tls_ctx_options, options->allocator);
1158         struct aws_string *proxy_no_verify_peer_string = NULL;
1159         if (aws_get_environment_value(
1160                 options->allocator, s_proxy_no_verify_peer_env_var, &proxy_no_verify_peer_string) == AWS_OP_SUCCESS &&
1161             proxy_no_verify_peer_string != NULL) {
1162             /* turn off the peer verification, if setup from envrionment variable. Mostly for testing. */
1163             aws_tls_ctx_options_set_verify_peer(&tls_ctx_options, false);
1164             aws_string_destroy(proxy_no_verify_peer_string);
1165         }
1166         tls_ctx = aws_tls_client_ctx_new(options->allocator, &tls_ctx_options);
1167         aws_tls_ctx_options_clean_up(&tls_ctx_options);
1168         if (!tls_ctx) {
1169             AWS_LOGF_ERROR(AWS_LS_HTTP_CONNECTION, "Failed to create default TLS context.");
1170             return AWS_OP_ERR;
1171         }
1172         aws_tls_connection_options_init_from_ctx(default_tls_connection_options, tls_ctx);
1173         /* tls options hold a ref to the ctx */
1174         aws_tls_ctx_release(tls_ctx);
1175         if (aws_tls_connection_options_set_server_name(
1176                 default_tls_connection_options, options->allocator, &proxy_uri->host_name)) {
1177             AWS_LOGF_ERROR(AWS_LS_HTTP_CONNECTION, "Failed set server name for TLS connection options.");
1178             return AWS_OP_ERR;
1179         }
1180         proxy_options->tls_options = default_tls_connection_options;
1181 #endif
1182     }
1183     return AWS_OP_SUCCESS;
1184 }
1185 
s_connect_proxy_via_env_variable(const struct aws_http_client_connection_options * options)1186 static int s_connect_proxy_via_env_variable(const struct aws_http_client_connection_options *options) {
1187     struct aws_http_proxy_options proxy_options;
1188     AWS_ZERO_STRUCT(proxy_options);
1189     struct aws_uri proxy_uri;
1190     AWS_ZERO_STRUCT(proxy_uri);
1191     struct aws_tls_connection_options default_tls_connection_options;
1192     AWS_ZERO_STRUCT(default_tls_connection_options);
1193     bool found = false;
1194     bool success = false;
1195     if (s_proxy_uri_init_from_env_variable(options->allocator, options, &proxy_uri, &found)) {
1196         /* Envrionment is set but failed to parse it */
1197         goto done;
1198     }
1199     if (found) {
1200         proxy_options.host = proxy_uri.host_name;
1201         proxy_options.port = proxy_uri.port;
1202         proxy_options.connection_type = options->proxy_ev_settings->connection_type;
1203         if (proxy_options.connection_type == AWS_HPCT_HTTP_LEGACY) {
1204             if (options->tls_options) {
1205                 /* Use tunneling when main connection use TLS. */
1206                 proxy_options.connection_type = AWS_HPCT_HTTP_TUNNEL;
1207             } else {
1208                 /* Use forwarding proxy when main connection use clear text. */
1209                 proxy_options.connection_type = AWS_HPCT_HTTP_FORWARD;
1210             }
1211         }
1212         if (aws_byte_cursor_eq_ignore_case(&proxy_uri.scheme, &aws_http_scheme_https)) {
1213             if (s_setup_proxy_tls_env_variable(options, &default_tls_connection_options, &proxy_options, &proxy_uri)) {
1214                 goto done;
1215             }
1216         }
1217         /* Support basic authentication. */
1218         if (proxy_uri.password.len) {
1219             /* Has no empty password set */
1220             struct aws_http_proxy_strategy_basic_auth_options config = {
1221                 .proxy_connection_type = proxy_options.connection_type,
1222                 .user_name = proxy_uri.user,
1223                 .password = proxy_uri.password,
1224             };
1225             proxy_options.proxy_strategy = aws_http_proxy_strategy_new_basic_auth(options->allocator, &config);
1226         }
1227     } else {
1228         success = true;
1229         goto done;
1230     }
1231     struct aws_http_client_connection_options copied_options = *options;
1232     copied_options.proxy_options = &proxy_options;
1233     if (s_connect_proxy(&copied_options)) {
1234         goto done;
1235     }
1236     success = true;
1237 done:
1238     aws_tls_connection_options_clean_up(&default_tls_connection_options);
1239     aws_http_proxy_strategy_release(proxy_options.proxy_strategy);
1240     aws_uri_clean_up(&proxy_uri);
1241     if (success && !found) {
1242         /* Successfully, but no envrionment variable found. Connect without proxy */
1243         return aws_http_client_connect_internal(options, NULL);
1244     }
1245     return success ? AWS_OP_SUCCESS : AWS_OP_ERR;
1246 }
1247 
1248 /*
1249  * Dispatches a proxy-enabled connection request to the appropriate top-level connection function
1250  */
aws_http_client_connect_via_proxy(const struct aws_http_client_connection_options * options)1251 int aws_http_client_connect_via_proxy(const struct aws_http_client_connection_options *options) {
1252     if (options->proxy_options == NULL && options->proxy_ev_settings &&
1253         options->proxy_ev_settings->env_var_type == AWS_HPEV_ENABLE) {
1254         return s_connect_proxy_via_env_variable(options);
1255     }
1256     return s_connect_proxy(options);
1257 }
1258 
s_aws_http_proxy_config_new(struct aws_allocator * allocator,const struct aws_http_proxy_options * proxy_options,enum aws_http_proxy_connection_type override_proxy_connection_type)1259 static struct aws_http_proxy_config *s_aws_http_proxy_config_new(
1260     struct aws_allocator *allocator,
1261     const struct aws_http_proxy_options *proxy_options,
1262     enum aws_http_proxy_connection_type override_proxy_connection_type) {
1263     AWS_FATAL_ASSERT(proxy_options != NULL);
1264 
1265     struct aws_http_proxy_config *config = aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_config));
1266     if (config == NULL) {
1267         return NULL;
1268     }
1269 
1270     config->connection_type = override_proxy_connection_type;
1271 
1272     if (aws_byte_buf_init_copy_from_cursor(&config->host, allocator, proxy_options->host)) {
1273 
1274         goto on_error;
1275     }
1276 
1277     if (proxy_options->tls_options) {
1278         config->tls_options = aws_mem_calloc(allocator, 1, sizeof(struct aws_tls_connection_options));
1279         if (aws_tls_connection_options_copy(config->tls_options, proxy_options->tls_options)) {
1280             goto on_error;
1281         }
1282     }
1283 
1284     config->allocator = allocator;
1285     config->port = proxy_options->port;
1286 
1287     if (proxy_options->proxy_strategy != NULL) {
1288         config->proxy_strategy = aws_http_proxy_strategy_acquire(proxy_options->proxy_strategy);
1289     } else if (proxy_options->auth_type == AWS_HPAT_BASIC) {
1290         struct aws_http_proxy_strategy_basic_auth_options basic_config;
1291         AWS_ZERO_STRUCT(basic_config);
1292 
1293         basic_config.proxy_connection_type = override_proxy_connection_type;
1294         basic_config.user_name = proxy_options->auth_username;
1295         basic_config.password = proxy_options->auth_password;
1296 
1297         config->proxy_strategy = aws_http_proxy_strategy_new_basic_auth(allocator, &basic_config);
1298     }
1299 
1300     if (config->proxy_strategy == NULL) {
1301         switch (override_proxy_connection_type) {
1302             case AWS_HPCT_HTTP_FORWARD:
1303                 config->proxy_strategy = aws_http_proxy_strategy_new_forwarding_identity(allocator);
1304                 break;
1305 
1306             case AWS_HPCT_HTTP_TUNNEL:
1307                 config->proxy_strategy = aws_http_proxy_strategy_new_tunneling_one_time_identity(allocator);
1308                 break;
1309 
1310             default:
1311                 break;
1312         }
1313 
1314         if (config->proxy_strategy == NULL) {
1315             goto on_error;
1316         }
1317     }
1318 
1319     return config;
1320 
1321 on_error:
1322 
1323     aws_http_proxy_config_destroy(config);
1324 
1325     return NULL;
1326 }
1327 
aws_http_proxy_config_new_from_connection_options(struct aws_allocator * allocator,const struct aws_http_client_connection_options * options)1328 struct aws_http_proxy_config *aws_http_proxy_config_new_from_connection_options(
1329     struct aws_allocator *allocator,
1330     const struct aws_http_client_connection_options *options) {
1331     AWS_FATAL_ASSERT(options != NULL);
1332     AWS_FATAL_ASSERT(options->proxy_options != NULL);
1333 
1334     return s_aws_http_proxy_config_new(
1335         allocator,
1336         options->proxy_options,
1337         s_determine_proxy_connection_type(options->proxy_options->connection_type, options->tls_options));
1338 }
1339 
aws_http_proxy_config_new_from_manager_options(struct aws_allocator * allocator,const struct aws_http_connection_manager_options * options)1340 struct aws_http_proxy_config *aws_http_proxy_config_new_from_manager_options(
1341     struct aws_allocator *allocator,
1342     const struct aws_http_connection_manager_options *options) {
1343     AWS_FATAL_ASSERT(options != NULL);
1344     AWS_FATAL_ASSERT(options->proxy_options != NULL);
1345 
1346     return s_aws_http_proxy_config_new(
1347         allocator,
1348         options->proxy_options,
1349         s_determine_proxy_connection_type(options->proxy_options->connection_type, options->tls_connection_options));
1350 }
1351 
aws_http_proxy_config_new_tunneling_from_proxy_options(struct aws_allocator * allocator,const struct aws_http_proxy_options * proxy_options)1352 struct aws_http_proxy_config *aws_http_proxy_config_new_tunneling_from_proxy_options(
1353     struct aws_allocator *allocator,
1354     const struct aws_http_proxy_options *proxy_options) {
1355 
1356     return s_aws_http_proxy_config_new(allocator, proxy_options, AWS_HPCT_HTTP_TUNNEL);
1357 }
1358 
aws_http_proxy_config_new_from_proxy_options(struct aws_allocator * allocator,const struct aws_http_proxy_options * proxy_options)1359 struct aws_http_proxy_config *aws_http_proxy_config_new_from_proxy_options(
1360     struct aws_allocator *allocator,
1361     const struct aws_http_proxy_options *proxy_options) {
1362     if (proxy_options->connection_type == AWS_HPCT_HTTP_LEGACY) {
1363         AWS_LOGF_ERROR(AWS_LS_HTTP_PROXY_NEGOTIATION, "LEGACY type is not supported to create proxy config");
1364         return NULL;
1365     }
1366 
1367     return s_aws_http_proxy_config_new(allocator, proxy_options, proxy_options->connection_type);
1368 }
1369 
aws_http_proxy_config_new_clone(struct aws_allocator * allocator,const struct aws_http_proxy_config * proxy_config)1370 struct aws_http_proxy_config *aws_http_proxy_config_new_clone(
1371     struct aws_allocator *allocator,
1372     const struct aws_http_proxy_config *proxy_config) {
1373 
1374     AWS_FATAL_ASSERT(proxy_config != NULL);
1375 
1376     struct aws_http_proxy_config *config = aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_config));
1377     if (config == NULL) {
1378         return NULL;
1379     }
1380 
1381     config->connection_type = proxy_config->connection_type;
1382 
1383     if (aws_byte_buf_init_copy_from_cursor(&config->host, allocator, aws_byte_cursor_from_buf(&proxy_config->host))) {
1384         goto on_error;
1385     }
1386 
1387     if (proxy_config->tls_options) {
1388         config->tls_options = aws_mem_calloc(allocator, 1, sizeof(struct aws_tls_connection_options));
1389         if (aws_tls_connection_options_copy(config->tls_options, proxy_config->tls_options)) {
1390             goto on_error;
1391         }
1392     }
1393 
1394     config->allocator = allocator;
1395     config->port = proxy_config->port;
1396     config->proxy_strategy = aws_http_proxy_strategy_acquire(proxy_config->proxy_strategy);
1397 
1398     return config;
1399 
1400 on_error:
1401 
1402     aws_http_proxy_config_destroy(config);
1403 
1404     return NULL;
1405 }
1406 
aws_http_proxy_config_destroy(struct aws_http_proxy_config * config)1407 void aws_http_proxy_config_destroy(struct aws_http_proxy_config *config) {
1408     if (config == NULL) {
1409         return;
1410     }
1411 
1412     aws_byte_buf_clean_up(&config->host);
1413 
1414     if (config->tls_options) {
1415         aws_tls_connection_options_clean_up(config->tls_options);
1416         aws_mem_release(config->allocator, config->tls_options);
1417     }
1418 
1419     aws_http_proxy_strategy_release(config->proxy_strategy);
1420 
1421     aws_mem_release(config->allocator, config);
1422 }
1423 
aws_http_proxy_options_init_from_config(struct aws_http_proxy_options * options,const struct aws_http_proxy_config * config)1424 void aws_http_proxy_options_init_from_config(
1425     struct aws_http_proxy_options *options,
1426     const struct aws_http_proxy_config *config) {
1427     AWS_FATAL_ASSERT(options && config);
1428 
1429     options->connection_type = config->connection_type;
1430     options->host = aws_byte_cursor_from_buf(&config->host);
1431     options->port = config->port;
1432     options->tls_options = config->tls_options;
1433     options->proxy_strategy = config->proxy_strategy;
1434 }
1435 
aws_http_options_validate_proxy_configuration(const struct aws_http_client_connection_options * options)1436 int aws_http_options_validate_proxy_configuration(const struct aws_http_client_connection_options *options) {
1437     if (options == NULL || options->proxy_options == NULL) {
1438         return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
1439     }
1440 
1441     enum aws_http_proxy_connection_type proxy_type = options->proxy_options->connection_type;
1442     if (proxy_type == AWS_HPCT_HTTP_FORWARD && options->tls_options != NULL) {
1443         return aws_raise_error(AWS_ERROR_INVALID_STATE);
1444     }
1445 
1446     struct aws_http_proxy_strategy *proxy_strategy = options->proxy_options->proxy_strategy;
1447     if (proxy_strategy != NULL) {
1448         if (proxy_strategy->proxy_connection_type != proxy_type) {
1449             return aws_raise_error(AWS_ERROR_INVALID_STATE);
1450         }
1451     }
1452 
1453     return AWS_OP_SUCCESS;
1454 }
1455 
1456 struct aws_proxied_socket_channel_user_data {
1457     struct aws_allocator *allocator;
1458     struct aws_client_bootstrap *bootstrap;
1459     struct aws_channel *channel;
1460     aws_client_bootstrap_on_channel_event_fn *original_setup_callback;
1461     aws_client_bootstrap_on_channel_event_fn *original_shutdown_callback;
1462     void *original_user_data;
1463 };
1464 
s_proxied_socket_channel_user_data_destroy(struct aws_proxied_socket_channel_user_data * user_data)1465 static void s_proxied_socket_channel_user_data_destroy(struct aws_proxied_socket_channel_user_data *user_data) {
1466     if (user_data == NULL) {
1467         return;
1468     }
1469 
1470     aws_client_bootstrap_release(user_data->bootstrap);
1471 
1472     aws_mem_release(user_data->allocator, user_data);
1473 }
1474 
s_proxied_socket_channel_user_data_new(struct aws_allocator * allocator,struct aws_socket_channel_bootstrap_options * channel_options)1475 static struct aws_proxied_socket_channel_user_data *s_proxied_socket_channel_user_data_new(
1476     struct aws_allocator *allocator,
1477     struct aws_socket_channel_bootstrap_options *channel_options) {
1478     struct aws_proxied_socket_channel_user_data *user_data =
1479         aws_mem_calloc(allocator, 1, sizeof(struct aws_proxied_socket_channel_user_data));
1480     if (user_data == NULL) {
1481         return NULL;
1482     }
1483 
1484     user_data->allocator = allocator;
1485     user_data->original_setup_callback = channel_options->setup_callback;
1486     user_data->original_shutdown_callback = channel_options->shutdown_callback;
1487     user_data->original_user_data = channel_options->user_data;
1488     user_data->bootstrap = aws_client_bootstrap_acquire(channel_options->bootstrap);
1489 
1490     return user_data;
1491 }
1492 
s_http_proxied_socket_channel_setup(struct aws_client_bootstrap * bootstrap,int error_code,struct aws_channel * channel,void * user_data)1493 static void s_http_proxied_socket_channel_setup(
1494     struct aws_client_bootstrap *bootstrap,
1495     int error_code,
1496     struct aws_channel *channel,
1497     void *user_data) {
1498 
1499     (void)bootstrap;
1500     struct aws_proxied_socket_channel_user_data *proxied_user_data = user_data;
1501 
1502     if (error_code != AWS_ERROR_SUCCESS || channel == NULL) {
1503         proxied_user_data->original_setup_callback(
1504             proxied_user_data->bootstrap, error_code, NULL, proxied_user_data->original_user_data);
1505         s_proxied_socket_channel_user_data_destroy(proxied_user_data);
1506         return;
1507     }
1508 
1509     proxied_user_data->channel = channel;
1510 
1511     proxied_user_data->original_setup_callback(
1512         proxied_user_data->bootstrap,
1513         AWS_ERROR_SUCCESS,
1514         proxied_user_data->channel,
1515         proxied_user_data->original_user_data);
1516 }
1517 
s_http_proxied_socket_channel_shutdown(struct aws_client_bootstrap * bootstrap,int error_code,struct aws_channel * channel,void * user_data)1518 static void s_http_proxied_socket_channel_shutdown(
1519     struct aws_client_bootstrap *bootstrap,
1520     int error_code,
1521     struct aws_channel *channel,
1522     void *user_data) {
1523     (void)bootstrap;
1524     (void)channel;
1525     struct aws_proxied_socket_channel_user_data *proxied_user_data = user_data;
1526     proxied_user_data->original_shutdown_callback(
1527         proxied_user_data->bootstrap, error_code, proxied_user_data->channel, proxied_user_data->original_user_data);
1528 
1529     s_proxied_socket_channel_user_data_destroy(proxied_user_data);
1530 }
1531 
aws_http_proxy_new_socket_channel(struct aws_socket_channel_bootstrap_options * channel_options,struct aws_http_proxy_options * proxy_options)1532 int aws_http_proxy_new_socket_channel(
1533     struct aws_socket_channel_bootstrap_options *channel_options,
1534     struct aws_http_proxy_options *proxy_options) {
1535     (void)channel_options;
1536     (void)proxy_options;
1537 
1538     AWS_FATAL_ASSERT(channel_options != NULL && channel_options->bootstrap != NULL);
1539     AWS_FATAL_ASSERT(proxy_options != NULL);
1540 
1541     if (proxy_options->connection_type != AWS_HPCT_HTTP_TUNNEL) {
1542         AWS_LOGF_ERROR(
1543             AWS_LS_HTTP_PROXY_NEGOTIATION,
1544             "Creating a raw protocol channel through an http proxy requires a tunneling proxy "
1545             "configuration");
1546         return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
1547     }
1548 
1549     if (channel_options->tls_options == NULL) {
1550         AWS_LOGF_ERROR(
1551             AWS_LS_HTTP_PROXY_NEGOTIATION,
1552             "Creating a raw protocol channel through an http proxy requires tls to the endpoint");
1553         return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
1554     }
1555 
1556     struct aws_allocator *allocator = channel_options->bootstrap->allocator;
1557     struct aws_proxied_socket_channel_user_data *user_data =
1558         s_proxied_socket_channel_user_data_new(allocator, channel_options);
1559 
1560     struct aws_http_client_connection_options http_connection_options = AWS_HTTP_CLIENT_CONNECTION_OPTIONS_INIT;
1561     http_connection_options.allocator = allocator;
1562     http_connection_options.bootstrap = channel_options->bootstrap;
1563     http_connection_options.host_name = aws_byte_cursor_from_c_str(channel_options->host_name);
1564     http_connection_options.port = channel_options->port;
1565     http_connection_options.socket_options = channel_options->socket_options;
1566     http_connection_options.tls_options = channel_options->tls_options;
1567     http_connection_options.proxy_options = proxy_options;
1568     http_connection_options.user_data = user_data;
1569     http_connection_options.on_setup = NULL;    /* use channel callbacks, not http callbacks */
1570     http_connection_options.on_shutdown = NULL; /* use channel callbacks, not http callbacks */
1571 
1572     if (s_aws_http_client_connect_via_tunneling_proxy(
1573             &http_connection_options, s_http_proxied_socket_channel_setup, s_http_proxied_socket_channel_shutdown)) {
1574         goto on_error;
1575     }
1576 
1577     return AWS_OP_SUCCESS;
1578 
1579 on_error:
1580 
1581     s_proxied_socket_channel_user_data_destroy(user_data);
1582 
1583     return AWS_OP_ERR;
1584 }
1585