1 /**
2  * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3  * SPDX-License-Identifier: Apache-2.0.
4  */
5 
6 #include "aws/s3/private/s3_auto_ranged_get.h"
7 #include "aws/s3/private/s3_auto_ranged_put.h"
8 #include "aws/s3/private/s3_client_impl.h"
9 #include "aws/s3/private/s3_default_meta_request.h"
10 #include "aws/s3/private/s3_meta_request_impl.h"
11 #include "aws/s3/private/s3_util.h"
12 
13 #include <aws/auth/credentials.h>
14 #include <aws/common/assert.h>
15 #include <aws/common/atomics.h>
16 #include <aws/common/clock.h>
17 #include <aws/common/device_random.h>
18 #include <aws/common/environment.h>
19 #include <aws/common/string.h>
20 #include <aws/common/system_info.h>
21 #include <aws/http/connection.h>
22 #include <aws/http/connection_manager.h>
23 #include <aws/http/proxy.h>
24 #include <aws/http/request_response.h>
25 #include <aws/io/channel_bootstrap.h>
26 #include <aws/io/event_loop.h>
27 #include <aws/io/host_resolver.h>
28 #include <aws/io/retry_strategy.h>
29 #include <aws/io/socket.h>
30 #include <aws/io/stream.h>
31 #include <aws/io/tls_channel_handler.h>
32 #include <aws/io/uri.h>
33 
34 #include <inttypes.h>
35 #include <math.h>
36 
37 static const uint32_t s_connection_timeout_ms = 3000;
38 static const uint16_t s_http_port = 80;
39 static const uint16_t s_https_port = 443;
40 
41 static void s_s3_endpoint_on_host_resolver_address_resolved(
42     struct aws_host_resolver *resolver,
43     const struct aws_string *host_name,
44     int err_code,
45     const struct aws_array_list *host_addresses,
46     void *user_data);
47 
48 static struct aws_http_connection_manager *s_s3_endpoint_create_http_connection_manager(
49     struct aws_s3_endpoint *endpoint,
50     const struct aws_string *host_name,
51     struct aws_client_bootstrap *client_bootstrap,
52     const struct aws_tls_connection_options *tls_connection_options,
53     uint32_t max_connections);
54 
55 static void s_s3_endpoint_http_connection_manager_shutdown_callback(void *user_data);
56 
57 static void s_s3_endpoint_ref_count_zero(void *user_data);
58 
aws_s3_endpoint_new(struct aws_allocator * allocator,const struct aws_s3_endpoint_options * options)59 struct aws_s3_endpoint *aws_s3_endpoint_new(
60     struct aws_allocator *allocator,
61     const struct aws_s3_endpoint_options *options) {
62     AWS_PRECONDITION(allocator);
63     AWS_PRECONDITION(options);
64     AWS_PRECONDITION(options->host_name);
65 
66     struct aws_s3_endpoint *endpoint = aws_mem_calloc(allocator, 1, sizeof(struct aws_s3_endpoint));
67     aws_ref_count_init(&endpoint->ref_count, endpoint, s_s3_endpoint_ref_count_zero);
68 
69     endpoint->allocator = allocator;
70     endpoint->host_name = options->host_name;
71 
72     struct aws_host_resolution_config host_resolver_config;
73     AWS_ZERO_STRUCT(host_resolver_config);
74     host_resolver_config.impl = aws_default_dns_resolve;
75     host_resolver_config.max_ttl = options->dns_host_address_ttl_seconds;
76     host_resolver_config.impl_data = NULL;
77 
78     if (aws_host_resolver_resolve_host(
79             options->client_bootstrap->host_resolver,
80             endpoint->host_name,
81             s_s3_endpoint_on_host_resolver_address_resolved,
82             &host_resolver_config,
83             NULL)) {
84 
85         AWS_LOGF_ERROR(
86             AWS_LS_S3_ENDPOINT,
87             "id=%p: Error trying to resolve host for endpoint %s",
88             (void *)endpoint,
89             (const char *)endpoint->host_name->bytes);
90 
91         goto error_cleanup;
92     }
93 
94     endpoint->http_connection_manager = s_s3_endpoint_create_http_connection_manager(
95         endpoint,
96         options->host_name,
97         options->client_bootstrap,
98         options->tls_connection_options,
99         options->max_connections);
100 
101     if (endpoint->http_connection_manager == NULL) {
102         goto error_cleanup;
103     }
104 
105     endpoint->ref_count_zero_callback = options->ref_count_zero_callback;
106     endpoint->shutdown_callback = options->shutdown_callback;
107     endpoint->user_data = options->user_data;
108 
109     return endpoint;
110 
111 error_cleanup:
112 
113     aws_string_destroy(options->host_name);
114 
115     aws_mem_release(allocator, endpoint);
116 
117     return NULL;
118 }
119 
s_s3_endpoint_create_http_connection_manager(struct aws_s3_endpoint * endpoint,const struct aws_string * host_name,struct aws_client_bootstrap * client_bootstrap,const struct aws_tls_connection_options * tls_connection_options,uint32_t max_connections)120 static struct aws_http_connection_manager *s_s3_endpoint_create_http_connection_manager(
121     struct aws_s3_endpoint *endpoint,
122     const struct aws_string *host_name,
123     struct aws_client_bootstrap *client_bootstrap,
124     const struct aws_tls_connection_options *tls_connection_options,
125     uint32_t max_connections) {
126     AWS_PRECONDITION(endpoint);
127     AWS_PRECONDITION(client_bootstrap);
128     AWS_PRECONDITION(host_name);
129 
130     struct aws_byte_cursor host_name_cursor = aws_byte_cursor_from_string(host_name);
131 
132     /* Try to set up an HTTP connection manager. */
133     struct aws_socket_options socket_options;
134     AWS_ZERO_STRUCT(socket_options);
135     socket_options.type = AWS_SOCKET_STREAM;
136     socket_options.domain = AWS_SOCKET_IPV4;
137     socket_options.connect_timeout_ms = s_connection_timeout_ms;
138     struct proxy_env_var_settings proxy_ev_settings;
139     AWS_ZERO_STRUCT(proxy_ev_settings);
140     /* Turn on envrionment variable for proxy by default */
141     proxy_ev_settings.env_var_type = AWS_HPEV_ENABLE;
142 
143     struct aws_http_connection_manager_options manager_options;
144     AWS_ZERO_STRUCT(manager_options);
145     manager_options.bootstrap = client_bootstrap;
146     manager_options.initial_window_size = SIZE_MAX;
147     manager_options.socket_options = &socket_options;
148     manager_options.host = host_name_cursor;
149     manager_options.max_connections = max_connections;
150     manager_options.shutdown_complete_callback = s_s3_endpoint_http_connection_manager_shutdown_callback;
151     manager_options.shutdown_complete_user_data = endpoint;
152     manager_options.proxy_ev_settings = &proxy_ev_settings;
153 
154     struct aws_tls_connection_options *manager_tls_options = NULL;
155 
156     if (tls_connection_options != NULL) {
157         manager_tls_options = aws_mem_calloc(endpoint->allocator, 1, sizeof(struct aws_tls_connection_options));
158         aws_tls_connection_options_copy(manager_tls_options, tls_connection_options);
159 
160         /* TODO fix this in the actual aws_tls_connection_options_set_server_name function. */
161         if (manager_tls_options->server_name != NULL) {
162             aws_string_destroy(manager_tls_options->server_name);
163             manager_tls_options->server_name = NULL;
164         }
165 
166         aws_tls_connection_options_set_server_name(manager_tls_options, endpoint->allocator, &host_name_cursor);
167 
168         manager_options.tls_connection_options = manager_tls_options;
169         manager_options.port = s_https_port;
170     } else {
171         manager_options.port = s_http_port;
172     }
173 
174     struct aws_http_connection_manager *http_connection_manager =
175         aws_http_connection_manager_new(endpoint->allocator, &manager_options);
176 
177     if (manager_tls_options != NULL) {
178         aws_tls_connection_options_clean_up(manager_tls_options);
179         aws_mem_release(endpoint->allocator, manager_tls_options);
180         manager_tls_options = NULL;
181     }
182 
183     if (http_connection_manager == NULL) {
184         AWS_LOGF_ERROR(AWS_LS_S3_ENDPOINT, "id=%p: Could not create http connection manager.", (void *)endpoint);
185         return NULL;
186     }
187 
188     AWS_LOGF_DEBUG(
189         AWS_LS_S3_ENDPOINT,
190         "id=%p: Created connection manager %p for endpoint",
191         (void *)endpoint,
192         (void *)endpoint->http_connection_manager);
193 
194     return http_connection_manager;
195 }
196 
aws_s3_endpoint_acquire(struct aws_s3_endpoint * endpoint)197 struct aws_s3_endpoint *aws_s3_endpoint_acquire(struct aws_s3_endpoint *endpoint) {
198     AWS_PRECONDITION(endpoint);
199 
200     aws_ref_count_acquire(&endpoint->ref_count);
201 
202     return endpoint;
203 }
204 
aws_s3_endpoint_release(struct aws_s3_endpoint * endpoint)205 void aws_s3_endpoint_release(struct aws_s3_endpoint *endpoint) {
206     if (endpoint == NULL) {
207         return;
208     }
209 
210     aws_ref_count_release(&endpoint->ref_count);
211 }
212 
s_s3_endpoint_ref_count_zero(void * user_data)213 static void s_s3_endpoint_ref_count_zero(void *user_data) {
214     struct aws_s3_endpoint *endpoint = user_data;
215     AWS_PRECONDITION(endpoint);
216 
217     if (endpoint->ref_count_zero_callback != NULL && !endpoint->ref_count_zero_callback(endpoint)) {
218         return;
219     }
220 
221     if (endpoint->http_connection_manager != NULL) {
222         struct aws_http_connection_manager *http_connection_manager = endpoint->http_connection_manager;
223         endpoint->http_connection_manager = NULL;
224         aws_http_connection_manager_release(http_connection_manager);
225     } else {
226         s_s3_endpoint_http_connection_manager_shutdown_callback(endpoint->user_data);
227     }
228 }
229 
s_s3_endpoint_http_connection_manager_shutdown_callback(void * user_data)230 static void s_s3_endpoint_http_connection_manager_shutdown_callback(void *user_data) {
231     struct aws_s3_endpoint *endpoint = user_data;
232     AWS_ASSERT(endpoint);
233 
234     aws_s3_endpoint_shutdown_fn *shutdown_callback = endpoint->shutdown_callback;
235     void *endpoint_user_data = endpoint->user_data;
236 
237     aws_mem_release(endpoint->allocator, endpoint);
238 
239     if (shutdown_callback != NULL) {
240         shutdown_callback(endpoint_user_data);
241     }
242 }
243 
s_s3_endpoint_on_host_resolver_address_resolved(struct aws_host_resolver * resolver,const struct aws_string * host_name,int err_code,const struct aws_array_list * host_addresses,void * user_data)244 static void s_s3_endpoint_on_host_resolver_address_resolved(
245     struct aws_host_resolver *resolver,
246     const struct aws_string *host_name,
247     int err_code,
248     const struct aws_array_list *host_addresses,
249     void *user_data) {
250     (void)resolver;
251     (void)host_name;
252     (void)err_code;
253     (void)host_addresses;
254     (void)user_data;
255 }
256