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