mirror of https://github.com/grpc/grpc.git
commit
ba801e6465
36 changed files with 1958 additions and 1659 deletions
@ -0,0 +1,261 @@ |
|||||||
|
/*
|
||||||
|
* |
||||||
|
* Copyright 2015, Google Inc. |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* * Redistributions in binary form must reproduce the above |
||||||
|
* copyright notice, this list of conditions and the following disclaimer |
||||||
|
* in the documentation and/or other materials provided with the |
||||||
|
* distribution. |
||||||
|
* * Neither the name of Google Inc. nor the names of its |
||||||
|
* contributors may be used to endorse or promote products derived from |
||||||
|
* this software without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "src/core/ext/transport/chttp2/client/chttp2_connector.h" |
||||||
|
|
||||||
|
#include <grpc/grpc.h> |
||||||
|
|
||||||
|
#include <string.h> |
||||||
|
|
||||||
|
#include <grpc/slice_buffer.h> |
||||||
|
#include <grpc/support/alloc.h> |
||||||
|
#include <grpc/support/string_util.h> |
||||||
|
|
||||||
|
#include "src/core/ext/client_channel/connector.h" |
||||||
|
#include "src/core/ext/client_channel/http_connect_handshaker.h" |
||||||
|
#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h" |
||||||
|
#include "src/core/lib/channel/channel_args.h" |
||||||
|
#include "src/core/lib/channel/handshaker.h" |
||||||
|
#include "src/core/lib/iomgr/tcp_client.h" |
||||||
|
#include "src/core/lib/security/transport/security_connector.h" |
||||||
|
|
||||||
|
typedef struct { |
||||||
|
grpc_connector base; |
||||||
|
|
||||||
|
gpr_mu mu; |
||||||
|
gpr_refcount refs; |
||||||
|
|
||||||
|
bool shutdown; |
||||||
|
|
||||||
|
char *server_name; |
||||||
|
grpc_chttp2_create_handshakers_func create_handshakers; |
||||||
|
void *create_handshakers_user_data; |
||||||
|
|
||||||
|
grpc_closure *notify; |
||||||
|
grpc_connect_in_args args; |
||||||
|
grpc_connect_out_args *result; |
||||||
|
grpc_closure initial_string_sent; |
||||||
|
grpc_slice_buffer initial_string_buffer; |
||||||
|
|
||||||
|
grpc_endpoint *endpoint; // Non-NULL until handshaking starts.
|
||||||
|
|
||||||
|
grpc_closure connected; |
||||||
|
|
||||||
|
grpc_handshake_manager *handshake_mgr; |
||||||
|
} chttp2_connector; |
||||||
|
|
||||||
|
static void chttp2_connector_ref(grpc_connector *con) { |
||||||
|
chttp2_connector *c = (chttp2_connector *)con; |
||||||
|
gpr_ref(&c->refs); |
||||||
|
} |
||||||
|
|
||||||
|
static void chttp2_connector_unref(grpc_exec_ctx *exec_ctx, |
||||||
|
grpc_connector *con) { |
||||||
|
chttp2_connector *c = (chttp2_connector *)con; |
||||||
|
if (gpr_unref(&c->refs)) { |
||||||
|
/* c->initial_string_buffer does not need to be destroyed */ |
||||||
|
gpr_mu_destroy(&c->mu); |
||||||
|
// If handshaking is not yet in progress, destroy the endpoint.
|
||||||
|
// Otherwise, the handshaker will do this for us.
|
||||||
|
if (c->endpoint != NULL) grpc_endpoint_destroy(exec_ctx, c->endpoint); |
||||||
|
gpr_free(c->server_name); |
||||||
|
gpr_free(c); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static void chttp2_connector_shutdown(grpc_exec_ctx *exec_ctx, |
||||||
|
grpc_connector *con) { |
||||||
|
chttp2_connector *c = (chttp2_connector *)con; |
||||||
|
gpr_mu_lock(&c->mu); |
||||||
|
c->shutdown = true; |
||||||
|
if (c->handshake_mgr != NULL) { |
||||||
|
grpc_handshake_manager_shutdown(exec_ctx, c->handshake_mgr); |
||||||
|
} |
||||||
|
// If handshaking is not yet in progress, shutdown the endpoint.
|
||||||
|
// Otherwise, the handshaker will do this for us.
|
||||||
|
if (c->endpoint != NULL) grpc_endpoint_shutdown(exec_ctx, c->endpoint); |
||||||
|
gpr_mu_unlock(&c->mu); |
||||||
|
} |
||||||
|
|
||||||
|
static void on_handshake_done(grpc_exec_ctx *exec_ctx, void *arg, |
||||||
|
grpc_error *error) { |
||||||
|
grpc_handshaker_args *args = arg; |
||||||
|
chttp2_connector *c = args->user_data; |
||||||
|
gpr_mu_lock(&c->mu); |
||||||
|
if (error != GRPC_ERROR_NONE || c->shutdown) { |
||||||
|
if (error == GRPC_ERROR_NONE) { |
||||||
|
error = GRPC_ERROR_CREATE("connector shutdown"); |
||||||
|
// We were shut down after handshaking completed successfully, so
|
||||||
|
// destroy the endpoint here.
|
||||||
|
// TODO(ctiller): It is currently necessary to shutdown endpoints
|
||||||
|
// before destroying them, even if we know that there are no
|
||||||
|
// pending read/write callbacks. This should be fixed, at which
|
||||||
|
// point this can be removed.
|
||||||
|
grpc_endpoint_shutdown(exec_ctx, args->endpoint); |
||||||
|
grpc_endpoint_destroy(exec_ctx, args->endpoint); |
||||||
|
grpc_channel_args_destroy(args->args); |
||||||
|
grpc_slice_buffer_destroy(args->read_buffer); |
||||||
|
gpr_free(args->read_buffer); |
||||||
|
} else { |
||||||
|
error = GRPC_ERROR_REF(error); |
||||||
|
} |
||||||
|
memset(c->result, 0, sizeof(*c->result)); |
||||||
|
} else { |
||||||
|
c->result->transport = |
||||||
|
grpc_create_chttp2_transport(exec_ctx, args->args, args->endpoint, 1); |
||||||
|
GPR_ASSERT(c->result->transport); |
||||||
|
grpc_chttp2_transport_start_reading(exec_ctx, c->result->transport, |
||||||
|
args->read_buffer); |
||||||
|
c->result->channel_args = args->args; |
||||||
|
} |
||||||
|
grpc_closure *notify = c->notify; |
||||||
|
c->notify = NULL; |
||||||
|
grpc_exec_ctx_sched(exec_ctx, notify, error, NULL); |
||||||
|
grpc_handshake_manager_destroy(exec_ctx, c->handshake_mgr); |
||||||
|
c->handshake_mgr = NULL; |
||||||
|
gpr_mu_unlock(&c->mu); |
||||||
|
chttp2_connector_unref(exec_ctx, (grpc_connector *)c); |
||||||
|
} |
||||||
|
|
||||||
|
static void start_handshake_locked(grpc_exec_ctx *exec_ctx, |
||||||
|
chttp2_connector *c) { |
||||||
|
c->handshake_mgr = grpc_handshake_manager_create(); |
||||||
|
char *proxy_name = grpc_get_http_proxy_server(); |
||||||
|
if (proxy_name != NULL) { |
||||||
|
grpc_handshake_manager_add( |
||||||
|
c->handshake_mgr, |
||||||
|
grpc_http_connect_handshaker_create(proxy_name, c->server_name)); |
||||||
|
gpr_free(proxy_name); |
||||||
|
} |
||||||
|
if (c->create_handshakers != NULL) { |
||||||
|
c->create_handshakers(exec_ctx, c->create_handshakers_user_data, |
||||||
|
c->handshake_mgr); |
||||||
|
} |
||||||
|
grpc_handshake_manager_do_handshake( |
||||||
|
exec_ctx, c->handshake_mgr, c->endpoint, c->args.channel_args, |
||||||
|
c->args.deadline, NULL /* acceptor */, on_handshake_done, c); |
||||||
|
c->endpoint = NULL; // Endpoint handed off to handshake manager.
|
||||||
|
} |
||||||
|
|
||||||
|
static void on_initial_connect_string_sent(grpc_exec_ctx *exec_ctx, void *arg, |
||||||
|
grpc_error *error) { |
||||||
|
chttp2_connector *c = arg; |
||||||
|
gpr_mu_lock(&c->mu); |
||||||
|
if (error != GRPC_ERROR_NONE || c->shutdown) { |
||||||
|
if (error == GRPC_ERROR_NONE) { |
||||||
|
error = GRPC_ERROR_CREATE("connector shutdown"); |
||||||
|
} else { |
||||||
|
error = GRPC_ERROR_REF(error); |
||||||
|
} |
||||||
|
memset(c->result, 0, sizeof(*c->result)); |
||||||
|
grpc_closure *notify = c->notify; |
||||||
|
c->notify = NULL; |
||||||
|
grpc_exec_ctx_sched(exec_ctx, notify, error, NULL); |
||||||
|
gpr_mu_unlock(&c->mu); |
||||||
|
chttp2_connector_unref(exec_ctx, arg); |
||||||
|
} else { |
||||||
|
start_handshake_locked(exec_ctx, c); |
||||||
|
gpr_mu_unlock(&c->mu); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static void connected(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { |
||||||
|
chttp2_connector *c = arg; |
||||||
|
gpr_mu_lock(&c->mu); |
||||||
|
if (error != GRPC_ERROR_NONE || c->shutdown) { |
||||||
|
if (error == GRPC_ERROR_NONE) { |
||||||
|
error = GRPC_ERROR_CREATE("connector shutdown"); |
||||||
|
} else { |
||||||
|
error = GRPC_ERROR_REF(error); |
||||||
|
} |
||||||
|
memset(c->result, 0, sizeof(*c->result)); |
||||||
|
grpc_closure *notify = c->notify; |
||||||
|
c->notify = NULL; |
||||||
|
grpc_exec_ctx_sched(exec_ctx, notify, error, NULL); |
||||||
|
gpr_mu_unlock(&c->mu); |
||||||
|
chttp2_connector_unref(exec_ctx, arg); |
||||||
|
} else { |
||||||
|
GPR_ASSERT(c->endpoint != NULL); |
||||||
|
if (!GRPC_SLICE_IS_EMPTY(c->args.initial_connect_string)) { |
||||||
|
grpc_closure_init(&c->initial_string_sent, on_initial_connect_string_sent, |
||||||
|
c); |
||||||
|
grpc_slice_buffer_init(&c->initial_string_buffer); |
||||||
|
grpc_slice_buffer_add(&c->initial_string_buffer, |
||||||
|
c->args.initial_connect_string); |
||||||
|
grpc_endpoint_write(exec_ctx, c->endpoint, &c->initial_string_buffer, |
||||||
|
&c->initial_string_sent); |
||||||
|
} else { |
||||||
|
start_handshake_locked(exec_ctx, c); |
||||||
|
} |
||||||
|
gpr_mu_unlock(&c->mu); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static void chttp2_connector_connect(grpc_exec_ctx *exec_ctx, |
||||||
|
grpc_connector *con, |
||||||
|
const grpc_connect_in_args *args, |
||||||
|
grpc_connect_out_args *result, |
||||||
|
grpc_closure *notify) { |
||||||
|
chttp2_connector *c = (chttp2_connector *)con; |
||||||
|
gpr_mu_lock(&c->mu); |
||||||
|
GPR_ASSERT(c->notify == NULL); |
||||||
|
c->notify = notify; |
||||||
|
c->args = *args; |
||||||
|
c->result = result; |
||||||
|
GPR_ASSERT(c->endpoint == NULL); |
||||||
|
chttp2_connector_ref(con); // Ref taken for callback.
|
||||||
|
grpc_closure_init(&c->connected, connected, c); |
||||||
|
grpc_tcp_client_connect(exec_ctx, &c->connected, &c->endpoint, |
||||||
|
args->interested_parties, args->channel_args, |
||||||
|
args->addr, args->deadline); |
||||||
|
gpr_mu_unlock(&c->mu); |
||||||
|
} |
||||||
|
|
||||||
|
static const grpc_connector_vtable chttp2_connector_vtable = { |
||||||
|
chttp2_connector_ref, chttp2_connector_unref, chttp2_connector_shutdown, |
||||||
|
chttp2_connector_connect}; |
||||||
|
|
||||||
|
grpc_connector *grpc_chttp2_connector_create( |
||||||
|
grpc_exec_ctx *exec_ctx, const char *server_name, |
||||||
|
grpc_chttp2_create_handshakers_func create_handshakers, |
||||||
|
void *create_handshakers_user_data) { |
||||||
|
chttp2_connector *c = gpr_malloc(sizeof(*c)); |
||||||
|
memset(c, 0, sizeof(*c)); |
||||||
|
c->base.vtable = &chttp2_connector_vtable; |
||||||
|
gpr_mu_init(&c->mu); |
||||||
|
gpr_ref_init(&c->refs, 1); |
||||||
|
c->server_name = gpr_strdup(server_name); |
||||||
|
c->create_handshakers = create_handshakers; |
||||||
|
c->create_handshakers_user_data = create_handshakers_user_data; |
||||||
|
return &c->base; |
||||||
|
} |
@ -0,0 +1,52 @@ |
|||||||
|
/*
|
||||||
|
* |
||||||
|
* Copyright 2015, Google Inc. |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* * Redistributions in binary form must reproduce the above |
||||||
|
* copyright notice, this list of conditions and the following disclaimer |
||||||
|
* in the documentation and/or other materials provided with the |
||||||
|
* distribution. |
||||||
|
* * Neither the name of Google Inc. nor the names of its |
||||||
|
* contributors may be used to endorse or promote products derived from |
||||||
|
* this software without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_CLIENT_CHTTP2_CONNECTOR_H |
||||||
|
#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_CLIENT_CHTTP2_CONNECTOR_H |
||||||
|
|
||||||
|
#include "src/core/ext/client_channel/connector.h" |
||||||
|
#include "src/core/lib/channel/handshaker.h" |
||||||
|
#include "src/core/lib/iomgr/exec_ctx.h" |
||||||
|
|
||||||
|
typedef void (*grpc_chttp2_create_handshakers_func)( |
||||||
|
grpc_exec_ctx* exec_ctx, void* user_data, |
||||||
|
grpc_handshake_manager* handshake_mgr); |
||||||
|
|
||||||
|
/// If \a create_handshakers is non-NULL, it will be called with
|
||||||
|
/// \a create_handshakers_user_data to add handshakers.
|
||||||
|
grpc_connector* grpc_chttp2_connector_create( |
||||||
|
grpc_exec_ctx* exec_ctx, const char* server_name, |
||||||
|
grpc_chttp2_create_handshakers_func create_handshakers, |
||||||
|
void* create_handshakers_user_data); |
||||||
|
|
||||||
|
#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_CLIENT_CHTTP2_CONNECTOR_H */ |
@ -0,0 +1,356 @@ |
|||||||
|
/*
|
||||||
|
* |
||||||
|
* Copyright 2015, Google Inc. |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* * Redistributions in binary form must reproduce the above |
||||||
|
* copyright notice, this list of conditions and the following disclaimer |
||||||
|
* in the documentation and/or other materials provided with the |
||||||
|
* distribution. |
||||||
|
* * Neither the name of Google Inc. nor the names of its |
||||||
|
* contributors may be used to endorse or promote products derived from |
||||||
|
* this software without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "src/core/ext/transport/chttp2/server/chttp2_server.h" |
||||||
|
|
||||||
|
#include <grpc/grpc.h> |
||||||
|
|
||||||
|
#include <string.h> |
||||||
|
|
||||||
|
#include <grpc/support/alloc.h> |
||||||
|
#include <grpc/support/log.h> |
||||||
|
#include <grpc/support/string_util.h> |
||||||
|
#include <grpc/support/sync.h> |
||||||
|
#include <grpc/support/useful.h> |
||||||
|
|
||||||
|
#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h" |
||||||
|
#include "src/core/lib/channel/channel_args.h" |
||||||
|
#include "src/core/lib/channel/handshaker.h" |
||||||
|
#include "src/core/lib/channel/http_server_filter.h" |
||||||
|
#include "src/core/lib/iomgr/endpoint.h" |
||||||
|
#include "src/core/lib/iomgr/resolve_address.h" |
||||||
|
#include "src/core/lib/iomgr/tcp_server.h" |
||||||
|
#include "src/core/lib/surface/api_trace.h" |
||||||
|
#include "src/core/lib/surface/server.h" |
||||||
|
|
||||||
|
void grpc_chttp2_server_handshaker_factory_create_handshakers( |
||||||
|
grpc_exec_ctx *exec_ctx, |
||||||
|
grpc_chttp2_server_handshaker_factory *handshaker_factory, |
||||||
|
grpc_handshake_manager *handshake_mgr) { |
||||||
|
if (handshaker_factory != NULL) { |
||||||
|
handshaker_factory->vtable->create_handshakers( |
||||||
|
exec_ctx, handshaker_factory, handshake_mgr); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void grpc_chttp2_server_handshaker_factory_destroy( |
||||||
|
grpc_exec_ctx *exec_ctx, |
||||||
|
grpc_chttp2_server_handshaker_factory *handshaker_factory) { |
||||||
|
if (handshaker_factory != NULL) { |
||||||
|
handshaker_factory->vtable->destroy(exec_ctx, handshaker_factory); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
typedef struct pending_handshake_manager_node { |
||||||
|
grpc_handshake_manager *handshake_mgr; |
||||||
|
struct pending_handshake_manager_node *next; |
||||||
|
} pending_handshake_manager_node; |
||||||
|
|
||||||
|
typedef struct { |
||||||
|
grpc_server *server; |
||||||
|
grpc_tcp_server *tcp_server; |
||||||
|
grpc_channel_args *args; |
||||||
|
grpc_chttp2_server_handshaker_factory *handshaker_factory; |
||||||
|
gpr_mu mu; |
||||||
|
bool shutdown; |
||||||
|
grpc_closure tcp_server_shutdown_complete; |
||||||
|
grpc_closure *server_destroy_listener_done; |
||||||
|
pending_handshake_manager_node *pending_handshake_mgrs; |
||||||
|
} server_state; |
||||||
|
|
||||||
|
typedef struct { |
||||||
|
server_state *server_state; |
||||||
|
grpc_pollset *accepting_pollset; |
||||||
|
grpc_tcp_server_acceptor *acceptor; |
||||||
|
grpc_handshake_manager *handshake_mgr; |
||||||
|
} server_connection_state; |
||||||
|
|
||||||
|
static void pending_handshake_manager_add_locked( |
||||||
|
server_state *state, grpc_handshake_manager *handshake_mgr) { |
||||||
|
pending_handshake_manager_node *node = gpr_malloc(sizeof(*node)); |
||||||
|
node->handshake_mgr = handshake_mgr; |
||||||
|
node->next = state->pending_handshake_mgrs; |
||||||
|
state->pending_handshake_mgrs = node; |
||||||
|
} |
||||||
|
|
||||||
|
static void pending_handshake_manager_remove_locked( |
||||||
|
server_state *state, grpc_handshake_manager *handshake_mgr) { |
||||||
|
pending_handshake_manager_node **prev_node = &state->pending_handshake_mgrs; |
||||||
|
for (pending_handshake_manager_node *node = state->pending_handshake_mgrs; |
||||||
|
node != NULL; node = node->next) { |
||||||
|
if (node->handshake_mgr == handshake_mgr) { |
||||||
|
*prev_node = node->next; |
||||||
|
gpr_free(node); |
||||||
|
break; |
||||||
|
} |
||||||
|
prev_node = &node->next; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static void pending_handshake_manager_shutdown_locked(grpc_exec_ctx *exec_ctx, |
||||||
|
server_state *state) { |
||||||
|
pending_handshake_manager_node *prev_node = NULL; |
||||||
|
for (pending_handshake_manager_node *node = state->pending_handshake_mgrs; |
||||||
|
node != NULL; node = node->next) { |
||||||
|
grpc_handshake_manager_shutdown(exec_ctx, node->handshake_mgr); |
||||||
|
gpr_free(prev_node); |
||||||
|
prev_node = node; |
||||||
|
} |
||||||
|
gpr_free(prev_node); |
||||||
|
state->pending_handshake_mgrs = NULL; |
||||||
|
} |
||||||
|
|
||||||
|
static void on_handshake_done(grpc_exec_ctx *exec_ctx, void *arg, |
||||||
|
grpc_error *error) { |
||||||
|
grpc_handshaker_args *args = arg; |
||||||
|
server_connection_state *connection_state = args->user_data; |
||||||
|
gpr_mu_lock(&connection_state->server_state->mu); |
||||||
|
if (error != GRPC_ERROR_NONE || connection_state->server_state->shutdown) { |
||||||
|
const char *error_str = grpc_error_string(error); |
||||||
|
gpr_log(GPR_ERROR, "Handshaking failed: %s", error_str); |
||||||
|
grpc_error_free_string(error_str); |
||||||
|
if (error == GRPC_ERROR_NONE) { |
||||||
|
// We were shut down after handshaking completed successfully, so
|
||||||
|
// destroy the endpoint here.
|
||||||
|
// TODO(ctiller): It is currently necessary to shutdown endpoints
|
||||||
|
// before destroying them, even if we know that there are no
|
||||||
|
// pending read/write callbacks. This should be fixed, at which
|
||||||
|
// point this can be removed.
|
||||||
|
grpc_endpoint_shutdown(exec_ctx, args->endpoint); |
||||||
|
grpc_endpoint_destroy(exec_ctx, args->endpoint); |
||||||
|
grpc_channel_args_destroy(args->args); |
||||||
|
grpc_slice_buffer_destroy(args->read_buffer); |
||||||
|
gpr_free(args->read_buffer); |
||||||
|
} |
||||||
|
} else { |
||||||
|
grpc_transport *transport = |
||||||
|
grpc_create_chttp2_transport(exec_ctx, args->args, args->endpoint, 0); |
||||||
|
grpc_server_setup_transport( |
||||||
|
exec_ctx, connection_state->server_state->server, transport, |
||||||
|
connection_state->accepting_pollset, args->args); |
||||||
|
grpc_chttp2_transport_start_reading(exec_ctx, transport, args->read_buffer); |
||||||
|
grpc_channel_args_destroy(args->args); |
||||||
|
} |
||||||
|
pending_handshake_manager_remove_locked(connection_state->server_state, |
||||||
|
connection_state->handshake_mgr); |
||||||
|
gpr_mu_unlock(&connection_state->server_state->mu); |
||||||
|
grpc_handshake_manager_destroy(exec_ctx, connection_state->handshake_mgr); |
||||||
|
grpc_tcp_server_unref(exec_ctx, connection_state->server_state->tcp_server); |
||||||
|
gpr_free(connection_state); |
||||||
|
} |
||||||
|
|
||||||
|
static void on_accept(grpc_exec_ctx *exec_ctx, void *arg, grpc_endpoint *tcp, |
||||||
|
grpc_pollset *accepting_pollset, |
||||||
|
grpc_tcp_server_acceptor *acceptor) { |
||||||
|
server_state *state = arg; |
||||||
|
gpr_mu_lock(&state->mu); |
||||||
|
if (state->shutdown) { |
||||||
|
gpr_mu_unlock(&state->mu); |
||||||
|
grpc_endpoint_destroy(exec_ctx, tcp); |
||||||
|
return; |
||||||
|
} |
||||||
|
grpc_handshake_manager *handshake_mgr = grpc_handshake_manager_create(); |
||||||
|
pending_handshake_manager_add_locked(state, handshake_mgr); |
||||||
|
gpr_mu_unlock(&state->mu); |
||||||
|
grpc_tcp_server_ref(state->tcp_server); |
||||||
|
server_connection_state *connection_state = |
||||||
|
gpr_malloc(sizeof(*connection_state)); |
||||||
|
connection_state->server_state = state; |
||||||
|
connection_state->accepting_pollset = accepting_pollset; |
||||||
|
connection_state->acceptor = acceptor; |
||||||
|
connection_state->handshake_mgr = handshake_mgr; |
||||||
|
grpc_chttp2_server_handshaker_factory_create_handshakers( |
||||||
|
exec_ctx, state->handshaker_factory, connection_state->handshake_mgr); |
||||||
|
// TODO(roth): We should really get this timeout value from channel
|
||||||
|
// args instead of hard-coding it.
|
||||||
|
const gpr_timespec deadline = gpr_time_add( |
||||||
|
gpr_now(GPR_CLOCK_MONOTONIC), gpr_time_from_seconds(120, GPR_TIMESPAN)); |
||||||
|
grpc_handshake_manager_do_handshake( |
||||||
|
exec_ctx, connection_state->handshake_mgr, tcp, state->args, deadline, |
||||||
|
acceptor, on_handshake_done, connection_state); |
||||||
|
} |
||||||
|
|
||||||
|
/* Server callback: start listening on our ports */ |
||||||
|
static void server_start_listener(grpc_exec_ctx *exec_ctx, grpc_server *server, |
||||||
|
void *arg, grpc_pollset **pollsets, |
||||||
|
size_t pollset_count) { |
||||||
|
server_state *state = arg; |
||||||
|
gpr_mu_lock(&state->mu); |
||||||
|
state->shutdown = false; |
||||||
|
gpr_mu_unlock(&state->mu); |
||||||
|
grpc_tcp_server_start(exec_ctx, state->tcp_server, pollsets, pollset_count, |
||||||
|
on_accept, state); |
||||||
|
} |
||||||
|
|
||||||
|
static void tcp_server_shutdown_complete(grpc_exec_ctx *exec_ctx, void *arg, |
||||||
|
grpc_error *error) { |
||||||
|
server_state *state = arg; |
||||||
|
/* ensure all threads have unlocked */ |
||||||
|
gpr_mu_lock(&state->mu); |
||||||
|
grpc_closure *destroy_done = state->server_destroy_listener_done; |
||||||
|
GPR_ASSERT(state->shutdown); |
||||||
|
pending_handshake_manager_shutdown_locked(exec_ctx, state); |
||||||
|
gpr_mu_unlock(&state->mu); |
||||||
|
// Flush queued work before destroying handshaker factory, since that
|
||||||
|
// may do a synchronous unref.
|
||||||
|
grpc_exec_ctx_flush(exec_ctx); |
||||||
|
grpc_chttp2_server_handshaker_factory_destroy(exec_ctx, |
||||||
|
state->handshaker_factory); |
||||||
|
if (destroy_done != NULL) { |
||||||
|
destroy_done->cb(exec_ctx, destroy_done->cb_arg, GRPC_ERROR_REF(error)); |
||||||
|
grpc_exec_ctx_flush(exec_ctx); |
||||||
|
} |
||||||
|
grpc_channel_args_destroy(state->args); |
||||||
|
gpr_mu_destroy(&state->mu); |
||||||
|
gpr_free(state); |
||||||
|
} |
||||||
|
|
||||||
|
/* Server callback: destroy the tcp listener (so we don't generate further
|
||||||
|
callbacks) */ |
||||||
|
static void server_destroy_listener(grpc_exec_ctx *exec_ctx, |
||||||
|
grpc_server *server, void *arg, |
||||||
|
grpc_closure *destroy_done) { |
||||||
|
server_state *state = arg; |
||||||
|
gpr_mu_lock(&state->mu); |
||||||
|
state->shutdown = true; |
||||||
|
state->server_destroy_listener_done = destroy_done; |
||||||
|
grpc_tcp_server *tcp_server = state->tcp_server; |
||||||
|
gpr_mu_unlock(&state->mu); |
||||||
|
grpc_tcp_server_shutdown_listeners(exec_ctx, tcp_server); |
||||||
|
grpc_tcp_server_unref(exec_ctx, tcp_server); |
||||||
|
} |
||||||
|
|
||||||
|
grpc_error *grpc_chttp2_server_add_port( |
||||||
|
grpc_exec_ctx *exec_ctx, grpc_server *server, const char *addr, |
||||||
|
grpc_channel_args *args, |
||||||
|
grpc_chttp2_server_handshaker_factory *handshaker_factory, int *port_num) { |
||||||
|
grpc_resolved_addresses *resolved = NULL; |
||||||
|
grpc_tcp_server *tcp_server = NULL; |
||||||
|
size_t i; |
||||||
|
size_t count = 0; |
||||||
|
int port_temp; |
||||||
|
grpc_error *err = GRPC_ERROR_NONE; |
||||||
|
server_state *state = NULL; |
||||||
|
grpc_error **errors = NULL; |
||||||
|
|
||||||
|
*port_num = -1; |
||||||
|
|
||||||
|
/* resolve address */ |
||||||
|
err = grpc_blocking_resolve_address(addr, "https", &resolved); |
||||||
|
if (err != GRPC_ERROR_NONE) { |
||||||
|
goto error; |
||||||
|
} |
||||||
|
state = gpr_malloc(sizeof(*state)); |
||||||
|
memset(state, 0, sizeof(*state)); |
||||||
|
grpc_closure_init(&state->tcp_server_shutdown_complete, |
||||||
|
tcp_server_shutdown_complete, state); |
||||||
|
err = |
||||||
|
grpc_tcp_server_create(exec_ctx, &state->tcp_server_shutdown_complete, |
||||||
|
args, &tcp_server); |
||||||
|
if (err != GRPC_ERROR_NONE) { |
||||||
|
goto error; |
||||||
|
} |
||||||
|
|
||||||
|
state->server = server; |
||||||
|
state->tcp_server = tcp_server; |
||||||
|
state->args = args; |
||||||
|
state->handshaker_factory = handshaker_factory; |
||||||
|
state->shutdown = true; |
||||||
|
gpr_mu_init(&state->mu); |
||||||
|
|
||||||
|
const size_t naddrs = resolved->naddrs; |
||||||
|
errors = gpr_malloc(sizeof(*errors) * naddrs); |
||||||
|
for (i = 0; i < naddrs; i++) { |
||||||
|
errors[i] = |
||||||
|
grpc_tcp_server_add_port(tcp_server, &resolved->addrs[i], &port_temp); |
||||||
|
if (errors[i] == GRPC_ERROR_NONE) { |
||||||
|
if (*port_num == -1) { |
||||||
|
*port_num = port_temp; |
||||||
|
} else { |
||||||
|
GPR_ASSERT(*port_num == port_temp); |
||||||
|
} |
||||||
|
count++; |
||||||
|
} |
||||||
|
} |
||||||
|
if (count == 0) { |
||||||
|
char *msg; |
||||||
|
gpr_asprintf(&msg, "No address added out of total %" PRIuPTR " resolved", |
||||||
|
naddrs); |
||||||
|
err = GRPC_ERROR_CREATE_REFERENCING(msg, errors, naddrs); |
||||||
|
gpr_free(msg); |
||||||
|
goto error; |
||||||
|
} else if (count != naddrs) { |
||||||
|
char *msg; |
||||||
|
gpr_asprintf(&msg, "Only %" PRIuPTR |
||||||
|
" addresses added out of total %" PRIuPTR " resolved", |
||||||
|
count, naddrs); |
||||||
|
err = GRPC_ERROR_CREATE_REFERENCING(msg, errors, naddrs); |
||||||
|
gpr_free(msg); |
||||||
|
|
||||||
|
const char *warning_message = grpc_error_string(err); |
||||||
|
gpr_log(GPR_INFO, "WARNING: %s", warning_message); |
||||||
|
grpc_error_free_string(warning_message); |
||||||
|
/* we managed to bind some addresses: continue */ |
||||||
|
} |
||||||
|
grpc_resolved_addresses_destroy(resolved); |
||||||
|
|
||||||
|
/* Register with the server only upon success */ |
||||||
|
grpc_server_add_listener(exec_ctx, server, state, server_start_listener, |
||||||
|
server_destroy_listener); |
||||||
|
goto done; |
||||||
|
|
||||||
|
/* Error path: cleanup and return */ |
||||||
|
error: |
||||||
|
GPR_ASSERT(err != GRPC_ERROR_NONE); |
||||||
|
if (resolved) { |
||||||
|
grpc_resolved_addresses_destroy(resolved); |
||||||
|
} |
||||||
|
if (tcp_server) { |
||||||
|
grpc_tcp_server_unref(exec_ctx, tcp_server); |
||||||
|
} |
||||||
|
grpc_channel_args_destroy(state->args); |
||||||
|
grpc_chttp2_server_handshaker_factory_destroy(exec_ctx, |
||||||
|
state->handshaker_factory); |
||||||
|
gpr_free(state); |
||||||
|
*port_num = 0; |
||||||
|
|
||||||
|
done: |
||||||
|
if (errors != NULL) { |
||||||
|
for (i = 0; i < naddrs; i++) { |
||||||
|
GRPC_ERROR_UNREF(errors[i]); |
||||||
|
} |
||||||
|
gpr_free(errors); |
||||||
|
} |
||||||
|
return err; |
||||||
|
} |
@ -0,0 +1,79 @@ |
|||||||
|
/*
|
||||||
|
* |
||||||
|
* Copyright 2016, Google Inc. |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* * Redistributions in binary form must reproduce the above |
||||||
|
* copyright notice, this list of conditions and the following disclaimer |
||||||
|
* in the documentation and/or other materials provided with the |
||||||
|
* distribution. |
||||||
|
* * Neither the name of Google Inc. nor the names of its |
||||||
|
* contributors may be used to endorse or promote products derived from |
||||||
|
* this software without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_SERVER_CHTTP2_SERVER_H |
||||||
|
#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_SERVER_CHTTP2_SERVER_H |
||||||
|
|
||||||
|
#include <grpc/impl/codegen/grpc_types.h> |
||||||
|
|
||||||
|
#include "src/core/lib/channel/handshaker.h" |
||||||
|
#include "src/core/lib/iomgr/exec_ctx.h" |
||||||
|
|
||||||
|
/// A server handshaker factory is used to create handshakers for server
|
||||||
|
/// connections.
|
||||||
|
typedef struct grpc_chttp2_server_handshaker_factory |
||||||
|
grpc_chttp2_server_handshaker_factory; |
||||||
|
|
||||||
|
typedef struct { |
||||||
|
void (*create_handshakers)( |
||||||
|
grpc_exec_ctx *exec_ctx, |
||||||
|
grpc_chttp2_server_handshaker_factory *handshaker_factory, |
||||||
|
grpc_handshake_manager *handshake_mgr); |
||||||
|
void (*destroy)(grpc_exec_ctx *exec_ctx, |
||||||
|
grpc_chttp2_server_handshaker_factory *handshaker_factory); |
||||||
|
} grpc_chttp2_server_handshaker_factory_vtable; |
||||||
|
|
||||||
|
struct grpc_chttp2_server_handshaker_factory { |
||||||
|
const grpc_chttp2_server_handshaker_factory_vtable *vtable; |
||||||
|
}; |
||||||
|
|
||||||
|
void grpc_chttp2_server_handshaker_factory_create_handshakers( |
||||||
|
grpc_exec_ctx *exec_ctx, |
||||||
|
grpc_chttp2_server_handshaker_factory *handshaker_factory, |
||||||
|
grpc_handshake_manager *handshake_mgr); |
||||||
|
|
||||||
|
void grpc_chttp2_server_handshaker_factory_destroy( |
||||||
|
grpc_exec_ctx *exec_ctx, |
||||||
|
grpc_chttp2_server_handshaker_factory *handshaker_factory); |
||||||
|
|
||||||
|
/// Adds a port to \a server. Sets \a port_num to the port number.
|
||||||
|
/// If \a handshaker_factory is not NULL, it will be used to create
|
||||||
|
/// handshakers for the port.
|
||||||
|
/// Takes ownership of \a args and \a handshaker_factory.
|
||||||
|
grpc_error *grpc_chttp2_server_add_port( |
||||||
|
grpc_exec_ctx *exec_ctx, grpc_server *server, const char *addr, |
||||||
|
grpc_channel_args *args, |
||||||
|
grpc_chttp2_server_handshaker_factory *handshaker_factory, |
||||||
|
int *port_num); |
||||||
|
|
||||||
|
#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_SERVER_CHTTP2_SERVER_H */ |
@ -1,374 +0,0 @@ |
|||||||
/*
|
|
||||||
* |
|
||||||
* Copyright 2015, Google Inc. |
|
||||||
* All rights reserved. |
|
||||||
* |
|
||||||
* Redistribution and use in source and binary forms, with or without |
|
||||||
* modification, are permitted provided that the following conditions are |
|
||||||
* met: |
|
||||||
* |
|
||||||
* * Redistributions of source code must retain the above copyright |
|
||||||
* notice, this list of conditions and the following disclaimer. |
|
||||||
* * Redistributions in binary form must reproduce the above |
|
||||||
* copyright notice, this list of conditions and the following disclaimer |
|
||||||
* in the documentation and/or other materials provided with the |
|
||||||
* distribution. |
|
||||||
* * Neither the name of Google Inc. nor the names of its |
|
||||||
* contributors may be used to endorse or promote products derived from |
|
||||||
* this software without specific prior written permission. |
|
||||||
* |
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
||||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
||||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
||||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
||||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
||||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
||||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
||||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
||||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
||||||
* |
|
||||||
*/ |
|
||||||
|
|
||||||
#include "src/core/lib/security/transport/handshake.h" |
|
||||||
|
|
||||||
#include <stdbool.h> |
|
||||||
#include <string.h> |
|
||||||
|
|
||||||
#include <grpc/slice_buffer.h> |
|
||||||
#include <grpc/support/alloc.h> |
|
||||||
#include <grpc/support/log.h> |
|
||||||
#include "src/core/lib/iomgr/timer.h" |
|
||||||
#include "src/core/lib/security/context/security_context.h" |
|
||||||
#include "src/core/lib/security/transport/secure_endpoint.h" |
|
||||||
#include "src/core/lib/security/transport/tsi_error.h" |
|
||||||
|
|
||||||
#define GRPC_INITIAL_HANDSHAKE_BUFFER_SIZE 256 |
|
||||||
|
|
||||||
typedef struct { |
|
||||||
grpc_security_connector *connector; |
|
||||||
tsi_handshaker *handshaker; |
|
||||||
bool is_client_side; |
|
||||||
unsigned char *handshake_buffer; |
|
||||||
size_t handshake_buffer_size; |
|
||||||
grpc_endpoint *wrapped_endpoint; |
|
||||||
grpc_endpoint *secure_endpoint; |
|
||||||
grpc_slice_buffer left_overs; |
|
||||||
grpc_slice_buffer incoming; |
|
||||||
grpc_slice_buffer outgoing; |
|
||||||
grpc_security_handshake_done_cb cb; |
|
||||||
void *user_data; |
|
||||||
grpc_closure on_handshake_data_sent_to_peer; |
|
||||||
grpc_closure on_handshake_data_received_from_peer; |
|
||||||
grpc_auth_context *auth_context; |
|
||||||
grpc_timer timer; |
|
||||||
gpr_refcount refs; |
|
||||||
} grpc_security_handshake; |
|
||||||
|
|
||||||
static void on_handshake_data_received_from_peer(grpc_exec_ctx *exec_ctx, |
|
||||||
void *setup, |
|
||||||
grpc_error *error); |
|
||||||
|
|
||||||
static void on_handshake_data_sent_to_peer(grpc_exec_ctx *exec_ctx, void *setup, |
|
||||||
grpc_error *error); |
|
||||||
|
|
||||||
static void security_connector_remove_handshake(grpc_security_handshake *h) { |
|
||||||
GPR_ASSERT(!h->is_client_side); |
|
||||||
grpc_security_connector_handshake_list *node; |
|
||||||
grpc_security_connector_handshake_list *tmp; |
|
||||||
grpc_server_security_connector *sc = |
|
||||||
(grpc_server_security_connector *)h->connector; |
|
||||||
gpr_mu_lock(&sc->mu); |
|
||||||
node = sc->handshaking_handshakes; |
|
||||||
if (node && node->handshake == h) { |
|
||||||
sc->handshaking_handshakes = node->next; |
|
||||||
gpr_free(node); |
|
||||||
gpr_mu_unlock(&sc->mu); |
|
||||||
return; |
|
||||||
} |
|
||||||
while (node) { |
|
||||||
if (node->next->handshake == h) { |
|
||||||
tmp = node->next; |
|
||||||
node->next = node->next->next; |
|
||||||
gpr_free(tmp); |
|
||||||
gpr_mu_unlock(&sc->mu); |
|
||||||
return; |
|
||||||
} |
|
||||||
node = node->next; |
|
||||||
} |
|
||||||
gpr_mu_unlock(&sc->mu); |
|
||||||
} |
|
||||||
|
|
||||||
static void unref_handshake(grpc_security_handshake *h) { |
|
||||||
if (gpr_unref(&h->refs)) { |
|
||||||
if (h->handshaker != NULL) tsi_handshaker_destroy(h->handshaker); |
|
||||||
if (h->handshake_buffer != NULL) gpr_free(h->handshake_buffer); |
|
||||||
grpc_slice_buffer_destroy(&h->left_overs); |
|
||||||
grpc_slice_buffer_destroy(&h->outgoing); |
|
||||||
grpc_slice_buffer_destroy(&h->incoming); |
|
||||||
GRPC_AUTH_CONTEXT_UNREF(h->auth_context, "handshake"); |
|
||||||
GRPC_SECURITY_CONNECTOR_UNREF(h->connector, "handshake"); |
|
||||||
gpr_free(h); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
static void security_handshake_done(grpc_exec_ctx *exec_ctx, |
|
||||||
grpc_security_handshake *h, |
|
||||||
grpc_error *error) { |
|
||||||
grpc_timer_cancel(exec_ctx, &h->timer); |
|
||||||
if (!h->is_client_side) { |
|
||||||
security_connector_remove_handshake(h); |
|
||||||
} |
|
||||||
if (error == GRPC_ERROR_NONE) { |
|
||||||
h->cb(exec_ctx, h->user_data, GRPC_SECURITY_OK, h->secure_endpoint, |
|
||||||
h->auth_context); |
|
||||||
} else { |
|
||||||
const char *msg = grpc_error_string(error); |
|
||||||
gpr_log(GPR_DEBUG, "Security handshake failed: %s", msg); |
|
||||||
grpc_error_free_string(msg); |
|
||||||
|
|
||||||
if (h->secure_endpoint != NULL) { |
|
||||||
grpc_endpoint_shutdown(exec_ctx, h->secure_endpoint); |
|
||||||
grpc_endpoint_destroy(exec_ctx, h->secure_endpoint); |
|
||||||
} else { |
|
||||||
grpc_endpoint_destroy(exec_ctx, h->wrapped_endpoint); |
|
||||||
} |
|
||||||
h->cb(exec_ctx, h->user_data, GRPC_SECURITY_ERROR, NULL, NULL); |
|
||||||
} |
|
||||||
unref_handshake(h); |
|
||||||
GRPC_ERROR_UNREF(error); |
|
||||||
} |
|
||||||
|
|
||||||
static void on_peer_checked(grpc_exec_ctx *exec_ctx, void *user_data, |
|
||||||
grpc_security_status status, |
|
||||||
grpc_auth_context *auth_context) { |
|
||||||
grpc_security_handshake *h = user_data; |
|
||||||
tsi_frame_protector *protector; |
|
||||||
tsi_result result; |
|
||||||
if (status != GRPC_SECURITY_OK) { |
|
||||||
security_handshake_done( |
|
||||||
exec_ctx, h, |
|
||||||
grpc_error_set_int(GRPC_ERROR_CREATE("Error checking peer."), |
|
||||||
GRPC_ERROR_INT_SECURITY_STATUS, status)); |
|
||||||
return; |
|
||||||
} |
|
||||||
h->auth_context = GRPC_AUTH_CONTEXT_REF(auth_context, "handshake"); |
|
||||||
result = |
|
||||||
tsi_handshaker_create_frame_protector(h->handshaker, NULL, &protector); |
|
||||||
if (result != TSI_OK) { |
|
||||||
security_handshake_done( |
|
||||||
exec_ctx, h, |
|
||||||
grpc_set_tsi_error_result( |
|
||||||
GRPC_ERROR_CREATE("Frame protector creation failed"), result)); |
|
||||||
return; |
|
||||||
} |
|
||||||
h->secure_endpoint = |
|
||||||
grpc_secure_endpoint_create(protector, h->wrapped_endpoint, |
|
||||||
h->left_overs.slices, h->left_overs.count); |
|
||||||
h->left_overs.count = 0; |
|
||||||
h->left_overs.length = 0; |
|
||||||
security_handshake_done(exec_ctx, h, GRPC_ERROR_NONE); |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
static void check_peer(grpc_exec_ctx *exec_ctx, grpc_security_handshake *h) { |
|
||||||
tsi_peer peer; |
|
||||||
tsi_result result = tsi_handshaker_extract_peer(h->handshaker, &peer); |
|
||||||
|
|
||||||
if (result != TSI_OK) { |
|
||||||
security_handshake_done( |
|
||||||
exec_ctx, h, grpc_set_tsi_error_result( |
|
||||||
GRPC_ERROR_CREATE("Peer extraction failed"), result)); |
|
||||||
return; |
|
||||||
} |
|
||||||
grpc_security_connector_check_peer(exec_ctx, h->connector, peer, |
|
||||||
on_peer_checked, h); |
|
||||||
} |
|
||||||
|
|
||||||
static void send_handshake_bytes_to_peer(grpc_exec_ctx *exec_ctx, |
|
||||||
grpc_security_handshake *h) { |
|
||||||
size_t offset = 0; |
|
||||||
tsi_result result = TSI_OK; |
|
||||||
grpc_slice to_send; |
|
||||||
|
|
||||||
do { |
|
||||||
size_t to_send_size = h->handshake_buffer_size - offset; |
|
||||||
result = tsi_handshaker_get_bytes_to_send_to_peer( |
|
||||||
h->handshaker, h->handshake_buffer + offset, &to_send_size); |
|
||||||
offset += to_send_size; |
|
||||||
if (result == TSI_INCOMPLETE_DATA) { |
|
||||||
h->handshake_buffer_size *= 2; |
|
||||||
h->handshake_buffer = |
|
||||||
gpr_realloc(h->handshake_buffer, h->handshake_buffer_size); |
|
||||||
} |
|
||||||
} while (result == TSI_INCOMPLETE_DATA); |
|
||||||
|
|
||||||
if (result != TSI_OK) { |
|
||||||
security_handshake_done(exec_ctx, h, |
|
||||||
grpc_set_tsi_error_result( |
|
||||||
GRPC_ERROR_CREATE("Handshake failed"), result)); |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
to_send = |
|
||||||
grpc_slice_from_copied_buffer((const char *)h->handshake_buffer, offset); |
|
||||||
grpc_slice_buffer_reset_and_unref(&h->outgoing); |
|
||||||
grpc_slice_buffer_add(&h->outgoing, to_send); |
|
||||||
/* TODO(klempner,jboeuf): This should probably use the client setup
|
|
||||||
deadline */ |
|
||||||
grpc_endpoint_write(exec_ctx, h->wrapped_endpoint, &h->outgoing, |
|
||||||
&h->on_handshake_data_sent_to_peer); |
|
||||||
} |
|
||||||
|
|
||||||
static void on_handshake_data_received_from_peer(grpc_exec_ctx *exec_ctx, |
|
||||||
void *handshake, |
|
||||||
grpc_error *error) { |
|
||||||
grpc_security_handshake *h = handshake; |
|
||||||
size_t consumed_slice_size = 0; |
|
||||||
tsi_result result = TSI_OK; |
|
||||||
size_t i; |
|
||||||
size_t num_left_overs; |
|
||||||
int has_left_overs_in_current_slice = 0; |
|
||||||
|
|
||||||
if (error != GRPC_ERROR_NONE) { |
|
||||||
security_handshake_done( |
|
||||||
exec_ctx, h, |
|
||||||
GRPC_ERROR_CREATE_REFERENCING("Handshake read failed", &error, 1)); |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
for (i = 0; i < h->incoming.count; i++) { |
|
||||||
consumed_slice_size = GRPC_SLICE_LENGTH(h->incoming.slices[i]); |
|
||||||
result = tsi_handshaker_process_bytes_from_peer( |
|
||||||
h->handshaker, GRPC_SLICE_START_PTR(h->incoming.slices[i]), |
|
||||||
&consumed_slice_size); |
|
||||||
if (!tsi_handshaker_is_in_progress(h->handshaker)) break; |
|
||||||
} |
|
||||||
|
|
||||||
if (tsi_handshaker_is_in_progress(h->handshaker)) { |
|
||||||
/* We may need more data. */ |
|
||||||
if (result == TSI_INCOMPLETE_DATA) { |
|
||||||
grpc_endpoint_read(exec_ctx, h->wrapped_endpoint, &h->incoming, |
|
||||||
&h->on_handshake_data_received_from_peer); |
|
||||||
return; |
|
||||||
} else { |
|
||||||
send_handshake_bytes_to_peer(exec_ctx, h); |
|
||||||
return; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if (result != TSI_OK) { |
|
||||||
security_handshake_done(exec_ctx, h, |
|
||||||
grpc_set_tsi_error_result( |
|
||||||
GRPC_ERROR_CREATE("Handshake failed"), result)); |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
/* Handshake is done and successful this point. */ |
|
||||||
has_left_overs_in_current_slice = |
|
||||||
(consumed_slice_size < GRPC_SLICE_LENGTH(h->incoming.slices[i])); |
|
||||||
num_left_overs = |
|
||||||
(has_left_overs_in_current_slice ? 1 : 0) + h->incoming.count - i - 1; |
|
||||||
if (num_left_overs == 0) { |
|
||||||
check_peer(exec_ctx, h); |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
/* Put the leftovers in our buffer (ownership transfered). */ |
|
||||||
if (has_left_overs_in_current_slice) { |
|
||||||
grpc_slice_buffer_add( |
|
||||||
&h->left_overs, |
|
||||||
grpc_slice_split_tail(&h->incoming.slices[i], consumed_slice_size)); |
|
||||||
grpc_slice_unref( |
|
||||||
h->incoming.slices[i]); /* split_tail above increments refcount. */ |
|
||||||
} |
|
||||||
grpc_slice_buffer_addn( |
|
||||||
&h->left_overs, &h->incoming.slices[i + 1], |
|
||||||
num_left_overs - (size_t)has_left_overs_in_current_slice); |
|
||||||
check_peer(exec_ctx, h); |
|
||||||
} |
|
||||||
|
|
||||||
/* If handshake is NULL, the handshake is done. */ |
|
||||||
static void on_handshake_data_sent_to_peer(grpc_exec_ctx *exec_ctx, |
|
||||||
void *handshake, grpc_error *error) { |
|
||||||
grpc_security_handshake *h = handshake; |
|
||||||
|
|
||||||
/* Make sure that write is OK. */ |
|
||||||
if (error != GRPC_ERROR_NONE) { |
|
||||||
if (handshake != NULL) |
|
||||||
security_handshake_done( |
|
||||||
exec_ctx, h, |
|
||||||
GRPC_ERROR_CREATE_REFERENCING("Handshake write failed", &error, 1)); |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
/* We may be done. */ |
|
||||||
if (tsi_handshaker_is_in_progress(h->handshaker)) { |
|
||||||
/* TODO(klempner,jboeuf): This should probably use the client setup
|
|
||||||
deadline */ |
|
||||||
grpc_endpoint_read(exec_ctx, h->wrapped_endpoint, &h->incoming, |
|
||||||
&h->on_handshake_data_received_from_peer); |
|
||||||
} else { |
|
||||||
check_peer(exec_ctx, h); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
static void on_timeout(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { |
|
||||||
grpc_security_handshake *h = arg; |
|
||||||
if (error == GRPC_ERROR_NONE) { |
|
||||||
grpc_endpoint_shutdown(exec_ctx, h->wrapped_endpoint); |
|
||||||
} |
|
||||||
unref_handshake(h); |
|
||||||
} |
|
||||||
|
|
||||||
void grpc_do_security_handshake( |
|
||||||
grpc_exec_ctx *exec_ctx, tsi_handshaker *handshaker, |
|
||||||
grpc_security_connector *connector, bool is_client_side, |
|
||||||
grpc_endpoint *nonsecure_endpoint, grpc_slice_buffer *read_buffer, |
|
||||||
gpr_timespec deadline, grpc_security_handshake_done_cb cb, |
|
||||||
void *user_data) { |
|
||||||
grpc_security_connector_handshake_list *handshake_node; |
|
||||||
grpc_security_handshake *h = gpr_malloc(sizeof(grpc_security_handshake)); |
|
||||||
memset(h, 0, sizeof(grpc_security_handshake)); |
|
||||||
h->handshaker = handshaker; |
|
||||||
h->connector = GRPC_SECURITY_CONNECTOR_REF(connector, "handshake"); |
|
||||||
h->is_client_side = is_client_side; |
|
||||||
h->handshake_buffer_size = GRPC_INITIAL_HANDSHAKE_BUFFER_SIZE; |
|
||||||
h->handshake_buffer = gpr_malloc(h->handshake_buffer_size); |
|
||||||
h->wrapped_endpoint = nonsecure_endpoint; |
|
||||||
h->user_data = user_data; |
|
||||||
h->cb = cb; |
|
||||||
gpr_ref_init(&h->refs, 2); /* timer and handshake proper each get a ref */ |
|
||||||
grpc_closure_init(&h->on_handshake_data_sent_to_peer, |
|
||||||
on_handshake_data_sent_to_peer, h); |
|
||||||
grpc_closure_init(&h->on_handshake_data_received_from_peer, |
|
||||||
on_handshake_data_received_from_peer, h); |
|
||||||
grpc_slice_buffer_init(&h->left_overs); |
|
||||||
grpc_slice_buffer_init(&h->outgoing); |
|
||||||
grpc_slice_buffer_init(&h->incoming); |
|
||||||
if (read_buffer != NULL) { |
|
||||||
grpc_slice_buffer_move_into(read_buffer, &h->incoming); |
|
||||||
gpr_free(read_buffer); |
|
||||||
} |
|
||||||
if (!is_client_side) { |
|
||||||
grpc_server_security_connector *server_connector = |
|
||||||
(grpc_server_security_connector *)connector; |
|
||||||
handshake_node = gpr_malloc(sizeof(grpc_security_connector_handshake_list)); |
|
||||||
handshake_node->handshake = h; |
|
||||||
gpr_mu_lock(&server_connector->mu); |
|
||||||
handshake_node->next = server_connector->handshaking_handshakes; |
|
||||||
server_connector->handshaking_handshakes = handshake_node; |
|
||||||
gpr_mu_unlock(&server_connector->mu); |
|
||||||
} |
|
||||||
send_handshake_bytes_to_peer(exec_ctx, h); |
|
||||||
grpc_timer_init(exec_ctx, &h->timer, |
|
||||||
gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC), |
|
||||||
on_timeout, h, gpr_now(GPR_CLOCK_MONOTONIC)); |
|
||||||
} |
|
||||||
|
|
||||||
void grpc_security_handshake_shutdown(grpc_exec_ctx *exec_ctx, |
|
||||||
void *handshake) { |
|
||||||
grpc_security_handshake *h = handshake; |
|
||||||
grpc_endpoint_shutdown(exec_ctx, h->wrapped_endpoint); |
|
||||||
} |
|
@ -0,0 +1,450 @@ |
|||||||
|
/*
|
||||||
|
* |
||||||
|
* Copyright 2015, Google Inc. |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* * Redistributions in binary form must reproduce the above |
||||||
|
* copyright notice, this list of conditions and the following disclaimer |
||||||
|
* in the documentation and/or other materials provided with the |
||||||
|
* distribution. |
||||||
|
* * Neither the name of Google Inc. nor the names of its |
||||||
|
* contributors may be used to endorse or promote products derived from |
||||||
|
* this software without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "src/core/lib/security/transport/security_handshaker.h" |
||||||
|
|
||||||
|
#include <stdbool.h> |
||||||
|
#include <string.h> |
||||||
|
|
||||||
|
#include <grpc/slice_buffer.h> |
||||||
|
#include <grpc/support/alloc.h> |
||||||
|
#include <grpc/support/log.h> |
||||||
|
|
||||||
|
#include "src/core/lib/channel/channel_args.h" |
||||||
|
#include "src/core/lib/channel/handshaker.h" |
||||||
|
#include "src/core/lib/security/context/security_context.h" |
||||||
|
#include "src/core/lib/security/transport/secure_endpoint.h" |
||||||
|
#include "src/core/lib/security/transport/tsi_error.h" |
||||||
|
|
||||||
|
#define GRPC_INITIAL_HANDSHAKE_BUFFER_SIZE 256 |
||||||
|
|
||||||
|
typedef struct { |
||||||
|
grpc_handshaker base; |
||||||
|
|
||||||
|
// State set at creation time.
|
||||||
|
tsi_handshaker *handshaker; |
||||||
|
grpc_security_connector *connector; |
||||||
|
|
||||||
|
gpr_mu mu; |
||||||
|
gpr_refcount refs; |
||||||
|
|
||||||
|
bool shutdown; |
||||||
|
// Endpoint and read buffer to destroy after a shutdown.
|
||||||
|
grpc_endpoint *endpoint_to_destroy; |
||||||
|
grpc_slice_buffer *read_buffer_to_destroy; |
||||||
|
|
||||||
|
// State saved while performing the handshake.
|
||||||
|
grpc_handshaker_args *args; |
||||||
|
grpc_closure *on_handshake_done; |
||||||
|
|
||||||
|
unsigned char *handshake_buffer; |
||||||
|
size_t handshake_buffer_size; |
||||||
|
grpc_slice_buffer left_overs; |
||||||
|
grpc_slice_buffer outgoing; |
||||||
|
grpc_closure on_handshake_data_sent_to_peer; |
||||||
|
grpc_closure on_handshake_data_received_from_peer; |
||||||
|
grpc_closure on_peer_checked; |
||||||
|
grpc_auth_context *auth_context; |
||||||
|
} security_handshaker; |
||||||
|
|
||||||
|
static void security_handshaker_unref(grpc_exec_ctx *exec_ctx, |
||||||
|
security_handshaker *h) { |
||||||
|
if (gpr_unref(&h->refs)) { |
||||||
|
gpr_mu_destroy(&h->mu); |
||||||
|
tsi_handshaker_destroy(h->handshaker); |
||||||
|
if (h->endpoint_to_destroy != NULL) { |
||||||
|
grpc_endpoint_destroy(exec_ctx, h->endpoint_to_destroy); |
||||||
|
} |
||||||
|
if (h->read_buffer_to_destroy != NULL) { |
||||||
|
grpc_slice_buffer_destroy(h->read_buffer_to_destroy); |
||||||
|
gpr_free(h->read_buffer_to_destroy); |
||||||
|
} |
||||||
|
gpr_free(h->handshake_buffer); |
||||||
|
grpc_slice_buffer_destroy(&h->left_overs); |
||||||
|
grpc_slice_buffer_destroy(&h->outgoing); |
||||||
|
GRPC_AUTH_CONTEXT_UNREF(h->auth_context, "handshake"); |
||||||
|
GRPC_SECURITY_CONNECTOR_UNREF(h->connector, "handshake"); |
||||||
|
gpr_free(h); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Set args fields to NULL, saving the endpoint and read buffer for
|
||||||
|
// later destruction.
|
||||||
|
static void cleanup_args_for_failure_locked(security_handshaker *h) { |
||||||
|
h->endpoint_to_destroy = h->args->endpoint; |
||||||
|
h->args->endpoint = NULL; |
||||||
|
h->read_buffer_to_destroy = h->args->read_buffer; |
||||||
|
h->args->read_buffer = NULL; |
||||||
|
grpc_channel_args_destroy(h->args->args); |
||||||
|
h->args->args = NULL; |
||||||
|
} |
||||||
|
|
||||||
|
// If the handshake failed or we're shutting down, clean up and invoke the
|
||||||
|
// callback with the error.
|
||||||
|
static void security_handshake_failed_locked(grpc_exec_ctx *exec_ctx, |
||||||
|
security_handshaker *h, |
||||||
|
grpc_error *error) { |
||||||
|
if (error == GRPC_ERROR_NONE) { |
||||||
|
// If we were shut down after the handshake succeeded but before an
|
||||||
|
// endpoint callback was invoked, we need to generate our own error.
|
||||||
|
error = GRPC_ERROR_CREATE("Handshaker shutdown"); |
||||||
|
} |
||||||
|
const char *msg = grpc_error_string(error); |
||||||
|
gpr_log(GPR_DEBUG, "Security handshake failed: %s", msg); |
||||||
|
grpc_error_free_string(msg); |
||||||
|
if (!h->shutdown) { |
||||||
|
// TODO(ctiller): It is currently necessary to shutdown endpoints
|
||||||
|
// before destroying them, even if we know that there are no
|
||||||
|
// pending read/write callbacks. This should be fixed, at which
|
||||||
|
// point this can be removed.
|
||||||
|
grpc_endpoint_shutdown(exec_ctx, h->args->endpoint); |
||||||
|
// Not shutting down, so the write failed. Clean up before
|
||||||
|
// invoking the callback.
|
||||||
|
cleanup_args_for_failure_locked(h); |
||||||
|
} |
||||||
|
// Invoke callback.
|
||||||
|
grpc_exec_ctx_sched(exec_ctx, h->on_handshake_done, error, NULL); |
||||||
|
} |
||||||
|
|
||||||
|
static void on_peer_checked(grpc_exec_ctx *exec_ctx, void *arg, |
||||||
|
grpc_error *error) { |
||||||
|
security_handshaker *h = arg; |
||||||
|
gpr_mu_lock(&h->mu); |
||||||
|
if (error != GRPC_ERROR_NONE || h->shutdown) { |
||||||
|
security_handshake_failed_locked(exec_ctx, h, GRPC_ERROR_REF(error)); |
||||||
|
goto done; |
||||||
|
} |
||||||
|
// Get frame protector.
|
||||||
|
tsi_frame_protector *protector; |
||||||
|
tsi_result result = |
||||||
|
tsi_handshaker_create_frame_protector(h->handshaker, NULL, &protector); |
||||||
|
if (result != TSI_OK) { |
||||||
|
error = grpc_set_tsi_error_result( |
||||||
|
GRPC_ERROR_CREATE("Frame protector creation failed"), result); |
||||||
|
security_handshake_failed_locked(exec_ctx, h, error); |
||||||
|
goto done; |
||||||
|
} |
||||||
|
// Success.
|
||||||
|
// Create secure endpoint.
|
||||||
|
h->args->endpoint = grpc_secure_endpoint_create( |
||||||
|
protector, h->args->endpoint, h->left_overs.slices, h->left_overs.count); |
||||||
|
h->left_overs.count = 0; |
||||||
|
h->left_overs.length = 0; |
||||||
|
// Clear out the read buffer before it gets passed to the transport,
|
||||||
|
// since any excess bytes were already copied to h->left_overs.
|
||||||
|
grpc_slice_buffer_reset_and_unref(h->args->read_buffer); |
||||||
|
// Add auth context to channel args.
|
||||||
|
grpc_arg auth_context_arg = grpc_auth_context_to_arg(h->auth_context); |
||||||
|
grpc_channel_args *tmp_args = h->args->args; |
||||||
|
h->args->args = |
||||||
|
grpc_channel_args_copy_and_add(tmp_args, &auth_context_arg, 1); |
||||||
|
grpc_channel_args_destroy(tmp_args); |
||||||
|
// Invoke callback.
|
||||||
|
grpc_exec_ctx_sched(exec_ctx, h->on_handshake_done, GRPC_ERROR_NONE, NULL); |
||||||
|
// Set shutdown to true so that subsequent calls to
|
||||||
|
// security_handshaker_shutdown() do nothing.
|
||||||
|
h->shutdown = true; |
||||||
|
done: |
||||||
|
gpr_mu_unlock(&h->mu); |
||||||
|
security_handshaker_unref(exec_ctx, h); |
||||||
|
} |
||||||
|
|
||||||
|
static grpc_error *check_peer_locked(grpc_exec_ctx *exec_ctx, |
||||||
|
security_handshaker *h) { |
||||||
|
tsi_peer peer; |
||||||
|
tsi_result result = tsi_handshaker_extract_peer(h->handshaker, &peer); |
||||||
|
if (result != TSI_OK) { |
||||||
|
return grpc_set_tsi_error_result( |
||||||
|
GRPC_ERROR_CREATE("Peer extraction failed"), result); |
||||||
|
} |
||||||
|
grpc_security_connector_check_peer(exec_ctx, h->connector, peer, |
||||||
|
&h->auth_context, &h->on_peer_checked); |
||||||
|
return GRPC_ERROR_NONE; |
||||||
|
} |
||||||
|
|
||||||
|
static grpc_error *send_handshake_bytes_to_peer_locked(grpc_exec_ctx *exec_ctx, |
||||||
|
security_handshaker *h) { |
||||||
|
// Get data to send.
|
||||||
|
tsi_result result = TSI_OK; |
||||||
|
size_t offset = 0; |
||||||
|
do { |
||||||
|
size_t to_send_size = h->handshake_buffer_size - offset; |
||||||
|
result = tsi_handshaker_get_bytes_to_send_to_peer( |
||||||
|
h->handshaker, h->handshake_buffer + offset, &to_send_size); |
||||||
|
offset += to_send_size; |
||||||
|
if (result == TSI_INCOMPLETE_DATA) { |
||||||
|
h->handshake_buffer_size *= 2; |
||||||
|
h->handshake_buffer = |
||||||
|
gpr_realloc(h->handshake_buffer, h->handshake_buffer_size); |
||||||
|
} |
||||||
|
} while (result == TSI_INCOMPLETE_DATA); |
||||||
|
if (result != TSI_OK) { |
||||||
|
return grpc_set_tsi_error_result(GRPC_ERROR_CREATE("Handshake failed"), |
||||||
|
result); |
||||||
|
} |
||||||
|
// Send data.
|
||||||
|
grpc_slice to_send = |
||||||
|
grpc_slice_from_copied_buffer((const char *)h->handshake_buffer, offset); |
||||||
|
grpc_slice_buffer_reset_and_unref(&h->outgoing); |
||||||
|
grpc_slice_buffer_add(&h->outgoing, to_send); |
||||||
|
grpc_endpoint_write(exec_ctx, h->args->endpoint, &h->outgoing, |
||||||
|
&h->on_handshake_data_sent_to_peer); |
||||||
|
return GRPC_ERROR_NONE; |
||||||
|
} |
||||||
|
|
||||||
|
static void on_handshake_data_received_from_peer(grpc_exec_ctx *exec_ctx, |
||||||
|
void *arg, grpc_error *error) { |
||||||
|
security_handshaker *h = arg; |
||||||
|
gpr_mu_lock(&h->mu); |
||||||
|
if (error != GRPC_ERROR_NONE || h->shutdown) { |
||||||
|
security_handshake_failed_locked( |
||||||
|
exec_ctx, h, |
||||||
|
GRPC_ERROR_CREATE_REFERENCING("Handshake read failed", &error, 1)); |
||||||
|
gpr_mu_unlock(&h->mu); |
||||||
|
security_handshaker_unref(exec_ctx, h); |
||||||
|
return; |
||||||
|
} |
||||||
|
// Process received data.
|
||||||
|
tsi_result result = TSI_OK; |
||||||
|
size_t consumed_slice_size = 0; |
||||||
|
size_t i; |
||||||
|
for (i = 0; i < h->args->read_buffer->count; i++) { |
||||||
|
consumed_slice_size = GRPC_SLICE_LENGTH(h->args->read_buffer->slices[i]); |
||||||
|
result = tsi_handshaker_process_bytes_from_peer( |
||||||
|
h->handshaker, GRPC_SLICE_START_PTR(h->args->read_buffer->slices[i]), |
||||||
|
&consumed_slice_size); |
||||||
|
if (!tsi_handshaker_is_in_progress(h->handshaker)) break; |
||||||
|
} |
||||||
|
if (tsi_handshaker_is_in_progress(h->handshaker)) { |
||||||
|
/* We may need more data. */ |
||||||
|
if (result == TSI_INCOMPLETE_DATA) { |
||||||
|
grpc_endpoint_read(exec_ctx, h->args->endpoint, h->args->read_buffer, |
||||||
|
&h->on_handshake_data_received_from_peer); |
||||||
|
goto done; |
||||||
|
} else { |
||||||
|
error = send_handshake_bytes_to_peer_locked(exec_ctx, h); |
||||||
|
if (error != GRPC_ERROR_NONE) { |
||||||
|
security_handshake_failed_locked(exec_ctx, h, error); |
||||||
|
gpr_mu_unlock(&h->mu); |
||||||
|
security_handshaker_unref(exec_ctx, h); |
||||||
|
return; |
||||||
|
} |
||||||
|
goto done; |
||||||
|
} |
||||||
|
} |
||||||
|
if (result != TSI_OK) { |
||||||
|
security_handshake_failed_locked( |
||||||
|
exec_ctx, h, grpc_set_tsi_error_result( |
||||||
|
GRPC_ERROR_CREATE("Handshake failed"), result)); |
||||||
|
gpr_mu_unlock(&h->mu); |
||||||
|
security_handshaker_unref(exec_ctx, h); |
||||||
|
return; |
||||||
|
} |
||||||
|
/* Handshake is done and successful this point. */ |
||||||
|
bool has_left_overs_in_current_slice = |
||||||
|
(consumed_slice_size < |
||||||
|
GRPC_SLICE_LENGTH(h->args->read_buffer->slices[i])); |
||||||
|
size_t num_left_overs = (has_left_overs_in_current_slice ? 1 : 0) + |
||||||
|
h->args->read_buffer->count - i - 1; |
||||||
|
if (num_left_overs > 0) { |
||||||
|
/* Put the leftovers in our buffer (ownership transfered). */ |
||||||
|
if (has_left_overs_in_current_slice) { |
||||||
|
grpc_slice_buffer_add( |
||||||
|
&h->left_overs, |
||||||
|
grpc_slice_split_tail(&h->args->read_buffer->slices[i], |
||||||
|
consumed_slice_size)); |
||||||
|
/* split_tail above increments refcount. */ |
||||||
|
grpc_slice_unref(h->args->read_buffer->slices[i]); |
||||||
|
} |
||||||
|
grpc_slice_buffer_addn( |
||||||
|
&h->left_overs, &h->args->read_buffer->slices[i + 1], |
||||||
|
num_left_overs - (size_t)has_left_overs_in_current_slice); |
||||||
|
} |
||||||
|
// Check peer.
|
||||||
|
error = check_peer_locked(exec_ctx, h); |
||||||
|
if (error != GRPC_ERROR_NONE) { |
||||||
|
security_handshake_failed_locked(exec_ctx, h, error); |
||||||
|
gpr_mu_unlock(&h->mu); |
||||||
|
security_handshaker_unref(exec_ctx, h); |
||||||
|
return; |
||||||
|
} |
||||||
|
done: |
||||||
|
gpr_mu_unlock(&h->mu); |
||||||
|
} |
||||||
|
|
||||||
|
static void on_handshake_data_sent_to_peer(grpc_exec_ctx *exec_ctx, void *arg, |
||||||
|
grpc_error *error) { |
||||||
|
security_handshaker *h = arg; |
||||||
|
gpr_mu_lock(&h->mu); |
||||||
|
if (error != GRPC_ERROR_NONE || h->shutdown) { |
||||||
|
security_handshake_failed_locked( |
||||||
|
exec_ctx, h, |
||||||
|
GRPC_ERROR_CREATE_REFERENCING("Handshake write failed", &error, 1)); |
||||||
|
gpr_mu_unlock(&h->mu); |
||||||
|
security_handshaker_unref(exec_ctx, h); |
||||||
|
return; |
||||||
|
} |
||||||
|
/* We may be done. */ |
||||||
|
if (tsi_handshaker_is_in_progress(h->handshaker)) { |
||||||
|
grpc_endpoint_read(exec_ctx, h->args->endpoint, h->args->read_buffer, |
||||||
|
&h->on_handshake_data_received_from_peer); |
||||||
|
} else { |
||||||
|
error = check_peer_locked(exec_ctx, h); |
||||||
|
if (error != GRPC_ERROR_NONE) { |
||||||
|
security_handshake_failed_locked(exec_ctx, h, error); |
||||||
|
gpr_mu_unlock(&h->mu); |
||||||
|
security_handshaker_unref(exec_ctx, h); |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
gpr_mu_unlock(&h->mu); |
||||||
|
} |
||||||
|
|
||||||
|
//
|
||||||
|
// public handshaker API
|
||||||
|
//
|
||||||
|
|
||||||
|
static void security_handshaker_destroy(grpc_exec_ctx *exec_ctx, |
||||||
|
grpc_handshaker *handshaker) { |
||||||
|
security_handshaker *h = (security_handshaker *)handshaker; |
||||||
|
security_handshaker_unref(exec_ctx, h); |
||||||
|
} |
||||||
|
|
||||||
|
static void security_handshaker_shutdown(grpc_exec_ctx *exec_ctx, |
||||||
|
grpc_handshaker *handshaker) { |
||||||
|
security_handshaker *h = (security_handshaker *)handshaker; |
||||||
|
gpr_mu_lock(&h->mu); |
||||||
|
if (!h->shutdown) { |
||||||
|
h->shutdown = true; |
||||||
|
grpc_endpoint_shutdown(exec_ctx, h->args->endpoint); |
||||||
|
cleanup_args_for_failure_locked(h); |
||||||
|
} |
||||||
|
gpr_mu_unlock(&h->mu); |
||||||
|
} |
||||||
|
|
||||||
|
static void security_handshaker_do_handshake(grpc_exec_ctx *exec_ctx, |
||||||
|
grpc_handshaker *handshaker, |
||||||
|
grpc_tcp_server_acceptor *acceptor, |
||||||
|
grpc_closure *on_handshake_done, |
||||||
|
grpc_handshaker_args *args) { |
||||||
|
security_handshaker *h = (security_handshaker *)handshaker; |
||||||
|
gpr_mu_lock(&h->mu); |
||||||
|
h->args = args; |
||||||
|
h->on_handshake_done = on_handshake_done; |
||||||
|
gpr_ref(&h->refs); |
||||||
|
grpc_error *error = send_handshake_bytes_to_peer_locked(exec_ctx, h); |
||||||
|
if (error != GRPC_ERROR_NONE) { |
||||||
|
security_handshake_failed_locked(exec_ctx, h, error); |
||||||
|
gpr_mu_unlock(&h->mu); |
||||||
|
security_handshaker_unref(exec_ctx, h); |
||||||
|
return; |
||||||
|
} |
||||||
|
gpr_mu_unlock(&h->mu); |
||||||
|
} |
||||||
|
|
||||||
|
static const grpc_handshaker_vtable security_handshaker_vtable = { |
||||||
|
security_handshaker_destroy, security_handshaker_shutdown, |
||||||
|
security_handshaker_do_handshake}; |
||||||
|
|
||||||
|
static grpc_handshaker *security_handshaker_create( |
||||||
|
grpc_exec_ctx *exec_ctx, tsi_handshaker *handshaker, |
||||||
|
grpc_security_connector *connector) { |
||||||
|
security_handshaker *h = gpr_malloc(sizeof(security_handshaker)); |
||||||
|
memset(h, 0, sizeof(security_handshaker)); |
||||||
|
grpc_handshaker_init(&security_handshaker_vtable, &h->base); |
||||||
|
h->handshaker = handshaker; |
||||||
|
h->connector = GRPC_SECURITY_CONNECTOR_REF(connector, "handshake"); |
||||||
|
gpr_mu_init(&h->mu); |
||||||
|
gpr_ref_init(&h->refs, 1); |
||||||
|
h->handshake_buffer_size = GRPC_INITIAL_HANDSHAKE_BUFFER_SIZE; |
||||||
|
h->handshake_buffer = gpr_malloc(h->handshake_buffer_size); |
||||||
|
grpc_closure_init(&h->on_handshake_data_sent_to_peer, |
||||||
|
on_handshake_data_sent_to_peer, h); |
||||||
|
grpc_closure_init(&h->on_handshake_data_received_from_peer, |
||||||
|
on_handshake_data_received_from_peer, h); |
||||||
|
grpc_closure_init(&h->on_peer_checked, on_peer_checked, h); |
||||||
|
grpc_slice_buffer_init(&h->left_overs); |
||||||
|
grpc_slice_buffer_init(&h->outgoing); |
||||||
|
return &h->base; |
||||||
|
} |
||||||
|
|
||||||
|
//
|
||||||
|
// fail_handshaker
|
||||||
|
//
|
||||||
|
|
||||||
|
static void fail_handshaker_destroy(grpc_exec_ctx *exec_ctx, |
||||||
|
grpc_handshaker *handshaker) { |
||||||
|
gpr_free(handshaker); |
||||||
|
} |
||||||
|
|
||||||
|
static void fail_handshaker_shutdown(grpc_exec_ctx *exec_ctx, |
||||||
|
grpc_handshaker *handshaker) {} |
||||||
|
|
||||||
|
static void fail_handshaker_do_handshake(grpc_exec_ctx *exec_ctx, |
||||||
|
grpc_handshaker *handshaker, |
||||||
|
grpc_tcp_server_acceptor *acceptor, |
||||||
|
grpc_closure *on_handshake_done, |
||||||
|
grpc_handshaker_args *args) { |
||||||
|
grpc_exec_ctx_sched(exec_ctx, on_handshake_done, |
||||||
|
GRPC_ERROR_CREATE("Failed to create security handshaker"), |
||||||
|
NULL); |
||||||
|
} |
||||||
|
|
||||||
|
static const grpc_handshaker_vtable fail_handshaker_vtable = { |
||||||
|
fail_handshaker_destroy, fail_handshaker_shutdown, |
||||||
|
fail_handshaker_do_handshake}; |
||||||
|
|
||||||
|
static grpc_handshaker *fail_handshaker_create() { |
||||||
|
grpc_handshaker *h = gpr_malloc(sizeof(*h)); |
||||||
|
grpc_handshaker_init(&fail_handshaker_vtable, h); |
||||||
|
return h; |
||||||
|
} |
||||||
|
|
||||||
|
//
|
||||||
|
// exported functions
|
||||||
|
//
|
||||||
|
|
||||||
|
void grpc_security_create_handshakers(grpc_exec_ctx *exec_ctx, |
||||||
|
tsi_handshaker *handshaker, |
||||||
|
grpc_security_connector *connector, |
||||||
|
grpc_handshake_manager *handshake_mgr) { |
||||||
|
// If no TSI handshaker was created, add a handshaker that always fails.
|
||||||
|
// Otherwise, add a real security handshaker.
|
||||||
|
if (handshaker == NULL) { |
||||||
|
grpc_handshake_manager_add(handshake_mgr, fail_handshaker_create()); |
||||||
|
} else { |
||||||
|
grpc_handshake_manager_add( |
||||||
|
handshake_mgr, |
||||||
|
security_handshaker_create(exec_ctx, handshaker, connector)); |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue