diff options
author | orivej <orivej@yandex-team.ru> | 2022-02-10 16:44:49 +0300 |
---|---|---|
committer | Daniil Cherednik <dcherednik@yandex-team.ru> | 2022-02-10 16:44:49 +0300 |
commit | 718c552901d703c502ccbefdfc3c9028d608b947 (patch) | |
tree | 46534a98bbefcd7b1f3faa5b52c138ab27db75b7 /contrib/restricted/aws/aws-c-io/source/channel_bootstrap.c | |
parent | e9656aae26e0358d5378e5b63dcac5c8dbe0e4d0 (diff) | |
download | ydb-718c552901d703c502ccbefdfc3c9028d608b947.tar.gz |
Restoring authorship annotation for <orivej@yandex-team.ru>. Commit 1 of 2.
Diffstat (limited to 'contrib/restricted/aws/aws-c-io/source/channel_bootstrap.c')
-rw-r--r-- | contrib/restricted/aws/aws-c-io/source/channel_bootstrap.c | 2842 |
1 files changed, 1421 insertions, 1421 deletions
diff --git a/contrib/restricted/aws/aws-c-io/source/channel_bootstrap.c b/contrib/restricted/aws/aws-c-io/source/channel_bootstrap.c index f5e0ad7aff..581e72572f 100644 --- a/contrib/restricted/aws/aws-c-io/source/channel_bootstrap.c +++ b/contrib/restricted/aws/aws-c-io/source/channel_bootstrap.c @@ -1,1421 +1,1421 @@ -/** - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ -#include <aws/io/channel_bootstrap.h> - -#include <aws/common/ref_count.h> -#include <aws/common/string.h> -#include <aws/io/event_loop.h> -#include <aws/io/logging.h> -#include <aws/io/socket.h> -#include <aws/io/socket_channel_handler.h> -#include <aws/io/tls_channel_handler.h> - -#if _MSC_VER -/* non-constant aggregate initializer */ -# pragma warning(disable : 4204) -/* allow automatic variable to escape scope - (it's intentional and we make sure it doesn't actually return - before the task is finished).*/ -# pragma warning(disable : 4221) -#endif - -#define DEFAULT_DNS_TTL 30 - -static void s_client_bootstrap_destroy_impl(struct aws_client_bootstrap *bootstrap) { - AWS_ASSERT(bootstrap); - AWS_LOGF_DEBUG(AWS_LS_IO_CHANNEL_BOOTSTRAP, "id=%p: destroying", (void *)bootstrap); - aws_client_bootstrap_shutdown_complete_fn *on_shutdown_complete = bootstrap->on_shutdown_complete; - void *user_data = bootstrap->user_data; - - aws_event_loop_group_release(bootstrap->event_loop_group); - aws_host_resolver_release(bootstrap->host_resolver); - - aws_mem_release(bootstrap->allocator, bootstrap); - - if (on_shutdown_complete) { - on_shutdown_complete(user_data); - } -} - -struct aws_client_bootstrap *aws_client_bootstrap_acquire(struct aws_client_bootstrap *bootstrap) { - if (bootstrap != NULL) { - aws_ref_count_acquire(&bootstrap->ref_count); - } - - return bootstrap; -} - -void aws_client_bootstrap_release(struct aws_client_bootstrap *bootstrap) { - AWS_LOGF_DEBUG(AWS_LS_IO_CHANNEL_BOOTSTRAP, "id=%p: releasing bootstrap reference", (void *)bootstrap); - if (bootstrap != NULL) { - aws_ref_count_release(&bootstrap->ref_count); - } -} - -struct aws_client_bootstrap *aws_client_bootstrap_new( - struct aws_allocator *allocator, - const struct aws_client_bootstrap_options *options) { - AWS_ASSERT(allocator); - AWS_ASSERT(options); - AWS_ASSERT(options->event_loop_group); - - struct aws_client_bootstrap *bootstrap = aws_mem_calloc(allocator, 1, sizeof(struct aws_client_bootstrap)); - if (!bootstrap) { - return NULL; - } - - AWS_LOGF_INFO( - AWS_LS_IO_CHANNEL_BOOTSTRAP, - "id=%p: Initializing client bootstrap with event-loop group %p", - (void *)bootstrap, - (void *)options->event_loop_group); - - bootstrap->allocator = allocator; - bootstrap->event_loop_group = aws_event_loop_group_acquire(options->event_loop_group); - bootstrap->on_protocol_negotiated = NULL; - aws_ref_count_init( - &bootstrap->ref_count, bootstrap, (aws_simple_completion_callback *)s_client_bootstrap_destroy_impl); - bootstrap->host_resolver = aws_host_resolver_acquire(options->host_resolver); - bootstrap->on_shutdown_complete = options->on_shutdown_complete; - bootstrap->user_data = options->user_data; - - if (options->host_resolution_config) { - bootstrap->host_resolver_config = *options->host_resolution_config; - } else { - bootstrap->host_resolver_config = (struct aws_host_resolution_config){ - .impl = aws_default_dns_resolve, - .max_ttl = DEFAULT_DNS_TTL, - .impl_data = NULL, - }; - } - - return bootstrap; -} - -int aws_client_bootstrap_set_alpn_callback( - struct aws_client_bootstrap *bootstrap, - aws_channel_on_protocol_negotiated_fn *on_protocol_negotiated) { - AWS_ASSERT(on_protocol_negotiated); - - AWS_LOGF_DEBUG(AWS_LS_IO_CHANNEL_BOOTSTRAP, "id=%p: Setting ALPN callback", (void *)bootstrap); - bootstrap->on_protocol_negotiated = on_protocol_negotiated; - return AWS_OP_SUCCESS; -} - -struct client_channel_data { - struct aws_channel *channel; - struct aws_socket *socket; - struct aws_tls_connection_options tls_options; - aws_channel_on_protocol_negotiated_fn *on_protocol_negotiated; - aws_tls_on_data_read_fn *user_on_data_read; - aws_tls_on_negotiation_result_fn *user_on_negotiation_result; - aws_tls_on_error_fn *user_on_error; - void *tls_user_data; - bool use_tls; -}; - -struct client_connection_args { - struct aws_client_bootstrap *bootstrap; - aws_client_bootstrap_on_channel_event_fn *creation_callback; - aws_client_bootstrap_on_channel_event_fn *setup_callback; - aws_client_bootstrap_on_channel_event_fn *shutdown_callback; - struct client_channel_data channel_data; - struct aws_socket_options outgoing_options; - uint16_t outgoing_port; - struct aws_string *host_name; - void *user_data; - uint8_t addresses_count; - uint8_t failed_count; - bool connection_chosen; - bool setup_called; - bool enable_read_back_pressure; - - /* - * It is likely that all reference adjustments to the connection args take place in a single event loop - * thread and are thus thread-safe. I can imagine some complex future scenarios where that might not hold true - * and so it seems reasonable to switch now to a safe pattern. - * - */ - struct aws_ref_count ref_count; -}; - -static struct client_connection_args *s_client_connection_args_acquire(struct client_connection_args *args) { - if (args != NULL) { - aws_ref_count_acquire(&args->ref_count); - } - - return args; -} - -static void s_client_connection_args_destroy(struct client_connection_args *args) { - AWS_ASSERT(args); - - struct aws_allocator *allocator = args->bootstrap->allocator; - aws_client_bootstrap_release(args->bootstrap); - if (args->host_name) { - aws_string_destroy(args->host_name); - } - - if (args->channel_data.use_tls) { - aws_tls_connection_options_clean_up(&args->channel_data.tls_options); - } - - aws_mem_release(allocator, args); -} - -static void s_client_connection_args_release(struct client_connection_args *args) { - if (args != NULL) { - aws_ref_count_release(&args->ref_count); - } -} - -static void s_connection_args_setup_callback( - struct client_connection_args *args, - int error_code, - struct aws_channel *channel) { - /* setup_callback is always called exactly once */ - AWS_ASSERT(!args->setup_called); - if (!args->setup_called) { - AWS_ASSERT((error_code == AWS_OP_SUCCESS) == (channel != NULL)); - aws_client_bootstrap_on_channel_event_fn *setup_callback = args->setup_callback; - setup_callback(args->bootstrap, error_code, channel, args->user_data); - args->setup_called = true; - /* if setup_callback is called with an error, we will not call shutdown_callback */ - if (error_code) { - args->shutdown_callback = NULL; - } - s_client_connection_args_release(args); - } -} - -static void s_connection_args_creation_callback(struct client_connection_args *args, struct aws_channel *channel) { - - AWS_FATAL_ASSERT(channel != NULL); - - if (args->creation_callback) { - args->creation_callback(args->bootstrap, AWS_ERROR_SUCCESS, channel, args->user_data); - } -} - -static void s_connection_args_shutdown_callback( - struct client_connection_args *args, - int error_code, - struct aws_channel *channel) { - - if (!args->setup_called) { - /* if setup_callback was not called yet, an error occurred, ensure we tell the user *SOMETHING* */ - error_code = (error_code) ? error_code : AWS_ERROR_UNKNOWN; - s_connection_args_setup_callback(args, error_code, NULL); - return; - } - - aws_client_bootstrap_on_channel_event_fn *shutdown_callback = args->shutdown_callback; - if (shutdown_callback) { - shutdown_callback(args->bootstrap, error_code, channel, args->user_data); - } -} - -static void s_tls_client_on_negotiation_result( - struct aws_channel_handler *handler, - struct aws_channel_slot *slot, - int err_code, - void *user_data) { - struct client_connection_args *connection_args = user_data; - - if (connection_args->channel_data.user_on_negotiation_result) { - connection_args->channel_data.user_on_negotiation_result( - handler, slot, err_code, connection_args->channel_data.tls_user_data); - } - - AWS_LOGF_DEBUG( - AWS_LS_IO_CHANNEL_BOOTSTRAP, - "id=%p: tls negotiation result %d on channel %p", - (void *)connection_args->bootstrap, - err_code, - (void *)slot->channel); - - /* if an error occurred, the user callback will be delivered in shutdown */ - if (err_code) { - aws_channel_shutdown(slot->channel, err_code); - return; - } - - struct aws_channel *channel = connection_args->channel_data.channel; - s_connection_args_setup_callback(connection_args, AWS_ERROR_SUCCESS, channel); -} - -/* in the context of a channel bootstrap, we don't care about these, but since we're hooking into these APIs we have to - * provide a proxy for the user actually receiving their callbacks. */ -static void s_tls_client_on_data_read( - struct aws_channel_handler *handler, - struct aws_channel_slot *slot, - struct aws_byte_buf *buffer, - void *user_data) { - struct client_connection_args *connection_args = user_data; - - if (connection_args->channel_data.user_on_data_read) { - connection_args->channel_data.user_on_data_read( - handler, slot, buffer, connection_args->channel_data.tls_user_data); - } -} - -/* in the context of a channel bootstrap, we don't care about these, but since we're hooking into these APIs we have to - * provide a proxy for the user actually receiving their callbacks. */ -static void s_tls_client_on_error( - struct aws_channel_handler *handler, - struct aws_channel_slot *slot, - int err, - const char *message, - void *user_data) { - struct client_connection_args *connection_args = user_data; - - if (connection_args->channel_data.user_on_error) { - connection_args->channel_data.user_on_error( - handler, slot, err, message, connection_args->channel_data.tls_user_data); - } -} - -static inline int s_setup_client_tls(struct client_connection_args *connection_args, struct aws_channel *channel) { - struct aws_channel_slot *tls_slot = aws_channel_slot_new(channel); - - /* as far as cleanup goes, since this stuff is being added to a channel, the caller will free this memory - when they clean up the channel. */ - if (!tls_slot) { - return AWS_OP_ERR; - } - - struct aws_channel_handler *tls_handler = aws_tls_client_handler_new( - connection_args->bootstrap->allocator, &connection_args->channel_data.tls_options, tls_slot); - - if (!tls_handler) { - aws_mem_release(connection_args->bootstrap->allocator, (void *)tls_slot); - return AWS_OP_ERR; - } - - aws_channel_slot_insert_end(channel, tls_slot); - AWS_LOGF_TRACE( - AWS_LS_IO_CHANNEL_BOOTSTRAP, - "id=%p: Setting up client TLS on channel %p with handler %p on slot %p", - (void *)connection_args->bootstrap, - (void *)channel, - (void *)tls_handler, - (void *)tls_slot); - - if (aws_channel_slot_set_handler(tls_slot, tls_handler) != AWS_OP_SUCCESS) { - return AWS_OP_ERR; - } - - if (connection_args->channel_data.on_protocol_negotiated) { - struct aws_channel_slot *alpn_slot = aws_channel_slot_new(channel); - - if (!alpn_slot) { - return AWS_OP_ERR; - } - - struct aws_channel_handler *alpn_handler = aws_tls_alpn_handler_new( - connection_args->bootstrap->allocator, - connection_args->channel_data.on_protocol_negotiated, - connection_args->user_data); - - if (!alpn_handler) { - aws_mem_release(connection_args->bootstrap->allocator, (void *)alpn_slot); - return AWS_OP_ERR; - } - - AWS_LOGF_TRACE( - AWS_LS_IO_CHANNEL_BOOTSTRAP, - "id=%p: Setting up ALPN handler on channel " - "%p with handler %p on slot %p", - (void *)connection_args->bootstrap, - (void *)channel, - (void *)alpn_handler, - (void *)alpn_slot); - - aws_channel_slot_insert_right(tls_slot, alpn_slot); - if (aws_channel_slot_set_handler(alpn_slot, alpn_handler) != AWS_OP_SUCCESS) { - return AWS_OP_ERR; - } - } - - if (aws_tls_client_handler_start_negotiation(tls_handler) != AWS_OP_SUCCESS) { - return AWS_OP_ERR; - } - - return AWS_OP_SUCCESS; -} - -static void s_on_client_channel_on_setup_completed(struct aws_channel *channel, int error_code, void *user_data) { - struct client_connection_args *connection_args = user_data; - int err_code = error_code; - - if (!err_code) { - AWS_LOGF_DEBUG( - AWS_LS_IO_CHANNEL_BOOTSTRAP, - "id=%p: channel %p setup succeeded: bootstrapping.", - (void *)connection_args->bootstrap, - (void *)channel); - - struct aws_channel_slot *socket_slot = aws_channel_slot_new(channel); - - if (!socket_slot) { - err_code = aws_last_error(); - goto error; - } - - struct aws_channel_handler *socket_channel_handler = aws_socket_handler_new( - connection_args->bootstrap->allocator, - connection_args->channel_data.socket, - socket_slot, - g_aws_channel_max_fragment_size); - - if (!socket_channel_handler) { - err_code = aws_last_error(); - aws_channel_slot_remove(socket_slot); - socket_slot = NULL; - goto error; - } - - AWS_LOGF_TRACE( - AWS_LS_IO_CHANNEL_BOOTSTRAP, - "id=%p: Setting up socket handler on channel " - "%p with handler %p on slot %p.", - (void *)connection_args->bootstrap, - (void *)channel, - (void *)socket_channel_handler, - (void *)socket_slot); - - if (aws_channel_slot_set_handler(socket_slot, socket_channel_handler)) { - err_code = aws_last_error(); - goto error; - } - - if (connection_args->channel_data.use_tls) { - /* we don't want to notify the user that the channel is ready yet, since tls is still negotiating, wait - * for the negotiation callback and handle it then.*/ - if (s_setup_client_tls(connection_args, channel)) { - err_code = aws_last_error(); - goto error; - } - } else { - s_connection_args_setup_callback(connection_args, AWS_OP_SUCCESS, channel); - } - - return; - } - -error: - AWS_LOGF_ERROR( - AWS_LS_IO_CHANNEL_BOOTSTRAP, - "id=%p: channel %p setup failed with error %d.", - (void *)connection_args->bootstrap, - (void *)channel, - err_code); - aws_channel_shutdown(channel, err_code); - /* the channel shutdown callback will clean the channel up */ -} - -static void s_on_client_channel_on_shutdown(struct aws_channel *channel, int error_code, void *user_data) { - struct client_connection_args *connection_args = user_data; - - AWS_LOGF_DEBUG( - AWS_LS_IO_CHANNEL_BOOTSTRAP, - "id=%p: channel %p shutdown with error %d.", - (void *)connection_args->bootstrap, - (void *)channel, - error_code); - - /* note it's not safe to reference the bootstrap after the callback. */ - struct aws_allocator *allocator = connection_args->bootstrap->allocator; - s_connection_args_shutdown_callback(connection_args, error_code, channel); - - aws_channel_destroy(channel); - aws_socket_clean_up(connection_args->channel_data.socket); - aws_mem_release(allocator, connection_args->channel_data.socket); - s_client_connection_args_release(connection_args); -} - -static bool s_aws_socket_domain_uses_dns(enum aws_socket_domain domain) { - return domain == AWS_SOCKET_IPV4 || domain == AWS_SOCKET_IPV6; -} - -static void s_on_client_connection_established(struct aws_socket *socket, int error_code, void *user_data) { - struct client_connection_args *connection_args = user_data; - - AWS_LOGF_DEBUG( - AWS_LS_IO_CHANNEL_BOOTSTRAP, - "id=%p: client connection on socket %p completed with error %d.", - (void *)connection_args->bootstrap, - (void *)socket, - error_code); - - if (error_code) { - connection_args->failed_count++; - } - - if (error_code || connection_args->connection_chosen) { - if (s_aws_socket_domain_uses_dns(connection_args->outgoing_options.domain) && error_code) { - struct aws_host_address host_address; - host_address.host = connection_args->host_name; - host_address.address = - aws_string_new_from_c_str(connection_args->bootstrap->allocator, socket->remote_endpoint.address); - host_address.record_type = connection_args->outgoing_options.domain == AWS_SOCKET_IPV6 - ? AWS_ADDRESS_RECORD_TYPE_AAAA - : AWS_ADDRESS_RECORD_TYPE_A; - - if (host_address.address) { - AWS_LOGF_DEBUG( - AWS_LS_IO_CHANNEL_BOOTSTRAP, - "id=%p: recording bad address %s.", - (void *)connection_args->bootstrap, - socket->remote_endpoint.address); - aws_host_resolver_record_connection_failure(connection_args->bootstrap->host_resolver, &host_address); - aws_string_destroy((void *)host_address.address); - } - } - - AWS_LOGF_TRACE( - AWS_LS_IO_CHANNEL_BOOTSTRAP, - "id=%p: releasing socket %p either because we already have a " - "successful connection or because it errored out.", - (void *)connection_args->bootstrap, - (void *)socket); - aws_socket_close(socket); - - aws_socket_clean_up(socket); - aws_mem_release(connection_args->bootstrap->allocator, socket); - - /* if this is the last attempted connection and it failed, notify the user */ - if (connection_args->failed_count == connection_args->addresses_count) { - AWS_LOGF_ERROR( - AWS_LS_IO_CHANNEL_BOOTSTRAP, - "id=%p: Connection failed with error_code %d.", - (void *)connection_args->bootstrap, - error_code); - /* connection_args will be released after setup_callback */ - s_connection_args_setup_callback(connection_args, error_code, NULL); - } - - /* every connection task adds a ref, so every failure or cancel needs to dec one */ - s_client_connection_args_release(connection_args); - return; - } - - connection_args->connection_chosen = true; - connection_args->channel_data.socket = socket; - - struct aws_channel_options args = { - .on_setup_completed = s_on_client_channel_on_setup_completed, - .setup_user_data = connection_args, - .shutdown_user_data = connection_args, - .on_shutdown_completed = s_on_client_channel_on_shutdown, - }; - - args.enable_read_back_pressure = connection_args->enable_read_back_pressure; - args.event_loop = aws_socket_get_event_loop(socket); - - AWS_LOGF_TRACE( - AWS_LS_IO_CHANNEL_BOOTSTRAP, - "id=%p: Successful connection, creating a new channel using socket %p.", - (void *)connection_args->bootstrap, - (void *)socket); - - connection_args->channel_data.channel = aws_channel_new(connection_args->bootstrap->allocator, &args); - - if (!connection_args->channel_data.channel) { - aws_socket_clean_up(socket); - aws_mem_release(connection_args->bootstrap->allocator, connection_args->channel_data.socket); - connection_args->failed_count++; - - /* if this is the last attempted connection and it failed, notify the user */ - if (connection_args->failed_count == connection_args->addresses_count) { - s_connection_args_setup_callback(connection_args, error_code, NULL); - } - } else { - s_connection_args_creation_callback(connection_args, connection_args->channel_data.channel); - } -} - -struct connection_task_data { - struct aws_task task; - struct aws_socket_endpoint endpoint; - struct aws_socket_options options; - struct aws_host_address host_address; - struct client_connection_args *args; - struct aws_event_loop *connect_loop; -}; - -static void s_attempt_connection(struct aws_task *task, void *arg, enum aws_task_status status) { - (void)task; - struct connection_task_data *task_data = arg; - struct aws_allocator *allocator = task_data->args->bootstrap->allocator; - int err_code = 0; - - if (status != AWS_TASK_STATUS_RUN_READY) { - goto task_cancelled; - } - - struct aws_socket *outgoing_socket = aws_mem_acquire(allocator, sizeof(struct aws_socket)); - if (!outgoing_socket) { - goto socket_alloc_failed; - } - - if (aws_socket_init(outgoing_socket, allocator, &task_data->options)) { - goto socket_init_failed; - } - - if (aws_socket_connect( - outgoing_socket, - &task_data->endpoint, - task_data->connect_loop, - s_on_client_connection_established, - task_data->args)) { - - goto socket_connect_failed; - } - - goto cleanup_task; - -socket_connect_failed: - aws_host_resolver_record_connection_failure(task_data->args->bootstrap->host_resolver, &task_data->host_address); - aws_socket_clean_up(outgoing_socket); -socket_init_failed: - aws_mem_release(allocator, outgoing_socket); -socket_alloc_failed: - err_code = aws_last_error(); - AWS_LOGF_ERROR( - AWS_LS_IO_CHANNEL_BOOTSTRAP, - "id=%p: failed to create socket with error %d", - (void *)task_data->args->bootstrap, - err_code); -task_cancelled: - task_data->args->failed_count++; - /* if this is the last attempted connection and it failed, notify the user */ - if (task_data->args->failed_count == task_data->args->addresses_count) { - s_connection_args_setup_callback(task_data->args, err_code, NULL); - } - s_client_connection_args_release(task_data->args); - -cleanup_task: - aws_host_address_clean_up(&task_data->host_address); - aws_mem_release(allocator, task_data); -} - -static void s_on_host_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) { - (void)resolver; - (void)host_name; - - struct client_connection_args *client_connection_args = user_data; - struct aws_allocator *allocator = client_connection_args->bootstrap->allocator; - - if (err_code) { - AWS_LOGF_ERROR( - AWS_LS_IO_CHANNEL_BOOTSTRAP, - "id=%p: dns resolution failed, or all socket connections to the endpoint failed.", - (void *)client_connection_args->bootstrap); - s_connection_args_setup_callback(client_connection_args, err_code, NULL); - return; - } - - size_t host_addresses_len = aws_array_list_length(host_addresses); - AWS_FATAL_ASSERT(host_addresses_len > 0); - AWS_LOGF_TRACE( - AWS_LS_IO_CHANNEL_BOOTSTRAP, - "id=%p: dns resolution completed. Kicking off connections" - " on %llu addresses. First one back wins.", - (void *)client_connection_args->bootstrap, - (unsigned long long)host_addresses_len); - /* use this event loop for all outgoing connection attempts (only one will ultimately win). */ - struct aws_event_loop *connect_loop = - aws_event_loop_group_get_next_loop(client_connection_args->bootstrap->event_loop_group); - client_connection_args->addresses_count = (uint8_t)host_addresses_len; - - /* allocate all the task data first, in case it fails... */ - AWS_VARIABLE_LENGTH_ARRAY(struct connection_task_data *, tasks, host_addresses_len); - for (size_t i = 0; i < host_addresses_len; ++i) { - struct connection_task_data *task_data = tasks[i] = - aws_mem_calloc(allocator, 1, sizeof(struct connection_task_data)); - bool failed = task_data == NULL; - if (!failed) { - struct aws_host_address *host_address_ptr = NULL; - aws_array_list_get_at_ptr(host_addresses, (void **)&host_address_ptr, i); - - task_data->endpoint.port = client_connection_args->outgoing_port; - AWS_ASSERT(sizeof(task_data->endpoint.address) >= host_address_ptr->address->len + 1); - memcpy( - task_data->endpoint.address, - aws_string_bytes(host_address_ptr->address), - host_address_ptr->address->len); - task_data->endpoint.address[host_address_ptr->address->len] = 0; - - task_data->options = client_connection_args->outgoing_options; - task_data->options.domain = - host_address_ptr->record_type == AWS_ADDRESS_RECORD_TYPE_AAAA ? AWS_SOCKET_IPV6 : AWS_SOCKET_IPV4; - - failed = aws_host_address_copy(host_address_ptr, &task_data->host_address) != AWS_OP_SUCCESS; - task_data->args = client_connection_args; - task_data->connect_loop = connect_loop; - } - - if (failed) { - for (size_t j = 0; j <= i; ++j) { - if (tasks[j]) { - aws_host_address_clean_up(&tasks[j]->host_address); - aws_mem_release(allocator, tasks[j]); - } - } - int alloc_err_code = aws_last_error(); - AWS_LOGF_ERROR( - AWS_LS_IO_CHANNEL_BOOTSTRAP, - "id=%p: failed to allocate connection task data: err=%d", - (void *)client_connection_args->bootstrap, - alloc_err_code); - s_connection_args_setup_callback(client_connection_args, alloc_err_code, NULL); - return; - } - } - - /* ...then schedule all the tasks, which cannot fail */ - for (size_t i = 0; i < host_addresses_len; ++i) { - struct connection_task_data *task_data = tasks[i]; - /* each task needs to hold a ref to the args until completed */ - s_client_connection_args_acquire(task_data->args); - - aws_task_init(&task_data->task, s_attempt_connection, task_data, "attempt_connection"); - aws_event_loop_schedule_task_now(connect_loop, &task_data->task); - } -} - -int aws_client_bootstrap_new_socket_channel(struct aws_socket_channel_bootstrap_options *options) { - - struct aws_client_bootstrap *bootstrap = options->bootstrap; - AWS_FATAL_ASSERT(options->setup_callback); - AWS_FATAL_ASSERT(options->shutdown_callback); - AWS_FATAL_ASSERT(bootstrap); - - const struct aws_socket_options *socket_options = options->socket_options; - AWS_FATAL_ASSERT(socket_options != NULL); - - const struct aws_tls_connection_options *tls_options = options->tls_options; - - AWS_FATAL_ASSERT(tls_options == NULL || socket_options->type == AWS_SOCKET_STREAM); - aws_io_fatal_assert_library_initialized(); - - struct client_connection_args *client_connection_args = - aws_mem_calloc(bootstrap->allocator, 1, sizeof(struct client_connection_args)); - - if (!client_connection_args) { - return AWS_OP_ERR; - } - - const char *host_name = options->host_name; - uint16_t port = options->port; - - AWS_LOGF_TRACE( - AWS_LS_IO_CHANNEL_BOOTSTRAP, - "id=%p: attempting to initialize a new client channel to %s:%d", - (void *)bootstrap, - host_name, - (int)port); - - aws_ref_count_init( - &client_connection_args->ref_count, - client_connection_args, - (aws_simple_completion_callback *)s_client_connection_args_destroy); - client_connection_args->user_data = options->user_data; - client_connection_args->bootstrap = aws_client_bootstrap_acquire(bootstrap); - client_connection_args->creation_callback = options->creation_callback; - client_connection_args->setup_callback = options->setup_callback; - client_connection_args->shutdown_callback = options->shutdown_callback; - client_connection_args->outgoing_options = *socket_options; - client_connection_args->outgoing_port = port; - client_connection_args->enable_read_back_pressure = options->enable_read_back_pressure; - - if (tls_options) { - if (aws_tls_connection_options_copy(&client_connection_args->channel_data.tls_options, tls_options)) { - goto error; - } - client_connection_args->channel_data.use_tls = true; - - client_connection_args->channel_data.on_protocol_negotiated = bootstrap->on_protocol_negotiated; - client_connection_args->channel_data.tls_user_data = tls_options->user_data; - - /* in order to honor any callbacks a user may have installed on their tls_connection_options, - * we need to wrap them if they were set.*/ - if (bootstrap->on_protocol_negotiated) { - client_connection_args->channel_data.tls_options.advertise_alpn_message = true; - } - - if (tls_options->on_data_read) { - client_connection_args->channel_data.user_on_data_read = tls_options->on_data_read; - client_connection_args->channel_data.tls_options.on_data_read = s_tls_client_on_data_read; - } - - if (tls_options->on_error) { - client_connection_args->channel_data.user_on_error = tls_options->on_error; - client_connection_args->channel_data.tls_options.on_error = s_tls_client_on_error; - } - - if (tls_options->on_negotiation_result) { - client_connection_args->channel_data.user_on_negotiation_result = tls_options->on_negotiation_result; - } - - client_connection_args->channel_data.tls_options.on_negotiation_result = s_tls_client_on_negotiation_result; - client_connection_args->channel_data.tls_options.user_data = client_connection_args; - } - - if (s_aws_socket_domain_uses_dns(socket_options->domain)) { - client_connection_args->host_name = aws_string_new_from_c_str(bootstrap->allocator, host_name); - - if (!client_connection_args->host_name) { - goto error; - } - - if (aws_host_resolver_resolve_host( - bootstrap->host_resolver, - client_connection_args->host_name, - s_on_host_resolved, - &bootstrap->host_resolver_config, - client_connection_args)) { - goto error; - } - } else { - /* ensure that the pipe/domain socket name will fit in the endpoint address */ - const size_t host_name_len = strlen(host_name); - if (host_name_len >= AWS_ADDRESS_MAX_LEN) { - aws_raise_error(AWS_IO_SOCKET_INVALID_ADDRESS); - goto error; - } - - struct aws_socket_endpoint endpoint; - AWS_ZERO_STRUCT(endpoint); - memcpy(endpoint.address, host_name, host_name_len); - if (socket_options->domain == AWS_SOCKET_VSOCK) { - endpoint.port = port; - } else { - endpoint.port = 0; - } - - struct aws_socket *outgoing_socket = aws_mem_acquire(bootstrap->allocator, sizeof(struct aws_socket)); - - if (!outgoing_socket) { - goto error; - } - - if (aws_socket_init(outgoing_socket, bootstrap->allocator, socket_options)) { - aws_mem_release(bootstrap->allocator, outgoing_socket); - goto error; - } - - client_connection_args->addresses_count = 1; - - struct aws_event_loop *connect_loop = aws_event_loop_group_get_next_loop(bootstrap->event_loop_group); - - s_client_connection_args_acquire(client_connection_args); - if (aws_socket_connect( - outgoing_socket, &endpoint, connect_loop, s_on_client_connection_established, client_connection_args)) { - aws_socket_clean_up(outgoing_socket); - aws_mem_release(client_connection_args->bootstrap->allocator, outgoing_socket); - s_client_connection_args_release(client_connection_args); - goto error; - } - } - - return AWS_OP_SUCCESS; - -error: - if (client_connection_args) { - /* tls opt will also be freed when we clean up the connection arg */ - s_client_connection_args_release(client_connection_args); - } - return AWS_OP_ERR; -} - -void s_server_bootstrap_destroy_impl(struct aws_server_bootstrap *bootstrap) { - AWS_ASSERT(bootstrap); - aws_event_loop_group_release(bootstrap->event_loop_group); - aws_mem_release(bootstrap->allocator, bootstrap); -} - -struct aws_server_bootstrap *aws_server_bootstrap_acquire(struct aws_server_bootstrap *bootstrap) { - if (bootstrap != NULL) { - aws_ref_count_acquire(&bootstrap->ref_count); - } - - return bootstrap; -} - -void aws_server_bootstrap_release(struct aws_server_bootstrap *bootstrap) { - /* if destroy is being called, the user intends to not use the bootstrap anymore - * so we clean up the thread local state while the event loop thread is - * still alive */ - AWS_LOGF_DEBUG(AWS_LS_IO_CHANNEL_BOOTSTRAP, "id=%p: releasing server bootstrap reference", (void *)bootstrap); - if (bootstrap != NULL) { - aws_ref_count_release(&bootstrap->ref_count); - } -} - -struct aws_server_bootstrap *aws_server_bootstrap_new( - struct aws_allocator *allocator, - struct aws_event_loop_group *el_group) { - AWS_ASSERT(allocator); - AWS_ASSERT(el_group); - - struct aws_server_bootstrap *bootstrap = aws_mem_calloc(allocator, 1, sizeof(struct aws_server_bootstrap)); - if (!bootstrap) { - return NULL; - } - - AWS_LOGF_INFO( - AWS_LS_IO_CHANNEL_BOOTSTRAP, - "id=%p: Initializing server bootstrap with event-loop group %p", - (void *)bootstrap, - (void *)el_group); - - bootstrap->allocator = allocator; - bootstrap->event_loop_group = aws_event_loop_group_acquire(el_group); - bootstrap->on_protocol_negotiated = NULL; - aws_ref_count_init( - &bootstrap->ref_count, bootstrap, (aws_simple_completion_callback *)s_server_bootstrap_destroy_impl); - - return bootstrap; -} - -struct server_connection_args { - struct aws_server_bootstrap *bootstrap; - struct aws_socket listener; - aws_server_bootstrap_on_accept_channel_setup_fn *incoming_callback; - aws_server_bootstrap_on_accept_channel_shutdown_fn *shutdown_callback; - aws_server_bootstrap_on_server_listener_destroy_fn *destroy_callback; - struct aws_tls_connection_options tls_options; - aws_channel_on_protocol_negotiated_fn *on_protocol_negotiated; - aws_tls_on_data_read_fn *user_on_data_read; - aws_tls_on_negotiation_result_fn *user_on_negotiation_result; - aws_tls_on_error_fn *user_on_error; - struct aws_task listener_destroy_task; - void *tls_user_data; - void *user_data; - bool use_tls; - bool enable_read_back_pressure; - struct aws_ref_count ref_count; -}; - -struct server_channel_data { - struct aws_channel *channel; - struct aws_socket *socket; - struct server_connection_args *server_connection_args; - bool incoming_called; -}; - -static struct server_connection_args *s_server_connection_args_acquire(struct server_connection_args *args) { - if (args != NULL) { - aws_ref_count_acquire(&args->ref_count); - } - - return args; -} - -static void s_server_connection_args_destroy(struct server_connection_args *args) { - if (args == NULL) { - return; - } - - /* fire the destroy callback */ - if (args->destroy_callback) { - args->destroy_callback(args->bootstrap, args->user_data); - } - - struct aws_allocator *allocator = args->bootstrap->allocator; - aws_server_bootstrap_release(args->bootstrap); - if (args->use_tls) { - aws_tls_connection_options_clean_up(&args->tls_options); - } - - aws_mem_release(allocator, args); -} - -static void s_server_connection_args_release(struct server_connection_args *args) { - if (args != NULL) { - aws_ref_count_release(&args->ref_count); - } -} - -static void s_server_incoming_callback( - struct server_channel_data *channel_data, - int error_code, - struct aws_channel *channel) { - /* incoming_callback is always called exactly once for each channel */ - AWS_ASSERT(!channel_data->incoming_called); - struct server_connection_args *args = channel_data->server_connection_args; - args->incoming_callback(args->bootstrap, error_code, channel, args->user_data); - channel_data->incoming_called = true; -} - -static void s_tls_server_on_negotiation_result( - struct aws_channel_handler *handler, - struct aws_channel_slot *slot, - int err_code, - void *user_data) { - struct server_channel_data *channel_data = user_data; - struct server_connection_args *connection_args = channel_data->server_connection_args; - - if (connection_args->user_on_negotiation_result) { - connection_args->user_on_negotiation_result(handler, slot, err_code, connection_args->tls_user_data); - } - - AWS_LOGF_DEBUG( - AWS_LS_IO_CHANNEL_BOOTSTRAP, - "id=%p: tls negotiation result %d on channel %p", - (void *)connection_args->bootstrap, - err_code, - (void *)slot->channel); - - struct aws_channel *channel = slot->channel; - if (err_code) { - /* shut down the channel */ - aws_channel_shutdown(channel, err_code); - } else { - s_server_incoming_callback(channel_data, err_code, channel); - } -} - -/* in the context of a channel bootstrap, we don't care about these, but since we're hooking into these APIs we have to - * provide a proxy for the user actually receiving their callbacks. */ -static void s_tls_server_on_data_read( - struct aws_channel_handler *handler, - struct aws_channel_slot *slot, - struct aws_byte_buf *buffer, - void *user_data) { - struct server_connection_args *connection_args = user_data; - - if (connection_args->user_on_data_read) { - connection_args->user_on_data_read(handler, slot, buffer, connection_args->tls_user_data); - } -} - -/* in the context of a channel bootstrap, we don't care about these, but since we're hooking into these APIs we have to - * provide a proxy for the user actually receiving their callbacks. */ -static void s_tls_server_on_error( - struct aws_channel_handler *handler, - struct aws_channel_slot *slot, - int err, - const char *message, - void *user_data) { - struct server_connection_args *connection_args = user_data; - - if (connection_args->user_on_error) { - connection_args->user_on_error(handler, slot, err, message, connection_args->tls_user_data); - } -} - -static inline int s_setup_server_tls(struct server_channel_data *channel_data, struct aws_channel *channel) { - struct aws_channel_slot *tls_slot = NULL; - struct aws_channel_handler *tls_handler = NULL; - struct server_connection_args *connection_args = channel_data->server_connection_args; - - /* as far as cleanup goes here, since we're adding things to a channel, if a slot is ever successfully - added to the channel, we leave it there. The caller will clean up the channel and it will clean this memory - up as well. */ - tls_slot = aws_channel_slot_new(channel); - - if (!tls_slot) { - return AWS_OP_ERR; - } - - /* Shallow-copy tls_options so we can override the user_data, making it specific to this channel */ - struct aws_tls_connection_options tls_options = connection_args->tls_options; - tls_options.user_data = channel_data; - tls_handler = aws_tls_server_handler_new(connection_args->bootstrap->allocator, &tls_options, tls_slot); - - if (!tls_handler) { - aws_mem_release(connection_args->bootstrap->allocator, tls_slot); - return AWS_OP_ERR; - } - - AWS_LOGF_TRACE( - AWS_LS_IO_CHANNEL_BOOTSTRAP, - "id=%p: Setting up server TLS on channel %p with handler %p on slot %p", - (void *)connection_args->bootstrap, - (void *)channel, - (void *)tls_handler, - (void *)tls_slot); - - aws_channel_slot_insert_end(channel, tls_slot); - - if (aws_channel_slot_set_handler(tls_slot, tls_handler)) { - return AWS_OP_ERR; - } - - if (connection_args->on_protocol_negotiated) { - struct aws_channel_slot *alpn_slot = NULL; - struct aws_channel_handler *alpn_handler = NULL; - alpn_slot = aws_channel_slot_new(channel); - - if (!alpn_slot) { - return AWS_OP_ERR; - } - - alpn_handler = aws_tls_alpn_handler_new( - connection_args->bootstrap->allocator, connection_args->on_protocol_negotiated, connection_args->user_data); - - if (!alpn_handler) { - aws_channel_slot_remove(alpn_slot); - return AWS_OP_ERR; - } - - AWS_LOGF_TRACE( - AWS_LS_IO_CHANNEL_BOOTSTRAP, - "id=%p: Setting up ALPN handler on channel " - "%p with handler %p on slot %p", - (void *)connection_args->bootstrap, - (void *)channel, - (void *)alpn_handler, - (void *)alpn_slot); - - aws_channel_slot_insert_right(tls_slot, alpn_slot); - - if (aws_channel_slot_set_handler(alpn_slot, alpn_handler)) { - return AWS_OP_ERR; - } - } - - return AWS_OP_SUCCESS; -} - -static void s_on_server_channel_on_setup_completed(struct aws_channel *channel, int error_code, void *user_data) { - struct server_channel_data *channel_data = user_data; - - int err_code = error_code; - if (err_code) { - /* channel fail to set up no destroy callback will fire */ - AWS_LOGF_ERROR( - AWS_LS_IO_CHANNEL_BOOTSTRAP, - "id=%p: channel %p setup failed with error %d.", - (void *)channel_data->server_connection_args->bootstrap, - (void *)channel, - err_code); - - aws_channel_destroy(channel); - struct aws_allocator *allocator = channel_data->socket->allocator; - aws_socket_clean_up(channel_data->socket); - aws_mem_release(allocator, (void *)channel_data->socket); - s_server_incoming_callback(channel_data, err_code, NULL); - aws_mem_release(channel_data->server_connection_args->bootstrap->allocator, channel_data); - /* no shutdown call back will be fired, we release the ref_count of connection arg here */ - s_server_connection_args_release(channel_data->server_connection_args); - return; - } - - AWS_LOGF_DEBUG( - AWS_LS_IO_CHANNEL_BOOTSTRAP, - "id=%p: channel %p setup succeeded: bootstrapping.", - (void *)channel_data->server_connection_args->bootstrap, - (void *)channel); - - struct aws_channel_slot *socket_slot = aws_channel_slot_new(channel); - - if (!socket_slot) { - err_code = aws_last_error(); - goto error; - } - - struct aws_channel_handler *socket_channel_handler = aws_socket_handler_new( - channel_data->server_connection_args->bootstrap->allocator, - channel_data->socket, - socket_slot, - g_aws_channel_max_fragment_size); - - if (!socket_channel_handler) { - err_code = aws_last_error(); - aws_channel_slot_remove(socket_slot); - socket_slot = NULL; - goto error; - } - - AWS_LOGF_TRACE( - AWS_LS_IO_CHANNEL_BOOTSTRAP, - "id=%p: Setting up socket handler on channel " - "%p with handler %p on slot %p.", - (void *)channel_data->server_connection_args->bootstrap, - (void *)channel, - (void *)socket_channel_handler, - (void *)socket_slot); - - if (aws_channel_slot_set_handler(socket_slot, socket_channel_handler)) { - err_code = aws_last_error(); - goto error; - } - - if (channel_data->server_connection_args->use_tls) { - /* incoming callback will be invoked upon the negotiation completion so don't do it - * here. */ - if (s_setup_server_tls(channel_data, channel)) { - err_code = aws_last_error(); - goto error; - } - } else { - s_server_incoming_callback(channel_data, AWS_OP_SUCCESS, channel); - } - return; - -error: - /* shut down the channel */ - aws_channel_shutdown(channel, err_code); -} - -static void s_on_server_channel_on_shutdown(struct aws_channel *channel, int error_code, void *user_data) { - struct server_channel_data *channel_data = user_data; - struct server_connection_args *args = channel_data->server_connection_args; - AWS_LOGF_DEBUG( - AWS_LS_IO_CHANNEL_BOOTSTRAP, - "id=%p: channel %p shutdown with error %d.", - (void *)args->bootstrap, - (void *)channel, - error_code); - - void *server_shutdown_user_data = args->user_data; - struct aws_server_bootstrap *server_bootstrap = args->bootstrap; - struct aws_allocator *allocator = server_bootstrap->allocator; - - if (!channel_data->incoming_called) { - error_code = (error_code) ? error_code : AWS_ERROR_UNKNOWN; - s_server_incoming_callback(channel_data, error_code, NULL); - } else { - args->shutdown_callback(server_bootstrap, error_code, channel, server_shutdown_user_data); - } - - aws_channel_destroy(channel); - aws_socket_clean_up(channel_data->socket); - aws_mem_release(allocator, channel_data->socket); - s_server_connection_args_release(channel_data->server_connection_args); - - aws_mem_release(allocator, channel_data); -} - -void s_on_server_connection_result( - struct aws_socket *socket, - int error_code, - struct aws_socket *new_socket, - void *user_data) { - (void)socket; - struct server_connection_args *connection_args = user_data; - - s_server_connection_args_acquire(connection_args); - AWS_LOGF_DEBUG( - AWS_LS_IO_CHANNEL_BOOTSTRAP, - "id=%p: server connection on socket %p completed with error %d.", - (void *)connection_args->bootstrap, - (void *)socket, - error_code); - - if (!error_code) { - AWS_LOGF_TRACE( - AWS_LS_IO_CHANNEL_BOOTSTRAP, - "id=%p: creating a new channel for incoming " - "connection using socket %p.", - (void *)connection_args->bootstrap, - (void *)socket); - struct server_channel_data *channel_data = - aws_mem_calloc(connection_args->bootstrap->allocator, 1, sizeof(struct server_channel_data)); - if (!channel_data) { - goto error_cleanup; - } - channel_data->incoming_called = false; - channel_data->socket = new_socket; - channel_data->server_connection_args = connection_args; - - struct aws_event_loop *event_loop = - aws_event_loop_group_get_next_loop(connection_args->bootstrap->event_loop_group); - - struct aws_channel_options channel_args = { - .on_setup_completed = s_on_server_channel_on_setup_completed, - .setup_user_data = channel_data, - .shutdown_user_data = channel_data, - .on_shutdown_completed = s_on_server_channel_on_shutdown, - }; - - channel_args.event_loop = event_loop; - channel_args.enable_read_back_pressure = channel_data->server_connection_args->enable_read_back_pressure; - - if (aws_socket_assign_to_event_loop(new_socket, event_loop)) { - aws_mem_release(connection_args->bootstrap->allocator, (void *)channel_data); - goto error_cleanup; - } - - channel_data->channel = aws_channel_new(connection_args->bootstrap->allocator, &channel_args); - - if (!channel_data->channel) { - aws_mem_release(connection_args->bootstrap->allocator, (void *)channel_data); - goto error_cleanup; - } - } else { - /* no channel is created */ - connection_args->incoming_callback(connection_args->bootstrap, error_code, NULL, connection_args->user_data); - s_server_connection_args_release(connection_args); - } - - return; - -error_cleanup: - /* no channel is created */ - connection_args->incoming_callback(connection_args->bootstrap, aws_last_error(), NULL, connection_args->user_data); - - struct aws_allocator *allocator = new_socket->allocator; - aws_socket_clean_up(new_socket); - aws_mem_release(allocator, (void *)new_socket); - s_server_connection_args_release(connection_args); -} - -static void s_listener_destroy_task(struct aws_task *task, void *arg, enum aws_task_status status) { - (void)status; - (void)task; - struct server_connection_args *server_connection_args = arg; - - aws_socket_stop_accept(&server_connection_args->listener); - aws_socket_clean_up(&server_connection_args->listener); - s_server_connection_args_release(server_connection_args); -} - -struct aws_socket *aws_server_bootstrap_new_socket_listener( - const struct aws_server_socket_channel_bootstrap_options *bootstrap_options) { - AWS_PRECONDITION(bootstrap_options); - AWS_PRECONDITION(bootstrap_options->bootstrap); - AWS_PRECONDITION(bootstrap_options->incoming_callback) - AWS_PRECONDITION(bootstrap_options->shutdown_callback) - - struct server_connection_args *server_connection_args = - aws_mem_calloc(bootstrap_options->bootstrap->allocator, 1, sizeof(struct server_connection_args)); - if (!server_connection_args) { - return NULL; - } - - AWS_LOGF_INFO( - AWS_LS_IO_CHANNEL_BOOTSTRAP, - "id=%p: attempting to initialize a new " - "server socket listener for %s:%d", - (void *)server_connection_args->bootstrap, - bootstrap_options->host_name, - (int)bootstrap_options->port); - - aws_ref_count_init( - &server_connection_args->ref_count, - server_connection_args, - (aws_simple_completion_callback *)s_server_connection_args_destroy); - server_connection_args->user_data = bootstrap_options->user_data; - server_connection_args->bootstrap = aws_server_bootstrap_acquire(bootstrap_options->bootstrap); - server_connection_args->shutdown_callback = bootstrap_options->shutdown_callback; - server_connection_args->incoming_callback = bootstrap_options->incoming_callback; - server_connection_args->destroy_callback = bootstrap_options->destroy_callback; - server_connection_args->on_protocol_negotiated = bootstrap_options->bootstrap->on_protocol_negotiated; - server_connection_args->enable_read_back_pressure = bootstrap_options->enable_read_back_pressure; - - aws_task_init( - &server_connection_args->listener_destroy_task, - s_listener_destroy_task, - server_connection_args, - "listener socket destroy"); - - if (bootstrap_options->tls_options) { - AWS_LOGF_INFO( - AWS_LS_IO_CHANNEL_BOOTSTRAP, "id=%p: using tls on listener", (void *)bootstrap_options->tls_options); - if (aws_tls_connection_options_copy(&server_connection_args->tls_options, bootstrap_options->tls_options)) { - goto cleanup_server_connection_args; - } - - server_connection_args->use_tls = true; - - server_connection_args->tls_user_data = bootstrap_options->tls_options->user_data; - - /* in order to honor any callbacks a user may have installed on their tls_connection_options, - * we need to wrap them if they were set.*/ - if (bootstrap_options->bootstrap->on_protocol_negotiated) { - server_connection_args->tls_options.advertise_alpn_message = true; - } - - if (bootstrap_options->tls_options->on_data_read) { - server_connection_args->user_on_data_read = bootstrap_options->tls_options->on_data_read; - server_connection_args->tls_options.on_data_read = s_tls_server_on_data_read; - } - - if (bootstrap_options->tls_options->on_error) { - server_connection_args->user_on_error = bootstrap_options->tls_options->on_error; - server_connection_args->tls_options.on_error = s_tls_server_on_error; - } - - if (bootstrap_options->tls_options->on_negotiation_result) { - server_connection_args->user_on_negotiation_result = bootstrap_options->tls_options->on_negotiation_result; - } - - server_connection_args->tls_options.on_negotiation_result = s_tls_server_on_negotiation_result; - server_connection_args->tls_options.user_data = server_connection_args; - } - - struct aws_event_loop *connection_loop = - aws_event_loop_group_get_next_loop(bootstrap_options->bootstrap->event_loop_group); - - if (aws_socket_init( - &server_connection_args->listener, - bootstrap_options->bootstrap->allocator, - bootstrap_options->socket_options)) { - goto cleanup_server_connection_args; - } - - struct aws_socket_endpoint endpoint; - AWS_ZERO_STRUCT(endpoint); - size_t host_name_len = 0; - if (aws_secure_strlen(bootstrap_options->host_name, sizeof(endpoint.address), &host_name_len)) { - goto cleanup_server_connection_args; - } - - memcpy(endpoint.address, bootstrap_options->host_name, host_name_len); - endpoint.port = bootstrap_options->port; - - if (aws_socket_bind(&server_connection_args->listener, &endpoint)) { - goto cleanup_listener; - } - - if (aws_socket_listen(&server_connection_args->listener, 1024)) { - goto cleanup_listener; - } - - if (aws_socket_start_accept( - &server_connection_args->listener, - connection_loop, - s_on_server_connection_result, - server_connection_args)) { - goto cleanup_listener; - } - - return &server_connection_args->listener; - -cleanup_listener: - aws_socket_clean_up(&server_connection_args->listener); - -cleanup_server_connection_args: - s_server_connection_args_release(server_connection_args); - - return NULL; -} - -void aws_server_bootstrap_destroy_socket_listener(struct aws_server_bootstrap *bootstrap, struct aws_socket *listener) { - struct server_connection_args *server_connection_args = - AWS_CONTAINER_OF(listener, struct server_connection_args, listener); - - AWS_LOGF_DEBUG(AWS_LS_IO_CHANNEL_BOOTSTRAP, "id=%p: releasing bootstrap reference", (void *)bootstrap); - aws_event_loop_schedule_task_now(listener->event_loop, &server_connection_args->listener_destroy_task); -} - -int aws_server_bootstrap_set_alpn_callback( - struct aws_server_bootstrap *bootstrap, - aws_channel_on_protocol_negotiated_fn *on_protocol_negotiated) { - AWS_ASSERT(on_protocol_negotiated); - AWS_LOGF_DEBUG(AWS_LS_IO_CHANNEL_BOOTSTRAP, "id=%p: Setting ALPN callback", (void *)bootstrap); - bootstrap->on_protocol_negotiated = on_protocol_negotiated; - return AWS_OP_SUCCESS; -} +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#include <aws/io/channel_bootstrap.h> + +#include <aws/common/ref_count.h> +#include <aws/common/string.h> +#include <aws/io/event_loop.h> +#include <aws/io/logging.h> +#include <aws/io/socket.h> +#include <aws/io/socket_channel_handler.h> +#include <aws/io/tls_channel_handler.h> + +#if _MSC_VER +/* non-constant aggregate initializer */ +# pragma warning(disable : 4204) +/* allow automatic variable to escape scope + (it's intentional and we make sure it doesn't actually return + before the task is finished).*/ +# pragma warning(disable : 4221) +#endif + +#define DEFAULT_DNS_TTL 30 + +static void s_client_bootstrap_destroy_impl(struct aws_client_bootstrap *bootstrap) { + AWS_ASSERT(bootstrap); + AWS_LOGF_DEBUG(AWS_LS_IO_CHANNEL_BOOTSTRAP, "id=%p: destroying", (void *)bootstrap); + aws_client_bootstrap_shutdown_complete_fn *on_shutdown_complete = bootstrap->on_shutdown_complete; + void *user_data = bootstrap->user_data; + + aws_event_loop_group_release(bootstrap->event_loop_group); + aws_host_resolver_release(bootstrap->host_resolver); + + aws_mem_release(bootstrap->allocator, bootstrap); + + if (on_shutdown_complete) { + on_shutdown_complete(user_data); + } +} + +struct aws_client_bootstrap *aws_client_bootstrap_acquire(struct aws_client_bootstrap *bootstrap) { + if (bootstrap != NULL) { + aws_ref_count_acquire(&bootstrap->ref_count); + } + + return bootstrap; +} + +void aws_client_bootstrap_release(struct aws_client_bootstrap *bootstrap) { + AWS_LOGF_DEBUG(AWS_LS_IO_CHANNEL_BOOTSTRAP, "id=%p: releasing bootstrap reference", (void *)bootstrap); + if (bootstrap != NULL) { + aws_ref_count_release(&bootstrap->ref_count); + } +} + +struct aws_client_bootstrap *aws_client_bootstrap_new( + struct aws_allocator *allocator, + const struct aws_client_bootstrap_options *options) { + AWS_ASSERT(allocator); + AWS_ASSERT(options); + AWS_ASSERT(options->event_loop_group); + + struct aws_client_bootstrap *bootstrap = aws_mem_calloc(allocator, 1, sizeof(struct aws_client_bootstrap)); + if (!bootstrap) { + return NULL; + } + + AWS_LOGF_INFO( + AWS_LS_IO_CHANNEL_BOOTSTRAP, + "id=%p: Initializing client bootstrap with event-loop group %p", + (void *)bootstrap, + (void *)options->event_loop_group); + + bootstrap->allocator = allocator; + bootstrap->event_loop_group = aws_event_loop_group_acquire(options->event_loop_group); + bootstrap->on_protocol_negotiated = NULL; + aws_ref_count_init( + &bootstrap->ref_count, bootstrap, (aws_simple_completion_callback *)s_client_bootstrap_destroy_impl); + bootstrap->host_resolver = aws_host_resolver_acquire(options->host_resolver); + bootstrap->on_shutdown_complete = options->on_shutdown_complete; + bootstrap->user_data = options->user_data; + + if (options->host_resolution_config) { + bootstrap->host_resolver_config = *options->host_resolution_config; + } else { + bootstrap->host_resolver_config = (struct aws_host_resolution_config){ + .impl = aws_default_dns_resolve, + .max_ttl = DEFAULT_DNS_TTL, + .impl_data = NULL, + }; + } + + return bootstrap; +} + +int aws_client_bootstrap_set_alpn_callback( + struct aws_client_bootstrap *bootstrap, + aws_channel_on_protocol_negotiated_fn *on_protocol_negotiated) { + AWS_ASSERT(on_protocol_negotiated); + + AWS_LOGF_DEBUG(AWS_LS_IO_CHANNEL_BOOTSTRAP, "id=%p: Setting ALPN callback", (void *)bootstrap); + bootstrap->on_protocol_negotiated = on_protocol_negotiated; + return AWS_OP_SUCCESS; +} + +struct client_channel_data { + struct aws_channel *channel; + struct aws_socket *socket; + struct aws_tls_connection_options tls_options; + aws_channel_on_protocol_negotiated_fn *on_protocol_negotiated; + aws_tls_on_data_read_fn *user_on_data_read; + aws_tls_on_negotiation_result_fn *user_on_negotiation_result; + aws_tls_on_error_fn *user_on_error; + void *tls_user_data; + bool use_tls; +}; + +struct client_connection_args { + struct aws_client_bootstrap *bootstrap; + aws_client_bootstrap_on_channel_event_fn *creation_callback; + aws_client_bootstrap_on_channel_event_fn *setup_callback; + aws_client_bootstrap_on_channel_event_fn *shutdown_callback; + struct client_channel_data channel_data; + struct aws_socket_options outgoing_options; + uint16_t outgoing_port; + struct aws_string *host_name; + void *user_data; + uint8_t addresses_count; + uint8_t failed_count; + bool connection_chosen; + bool setup_called; + bool enable_read_back_pressure; + + /* + * It is likely that all reference adjustments to the connection args take place in a single event loop + * thread and are thus thread-safe. I can imagine some complex future scenarios where that might not hold true + * and so it seems reasonable to switch now to a safe pattern. + * + */ + struct aws_ref_count ref_count; +}; + +static struct client_connection_args *s_client_connection_args_acquire(struct client_connection_args *args) { + if (args != NULL) { + aws_ref_count_acquire(&args->ref_count); + } + + return args; +} + +static void s_client_connection_args_destroy(struct client_connection_args *args) { + AWS_ASSERT(args); + + struct aws_allocator *allocator = args->bootstrap->allocator; + aws_client_bootstrap_release(args->bootstrap); + if (args->host_name) { + aws_string_destroy(args->host_name); + } + + if (args->channel_data.use_tls) { + aws_tls_connection_options_clean_up(&args->channel_data.tls_options); + } + + aws_mem_release(allocator, args); +} + +static void s_client_connection_args_release(struct client_connection_args *args) { + if (args != NULL) { + aws_ref_count_release(&args->ref_count); + } +} + +static void s_connection_args_setup_callback( + struct client_connection_args *args, + int error_code, + struct aws_channel *channel) { + /* setup_callback is always called exactly once */ + AWS_ASSERT(!args->setup_called); + if (!args->setup_called) { + AWS_ASSERT((error_code == AWS_OP_SUCCESS) == (channel != NULL)); + aws_client_bootstrap_on_channel_event_fn *setup_callback = args->setup_callback; + setup_callback(args->bootstrap, error_code, channel, args->user_data); + args->setup_called = true; + /* if setup_callback is called with an error, we will not call shutdown_callback */ + if (error_code) { + args->shutdown_callback = NULL; + } + s_client_connection_args_release(args); + } +} + +static void s_connection_args_creation_callback(struct client_connection_args *args, struct aws_channel *channel) { + + AWS_FATAL_ASSERT(channel != NULL); + + if (args->creation_callback) { + args->creation_callback(args->bootstrap, AWS_ERROR_SUCCESS, channel, args->user_data); + } +} + +static void s_connection_args_shutdown_callback( + struct client_connection_args *args, + int error_code, + struct aws_channel *channel) { + + if (!args->setup_called) { + /* if setup_callback was not called yet, an error occurred, ensure we tell the user *SOMETHING* */ + error_code = (error_code) ? error_code : AWS_ERROR_UNKNOWN; + s_connection_args_setup_callback(args, error_code, NULL); + return; + } + + aws_client_bootstrap_on_channel_event_fn *shutdown_callback = args->shutdown_callback; + if (shutdown_callback) { + shutdown_callback(args->bootstrap, error_code, channel, args->user_data); + } +} + +static void s_tls_client_on_negotiation_result( + struct aws_channel_handler *handler, + struct aws_channel_slot *slot, + int err_code, + void *user_data) { + struct client_connection_args *connection_args = user_data; + + if (connection_args->channel_data.user_on_negotiation_result) { + connection_args->channel_data.user_on_negotiation_result( + handler, slot, err_code, connection_args->channel_data.tls_user_data); + } + + AWS_LOGF_DEBUG( + AWS_LS_IO_CHANNEL_BOOTSTRAP, + "id=%p: tls negotiation result %d on channel %p", + (void *)connection_args->bootstrap, + err_code, + (void *)slot->channel); + + /* if an error occurred, the user callback will be delivered in shutdown */ + if (err_code) { + aws_channel_shutdown(slot->channel, err_code); + return; + } + + struct aws_channel *channel = connection_args->channel_data.channel; + s_connection_args_setup_callback(connection_args, AWS_ERROR_SUCCESS, channel); +} + +/* in the context of a channel bootstrap, we don't care about these, but since we're hooking into these APIs we have to + * provide a proxy for the user actually receiving their callbacks. */ +static void s_tls_client_on_data_read( + struct aws_channel_handler *handler, + struct aws_channel_slot *slot, + struct aws_byte_buf *buffer, + void *user_data) { + struct client_connection_args *connection_args = user_data; + + if (connection_args->channel_data.user_on_data_read) { + connection_args->channel_data.user_on_data_read( + handler, slot, buffer, connection_args->channel_data.tls_user_data); + } +} + +/* in the context of a channel bootstrap, we don't care about these, but since we're hooking into these APIs we have to + * provide a proxy for the user actually receiving their callbacks. */ +static void s_tls_client_on_error( + struct aws_channel_handler *handler, + struct aws_channel_slot *slot, + int err, + const char *message, + void *user_data) { + struct client_connection_args *connection_args = user_data; + + if (connection_args->channel_data.user_on_error) { + connection_args->channel_data.user_on_error( + handler, slot, err, message, connection_args->channel_data.tls_user_data); + } +} + +static inline int s_setup_client_tls(struct client_connection_args *connection_args, struct aws_channel *channel) { + struct aws_channel_slot *tls_slot = aws_channel_slot_new(channel); + + /* as far as cleanup goes, since this stuff is being added to a channel, the caller will free this memory + when they clean up the channel. */ + if (!tls_slot) { + return AWS_OP_ERR; + } + + struct aws_channel_handler *tls_handler = aws_tls_client_handler_new( + connection_args->bootstrap->allocator, &connection_args->channel_data.tls_options, tls_slot); + + if (!tls_handler) { + aws_mem_release(connection_args->bootstrap->allocator, (void *)tls_slot); + return AWS_OP_ERR; + } + + aws_channel_slot_insert_end(channel, tls_slot); + AWS_LOGF_TRACE( + AWS_LS_IO_CHANNEL_BOOTSTRAP, + "id=%p: Setting up client TLS on channel %p with handler %p on slot %p", + (void *)connection_args->bootstrap, + (void *)channel, + (void *)tls_handler, + (void *)tls_slot); + + if (aws_channel_slot_set_handler(tls_slot, tls_handler) != AWS_OP_SUCCESS) { + return AWS_OP_ERR; + } + + if (connection_args->channel_data.on_protocol_negotiated) { + struct aws_channel_slot *alpn_slot = aws_channel_slot_new(channel); + + if (!alpn_slot) { + return AWS_OP_ERR; + } + + struct aws_channel_handler *alpn_handler = aws_tls_alpn_handler_new( + connection_args->bootstrap->allocator, + connection_args->channel_data.on_protocol_negotiated, + connection_args->user_data); + + if (!alpn_handler) { + aws_mem_release(connection_args->bootstrap->allocator, (void *)alpn_slot); + return AWS_OP_ERR; + } + + AWS_LOGF_TRACE( + AWS_LS_IO_CHANNEL_BOOTSTRAP, + "id=%p: Setting up ALPN handler on channel " + "%p with handler %p on slot %p", + (void *)connection_args->bootstrap, + (void *)channel, + (void *)alpn_handler, + (void *)alpn_slot); + + aws_channel_slot_insert_right(tls_slot, alpn_slot); + if (aws_channel_slot_set_handler(alpn_slot, alpn_handler) != AWS_OP_SUCCESS) { + return AWS_OP_ERR; + } + } + + if (aws_tls_client_handler_start_negotiation(tls_handler) != AWS_OP_SUCCESS) { + return AWS_OP_ERR; + } + + return AWS_OP_SUCCESS; +} + +static void s_on_client_channel_on_setup_completed(struct aws_channel *channel, int error_code, void *user_data) { + struct client_connection_args *connection_args = user_data; + int err_code = error_code; + + if (!err_code) { + AWS_LOGF_DEBUG( + AWS_LS_IO_CHANNEL_BOOTSTRAP, + "id=%p: channel %p setup succeeded: bootstrapping.", + (void *)connection_args->bootstrap, + (void *)channel); + + struct aws_channel_slot *socket_slot = aws_channel_slot_new(channel); + + if (!socket_slot) { + err_code = aws_last_error(); + goto error; + } + + struct aws_channel_handler *socket_channel_handler = aws_socket_handler_new( + connection_args->bootstrap->allocator, + connection_args->channel_data.socket, + socket_slot, + g_aws_channel_max_fragment_size); + + if (!socket_channel_handler) { + err_code = aws_last_error(); + aws_channel_slot_remove(socket_slot); + socket_slot = NULL; + goto error; + } + + AWS_LOGF_TRACE( + AWS_LS_IO_CHANNEL_BOOTSTRAP, + "id=%p: Setting up socket handler on channel " + "%p with handler %p on slot %p.", + (void *)connection_args->bootstrap, + (void *)channel, + (void *)socket_channel_handler, + (void *)socket_slot); + + if (aws_channel_slot_set_handler(socket_slot, socket_channel_handler)) { + err_code = aws_last_error(); + goto error; + } + + if (connection_args->channel_data.use_tls) { + /* we don't want to notify the user that the channel is ready yet, since tls is still negotiating, wait + * for the negotiation callback and handle it then.*/ + if (s_setup_client_tls(connection_args, channel)) { + err_code = aws_last_error(); + goto error; + } + } else { + s_connection_args_setup_callback(connection_args, AWS_OP_SUCCESS, channel); + } + + return; + } + +error: + AWS_LOGF_ERROR( + AWS_LS_IO_CHANNEL_BOOTSTRAP, + "id=%p: channel %p setup failed with error %d.", + (void *)connection_args->bootstrap, + (void *)channel, + err_code); + aws_channel_shutdown(channel, err_code); + /* the channel shutdown callback will clean the channel up */ +} + +static void s_on_client_channel_on_shutdown(struct aws_channel *channel, int error_code, void *user_data) { + struct client_connection_args *connection_args = user_data; + + AWS_LOGF_DEBUG( + AWS_LS_IO_CHANNEL_BOOTSTRAP, + "id=%p: channel %p shutdown with error %d.", + (void *)connection_args->bootstrap, + (void *)channel, + error_code); + + /* note it's not safe to reference the bootstrap after the callback. */ + struct aws_allocator *allocator = connection_args->bootstrap->allocator; + s_connection_args_shutdown_callback(connection_args, error_code, channel); + + aws_channel_destroy(channel); + aws_socket_clean_up(connection_args->channel_data.socket); + aws_mem_release(allocator, connection_args->channel_data.socket); + s_client_connection_args_release(connection_args); +} + +static bool s_aws_socket_domain_uses_dns(enum aws_socket_domain domain) { + return domain == AWS_SOCKET_IPV4 || domain == AWS_SOCKET_IPV6; +} + +static void s_on_client_connection_established(struct aws_socket *socket, int error_code, void *user_data) { + struct client_connection_args *connection_args = user_data; + + AWS_LOGF_DEBUG( + AWS_LS_IO_CHANNEL_BOOTSTRAP, + "id=%p: client connection on socket %p completed with error %d.", + (void *)connection_args->bootstrap, + (void *)socket, + error_code); + + if (error_code) { + connection_args->failed_count++; + } + + if (error_code || connection_args->connection_chosen) { + if (s_aws_socket_domain_uses_dns(connection_args->outgoing_options.domain) && error_code) { + struct aws_host_address host_address; + host_address.host = connection_args->host_name; + host_address.address = + aws_string_new_from_c_str(connection_args->bootstrap->allocator, socket->remote_endpoint.address); + host_address.record_type = connection_args->outgoing_options.domain == AWS_SOCKET_IPV6 + ? AWS_ADDRESS_RECORD_TYPE_AAAA + : AWS_ADDRESS_RECORD_TYPE_A; + + if (host_address.address) { + AWS_LOGF_DEBUG( + AWS_LS_IO_CHANNEL_BOOTSTRAP, + "id=%p: recording bad address %s.", + (void *)connection_args->bootstrap, + socket->remote_endpoint.address); + aws_host_resolver_record_connection_failure(connection_args->bootstrap->host_resolver, &host_address); + aws_string_destroy((void *)host_address.address); + } + } + + AWS_LOGF_TRACE( + AWS_LS_IO_CHANNEL_BOOTSTRAP, + "id=%p: releasing socket %p either because we already have a " + "successful connection or because it errored out.", + (void *)connection_args->bootstrap, + (void *)socket); + aws_socket_close(socket); + + aws_socket_clean_up(socket); + aws_mem_release(connection_args->bootstrap->allocator, socket); + + /* if this is the last attempted connection and it failed, notify the user */ + if (connection_args->failed_count == connection_args->addresses_count) { + AWS_LOGF_ERROR( + AWS_LS_IO_CHANNEL_BOOTSTRAP, + "id=%p: Connection failed with error_code %d.", + (void *)connection_args->bootstrap, + error_code); + /* connection_args will be released after setup_callback */ + s_connection_args_setup_callback(connection_args, error_code, NULL); + } + + /* every connection task adds a ref, so every failure or cancel needs to dec one */ + s_client_connection_args_release(connection_args); + return; + } + + connection_args->connection_chosen = true; + connection_args->channel_data.socket = socket; + + struct aws_channel_options args = { + .on_setup_completed = s_on_client_channel_on_setup_completed, + .setup_user_data = connection_args, + .shutdown_user_data = connection_args, + .on_shutdown_completed = s_on_client_channel_on_shutdown, + }; + + args.enable_read_back_pressure = connection_args->enable_read_back_pressure; + args.event_loop = aws_socket_get_event_loop(socket); + + AWS_LOGF_TRACE( + AWS_LS_IO_CHANNEL_BOOTSTRAP, + "id=%p: Successful connection, creating a new channel using socket %p.", + (void *)connection_args->bootstrap, + (void *)socket); + + connection_args->channel_data.channel = aws_channel_new(connection_args->bootstrap->allocator, &args); + + if (!connection_args->channel_data.channel) { + aws_socket_clean_up(socket); + aws_mem_release(connection_args->bootstrap->allocator, connection_args->channel_data.socket); + connection_args->failed_count++; + + /* if this is the last attempted connection and it failed, notify the user */ + if (connection_args->failed_count == connection_args->addresses_count) { + s_connection_args_setup_callback(connection_args, error_code, NULL); + } + } else { + s_connection_args_creation_callback(connection_args, connection_args->channel_data.channel); + } +} + +struct connection_task_data { + struct aws_task task; + struct aws_socket_endpoint endpoint; + struct aws_socket_options options; + struct aws_host_address host_address; + struct client_connection_args *args; + struct aws_event_loop *connect_loop; +}; + +static void s_attempt_connection(struct aws_task *task, void *arg, enum aws_task_status status) { + (void)task; + struct connection_task_data *task_data = arg; + struct aws_allocator *allocator = task_data->args->bootstrap->allocator; + int err_code = 0; + + if (status != AWS_TASK_STATUS_RUN_READY) { + goto task_cancelled; + } + + struct aws_socket *outgoing_socket = aws_mem_acquire(allocator, sizeof(struct aws_socket)); + if (!outgoing_socket) { + goto socket_alloc_failed; + } + + if (aws_socket_init(outgoing_socket, allocator, &task_data->options)) { + goto socket_init_failed; + } + + if (aws_socket_connect( + outgoing_socket, + &task_data->endpoint, + task_data->connect_loop, + s_on_client_connection_established, + task_data->args)) { + + goto socket_connect_failed; + } + + goto cleanup_task; + +socket_connect_failed: + aws_host_resolver_record_connection_failure(task_data->args->bootstrap->host_resolver, &task_data->host_address); + aws_socket_clean_up(outgoing_socket); +socket_init_failed: + aws_mem_release(allocator, outgoing_socket); +socket_alloc_failed: + err_code = aws_last_error(); + AWS_LOGF_ERROR( + AWS_LS_IO_CHANNEL_BOOTSTRAP, + "id=%p: failed to create socket with error %d", + (void *)task_data->args->bootstrap, + err_code); +task_cancelled: + task_data->args->failed_count++; + /* if this is the last attempted connection and it failed, notify the user */ + if (task_data->args->failed_count == task_data->args->addresses_count) { + s_connection_args_setup_callback(task_data->args, err_code, NULL); + } + s_client_connection_args_release(task_data->args); + +cleanup_task: + aws_host_address_clean_up(&task_data->host_address); + aws_mem_release(allocator, task_data); +} + +static void s_on_host_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) { + (void)resolver; + (void)host_name; + + struct client_connection_args *client_connection_args = user_data; + struct aws_allocator *allocator = client_connection_args->bootstrap->allocator; + + if (err_code) { + AWS_LOGF_ERROR( + AWS_LS_IO_CHANNEL_BOOTSTRAP, + "id=%p: dns resolution failed, or all socket connections to the endpoint failed.", + (void *)client_connection_args->bootstrap); + s_connection_args_setup_callback(client_connection_args, err_code, NULL); + return; + } + + size_t host_addresses_len = aws_array_list_length(host_addresses); + AWS_FATAL_ASSERT(host_addresses_len > 0); + AWS_LOGF_TRACE( + AWS_LS_IO_CHANNEL_BOOTSTRAP, + "id=%p: dns resolution completed. Kicking off connections" + " on %llu addresses. First one back wins.", + (void *)client_connection_args->bootstrap, + (unsigned long long)host_addresses_len); + /* use this event loop for all outgoing connection attempts (only one will ultimately win). */ + struct aws_event_loop *connect_loop = + aws_event_loop_group_get_next_loop(client_connection_args->bootstrap->event_loop_group); + client_connection_args->addresses_count = (uint8_t)host_addresses_len; + + /* allocate all the task data first, in case it fails... */ + AWS_VARIABLE_LENGTH_ARRAY(struct connection_task_data *, tasks, host_addresses_len); + for (size_t i = 0; i < host_addresses_len; ++i) { + struct connection_task_data *task_data = tasks[i] = + aws_mem_calloc(allocator, 1, sizeof(struct connection_task_data)); + bool failed = task_data == NULL; + if (!failed) { + struct aws_host_address *host_address_ptr = NULL; + aws_array_list_get_at_ptr(host_addresses, (void **)&host_address_ptr, i); + + task_data->endpoint.port = client_connection_args->outgoing_port; + AWS_ASSERT(sizeof(task_data->endpoint.address) >= host_address_ptr->address->len + 1); + memcpy( + task_data->endpoint.address, + aws_string_bytes(host_address_ptr->address), + host_address_ptr->address->len); + task_data->endpoint.address[host_address_ptr->address->len] = 0; + + task_data->options = client_connection_args->outgoing_options; + task_data->options.domain = + host_address_ptr->record_type == AWS_ADDRESS_RECORD_TYPE_AAAA ? AWS_SOCKET_IPV6 : AWS_SOCKET_IPV4; + + failed = aws_host_address_copy(host_address_ptr, &task_data->host_address) != AWS_OP_SUCCESS; + task_data->args = client_connection_args; + task_data->connect_loop = connect_loop; + } + + if (failed) { + for (size_t j = 0; j <= i; ++j) { + if (tasks[j]) { + aws_host_address_clean_up(&tasks[j]->host_address); + aws_mem_release(allocator, tasks[j]); + } + } + int alloc_err_code = aws_last_error(); + AWS_LOGF_ERROR( + AWS_LS_IO_CHANNEL_BOOTSTRAP, + "id=%p: failed to allocate connection task data: err=%d", + (void *)client_connection_args->bootstrap, + alloc_err_code); + s_connection_args_setup_callback(client_connection_args, alloc_err_code, NULL); + return; + } + } + + /* ...then schedule all the tasks, which cannot fail */ + for (size_t i = 0; i < host_addresses_len; ++i) { + struct connection_task_data *task_data = tasks[i]; + /* each task needs to hold a ref to the args until completed */ + s_client_connection_args_acquire(task_data->args); + + aws_task_init(&task_data->task, s_attempt_connection, task_data, "attempt_connection"); + aws_event_loop_schedule_task_now(connect_loop, &task_data->task); + } +} + +int aws_client_bootstrap_new_socket_channel(struct aws_socket_channel_bootstrap_options *options) { + + struct aws_client_bootstrap *bootstrap = options->bootstrap; + AWS_FATAL_ASSERT(options->setup_callback); + AWS_FATAL_ASSERT(options->shutdown_callback); + AWS_FATAL_ASSERT(bootstrap); + + const struct aws_socket_options *socket_options = options->socket_options; + AWS_FATAL_ASSERT(socket_options != NULL); + + const struct aws_tls_connection_options *tls_options = options->tls_options; + + AWS_FATAL_ASSERT(tls_options == NULL || socket_options->type == AWS_SOCKET_STREAM); + aws_io_fatal_assert_library_initialized(); + + struct client_connection_args *client_connection_args = + aws_mem_calloc(bootstrap->allocator, 1, sizeof(struct client_connection_args)); + + if (!client_connection_args) { + return AWS_OP_ERR; + } + + const char *host_name = options->host_name; + uint16_t port = options->port; + + AWS_LOGF_TRACE( + AWS_LS_IO_CHANNEL_BOOTSTRAP, + "id=%p: attempting to initialize a new client channel to %s:%d", + (void *)bootstrap, + host_name, + (int)port); + + aws_ref_count_init( + &client_connection_args->ref_count, + client_connection_args, + (aws_simple_completion_callback *)s_client_connection_args_destroy); + client_connection_args->user_data = options->user_data; + client_connection_args->bootstrap = aws_client_bootstrap_acquire(bootstrap); + client_connection_args->creation_callback = options->creation_callback; + client_connection_args->setup_callback = options->setup_callback; + client_connection_args->shutdown_callback = options->shutdown_callback; + client_connection_args->outgoing_options = *socket_options; + client_connection_args->outgoing_port = port; + client_connection_args->enable_read_back_pressure = options->enable_read_back_pressure; + + if (tls_options) { + if (aws_tls_connection_options_copy(&client_connection_args->channel_data.tls_options, tls_options)) { + goto error; + } + client_connection_args->channel_data.use_tls = true; + + client_connection_args->channel_data.on_protocol_negotiated = bootstrap->on_protocol_negotiated; + client_connection_args->channel_data.tls_user_data = tls_options->user_data; + + /* in order to honor any callbacks a user may have installed on their tls_connection_options, + * we need to wrap them if they were set.*/ + if (bootstrap->on_protocol_negotiated) { + client_connection_args->channel_data.tls_options.advertise_alpn_message = true; + } + + if (tls_options->on_data_read) { + client_connection_args->channel_data.user_on_data_read = tls_options->on_data_read; + client_connection_args->channel_data.tls_options.on_data_read = s_tls_client_on_data_read; + } + + if (tls_options->on_error) { + client_connection_args->channel_data.user_on_error = tls_options->on_error; + client_connection_args->channel_data.tls_options.on_error = s_tls_client_on_error; + } + + if (tls_options->on_negotiation_result) { + client_connection_args->channel_data.user_on_negotiation_result = tls_options->on_negotiation_result; + } + + client_connection_args->channel_data.tls_options.on_negotiation_result = s_tls_client_on_negotiation_result; + client_connection_args->channel_data.tls_options.user_data = client_connection_args; + } + + if (s_aws_socket_domain_uses_dns(socket_options->domain)) { + client_connection_args->host_name = aws_string_new_from_c_str(bootstrap->allocator, host_name); + + if (!client_connection_args->host_name) { + goto error; + } + + if (aws_host_resolver_resolve_host( + bootstrap->host_resolver, + client_connection_args->host_name, + s_on_host_resolved, + &bootstrap->host_resolver_config, + client_connection_args)) { + goto error; + } + } else { + /* ensure that the pipe/domain socket name will fit in the endpoint address */ + const size_t host_name_len = strlen(host_name); + if (host_name_len >= AWS_ADDRESS_MAX_LEN) { + aws_raise_error(AWS_IO_SOCKET_INVALID_ADDRESS); + goto error; + } + + struct aws_socket_endpoint endpoint; + AWS_ZERO_STRUCT(endpoint); + memcpy(endpoint.address, host_name, host_name_len); + if (socket_options->domain == AWS_SOCKET_VSOCK) { + endpoint.port = port; + } else { + endpoint.port = 0; + } + + struct aws_socket *outgoing_socket = aws_mem_acquire(bootstrap->allocator, sizeof(struct aws_socket)); + + if (!outgoing_socket) { + goto error; + } + + if (aws_socket_init(outgoing_socket, bootstrap->allocator, socket_options)) { + aws_mem_release(bootstrap->allocator, outgoing_socket); + goto error; + } + + client_connection_args->addresses_count = 1; + + struct aws_event_loop *connect_loop = aws_event_loop_group_get_next_loop(bootstrap->event_loop_group); + + s_client_connection_args_acquire(client_connection_args); + if (aws_socket_connect( + outgoing_socket, &endpoint, connect_loop, s_on_client_connection_established, client_connection_args)) { + aws_socket_clean_up(outgoing_socket); + aws_mem_release(client_connection_args->bootstrap->allocator, outgoing_socket); + s_client_connection_args_release(client_connection_args); + goto error; + } + } + + return AWS_OP_SUCCESS; + +error: + if (client_connection_args) { + /* tls opt will also be freed when we clean up the connection arg */ + s_client_connection_args_release(client_connection_args); + } + return AWS_OP_ERR; +} + +void s_server_bootstrap_destroy_impl(struct aws_server_bootstrap *bootstrap) { + AWS_ASSERT(bootstrap); + aws_event_loop_group_release(bootstrap->event_loop_group); + aws_mem_release(bootstrap->allocator, bootstrap); +} + +struct aws_server_bootstrap *aws_server_bootstrap_acquire(struct aws_server_bootstrap *bootstrap) { + if (bootstrap != NULL) { + aws_ref_count_acquire(&bootstrap->ref_count); + } + + return bootstrap; +} + +void aws_server_bootstrap_release(struct aws_server_bootstrap *bootstrap) { + /* if destroy is being called, the user intends to not use the bootstrap anymore + * so we clean up the thread local state while the event loop thread is + * still alive */ + AWS_LOGF_DEBUG(AWS_LS_IO_CHANNEL_BOOTSTRAP, "id=%p: releasing server bootstrap reference", (void *)bootstrap); + if (bootstrap != NULL) { + aws_ref_count_release(&bootstrap->ref_count); + } +} + +struct aws_server_bootstrap *aws_server_bootstrap_new( + struct aws_allocator *allocator, + struct aws_event_loop_group *el_group) { + AWS_ASSERT(allocator); + AWS_ASSERT(el_group); + + struct aws_server_bootstrap *bootstrap = aws_mem_calloc(allocator, 1, sizeof(struct aws_server_bootstrap)); + if (!bootstrap) { + return NULL; + } + + AWS_LOGF_INFO( + AWS_LS_IO_CHANNEL_BOOTSTRAP, + "id=%p: Initializing server bootstrap with event-loop group %p", + (void *)bootstrap, + (void *)el_group); + + bootstrap->allocator = allocator; + bootstrap->event_loop_group = aws_event_loop_group_acquire(el_group); + bootstrap->on_protocol_negotiated = NULL; + aws_ref_count_init( + &bootstrap->ref_count, bootstrap, (aws_simple_completion_callback *)s_server_bootstrap_destroy_impl); + + return bootstrap; +} + +struct server_connection_args { + struct aws_server_bootstrap *bootstrap; + struct aws_socket listener; + aws_server_bootstrap_on_accept_channel_setup_fn *incoming_callback; + aws_server_bootstrap_on_accept_channel_shutdown_fn *shutdown_callback; + aws_server_bootstrap_on_server_listener_destroy_fn *destroy_callback; + struct aws_tls_connection_options tls_options; + aws_channel_on_protocol_negotiated_fn *on_protocol_negotiated; + aws_tls_on_data_read_fn *user_on_data_read; + aws_tls_on_negotiation_result_fn *user_on_negotiation_result; + aws_tls_on_error_fn *user_on_error; + struct aws_task listener_destroy_task; + void *tls_user_data; + void *user_data; + bool use_tls; + bool enable_read_back_pressure; + struct aws_ref_count ref_count; +}; + +struct server_channel_data { + struct aws_channel *channel; + struct aws_socket *socket; + struct server_connection_args *server_connection_args; + bool incoming_called; +}; + +static struct server_connection_args *s_server_connection_args_acquire(struct server_connection_args *args) { + if (args != NULL) { + aws_ref_count_acquire(&args->ref_count); + } + + return args; +} + +static void s_server_connection_args_destroy(struct server_connection_args *args) { + if (args == NULL) { + return; + } + + /* fire the destroy callback */ + if (args->destroy_callback) { + args->destroy_callback(args->bootstrap, args->user_data); + } + + struct aws_allocator *allocator = args->bootstrap->allocator; + aws_server_bootstrap_release(args->bootstrap); + if (args->use_tls) { + aws_tls_connection_options_clean_up(&args->tls_options); + } + + aws_mem_release(allocator, args); +} + +static void s_server_connection_args_release(struct server_connection_args *args) { + if (args != NULL) { + aws_ref_count_release(&args->ref_count); + } +} + +static void s_server_incoming_callback( + struct server_channel_data *channel_data, + int error_code, + struct aws_channel *channel) { + /* incoming_callback is always called exactly once for each channel */ + AWS_ASSERT(!channel_data->incoming_called); + struct server_connection_args *args = channel_data->server_connection_args; + args->incoming_callback(args->bootstrap, error_code, channel, args->user_data); + channel_data->incoming_called = true; +} + +static void s_tls_server_on_negotiation_result( + struct aws_channel_handler *handler, + struct aws_channel_slot *slot, + int err_code, + void *user_data) { + struct server_channel_data *channel_data = user_data; + struct server_connection_args *connection_args = channel_data->server_connection_args; + + if (connection_args->user_on_negotiation_result) { + connection_args->user_on_negotiation_result(handler, slot, err_code, connection_args->tls_user_data); + } + + AWS_LOGF_DEBUG( + AWS_LS_IO_CHANNEL_BOOTSTRAP, + "id=%p: tls negotiation result %d on channel %p", + (void *)connection_args->bootstrap, + err_code, + (void *)slot->channel); + + struct aws_channel *channel = slot->channel; + if (err_code) { + /* shut down the channel */ + aws_channel_shutdown(channel, err_code); + } else { + s_server_incoming_callback(channel_data, err_code, channel); + } +} + +/* in the context of a channel bootstrap, we don't care about these, but since we're hooking into these APIs we have to + * provide a proxy for the user actually receiving their callbacks. */ +static void s_tls_server_on_data_read( + struct aws_channel_handler *handler, + struct aws_channel_slot *slot, + struct aws_byte_buf *buffer, + void *user_data) { + struct server_connection_args *connection_args = user_data; + + if (connection_args->user_on_data_read) { + connection_args->user_on_data_read(handler, slot, buffer, connection_args->tls_user_data); + } +} + +/* in the context of a channel bootstrap, we don't care about these, but since we're hooking into these APIs we have to + * provide a proxy for the user actually receiving their callbacks. */ +static void s_tls_server_on_error( + struct aws_channel_handler *handler, + struct aws_channel_slot *slot, + int err, + const char *message, + void *user_data) { + struct server_connection_args *connection_args = user_data; + + if (connection_args->user_on_error) { + connection_args->user_on_error(handler, slot, err, message, connection_args->tls_user_data); + } +} + +static inline int s_setup_server_tls(struct server_channel_data *channel_data, struct aws_channel *channel) { + struct aws_channel_slot *tls_slot = NULL; + struct aws_channel_handler *tls_handler = NULL; + struct server_connection_args *connection_args = channel_data->server_connection_args; + + /* as far as cleanup goes here, since we're adding things to a channel, if a slot is ever successfully + added to the channel, we leave it there. The caller will clean up the channel and it will clean this memory + up as well. */ + tls_slot = aws_channel_slot_new(channel); + + if (!tls_slot) { + return AWS_OP_ERR; + } + + /* Shallow-copy tls_options so we can override the user_data, making it specific to this channel */ + struct aws_tls_connection_options tls_options = connection_args->tls_options; + tls_options.user_data = channel_data; + tls_handler = aws_tls_server_handler_new(connection_args->bootstrap->allocator, &tls_options, tls_slot); + + if (!tls_handler) { + aws_mem_release(connection_args->bootstrap->allocator, tls_slot); + return AWS_OP_ERR; + } + + AWS_LOGF_TRACE( + AWS_LS_IO_CHANNEL_BOOTSTRAP, + "id=%p: Setting up server TLS on channel %p with handler %p on slot %p", + (void *)connection_args->bootstrap, + (void *)channel, + (void *)tls_handler, + (void *)tls_slot); + + aws_channel_slot_insert_end(channel, tls_slot); + + if (aws_channel_slot_set_handler(tls_slot, tls_handler)) { + return AWS_OP_ERR; + } + + if (connection_args->on_protocol_negotiated) { + struct aws_channel_slot *alpn_slot = NULL; + struct aws_channel_handler *alpn_handler = NULL; + alpn_slot = aws_channel_slot_new(channel); + + if (!alpn_slot) { + return AWS_OP_ERR; + } + + alpn_handler = aws_tls_alpn_handler_new( + connection_args->bootstrap->allocator, connection_args->on_protocol_negotiated, connection_args->user_data); + + if (!alpn_handler) { + aws_channel_slot_remove(alpn_slot); + return AWS_OP_ERR; + } + + AWS_LOGF_TRACE( + AWS_LS_IO_CHANNEL_BOOTSTRAP, + "id=%p: Setting up ALPN handler on channel " + "%p with handler %p on slot %p", + (void *)connection_args->bootstrap, + (void *)channel, + (void *)alpn_handler, + (void *)alpn_slot); + + aws_channel_slot_insert_right(tls_slot, alpn_slot); + + if (aws_channel_slot_set_handler(alpn_slot, alpn_handler)) { + return AWS_OP_ERR; + } + } + + return AWS_OP_SUCCESS; +} + +static void s_on_server_channel_on_setup_completed(struct aws_channel *channel, int error_code, void *user_data) { + struct server_channel_data *channel_data = user_data; + + int err_code = error_code; + if (err_code) { + /* channel fail to set up no destroy callback will fire */ + AWS_LOGF_ERROR( + AWS_LS_IO_CHANNEL_BOOTSTRAP, + "id=%p: channel %p setup failed with error %d.", + (void *)channel_data->server_connection_args->bootstrap, + (void *)channel, + err_code); + + aws_channel_destroy(channel); + struct aws_allocator *allocator = channel_data->socket->allocator; + aws_socket_clean_up(channel_data->socket); + aws_mem_release(allocator, (void *)channel_data->socket); + s_server_incoming_callback(channel_data, err_code, NULL); + aws_mem_release(channel_data->server_connection_args->bootstrap->allocator, channel_data); + /* no shutdown call back will be fired, we release the ref_count of connection arg here */ + s_server_connection_args_release(channel_data->server_connection_args); + return; + } + + AWS_LOGF_DEBUG( + AWS_LS_IO_CHANNEL_BOOTSTRAP, + "id=%p: channel %p setup succeeded: bootstrapping.", + (void *)channel_data->server_connection_args->bootstrap, + (void *)channel); + + struct aws_channel_slot *socket_slot = aws_channel_slot_new(channel); + + if (!socket_slot) { + err_code = aws_last_error(); + goto error; + } + + struct aws_channel_handler *socket_channel_handler = aws_socket_handler_new( + channel_data->server_connection_args->bootstrap->allocator, + channel_data->socket, + socket_slot, + g_aws_channel_max_fragment_size); + + if (!socket_channel_handler) { + err_code = aws_last_error(); + aws_channel_slot_remove(socket_slot); + socket_slot = NULL; + goto error; + } + + AWS_LOGF_TRACE( + AWS_LS_IO_CHANNEL_BOOTSTRAP, + "id=%p: Setting up socket handler on channel " + "%p with handler %p on slot %p.", + (void *)channel_data->server_connection_args->bootstrap, + (void *)channel, + (void *)socket_channel_handler, + (void *)socket_slot); + + if (aws_channel_slot_set_handler(socket_slot, socket_channel_handler)) { + err_code = aws_last_error(); + goto error; + } + + if (channel_data->server_connection_args->use_tls) { + /* incoming callback will be invoked upon the negotiation completion so don't do it + * here. */ + if (s_setup_server_tls(channel_data, channel)) { + err_code = aws_last_error(); + goto error; + } + } else { + s_server_incoming_callback(channel_data, AWS_OP_SUCCESS, channel); + } + return; + +error: + /* shut down the channel */ + aws_channel_shutdown(channel, err_code); +} + +static void s_on_server_channel_on_shutdown(struct aws_channel *channel, int error_code, void *user_data) { + struct server_channel_data *channel_data = user_data; + struct server_connection_args *args = channel_data->server_connection_args; + AWS_LOGF_DEBUG( + AWS_LS_IO_CHANNEL_BOOTSTRAP, + "id=%p: channel %p shutdown with error %d.", + (void *)args->bootstrap, + (void *)channel, + error_code); + + void *server_shutdown_user_data = args->user_data; + struct aws_server_bootstrap *server_bootstrap = args->bootstrap; + struct aws_allocator *allocator = server_bootstrap->allocator; + + if (!channel_data->incoming_called) { + error_code = (error_code) ? error_code : AWS_ERROR_UNKNOWN; + s_server_incoming_callback(channel_data, error_code, NULL); + } else { + args->shutdown_callback(server_bootstrap, error_code, channel, server_shutdown_user_data); + } + + aws_channel_destroy(channel); + aws_socket_clean_up(channel_data->socket); + aws_mem_release(allocator, channel_data->socket); + s_server_connection_args_release(channel_data->server_connection_args); + + aws_mem_release(allocator, channel_data); +} + +void s_on_server_connection_result( + struct aws_socket *socket, + int error_code, + struct aws_socket *new_socket, + void *user_data) { + (void)socket; + struct server_connection_args *connection_args = user_data; + + s_server_connection_args_acquire(connection_args); + AWS_LOGF_DEBUG( + AWS_LS_IO_CHANNEL_BOOTSTRAP, + "id=%p: server connection on socket %p completed with error %d.", + (void *)connection_args->bootstrap, + (void *)socket, + error_code); + + if (!error_code) { + AWS_LOGF_TRACE( + AWS_LS_IO_CHANNEL_BOOTSTRAP, + "id=%p: creating a new channel for incoming " + "connection using socket %p.", + (void *)connection_args->bootstrap, + (void *)socket); + struct server_channel_data *channel_data = + aws_mem_calloc(connection_args->bootstrap->allocator, 1, sizeof(struct server_channel_data)); + if (!channel_data) { + goto error_cleanup; + } + channel_data->incoming_called = false; + channel_data->socket = new_socket; + channel_data->server_connection_args = connection_args; + + struct aws_event_loop *event_loop = + aws_event_loop_group_get_next_loop(connection_args->bootstrap->event_loop_group); + + struct aws_channel_options channel_args = { + .on_setup_completed = s_on_server_channel_on_setup_completed, + .setup_user_data = channel_data, + .shutdown_user_data = channel_data, + .on_shutdown_completed = s_on_server_channel_on_shutdown, + }; + + channel_args.event_loop = event_loop; + channel_args.enable_read_back_pressure = channel_data->server_connection_args->enable_read_back_pressure; + + if (aws_socket_assign_to_event_loop(new_socket, event_loop)) { + aws_mem_release(connection_args->bootstrap->allocator, (void *)channel_data); + goto error_cleanup; + } + + channel_data->channel = aws_channel_new(connection_args->bootstrap->allocator, &channel_args); + + if (!channel_data->channel) { + aws_mem_release(connection_args->bootstrap->allocator, (void *)channel_data); + goto error_cleanup; + } + } else { + /* no channel is created */ + connection_args->incoming_callback(connection_args->bootstrap, error_code, NULL, connection_args->user_data); + s_server_connection_args_release(connection_args); + } + + return; + +error_cleanup: + /* no channel is created */ + connection_args->incoming_callback(connection_args->bootstrap, aws_last_error(), NULL, connection_args->user_data); + + struct aws_allocator *allocator = new_socket->allocator; + aws_socket_clean_up(new_socket); + aws_mem_release(allocator, (void *)new_socket); + s_server_connection_args_release(connection_args); +} + +static void s_listener_destroy_task(struct aws_task *task, void *arg, enum aws_task_status status) { + (void)status; + (void)task; + struct server_connection_args *server_connection_args = arg; + + aws_socket_stop_accept(&server_connection_args->listener); + aws_socket_clean_up(&server_connection_args->listener); + s_server_connection_args_release(server_connection_args); +} + +struct aws_socket *aws_server_bootstrap_new_socket_listener( + const struct aws_server_socket_channel_bootstrap_options *bootstrap_options) { + AWS_PRECONDITION(bootstrap_options); + AWS_PRECONDITION(bootstrap_options->bootstrap); + AWS_PRECONDITION(bootstrap_options->incoming_callback) + AWS_PRECONDITION(bootstrap_options->shutdown_callback) + + struct server_connection_args *server_connection_args = + aws_mem_calloc(bootstrap_options->bootstrap->allocator, 1, sizeof(struct server_connection_args)); + if (!server_connection_args) { + return NULL; + } + + AWS_LOGF_INFO( + AWS_LS_IO_CHANNEL_BOOTSTRAP, + "id=%p: attempting to initialize a new " + "server socket listener for %s:%d", + (void *)server_connection_args->bootstrap, + bootstrap_options->host_name, + (int)bootstrap_options->port); + + aws_ref_count_init( + &server_connection_args->ref_count, + server_connection_args, + (aws_simple_completion_callback *)s_server_connection_args_destroy); + server_connection_args->user_data = bootstrap_options->user_data; + server_connection_args->bootstrap = aws_server_bootstrap_acquire(bootstrap_options->bootstrap); + server_connection_args->shutdown_callback = bootstrap_options->shutdown_callback; + server_connection_args->incoming_callback = bootstrap_options->incoming_callback; + server_connection_args->destroy_callback = bootstrap_options->destroy_callback; + server_connection_args->on_protocol_negotiated = bootstrap_options->bootstrap->on_protocol_negotiated; + server_connection_args->enable_read_back_pressure = bootstrap_options->enable_read_back_pressure; + + aws_task_init( + &server_connection_args->listener_destroy_task, + s_listener_destroy_task, + server_connection_args, + "listener socket destroy"); + + if (bootstrap_options->tls_options) { + AWS_LOGF_INFO( + AWS_LS_IO_CHANNEL_BOOTSTRAP, "id=%p: using tls on listener", (void *)bootstrap_options->tls_options); + if (aws_tls_connection_options_copy(&server_connection_args->tls_options, bootstrap_options->tls_options)) { + goto cleanup_server_connection_args; + } + + server_connection_args->use_tls = true; + + server_connection_args->tls_user_data = bootstrap_options->tls_options->user_data; + + /* in order to honor any callbacks a user may have installed on their tls_connection_options, + * we need to wrap them if they were set.*/ + if (bootstrap_options->bootstrap->on_protocol_negotiated) { + server_connection_args->tls_options.advertise_alpn_message = true; + } + + if (bootstrap_options->tls_options->on_data_read) { + server_connection_args->user_on_data_read = bootstrap_options->tls_options->on_data_read; + server_connection_args->tls_options.on_data_read = s_tls_server_on_data_read; + } + + if (bootstrap_options->tls_options->on_error) { + server_connection_args->user_on_error = bootstrap_options->tls_options->on_error; + server_connection_args->tls_options.on_error = s_tls_server_on_error; + } + + if (bootstrap_options->tls_options->on_negotiation_result) { + server_connection_args->user_on_negotiation_result = bootstrap_options->tls_options->on_negotiation_result; + } + + server_connection_args->tls_options.on_negotiation_result = s_tls_server_on_negotiation_result; + server_connection_args->tls_options.user_data = server_connection_args; + } + + struct aws_event_loop *connection_loop = + aws_event_loop_group_get_next_loop(bootstrap_options->bootstrap->event_loop_group); + + if (aws_socket_init( + &server_connection_args->listener, + bootstrap_options->bootstrap->allocator, + bootstrap_options->socket_options)) { + goto cleanup_server_connection_args; + } + + struct aws_socket_endpoint endpoint; + AWS_ZERO_STRUCT(endpoint); + size_t host_name_len = 0; + if (aws_secure_strlen(bootstrap_options->host_name, sizeof(endpoint.address), &host_name_len)) { + goto cleanup_server_connection_args; + } + + memcpy(endpoint.address, bootstrap_options->host_name, host_name_len); + endpoint.port = bootstrap_options->port; + + if (aws_socket_bind(&server_connection_args->listener, &endpoint)) { + goto cleanup_listener; + } + + if (aws_socket_listen(&server_connection_args->listener, 1024)) { + goto cleanup_listener; + } + + if (aws_socket_start_accept( + &server_connection_args->listener, + connection_loop, + s_on_server_connection_result, + server_connection_args)) { + goto cleanup_listener; + } + + return &server_connection_args->listener; + +cleanup_listener: + aws_socket_clean_up(&server_connection_args->listener); + +cleanup_server_connection_args: + s_server_connection_args_release(server_connection_args); + + return NULL; +} + +void aws_server_bootstrap_destroy_socket_listener(struct aws_server_bootstrap *bootstrap, struct aws_socket *listener) { + struct server_connection_args *server_connection_args = + AWS_CONTAINER_OF(listener, struct server_connection_args, listener); + + AWS_LOGF_DEBUG(AWS_LS_IO_CHANNEL_BOOTSTRAP, "id=%p: releasing bootstrap reference", (void *)bootstrap); + aws_event_loop_schedule_task_now(listener->event_loop, &server_connection_args->listener_destroy_task); +} + +int aws_server_bootstrap_set_alpn_callback( + struct aws_server_bootstrap *bootstrap, + aws_channel_on_protocol_negotiated_fn *on_protocol_negotiated) { + AWS_ASSERT(on_protocol_negotiated); + AWS_LOGF_DEBUG(AWS_LS_IO_CHANNEL_BOOTSTRAP, "id=%p: Setting ALPN callback", (void *)bootstrap); + bootstrap->on_protocol_negotiated = on_protocol_negotiated; + return AWS_OP_SUCCESS; +} |