aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Alexander Polcyn <apolcyn@google.com>2017-10-28 16:57:29 -0700
committerGravatar Alexander Polcyn <apolcyn@google.com>2017-11-13 16:18:14 -0800
commit3f6b10afba43e2855efd2fbeb3f1f4ea6c2a9657 (patch)
tree19d44dea182b6a34e4c8e611aa4565356e0e94ee
parenta2465b02f283425b6355707800100a7504a62ee2 (diff)
Use existing read buffer in security handshaker if present
-rw-r--r--CMakeLists.txt38
-rw-r--r--Makefile42
-rw-r--r--build.yaml17
-rw-r--r--src/core/lib/security/transport/security_handshaker.cc45
-rw-r--r--test/core/handshake/readahead_handshaker_server_ssl.c103
-rw-r--r--test/core/handshake/server_ssl.c200
-rw-r--r--test/core/handshake/server_ssl_common.c237
-rw-r--r--test/core/handshake/server_ssl_common.h22
-rw-r--r--tools/run_tests/generated/sources_and_headers.json21
-rw-r--r--tools/run_tests/generated/tests.json20
10 files changed, 525 insertions, 220 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3ac6c9b63d..b3cbbbaea5 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -466,6 +466,9 @@ endif()
if(_gRPC_PLATFORM_LINUX)
add_dependencies(buildtests_c handshake_server)
endif()
+if(_gRPC_PLATFORM_LINUX)
+add_dependencies(buildtests_c handshake_server_with_readahead_handshaker)
+endif()
add_dependencies(buildtests_c hpack_parser_test)
add_dependencies(buildtests_c hpack_table_test)
add_dependencies(buildtests_c http_parser_test)
@@ -7395,6 +7398,7 @@ if(_gRPC_PLATFORM_LINUX)
add_executable(handshake_server
test/core/handshake/server_ssl.c
+ test/core/handshake/server_ssl_common.c
)
@@ -7424,6 +7428,40 @@ target_link_libraries(handshake_server
endif()
endif (gRPC_BUILD_TESTS)
if (gRPC_BUILD_TESTS)
+if(_gRPC_PLATFORM_LINUX)
+
+add_executable(handshake_server_with_readahead_handshaker
+ test/core/handshake/readahead_handshaker_server_ssl.c
+ test/core/handshake/server_ssl_common.c
+)
+
+
+target_include_directories(handshake_server_with_readahead_handshaker
+ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
+ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
+ PRIVATE ${BORINGSSL_ROOT_DIR}/include
+ PRIVATE ${PROTOBUF_ROOT_DIR}/src
+ PRIVATE ${BENCHMARK_ROOT_DIR}/include
+ PRIVATE ${ZLIB_ROOT_DIR}
+ PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/zlib
+ PRIVATE ${CARES_INCLUDE_DIR}
+ PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/cares/cares
+ PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/gflags/include
+ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/third_party/abseil-cpp
+)
+
+target_link_libraries(handshake_server_with_readahead_handshaker
+ ${_gRPC_SSL_LIBRARIES}
+ ${_gRPC_ALLTARGETS_LIBRARIES}
+ grpc_test_util
+ grpc
+ gpr_test_util
+ gpr
+)
+
+endif()
+endif (gRPC_BUILD_TESTS)
+if (gRPC_BUILD_TESTS)
add_executable(hpack_parser_test
test/core/transport/chttp2/hpack_parser_test.c
diff --git a/Makefile b/Makefile
index 325c9123d9..7c0ee0d17e 100644
--- a/Makefile
+++ b/Makefile
@@ -1023,6 +1023,7 @@ grpc_ssl_credentials_test: $(BINDIR)/$(CONFIG)/grpc_ssl_credentials_test
grpc_verify_jwt: $(BINDIR)/$(CONFIG)/grpc_verify_jwt
handshake_client: $(BINDIR)/$(CONFIG)/handshake_client
handshake_server: $(BINDIR)/$(CONFIG)/handshake_server
+handshake_server_with_readahead_handshaker: $(BINDIR)/$(CONFIG)/handshake_server_with_readahead_handshaker
hpack_parser_fuzzer_test: $(BINDIR)/$(CONFIG)/hpack_parser_fuzzer_test
hpack_parser_test: $(BINDIR)/$(CONFIG)/hpack_parser_test
hpack_table_test: $(BINDIR)/$(CONFIG)/hpack_table_test
@@ -1417,6 +1418,7 @@ buildtests_c: privatelibs_c \
$(BINDIR)/$(CONFIG)/grpc_ssl_credentials_test \
$(BINDIR)/$(CONFIG)/handshake_client \
$(BINDIR)/$(CONFIG)/handshake_server \
+ $(BINDIR)/$(CONFIG)/handshake_server_with_readahead_handshaker \
$(BINDIR)/$(CONFIG)/hpack_parser_test \
$(BINDIR)/$(CONFIG)/hpack_table_test \
$(BINDIR)/$(CONFIG)/http_parser_test \
@@ -1894,6 +1896,8 @@ test_c: buildtests_c
$(Q) $(BINDIR)/$(CONFIG)/handshake_client || ( echo test handshake_client failed ; exit 1 )
$(E) "[RUN] Testing handshake_server"
$(Q) $(BINDIR)/$(CONFIG)/handshake_server || ( echo test handshake_server failed ; exit 1 )
+ $(E) "[RUN] Testing handshake_server_with_readahead_handshaker"
+ $(Q) $(BINDIR)/$(CONFIG)/handshake_server_with_readahead_handshaker || ( echo test handshake_server_with_readahead_handshaker failed ; exit 1 )
$(E) "[RUN] Testing hpack_parser_test"
$(Q) $(BINDIR)/$(CONFIG)/hpack_parser_test || ( echo test hpack_parser_test failed ; exit 1 )
$(E) "[RUN] Testing hpack_table_test"
@@ -11221,6 +11225,7 @@ endif
HANDSHAKE_SERVER_SRC = \
test/core/handshake/server_ssl.c \
+ test/core/handshake/server_ssl_common.c \
HANDSHAKE_SERVER_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(HANDSHAKE_SERVER_SRC))))
ifeq ($(NO_SECURE),true)
@@ -11242,6 +11247,8 @@ endif
$(OBJDIR)/$(CONFIG)/test/core/handshake/server_ssl.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+$(OBJDIR)/$(CONFIG)/test/core/handshake/server_ssl_common.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
deps_handshake_server: $(HANDSHAKE_SERVER_OBJS:.o=.dep)
ifneq ($(NO_SECURE),true)
@@ -11251,6 +11258,41 @@ endif
endif
+HANDSHAKE_SERVER_WITH_READAHEAD_HANDSHAKER_SRC = \
+ test/core/handshake/readahead_handshaker_server_ssl.c \
+ test/core/handshake/server_ssl_common.c \
+
+HANDSHAKE_SERVER_WITH_READAHEAD_HANDSHAKER_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(HANDSHAKE_SERVER_WITH_READAHEAD_HANDSHAKER_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/handshake_server_with_readahead_handshaker: openssl_dep_error
+
+else
+
+
+
+$(BINDIR)/$(CONFIG)/handshake_server_with_readahead_handshaker: $(HANDSHAKE_SERVER_WITH_READAHEAD_HANDSHAKER_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+ $(E) "[LD] Linking $@"
+ $(Q) mkdir -p `dirname $@`
+ $(Q) $(LD) $(LDFLAGS) $(HANDSHAKE_SERVER_WITH_READAHEAD_HANDSHAKER_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/handshake_server_with_readahead_handshaker
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/core/handshake/readahead_handshaker_server_ssl.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+$(OBJDIR)/$(CONFIG)/test/core/handshake/server_ssl_common.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+deps_handshake_server_with_readahead_handshaker: $(HANDSHAKE_SERVER_WITH_READAHEAD_HANDSHAKER_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(HANDSHAKE_SERVER_WITH_READAHEAD_HANDSHAKER_OBJS:.o=.dep)
+endif
+endif
+
+
HPACK_PARSER_FUZZER_TEST_SRC = \
test/core/transport/chttp2/hpack_parser_fuzzer_test.c \
diff --git a/build.yaml b/build.yaml
index 592cbb9f8c..adefb5d6d7 100644
--- a/build.yaml
+++ b/build.yaml
@@ -2579,6 +2579,23 @@ targets:
language: c
src:
- test/core/handshake/server_ssl.c
+ - test/core/handshake/server_ssl_common.c
+ deps:
+ - grpc_test_util
+ - grpc
+ - gpr_test_util
+ - gpr
+ exclude_iomgrs:
+ - uv
+ platforms:
+ - linux
+ secure: true
+- name: handshake_server_with_readahead_handshaker
+ build: test
+ language: c
+ src:
+ - test/core/handshake/readahead_handshaker_server_ssl.c
+ - test/core/handshake/server_ssl_common.c
deps:
- grpc_test_util
- grpc
diff --git a/src/core/lib/security/transport/security_handshaker.cc b/src/core/lib/security/transport/security_handshaker.cc
index 3d19605617..b42f2699ae 100644
--- a/src/core/lib/security/transport/security_handshaker.cc
+++ b/src/core/lib/security/transport/security_handshaker.cc
@@ -65,6 +65,25 @@ typedef struct {
tsi_handshaker_result *handshaker_result;
} security_handshaker;
+static size_t move_read_buffer_into_handshake_buffer(grpc_exec_ctx *exec_ctx,
+ security_handshaker *h) {
+ size_t bytes_in_read_buffer = h->args->read_buffer->length;
+ if (h->handshake_buffer_size < bytes_in_read_buffer) {
+ h->handshake_buffer =
+ (uint8_t *)gpr_realloc(h->handshake_buffer, bytes_in_read_buffer);
+ h->handshake_buffer_size = bytes_in_read_buffer;
+ }
+ size_t offset = 0;
+ while (h->args->read_buffer->count > 0) {
+ grpc_slice next_slice = grpc_slice_buffer_take_first(h->args->read_buffer);
+ memcpy(h->handshake_buffer + offset, GRPC_SLICE_START_PTR(next_slice),
+ GRPC_SLICE_LENGTH(next_slice));
+ offset += GRPC_SLICE_LENGTH(next_slice);
+ grpc_slice_unref_internal(exec_ctx, next_slice);
+ }
+ return bytes_in_read_buffer;
+}
+
static void security_handshaker_unref(grpc_exec_ctx *exec_ctx,
security_handshaker *h) {
if (gpr_unref(&h->refs)) {
@@ -177,8 +196,6 @@ static void on_peer_checked_inner(grpc_exec_ctx *exec_ctx,
}
tsi_handshaker_result_destroy(h->handshaker_result);
h->handshaker_result = NULL;
- // Clear out the read buffer before it gets passed to the transport.
- grpc_slice_buffer_reset_and_unref_internal(exec_ctx, 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;
@@ -311,23 +328,8 @@ static void on_handshake_data_received_from_peer(grpc_exec_ctx *exec_ctx,
return;
}
// Copy all slices received.
- size_t i;
- size_t bytes_received_size = 0;
- for (i = 0; i < h->args->read_buffer->count; i++) {
- bytes_received_size += GRPC_SLICE_LENGTH(h->args->read_buffer->slices[i]);
- }
- if (bytes_received_size > h->handshake_buffer_size) {
- h->handshake_buffer =
- (uint8_t *)gpr_realloc(h->handshake_buffer, bytes_received_size);
- h->handshake_buffer_size = bytes_received_size;
- }
- size_t offset = 0;
- for (i = 0; i < h->args->read_buffer->count; i++) {
- size_t slice_size = GPR_SLICE_LENGTH(h->args->read_buffer->slices[i]);
- memcpy(h->handshake_buffer + offset,
- GRPC_SLICE_START_PTR(h->args->read_buffer->slices[i]), slice_size);
- offset += slice_size;
- }
+ size_t bytes_received_size =
+ move_read_buffer_into_handshake_buffer(exec_ctx, h);
// Call TSI handshaker.
error = do_handshaker_next_locked(exec_ctx, h, h->handshake_buffer,
bytes_received_size);
@@ -403,7 +405,10 @@ static void security_handshaker_do_handshake(grpc_exec_ctx *exec_ctx,
h->args = args;
h->on_handshake_done = on_handshake_done;
gpr_ref(&h->refs);
- grpc_error *error = do_handshaker_next_locked(exec_ctx, h, NULL, 0);
+ size_t bytes_received_size =
+ move_read_buffer_into_handshake_buffer(exec_ctx, h);
+ grpc_error *error = do_handshaker_next_locked(
+ exec_ctx, h, h->handshake_buffer, bytes_received_size);
if (error != GRPC_ERROR_NONE) {
security_handshake_failed_locked(exec_ctx, h, error);
gpr_mu_unlock(&h->mu);
diff --git a/test/core/handshake/readahead_handshaker_server_ssl.c b/test/core/handshake/readahead_handshaker_server_ssl.c
new file mode 100644
index 0000000000..f25c35615c
--- /dev/null
+++ b/test/core/handshake/readahead_handshaker_server_ssl.c
@@ -0,0 +1,103 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <arpa/inet.h>
+#include <openssl/err.h>
+#include <openssl/ssl.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <grpc/grpc.h>
+#include <grpc/grpc_security.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/thd.h>
+#include "src/core/lib/iomgr/load_file.h"
+#include "test/core/util/port.h"
+#include "test/core/util/test_config.h"
+
+#include "src/core/lib/channel/handshaker_factory.h"
+#include "src/core/lib/channel/handshaker_registry.h"
+#include "src/core/lib/security/transport/security_handshaker.h"
+
+#include "test/core/handshake/server_ssl_common.h"
+
+/* The purpose of this test is to exercise the case when a
+ * grpc *security_handshaker* begins its handshake with data already
+ * in the read buffer of the handshaker arg. This scenario is created by
+ * adding a fake "readahead" handshaker at the beginning of the server's
+ * handshaker list, which just reads from the connection and then places
+ * read bytes into the read buffer of the handshake arg (to be passed down
+ * to the security_handshaker). This test is meant to protect code relying on
+ * this functionality that lives outside of this repo. */
+
+static void readahead_handshaker_destroy(grpc_exec_ctx *ctx,
+ grpc_handshaker *handshaker) {
+ gpr_free(handshaker);
+}
+
+static void readahead_handshaker_shutdown(grpc_exec_ctx *ctx,
+ grpc_handshaker *handshaker,
+ grpc_error *error) {}
+
+static void readahead_handshaker_do_handshake(
+ grpc_exec_ctx *ctx, grpc_handshaker *handshaker,
+ grpc_tcp_server_acceptor *acceptor, grpc_closure *on_handshake_done,
+ grpc_handshaker_args *args) {
+ grpc_endpoint_read(ctx, args->endpoint, args->read_buffer, on_handshake_done);
+}
+
+const grpc_handshaker_vtable readahead_handshaker_vtable = {
+ readahead_handshaker_destroy, readahead_handshaker_shutdown,
+ readahead_handshaker_do_handshake};
+
+static grpc_handshaker *readahead_handshaker_create(grpc_exec_ctx *ctx) {
+ grpc_handshaker *h = (grpc_handshaker *)gpr_zalloc(sizeof(grpc_handshaker));
+ grpc_handshaker_init(&readahead_handshaker_vtable, h);
+ return h;
+}
+
+static void readahead_handshaker_factory_add_handshakers(
+ grpc_exec_ctx *exec_ctx, grpc_handshaker_factory *hf,
+ const grpc_channel_args *args, grpc_handshake_manager *handshake_mgr) {
+ grpc_handshake_manager_add(handshake_mgr,
+ readahead_handshaker_create(exec_ctx));
+}
+
+static void readahead_handshaker_factory_destroy(
+ grpc_exec_ctx *exec_ctx, grpc_handshaker_factory *handshaker_factory) {}
+
+static const grpc_handshaker_factory_vtable
+ readahead_handshaker_factory_vtable = {
+ readahead_handshaker_factory_add_handshakers,
+ readahead_handshaker_factory_destroy};
+
+int main(int argc, char *argv[]) {
+ grpc_handshaker_factory readahead_handshaker_factory = {
+ &readahead_handshaker_factory_vtable};
+ grpc_init();
+ grpc_handshaker_factory_register(true /* at_start */, HANDSHAKER_SERVER,
+ &readahead_handshaker_factory);
+ const char *full_alpn_list[] = {"grpc-exp", "h2"};
+ GPR_ASSERT(server_ssl_test(full_alpn_list, 2, "grpc-exp"));
+ grpc_shutdown();
+ return 0;
+}
diff --git a/test/core/handshake/server_ssl.c b/test/core/handshake/server_ssl.c
index 85a8b4de41..8b96076338 100644
--- a/test/core/handshake/server_ssl.c
+++ b/test/core/handshake/server_ssl.c
@@ -34,205 +34,7 @@
#include "test/core/util/port.h"
#include "test/core/util/test_config.h"
-#define SSL_CERT_PATH "src/core/tsi/test_creds/server1.pem"
-#define SSL_KEY_PATH "src/core/tsi/test_creds/server1.key"
-#define SSL_CA_PATH "src/core/tsi/test_creds/ca.pem"
-
-// Handshake completed signal to server thread.
-static gpr_event client_handshake_complete;
-
-static int create_socket(int port) {
- int s;
- struct sockaddr_in addr;
-
- addr.sin_family = AF_INET;
- addr.sin_port = htons((uint16_t)port);
- addr.sin_addr.s_addr = htonl(INADDR_ANY);
-
- s = socket(AF_INET, SOCK_STREAM, 0);
- if (s < 0) {
- perror("Unable to create socket");
- return -1;
- }
-
- if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
- perror("Unable to connect");
- return -1;
- }
-
- return s;
-}
-
-// Simple gRPC server. This listens until client_handshake_complete occurs.
-static void server_thread(void *arg) {
- const int port = *(int *)arg;
-
- // Load key pair and establish server SSL credentials.
- grpc_ssl_pem_key_cert_pair pem_key_cert_pair;
- grpc_slice ca_slice, cert_slice, key_slice;
- GPR_ASSERT(GRPC_LOG_IF_ERROR("load_file",
- grpc_load_file(SSL_CA_PATH, 1, &ca_slice)));
- GPR_ASSERT(GRPC_LOG_IF_ERROR("load_file",
- grpc_load_file(SSL_CERT_PATH, 1, &cert_slice)));
- GPR_ASSERT(GRPC_LOG_IF_ERROR("load_file",
- grpc_load_file(SSL_KEY_PATH, 1, &key_slice)));
- const char *ca_cert = (const char *)GRPC_SLICE_START_PTR(ca_slice);
- pem_key_cert_pair.private_key = (const char *)GRPC_SLICE_START_PTR(key_slice);
- pem_key_cert_pair.cert_chain = (const char *)GRPC_SLICE_START_PTR(cert_slice);
- grpc_server_credentials *ssl_creds = grpc_ssl_server_credentials_create(
- ca_cert, &pem_key_cert_pair, 1, 0, NULL);
-
- // Start server listening on local port.
- char *addr;
- gpr_asprintf(&addr, "127.0.0.1:%d", port);
- grpc_server *server = grpc_server_create(NULL, NULL);
- GPR_ASSERT(grpc_server_add_secure_http2_port(server, addr, ssl_creds));
- free(addr);
-
- grpc_completion_queue *cq = grpc_completion_queue_create_for_next(NULL);
-
- grpc_server_register_completion_queue(server, cq, NULL);
- grpc_server_start(server);
-
- // Wait a bounded number of time until client_handshake_complete is set,
- // sleeping between polls.
- int retries = 10;
- while (!gpr_event_get(&client_handshake_complete) && retries-- > 0) {
- const gpr_timespec cq_deadline = grpc_timeout_seconds_to_deadline(1);
- grpc_event ev = grpc_completion_queue_next(cq, cq_deadline, NULL);
- GPR_ASSERT(ev.type == GRPC_QUEUE_TIMEOUT);
- }
-
- gpr_log(GPR_INFO, "Shutting down server");
- grpc_server_shutdown_and_notify(server, cq, NULL);
- grpc_completion_queue_shutdown(cq);
-
- const gpr_timespec cq_deadline = grpc_timeout_seconds_to_deadline(5);
- grpc_event ev = grpc_completion_queue_next(cq, cq_deadline, NULL);
- GPR_ASSERT(ev.type == GRPC_OP_COMPLETE);
-
- grpc_server_destroy(server);
- grpc_completion_queue_destroy(cq);
- grpc_server_credentials_release(ssl_creds);
- grpc_slice_unref(cert_slice);
- grpc_slice_unref(key_slice);
- grpc_slice_unref(ca_slice);
-}
-
-// This test launches a gRPC server on a separate thread and then establishes a
-// TLS handshake via a minimal TLS client. The TLS client has configurable (via
-// alpn_list) ALPN settings and can probe at the supported ALPN preferences
-// using this (via alpn_expected).
-static bool server_ssl_test(const char *alpn_list[], unsigned int alpn_list_len,
- const char *alpn_expected) {
- bool success = true;
-
- grpc_init();
- int port = grpc_pick_unused_port_or_die();
- gpr_event_init(&client_handshake_complete);
-
- // Launch the gRPC server thread.
- gpr_thd_options thdopt = gpr_thd_options_default();
- gpr_thd_id thdid;
- gpr_thd_options_set_joinable(&thdopt);
- GPR_ASSERT(gpr_thd_new(&thdid, server_thread, &port, &thdopt));
-
- SSL_load_error_strings();
- OpenSSL_add_ssl_algorithms();
-
- const SSL_METHOD *method = TLSv1_2_client_method();
- SSL_CTX *ctx = SSL_CTX_new(method);
- if (!ctx) {
- perror("Unable to create SSL context");
- ERR_print_errors_fp(stderr);
- abort();
- }
-
- // Load key pair.
- if (SSL_CTX_use_certificate_file(ctx, SSL_CERT_PATH, SSL_FILETYPE_PEM) < 0) {
- ERR_print_errors_fp(stderr);
- abort();
- }
- if (SSL_CTX_use_PrivateKey_file(ctx, SSL_KEY_PATH, SSL_FILETYPE_PEM) < 0) {
- ERR_print_errors_fp(stderr);
- abort();
- }
-
- // Set the cipher list to match the one expressed in
- // src/core/tsi/ssl_transport_security.c.
- const char *cipher_list =
- "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-"
- "SHA384:ECDHE-RSA-AES256-GCM-SHA384";
- if (!SSL_CTX_set_cipher_list(ctx, cipher_list)) {
- ERR_print_errors_fp(stderr);
- gpr_log(GPR_ERROR, "Couldn't set server cipher list.");
- abort();
- }
-
- // Configure ALPN list the client will send to the server. This must match the
- // wire format, see documentation for SSL_CTX_set_alpn_protos.
- unsigned int alpn_protos_len = alpn_list_len;
- for (unsigned int i = 0; i < alpn_list_len; ++i) {
- alpn_protos_len += (unsigned int)strlen(alpn_list[i]);
- }
- unsigned char *alpn_protos = gpr_malloc(alpn_protos_len);
- unsigned char *p = alpn_protos;
- for (unsigned int i = 0; i < alpn_list_len; ++i) {
- const uint8_t len = (uint8_t)strlen(alpn_list[i]);
- *p++ = len;
- memcpy(p, alpn_list[i], len);
- p += len;
- }
- GPR_ASSERT(SSL_CTX_set_alpn_protos(ctx, alpn_protos, alpn_protos_len) == 0);
-
- // Try and connect to server. We allow a bounded number of retries as we might
- // be racing with the server setup on its separate thread.
- int retries = 10;
- int sock = -1;
- while (sock == -1 && retries-- > 0) {
- sock = create_socket(port);
- if (sock < 0) {
- sleep(1);
- }
- }
- GPR_ASSERT(sock > 0);
- gpr_log(GPR_INFO, "Connected to server on port %d", port);
-
- // Establish a SSL* and connect at SSL layer.
- SSL *ssl = SSL_new(ctx);
- GPR_ASSERT(ssl);
- SSL_set_fd(ssl, sock);
- if (SSL_connect(ssl) <= 0) {
- ERR_print_errors_fp(stderr);
- gpr_log(GPR_ERROR, "Handshake failed.");
- success = false;
- } else {
- gpr_log(GPR_INFO, "Handshake successful.");
- // Validate ALPN preferred by server matches alpn_expected.
- const unsigned char *alpn_selected;
- unsigned int alpn_selected_len;
- SSL_get0_alpn_selected(ssl, &alpn_selected, &alpn_selected_len);
- if (strlen(alpn_expected) != alpn_selected_len ||
- strncmp((const char *)alpn_selected, alpn_expected,
- alpn_selected_len) != 0) {
- gpr_log(GPR_ERROR, "Unexpected ALPN protocol preference");
- success = false;
- }
- }
- gpr_event_set(&client_handshake_complete, &client_handshake_complete);
-
- SSL_free(ssl);
- gpr_free(alpn_protos);
- SSL_CTX_free(ctx);
- EVP_cleanup();
- close(sock);
-
- gpr_thd_join(thdid);
-
- grpc_shutdown();
-
- return success;
-}
+#include "test/core/handshake/server_ssl_common.h"
int main(int argc, char *argv[]) {
// Handshake succeeeds when the client supplies the standard ALPN list.
diff --git a/test/core/handshake/server_ssl_common.c b/test/core/handshake/server_ssl_common.c
new file mode 100644
index 0000000000..d1911ecfde
--- /dev/null
+++ b/test/core/handshake/server_ssl_common.c
@@ -0,0 +1,237 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "test/core/handshake/server_ssl_common.h"
+
+#include <arpa/inet.h>
+#include <openssl/err.h>
+#include <openssl/ssl.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <grpc/grpc.h>
+#include <grpc/grpc_security.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/thd.h>
+#include "src/core/lib/iomgr/load_file.h"
+#include "test/core/util/port.h"
+#include "test/core/util/test_config.h"
+
+#define SSL_CERT_PATH "src/core/tsi/test_creds/server1.pem"
+#define SSL_KEY_PATH "src/core/tsi/test_creds/server1.key"
+#define SSL_CA_PATH "src/core/tsi/test_creds/ca.pem"
+
+// Handshake completed signal to server thread.
+static gpr_event client_handshake_complete;
+
+static int create_socket(int port) {
+ int s;
+ struct sockaddr_in addr;
+
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons((uint16_t)port);
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+
+ s = socket(AF_INET, SOCK_STREAM, 0);
+ if (s < 0) {
+ perror("Unable to create socket");
+ return -1;
+ }
+
+ if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ perror("Unable to connect");
+ return -1;
+ }
+
+ return s;
+}
+
+// Simple gRPC server. This listens until client_handshake_complete occurs.
+static void server_thread(void *arg) {
+ const int port = *(int *)arg;
+
+ // Load key pair and establish server SSL credentials.
+ grpc_ssl_pem_key_cert_pair pem_key_cert_pair;
+ grpc_slice ca_slice, cert_slice, key_slice;
+ GPR_ASSERT(GRPC_LOG_IF_ERROR("load_file",
+ grpc_load_file(SSL_CA_PATH, 1, &ca_slice)));
+ GPR_ASSERT(GRPC_LOG_IF_ERROR("load_file",
+ grpc_load_file(SSL_CERT_PATH, 1, &cert_slice)));
+ GPR_ASSERT(GRPC_LOG_IF_ERROR("load_file",
+ grpc_load_file(SSL_KEY_PATH, 1, &key_slice)));
+ const char *ca_cert = (const char *)GRPC_SLICE_START_PTR(ca_slice);
+ pem_key_cert_pair.private_key = (const char *)GRPC_SLICE_START_PTR(key_slice);
+ pem_key_cert_pair.cert_chain = (const char *)GRPC_SLICE_START_PTR(cert_slice);
+ grpc_server_credentials *ssl_creds = grpc_ssl_server_credentials_create(
+ ca_cert, &pem_key_cert_pair, 1, 0, NULL);
+
+ // Start server listening on local port.
+ char *addr;
+ gpr_asprintf(&addr, "127.0.0.1:%d", port);
+ grpc_server *server = grpc_server_create(NULL, NULL);
+ GPR_ASSERT(grpc_server_add_secure_http2_port(server, addr, ssl_creds));
+ free(addr);
+
+ grpc_completion_queue *cq = grpc_completion_queue_create_for_next(NULL);
+
+ grpc_server_register_completion_queue(server, cq, NULL);
+ grpc_server_start(server);
+
+ // Wait a bounded number of time until client_handshake_complete is set,
+ // sleeping between polls.
+ int retries = 10;
+ while (!gpr_event_get(&client_handshake_complete) && retries-- > 0) {
+ const gpr_timespec cq_deadline = grpc_timeout_seconds_to_deadline(1);
+ grpc_event ev = grpc_completion_queue_next(cq, cq_deadline, NULL);
+ GPR_ASSERT(ev.type == GRPC_QUEUE_TIMEOUT);
+ }
+
+ gpr_log(GPR_INFO, "Shutting down server");
+ grpc_server_shutdown_and_notify(server, cq, NULL);
+ grpc_completion_queue_shutdown(cq);
+
+ const gpr_timespec cq_deadline = grpc_timeout_seconds_to_deadline(5);
+ grpc_event ev = grpc_completion_queue_next(cq, cq_deadline, NULL);
+ GPR_ASSERT(ev.type == GRPC_OP_COMPLETE);
+
+ grpc_server_destroy(server);
+ grpc_completion_queue_destroy(cq);
+ grpc_server_credentials_release(ssl_creds);
+ grpc_slice_unref(cert_slice);
+ grpc_slice_unref(key_slice);
+ grpc_slice_unref(ca_slice);
+}
+
+// This test launches a gRPC server on a separate thread and then establishes a
+// TLS handshake via a minimal TLS client. The TLS client has configurable (via
+// alpn_list) ALPN settings and can probe at the supported ALPN preferences
+// using this (via alpn_expected).
+bool server_ssl_test(const char *alpn_list[], unsigned int alpn_list_len,
+ const char *alpn_expected) {
+ bool success = true;
+
+ grpc_init();
+ int port = grpc_pick_unused_port_or_die();
+ gpr_event_init(&client_handshake_complete);
+
+ // Launch the gRPC server thread.
+ gpr_thd_options thdopt = gpr_thd_options_default();
+ gpr_thd_id thdid;
+ gpr_thd_options_set_joinable(&thdopt);
+ GPR_ASSERT(gpr_thd_new(&thdid, server_thread, &port, &thdopt));
+
+ SSL_load_error_strings();
+ OpenSSL_add_ssl_algorithms();
+
+ const SSL_METHOD *method = TLSv1_2_client_method();
+ SSL_CTX *ctx = SSL_CTX_new(method);
+ if (!ctx) {
+ perror("Unable to create SSL context");
+ ERR_print_errors_fp(stderr);
+ abort();
+ }
+
+ // Load key pair.
+ if (SSL_CTX_use_certificate_file(ctx, SSL_CERT_PATH, SSL_FILETYPE_PEM) < 0) {
+ ERR_print_errors_fp(stderr);
+ abort();
+ }
+ if (SSL_CTX_use_PrivateKey_file(ctx, SSL_KEY_PATH, SSL_FILETYPE_PEM) < 0) {
+ ERR_print_errors_fp(stderr);
+ abort();
+ }
+
+ // Set the cipher list to match the one expressed in
+ // src/core/tsi/ssl_transport_security.c.
+ const char *cipher_list =
+ "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-"
+ "SHA384:ECDHE-RSA-AES256-GCM-SHA384";
+ if (!SSL_CTX_set_cipher_list(ctx, cipher_list)) {
+ ERR_print_errors_fp(stderr);
+ gpr_log(GPR_ERROR, "Couldn't set server cipher list.");
+ abort();
+ }
+
+ // Configure ALPN list the client will send to the server. This must match the
+ // wire format, see documentation for SSL_CTX_set_alpn_protos.
+ unsigned int alpn_protos_len = alpn_list_len;
+ for (unsigned int i = 0; i < alpn_list_len; ++i) {
+ alpn_protos_len += (unsigned int)strlen(alpn_list[i]);
+ }
+ unsigned char *alpn_protos = gpr_malloc(alpn_protos_len);
+ unsigned char *p = alpn_protos;
+ for (unsigned int i = 0; i < alpn_list_len; ++i) {
+ const uint8_t len = (uint8_t)strlen(alpn_list[i]);
+ *p++ = len;
+ memcpy(p, alpn_list[i], len);
+ p += len;
+ }
+ GPR_ASSERT(SSL_CTX_set_alpn_protos(ctx, alpn_protos, alpn_protos_len) == 0);
+
+ // Try and connect to server. We allow a bounded number of retries as we might
+ // be racing with the server setup on its separate thread.
+ int retries = 10;
+ int sock = -1;
+ while (sock == -1 && retries-- > 0) {
+ sock = create_socket(port);
+ if (sock < 0) {
+ sleep(1);
+ }
+ }
+ GPR_ASSERT(sock > 0);
+ gpr_log(GPR_INFO, "Connected to server on port %d", port);
+
+ // Establish a SSL* and connect at SSL layer.
+ SSL *ssl = SSL_new(ctx);
+ GPR_ASSERT(ssl);
+ SSL_set_fd(ssl, sock);
+ if (SSL_connect(ssl) <= 0) {
+ ERR_print_errors_fp(stderr);
+ gpr_log(GPR_ERROR, "Handshake failed.");
+ success = false;
+ } else {
+ gpr_log(GPR_INFO, "Handshake successful.");
+ // Validate ALPN preferred by server matches alpn_expected.
+ const unsigned char *alpn_selected;
+ unsigned int alpn_selected_len;
+ SSL_get0_alpn_selected(ssl, &alpn_selected, &alpn_selected_len);
+ if (strlen(alpn_expected) != alpn_selected_len ||
+ strncmp((const char *)alpn_selected, alpn_expected,
+ alpn_selected_len) != 0) {
+ gpr_log(GPR_ERROR, "Unexpected ALPN protocol preference");
+ success = false;
+ }
+ }
+ gpr_event_set(&client_handshake_complete, &client_handshake_complete);
+
+ SSL_free(ssl);
+ gpr_free(alpn_protos);
+ SSL_CTX_free(ctx);
+ EVP_cleanup();
+ close(sock);
+
+ gpr_thd_join(thdid);
+
+ grpc_shutdown();
+
+ return success;
+}
diff --git a/test/core/handshake/server_ssl_common.h b/test/core/handshake/server_ssl_common.h
new file mode 100644
index 0000000000..67b323d82f
--- /dev/null
+++ b/test/core/handshake/server_ssl_common.h
@@ -0,0 +1,22 @@
+//
+// Created by apolcyn on 10/31/17.
+//
+
+#ifndef GRPC_SERVER_SSL_COMMON_H
+#define GRPC_SERVER_SSL_COMMON_H
+
+#include <grpc/grpc.h>
+#include <grpc/grpc_security.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/thd.h>
+#include "src/core/lib/iomgr/load_file.h"
+#include "test/core/util/port.h"
+#include "test/core/util/test_config.h"
+
+bool server_ssl_test(const char *alpn_list[], unsigned int alpn_list_len,
+ const char *alpn_expected);
+
+#endif // GRPC_SERVER_SSL_COMMON_H
diff --git a/tools/run_tests/generated/sources_and_headers.json b/tools/run_tests/generated/sources_and_headers.json
index 46f4cb6532..9f5e39e054 100644
--- a/tools/run_tests/generated/sources_and_headers.json
+++ b/tools/run_tests/generated/sources_and_headers.json
@@ -1280,7 +1280,26 @@
"language": "c",
"name": "handshake_server",
"src": [
- "test/core/handshake/server_ssl.c"
+ "test/core/handshake/server_ssl.c",
+ "test/core/handshake/server_ssl_common.c"
+ ],
+ "third_party": false,
+ "type": "target"
+ },
+ {
+ "deps": [
+ "gpr",
+ "gpr_test_util",
+ "grpc",
+ "grpc_test_util"
+ ],
+ "headers": [],
+ "is_filegroup": false,
+ "language": "c",
+ "name": "handshake_server_with_readahead_handshaker",
+ "src": [
+ "test/core/handshake/readahead_handshaker_server_ssl.c",
+ "test/core/handshake/server_ssl_common.c"
],
"third_party": false,
"type": "target"
diff --git a/tools/run_tests/generated/tests.json b/tools/run_tests/generated/tests.json
index 3b1b6587f6..7f197b4ba0 100644
--- a/tools/run_tests/generated/tests.json
+++ b/tools/run_tests/generated/tests.json
@@ -1603,6 +1603,26 @@
"args": [],
"benchmark": false,
"ci_platforms": [
+ "linux"
+ ],
+ "cpu_cost": 1.0,
+ "exclude_configs": [],
+ "exclude_iomgrs": [
+ "uv"
+ ],
+ "flaky": false,
+ "gtest": false,
+ "language": "c",
+ "name": "handshake_server_with_readahead_handshaker",
+ "platforms": [
+ "linux"
+ ],
+ "uses_polling": true
+ },
+ {
+ "args": [],
+ "benchmark": false,
+ "ci_platforms": [
"linux",
"mac",
"posix",