blob: 3f509f0e3696c7582c3e890b97235b1a15701477 [file]
/** @file
SSL delay test server
@section license License
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you 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 <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <cstdio>
#include <cstdlib>
#include <unistd.h>
#include <cstring>
#include <openssl/ssl.h>
#include <fcntl.h>
#include <netinet/tcp.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <pthread.h>
#include <openssl/err.h>
#include <sys/time.h>
#include <sys/select.h>
#include <cerrno>
#include <csignal>
char req_buf[10000];
char post_buf[1000];
SSL_CTX *svr_ctx;
int connect_delay;
int ttfb_delay;
pthread_mutex_t *mutex_buf = nullptr;
struct thread_info {
struct addrinfo *result, *rp;
SSL_SESSION *session;
};
void
SSL_locking_callback(int mode, int type, const char * /* file ATS_UNUSED */, int /* line ATS_UNUSED */)
{
if (mode & CRYPTO_LOCK) {
pthread_mutex_lock(&mutex_buf[type]);
} else if (mode & CRYPTO_UNLOCK) {
pthread_mutex_unlock(&mutex_buf[type]);
} else {
printf("invalid SSL locking mode 0x%x\n", mode);
}
}
void
SSL_pthreads_thread_id([[maybe_unused]] CRYPTO_THREADID *id)
{
CRYPTO_THREADID_set_numeric(id, (unsigned long)pthread_self());
}
char input_buf[1000];
char response_buf[] = "200 HTTP/1.1\r\nConnection: close\r\n\r\n";
void *
run_session(void *arg)
{
int sfd = (intptr_t)arg;
SSL *ssl = SSL_new(svr_ctx);
if (ssl == nullptr) {
fprintf(stderr, "Failed to create ssl\n");
return nullptr;
}
SSL_set_max_proto_version(ssl, TLS1_2_VERSION);
SSL_set_fd(ssl, sfd);
fprintf(stderr, "Accept try %d\n", sfd);
// Potentially delay before processing the TLS handshake
if (connect_delay > 0) {
fprintf(stderr, "Connect delay %d\n", connect_delay);
// Delay here
sleep(connect_delay);
}
int did_accept = 0;
int ret = SSL_accept(ssl);
if (!did_accept && ret == 1) {
did_accept = 1;
fprintf(stderr, "Done accept\n");
} else {
int ssl_err = SSL_get_error(ssl, ret);
fprintf(stderr, "Failed accept ssl_error=%d errno=%d\n", ssl_err, errno);
return nullptr;
}
if (did_accept) {
ret = SSL_read(ssl, input_buf, sizeof(input_buf));
if (ret < 0) {
// Failure
fprintf(stderr, "Server read failure\n");
} else {
fprintf(stderr, "TTFB delay\n");
if (ttfb_delay > 0) {
// TTFB delay
sleep(ttfb_delay);
}
fprintf(stderr, "Write response\n");
ret = SSL_write(ssl, response_buf, strlen(response_buf));
fprintf(stderr, "Write response %d\n", ret);
if (ret <= 0) {
fprintf(stderr, "Server write failure\n");
} else {
fprintf(stderr, "Write response succeeded. Go to the next one\n");
}
}
}
if (ssl) {
SSL_free(ssl);
}
close(sfd);
return nullptr;
}
/**
* Simple TLS server with configurable delays
*/
int
main(int argc, char *argv[])
{
int sfd;
if (argc != 5) {
fprintf(stderr, "Usage: %s <listen port> <handshake delay> <ttfb delay> <cert/key pem file>\n", argv[0]);
exit(EXIT_FAILURE);
}
short listen_port = atoi(argv[1]);
connect_delay = atoi(argv[2]);
ttfb_delay = atoi(argv[3]);
const char *pem_file = argv[4];
// Ignore SIGPIPE which can be raised when a client disconnects during
// the handshake delay, killing the process unexpectedly.
signal(SIGPIPE, SIG_IGN);
fprintf(stderr, "Listen on %d connect delay=%d ttfb delay=%d\n", listen_port, connect_delay, ttfb_delay);
int listenfd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in serv_addr;
memset(&serv_addr, '0', sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(listen_port);
bind(listenfd, reinterpret_cast<struct sockaddr *>(&serv_addr), sizeof(serv_addr));
SSL_load_error_strings();
SSL_library_init();
mutex_buf = static_cast<pthread_mutex_t *>(OPENSSL_malloc(CRYPTO_num_locks() * sizeof(pthread_mutex_t)));
for (int i = 0; i < CRYPTO_num_locks(); i++) {
pthread_mutex_init(&mutex_buf[i], nullptr);
}
CRYPTO_set_locking_callback(SSL_locking_callback);
#if CRYPTO_THREADID_set_callback
CRYPTO_THREADID_set_callback(SSL_pthreads_thread_id);
#endif
svr_ctx = SSL_CTX_new(SSLv23_server_method());
// Associate cert and key
if (SSL_CTX_use_certificate_file(svr_ctx, pem_file, SSL_FILETYPE_PEM) != 1) {
printf("Failed to load certificate from %s\n", pem_file);
exit(1);
}
if (SSL_CTX_use_PrivateKey_file(svr_ctx, pem_file, SSL_FILETYPE_PEM) != 1) {
printf("Failed to load private key from %s\n", pem_file);
exit(1);
}
listen(listenfd, 10);
for (;;) {
sfd = accept(listenfd, (struct sockaddr *)nullptr, nullptr);
if (sfd < 0) {
if (errno == EINTR) {
continue;
}
printf("Listen failure errno=%d\n", errno);
exit(1);
}
fprintf(stderr, "Spawn off new sesson thread %d\n", sfd);
// Spawn off new thread
pthread_t thread_id;
pthread_create(&thread_id, nullptr, run_session, (void *)(static_cast<intptr_t>(sfd)));
}
exit(0);
}