|
|
@ -33,6 +33,7 @@ |
|
|
|
|
|
|
|
|
|
|
|
#include "src/core/security/security_connector.h" |
|
|
|
#include "src/core/security/security_connector.h" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include <stdbool.h> |
|
|
|
#include <string.h> |
|
|
|
#include <string.h> |
|
|
|
|
|
|
|
|
|
|
|
#include <grpc/support/alloc.h> |
|
|
|
#include <grpc/support/alloc.h> |
|
|
@ -110,31 +111,39 @@ const tsi_peer_property *tsi_peer_get_property_by_name(const tsi_peer *peer, |
|
|
|
return NULL; |
|
|
|
return NULL; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void grpc_security_connector_shutdown(grpc_exec_ctx *exec_ctx, |
|
|
|
void grpc_server_security_connector_shutdown( |
|
|
|
grpc_security_connector *connector) { |
|
|
|
grpc_exec_ctx *exec_ctx, grpc_server_security_connector *connector) { |
|
|
|
grpc_security_connector_handshake_list *tmp; |
|
|
|
grpc_security_connector_handshake_list *tmp; |
|
|
|
if (!connector->is_client_side) { |
|
|
|
gpr_mu_lock(&connector->mu); |
|
|
|
gpr_mu_lock(&connector->mu); |
|
|
|
while (connector->handshaking_handshakes) { |
|
|
|
while (connector->handshaking_handshakes) { |
|
|
|
tmp = connector->handshaking_handshakes; |
|
|
|
tmp = connector->handshaking_handshakes; |
|
|
|
grpc_security_handshake_shutdown( |
|
|
|
grpc_security_handshake_shutdown( |
|
|
|
exec_ctx, connector->handshaking_handshakes->handshake); |
|
|
|
exec_ctx, connector->handshaking_handshakes->handshake); |
|
|
|
connector->handshaking_handshakes = tmp->next; |
|
|
|
connector->handshaking_handshakes = tmp->next; |
|
|
|
gpr_free(tmp); |
|
|
|
gpr_free(tmp); |
|
|
|
} |
|
|
|
} |
|
|
|
gpr_mu_unlock(&connector->mu); |
|
|
|
gpr_mu_unlock(&connector->mu); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void grpc_channel_security_connector_do_handshake( |
|
|
|
|
|
|
|
grpc_exec_ctx *exec_ctx, grpc_channel_security_connector *sc, |
|
|
|
|
|
|
|
grpc_endpoint *nonsecure_endpoint, grpc_security_handshake_done_cb cb, |
|
|
|
|
|
|
|
void *user_data) { |
|
|
|
|
|
|
|
if (sc == NULL || nonsecure_endpoint == NULL) { |
|
|
|
|
|
|
|
cb(exec_ctx, user_data, GRPC_SECURITY_ERROR, NULL, NULL); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
sc->do_handshake(exec_ctx, sc, nonsecure_endpoint, cb, user_data); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void grpc_security_connector_do_handshake(grpc_exec_ctx *exec_ctx, |
|
|
|
void grpc_server_security_connector_do_handshake( |
|
|
|
grpc_security_connector *sc, |
|
|
|
grpc_exec_ctx *exec_ctx, grpc_server_security_connector *sc, |
|
|
|
grpc_endpoint *nonsecure_endpoint, |
|
|
|
grpc_tcp_server_acceptor *acceptor, grpc_endpoint *nonsecure_endpoint, |
|
|
|
grpc_security_handshake_done_cb cb, |
|
|
|
grpc_security_handshake_done_cb cb, void *user_data) { |
|
|
|
void *user_data) { |
|
|
|
|
|
|
|
if (sc == NULL || nonsecure_endpoint == NULL) { |
|
|
|
if (sc == NULL || nonsecure_endpoint == NULL) { |
|
|
|
cb(exec_ctx, user_data, GRPC_SECURITY_ERROR, NULL, NULL); |
|
|
|
cb(exec_ctx, user_data, GRPC_SECURITY_ERROR, NULL, NULL); |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
sc->vtable->do_handshake(exec_ctx, sc, nonsecure_endpoint, cb, user_data); |
|
|
|
sc->do_handshake(exec_ctx, sc, acceptor, nonsecure_endpoint, cb, user_data); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -248,7 +257,8 @@ static void fake_channel_destroy(grpc_security_connector *sc) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void fake_server_destroy(grpc_security_connector *sc) { |
|
|
|
static void fake_server_destroy(grpc_security_connector *sc) { |
|
|
|
gpr_mu_destroy(&sc->mu); |
|
|
|
grpc_server_security_connector *c = (grpc_server_security_connector *)sc; |
|
|
|
|
|
|
|
gpr_mu_destroy(&c->mu); |
|
|
|
gpr_free(sc); |
|
|
|
gpr_free(sc); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -298,49 +308,52 @@ static void fake_channel_check_call_host(grpc_exec_ctx *exec_ctx, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void fake_channel_do_handshake(grpc_exec_ctx *exec_ctx, |
|
|
|
static void fake_channel_do_handshake(grpc_exec_ctx *exec_ctx, |
|
|
|
grpc_security_connector *sc, |
|
|
|
grpc_channel_security_connector *sc, |
|
|
|
grpc_endpoint *nonsecure_endpoint, |
|
|
|
grpc_endpoint *nonsecure_endpoint, |
|
|
|
grpc_security_handshake_done_cb cb, |
|
|
|
grpc_security_handshake_done_cb cb, |
|
|
|
void *user_data) { |
|
|
|
void *user_data) { |
|
|
|
grpc_do_security_handshake(exec_ctx, tsi_create_fake_handshaker(1), sc, |
|
|
|
grpc_do_security_handshake(exec_ctx, tsi_create_fake_handshaker(1), &sc->base, |
|
|
|
nonsecure_endpoint, cb, user_data); |
|
|
|
true, nonsecure_endpoint, cb, user_data); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void fake_server_do_handshake(grpc_exec_ctx *exec_ctx, |
|
|
|
static void fake_server_do_handshake(grpc_exec_ctx *exec_ctx, |
|
|
|
grpc_security_connector *sc, |
|
|
|
grpc_server_security_connector *sc, |
|
|
|
|
|
|
|
grpc_tcp_server_acceptor *acceptor, |
|
|
|
grpc_endpoint *nonsecure_endpoint, |
|
|
|
grpc_endpoint *nonsecure_endpoint, |
|
|
|
grpc_security_handshake_done_cb cb, |
|
|
|
grpc_security_handshake_done_cb cb, |
|
|
|
void *user_data) { |
|
|
|
void *user_data) { |
|
|
|
grpc_do_security_handshake(exec_ctx, tsi_create_fake_handshaker(0), sc, |
|
|
|
grpc_do_security_handshake(exec_ctx, tsi_create_fake_handshaker(0), &sc->base, |
|
|
|
nonsecure_endpoint, cb, user_data); |
|
|
|
false, nonsecure_endpoint, cb, user_data); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static grpc_security_connector_vtable fake_channel_vtable = { |
|
|
|
static grpc_security_connector_vtable fake_channel_vtable = { |
|
|
|
fake_channel_destroy, fake_channel_do_handshake, fake_check_peer}; |
|
|
|
fake_channel_destroy, fake_check_peer}; |
|
|
|
|
|
|
|
|
|
|
|
static grpc_security_connector_vtable fake_server_vtable = { |
|
|
|
static grpc_security_connector_vtable fake_server_vtable = {fake_server_destroy, |
|
|
|
fake_server_destroy, fake_server_do_handshake, fake_check_peer}; |
|
|
|
fake_check_peer}; |
|
|
|
|
|
|
|
|
|
|
|
grpc_channel_security_connector *grpc_fake_channel_security_connector_create( |
|
|
|
grpc_channel_security_connector *grpc_fake_channel_security_connector_create( |
|
|
|
grpc_call_credentials *request_metadata_creds) { |
|
|
|
grpc_call_credentials *request_metadata_creds) { |
|
|
|
grpc_channel_security_connector *c = gpr_malloc(sizeof(*c)); |
|
|
|
grpc_channel_security_connector *c = gpr_malloc(sizeof(*c)); |
|
|
|
memset(c, 0, sizeof(*c)); |
|
|
|
memset(c, 0, sizeof(*c)); |
|
|
|
gpr_ref_init(&c->base.refcount, 1); |
|
|
|
gpr_ref_init(&c->base.refcount, 1); |
|
|
|
c->base.is_client_side = 1; |
|
|
|
|
|
|
|
c->base.url_scheme = GRPC_FAKE_SECURITY_URL_SCHEME; |
|
|
|
c->base.url_scheme = GRPC_FAKE_SECURITY_URL_SCHEME; |
|
|
|
c->base.vtable = &fake_channel_vtable; |
|
|
|
c->base.vtable = &fake_channel_vtable; |
|
|
|
c->request_metadata_creds = grpc_call_credentials_ref(request_metadata_creds); |
|
|
|
c->request_metadata_creds = grpc_call_credentials_ref(request_metadata_creds); |
|
|
|
c->check_call_host = fake_channel_check_call_host; |
|
|
|
c->check_call_host = fake_channel_check_call_host; |
|
|
|
|
|
|
|
c->do_handshake = fake_channel_do_handshake; |
|
|
|
return c; |
|
|
|
return c; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
grpc_security_connector *grpc_fake_server_security_connector_create(void) { |
|
|
|
grpc_server_security_connector *grpc_fake_server_security_connector_create( |
|
|
|
grpc_security_connector *c = gpr_malloc(sizeof(grpc_security_connector)); |
|
|
|
void) { |
|
|
|
memset(c, 0, sizeof(grpc_security_connector)); |
|
|
|
grpc_server_security_connector *c = |
|
|
|
gpr_ref_init(&c->refcount, 1); |
|
|
|
gpr_malloc(sizeof(grpc_server_security_connector)); |
|
|
|
c->is_client_side = 0; |
|
|
|
memset(c, 0, sizeof(*c)); |
|
|
|
c->vtable = &fake_server_vtable; |
|
|
|
gpr_ref_init(&c->base.refcount, 1); |
|
|
|
c->url_scheme = GRPC_FAKE_SECURITY_URL_SCHEME; |
|
|
|
c->base.vtable = &fake_server_vtable; |
|
|
|
|
|
|
|
c->base.url_scheme = GRPC_FAKE_SECURITY_URL_SCHEME; |
|
|
|
|
|
|
|
c->do_handshake = fake_server_do_handshake; |
|
|
|
gpr_mu_init(&c->mu); |
|
|
|
gpr_mu_init(&c->mu); |
|
|
|
return c; |
|
|
|
return c; |
|
|
|
} |
|
|
|
} |
|
|
@ -355,7 +368,7 @@ typedef struct { |
|
|
|
} grpc_ssl_channel_security_connector; |
|
|
|
} grpc_ssl_channel_security_connector; |
|
|
|
|
|
|
|
|
|
|
|
typedef struct { |
|
|
|
typedef struct { |
|
|
|
grpc_security_connector base; |
|
|
|
grpc_server_security_connector base; |
|
|
|
tsi_ssl_handshaker_factory *handshaker_factory; |
|
|
|
tsi_ssl_handshaker_factory *handshaker_factory; |
|
|
|
} grpc_ssl_server_security_connector; |
|
|
|
} grpc_ssl_server_security_connector; |
|
|
|
|
|
|
|
|
|
|
@ -378,12 +391,12 @@ static void ssl_server_destroy(grpc_security_connector *sc) { |
|
|
|
if (c->handshaker_factory != NULL) { |
|
|
|
if (c->handshaker_factory != NULL) { |
|
|
|
tsi_ssl_handshaker_factory_destroy(c->handshaker_factory); |
|
|
|
tsi_ssl_handshaker_factory_destroy(c->handshaker_factory); |
|
|
|
} |
|
|
|
} |
|
|
|
gpr_mu_destroy(&sc->mu); |
|
|
|
gpr_mu_destroy(&c->base.mu); |
|
|
|
gpr_free(sc); |
|
|
|
gpr_free(sc); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static grpc_security_status ssl_create_handshaker( |
|
|
|
static grpc_security_status ssl_create_handshaker( |
|
|
|
tsi_ssl_handshaker_factory *handshaker_factory, int is_client, |
|
|
|
tsi_ssl_handshaker_factory *handshaker_factory, bool is_client, |
|
|
|
const char *peer_name, tsi_handshaker **handshaker) { |
|
|
|
const char *peer_name, tsi_handshaker **handshaker) { |
|
|
|
tsi_result result = TSI_OK; |
|
|
|
tsi_result result = TSI_OK; |
|
|
|
if (handshaker_factory == NULL) return GRPC_SECURITY_ERROR; |
|
|
|
if (handshaker_factory == NULL) return GRPC_SECURITY_ERROR; |
|
|
@ -398,7 +411,7 @@ static grpc_security_status ssl_create_handshaker( |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void ssl_channel_do_handshake(grpc_exec_ctx *exec_ctx, |
|
|
|
static void ssl_channel_do_handshake(grpc_exec_ctx *exec_ctx, |
|
|
|
grpc_security_connector *sc, |
|
|
|
grpc_channel_security_connector *sc, |
|
|
|
grpc_endpoint *nonsecure_endpoint, |
|
|
|
grpc_endpoint *nonsecure_endpoint, |
|
|
|
grpc_security_handshake_done_cb cb, |
|
|
|
grpc_security_handshake_done_cb cb, |
|
|
|
void *user_data) { |
|
|
|
void *user_data) { |
|
|
@ -406,20 +419,21 @@ static void ssl_channel_do_handshake(grpc_exec_ctx *exec_ctx, |
|
|
|
(grpc_ssl_channel_security_connector *)sc; |
|
|
|
(grpc_ssl_channel_security_connector *)sc; |
|
|
|
tsi_handshaker *handshaker; |
|
|
|
tsi_handshaker *handshaker; |
|
|
|
grpc_security_status status = ssl_create_handshaker( |
|
|
|
grpc_security_status status = ssl_create_handshaker( |
|
|
|
c->handshaker_factory, 1, |
|
|
|
c->handshaker_factory, true, |
|
|
|
c->overridden_target_name != NULL ? c->overridden_target_name |
|
|
|
c->overridden_target_name != NULL ? c->overridden_target_name |
|
|
|
: c->target_name, |
|
|
|
: c->target_name, |
|
|
|
&handshaker); |
|
|
|
&handshaker); |
|
|
|
if (status != GRPC_SECURITY_OK) { |
|
|
|
if (status != GRPC_SECURITY_OK) { |
|
|
|
cb(exec_ctx, user_data, status, NULL, NULL); |
|
|
|
cb(exec_ctx, user_data, status, NULL, NULL); |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
grpc_do_security_handshake(exec_ctx, handshaker, sc, nonsecure_endpoint, cb, |
|
|
|
grpc_do_security_handshake(exec_ctx, handshaker, &sc->base, true, |
|
|
|
user_data); |
|
|
|
nonsecure_endpoint, cb, user_data); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void ssl_server_do_handshake(grpc_exec_ctx *exec_ctx, |
|
|
|
static void ssl_server_do_handshake(grpc_exec_ctx *exec_ctx, |
|
|
|
grpc_security_connector *sc, |
|
|
|
grpc_server_security_connector *sc, |
|
|
|
|
|
|
|
grpc_tcp_server_acceptor *acceptor, |
|
|
|
grpc_endpoint *nonsecure_endpoint, |
|
|
|
grpc_endpoint *nonsecure_endpoint, |
|
|
|
grpc_security_handshake_done_cb cb, |
|
|
|
grpc_security_handshake_done_cb cb, |
|
|
|
void *user_data) { |
|
|
|
void *user_data) { |
|
|
@ -427,12 +441,12 @@ static void ssl_server_do_handshake(grpc_exec_ctx *exec_ctx, |
|
|
|
(grpc_ssl_server_security_connector *)sc; |
|
|
|
(grpc_ssl_server_security_connector *)sc; |
|
|
|
tsi_handshaker *handshaker; |
|
|
|
tsi_handshaker *handshaker; |
|
|
|
grpc_security_status status = |
|
|
|
grpc_security_status status = |
|
|
|
ssl_create_handshaker(c->handshaker_factory, 0, NULL, &handshaker); |
|
|
|
ssl_create_handshaker(c->handshaker_factory, false, NULL, &handshaker); |
|
|
|
if (status != GRPC_SECURITY_OK) { |
|
|
|
if (status != GRPC_SECURITY_OK) { |
|
|
|
cb(exec_ctx, user_data, status, NULL, NULL); |
|
|
|
cb(exec_ctx, user_data, status, NULL, NULL); |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
grpc_do_security_handshake(exec_ctx, handshaker, sc, nonsecure_endpoint, cb, |
|
|
|
grpc_do_security_handshake(exec_ctx, handshaker, &sc->base, false, |
|
|
|
user_data); |
|
|
|
nonsecure_endpoint, cb, user_data); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -603,10 +617,10 @@ static void ssl_channel_check_call_host(grpc_exec_ctx *exec_ctx, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static grpc_security_connector_vtable ssl_channel_vtable = { |
|
|
|
static grpc_security_connector_vtable ssl_channel_vtable = { |
|
|
|
ssl_channel_destroy, ssl_channel_do_handshake, ssl_channel_check_peer}; |
|
|
|
ssl_channel_destroy, ssl_channel_check_peer}; |
|
|
|
|
|
|
|
|
|
|
|
static grpc_security_connector_vtable ssl_server_vtable = { |
|
|
|
static grpc_security_connector_vtable ssl_server_vtable = { |
|
|
|
ssl_server_destroy, ssl_server_do_handshake, ssl_server_check_peer}; |
|
|
|
ssl_server_destroy, ssl_server_check_peer}; |
|
|
|
|
|
|
|
|
|
|
|
static gpr_slice compute_default_pem_root_certs_once(void) { |
|
|
|
static gpr_slice compute_default_pem_root_certs_once(void) { |
|
|
|
gpr_slice result = gpr_empty_slice(); |
|
|
|
gpr_slice result = gpr_empty_slice(); |
|
|
@ -700,11 +714,11 @@ grpc_security_status grpc_ssl_channel_security_connector_create( |
|
|
|
|
|
|
|
|
|
|
|
gpr_ref_init(&c->base.base.refcount, 1); |
|
|
|
gpr_ref_init(&c->base.base.refcount, 1); |
|
|
|
c->base.base.vtable = &ssl_channel_vtable; |
|
|
|
c->base.base.vtable = &ssl_channel_vtable; |
|
|
|
c->base.base.is_client_side = 1; |
|
|
|
|
|
|
|
c->base.base.url_scheme = GRPC_SSL_URL_SCHEME; |
|
|
|
c->base.base.url_scheme = GRPC_SSL_URL_SCHEME; |
|
|
|
c->base.request_metadata_creds = |
|
|
|
c->base.request_metadata_creds = |
|
|
|
grpc_call_credentials_ref(request_metadata_creds); |
|
|
|
grpc_call_credentials_ref(request_metadata_creds); |
|
|
|
c->base.check_call_host = ssl_channel_check_call_host; |
|
|
|
c->base.check_call_host = ssl_channel_check_call_host; |
|
|
|
|
|
|
|
c->base.do_handshake = ssl_channel_do_handshake; |
|
|
|
gpr_split_host_port(target_name, &c->target_name, &port); |
|
|
|
gpr_split_host_port(target_name, &c->target_name, &port); |
|
|
|
gpr_free(port); |
|
|
|
gpr_free(port); |
|
|
|
if (overridden_target_name != NULL) { |
|
|
|
if (overridden_target_name != NULL) { |
|
|
@ -735,7 +749,7 @@ error: |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
grpc_security_status grpc_ssl_server_security_connector_create( |
|
|
|
grpc_security_status grpc_ssl_server_security_connector_create( |
|
|
|
const grpc_ssl_server_config *config, grpc_security_connector **sc) { |
|
|
|
const grpc_ssl_server_config *config, grpc_server_security_connector **sc) { |
|
|
|
size_t num_alpn_protocols = grpc_chttp2_num_alpn_versions(); |
|
|
|
size_t num_alpn_protocols = grpc_chttp2_num_alpn_versions(); |
|
|
|
const unsigned char **alpn_protocol_strings = |
|
|
|
const unsigned char **alpn_protocol_strings = |
|
|
|
gpr_malloc(sizeof(const char *) * num_alpn_protocols); |
|
|
|
gpr_malloc(sizeof(const char *) * num_alpn_protocols); |
|
|
@ -759,9 +773,9 @@ grpc_security_status grpc_ssl_server_security_connector_create( |
|
|
|
c = gpr_malloc(sizeof(grpc_ssl_server_security_connector)); |
|
|
|
c = gpr_malloc(sizeof(grpc_ssl_server_security_connector)); |
|
|
|
memset(c, 0, sizeof(grpc_ssl_server_security_connector)); |
|
|
|
memset(c, 0, sizeof(grpc_ssl_server_security_connector)); |
|
|
|
|
|
|
|
|
|
|
|
gpr_ref_init(&c->base.refcount, 1); |
|
|
|
gpr_ref_init(&c->base.base.refcount, 1); |
|
|
|
c->base.url_scheme = GRPC_SSL_URL_SCHEME; |
|
|
|
c->base.base.url_scheme = GRPC_SSL_URL_SCHEME; |
|
|
|
c->base.vtable = &ssl_server_vtable; |
|
|
|
c->base.base.vtable = &ssl_server_vtable; |
|
|
|
result = tsi_create_ssl_server_handshaker_factory( |
|
|
|
result = tsi_create_ssl_server_handshaker_factory( |
|
|
|
(const unsigned char **)config->pem_private_keys, |
|
|
|
(const unsigned char **)config->pem_private_keys, |
|
|
|
config->pem_private_keys_sizes, |
|
|
|
config->pem_private_keys_sizes, |
|
|
@ -774,11 +788,12 @@ grpc_security_status grpc_ssl_server_security_connector_create( |
|
|
|
if (result != TSI_OK) { |
|
|
|
if (result != TSI_OK) { |
|
|
|
gpr_log(GPR_ERROR, "Handshaker factory creation failed with %s.", |
|
|
|
gpr_log(GPR_ERROR, "Handshaker factory creation failed with %s.", |
|
|
|
tsi_result_to_string(result)); |
|
|
|
tsi_result_to_string(result)); |
|
|
|
ssl_server_destroy(&c->base); |
|
|
|
ssl_server_destroy(&c->base.base); |
|
|
|
*sc = NULL; |
|
|
|
*sc = NULL; |
|
|
|
goto error; |
|
|
|
goto error; |
|
|
|
} |
|
|
|
} |
|
|
|
gpr_mu_init(&c->base.mu); |
|
|
|
gpr_mu_init(&c->base.mu); |
|
|
|
|
|
|
|
c->base.do_handshake = ssl_server_do_handshake; |
|
|
|
*sc = &c->base; |
|
|
|
*sc = &c->base; |
|
|
|
gpr_free((void *)alpn_protocol_strings); |
|
|
|
gpr_free((void *)alpn_protocol_strings); |
|
|
|
gpr_free(alpn_protocol_string_lengths); |
|
|
|
gpr_free(alpn_protocol_string_lengths); |
|
|
|