This is an automated email from the ASF dual-hosted git repository.
zwoop pushed a commit to branch 7.1.x
in repository https://gitbox.apache.org/repos/asf/trafficserver.git
The following commit(s) were added to refs/heads/7.1.x by this push:
new 0e5090b Fix SSL handshake problems.
0e5090b is described below
commit 0e5090bdea5b988f4f97b1d95801e92e6b0e13f9
Author: Susan Hinrichs <[email protected]>
AuthorDate: Thu May 18 15:08:21 2017 +0000
Fix SSL handshake problems.
(cherry picked from commit c128363e2ce3ad435769221466627f7fbff86cb8)
---
iocore/net/SSLNetVConnection.cc | 305 ++++++++++++++---------------
tests/gold_tests/tls/gold/ssl-post.gold | 2 +
tests/gold_tests/tls/ssl-post.c | 330 ++++++++++++++++++++++++++++++++
tests/gold_tests/tls/ssl/server.key | 15 ++
tests/gold_tests/tls/ssl/server.pem | 32 ++++
tests/gold_tests/tls/tls.test.py | 101 ++++++++++
6 files changed, 634 insertions(+), 151 deletions(-)
diff --git a/iocore/net/SSLNetVConnection.cc b/iocore/net/SSLNetVConnection.cc
index cea206f..204ee7b 100644
--- a/iocore/net/SSLNetVConnection.cc
+++ b/iocore/net/SSLNetVConnection.cc
@@ -322,76 +322,63 @@ ssl_read_from_net(SSLNetVConnection *sslvc, EThread
*lthread, int64_t &ret)
int64_t
SSLNetVConnection::read_raw_data()
{
- int64_t r = 0;
- int64_t toread = INT_MAX;
-
// read data
- int64_t rattempted = 0, total_read = 0;
- unsigned niov = 0;
- IOVec tiovec[NET_MAX_IOV];
- if (toread) {
- IOBufferBlock *b = this->handShakeBuffer->first_write_block();
- do {
- niov = 0;
- rattempted = 0;
- while (b && niov < NET_MAX_IOV) {
- int64_t a = b->write_avail();
- if (a > 0) {
- tiovec[niov].iov_base = b->_end;
- int64_t togo = toread - total_read - rattempted;
- if (a > togo)
- a = togo;
- tiovec[niov].iov_len = a;
- rattempted += a;
- niov++;
- if (a >= togo)
- break;
- }
- b = b->next.get();
- }
-
- // If there was no room to write into the buffer then skip the read
- if (niov > 0) {
- ink_assert(niov > 0);
- ink_assert(niov <= countof(tiovec));
- r = socketManager.readv(this->con.fd, &tiovec[0], niov);
+ int64_t r = 0;
+ int64_t total_read = 0;
+ int64_t rattempted = 0;
+ char *buffer = 0;
+ int buf_len;
+ IOBufferBlock *b = this->handShakeBuffer->first_write_block();
+
+ rattempted = b->write_avail();
+ while (rattempted) {
+ buffer = b->_end;
+ buf_len = rattempted;
+ b = b->next.get();
+
+ r = socketManager.read(this->con.fd, buffer, buf_len);
+ NET_INCREMENT_DYN_STAT(net_calls_to_read_stat);
+ total_read += rattempted;
+
+ // last read failed or was incomplete
+ if (r != rattempted || !b)
+ break;
- NET_INCREMENT_DYN_STAT(net_calls_to_read_stat);
- total_read += rattempted;
- } else { // No more space to write, break out
- r = 0;
- break;
- }
- } while (rattempted && r == rattempted && total_read < toread);
+ rattempted = b->write_avail();
+ }
- // if we have already moved some bytes successfully, summarize in r
- if (total_read != rattempted) {
- if (r <= 0)
- r = total_read - rattempted;
- else
- r = total_read - rattempted + r;
- }
- // check for errors
- if (r <= 0) {
- if (r == -EAGAIN || r == -ENOTCONN) {
- NET_INCREMENT_DYN_STAT(net_calls_to_read_nodata_stat);
- }
- return r;
- }
- NET_SUM_DYN_STAT(net_read_bytes_stat, r);
+ // If we have already moved some bytes successfully, adjust total_read to
reflect reality
+ // If any read succeeded, we should return success
+ if (r != rattempted) {
+ if (r <= 0)
+ r = total_read - rattempted;
+ else
+ r = total_read - rattempted + r;
+ }
+ NET_SUM_DYN_STAT(net_read_bytes_stat, r);
+ if (r > 0) {
this->handShakeBuffer->fill(r);
+
+ char *start = this->handShakeReader->start();
+ char *end = this->handShakeReader->end();
+ this->handShakeBioStored = end - start;
+
+ // Sets up the buffer as a read only bio target
+ // Must be reset on each read
+ BIO *rbio = BIO_new_mem_buf(start, this->handShakeBioStored);
+ BIO_set_mem_eof_return(rbio, -1);
+ SSL_set0_rbio(this->ssl, rbio);
}
- char *start = this->handShakeReader->start();
- char *end = this->handShakeReader->end();
- this->handShakeBioStored = end - start;
+ Debug("ssl", "%p read r=%" PRId64 " total=%" PRId64 " bio=%d\n", this, r,
total_read, this->handShakeBioStored);
- // Sets up the buffer as a read only bio target
- // Must be reset on each read
- BIO *rbio = BIO_new_mem_buf(start, this->handShakeBioStored);
- BIO_set_mem_eof_return(rbio, -1);
- SSL_set0_rbio(this->ssl, rbio);
+ // check for errors
+ if (r <= 0) {
+ if (r == -EAGAIN || r == -ENOTCONN) {
+ NET_INCREMENT_DYN_STAT(net_calls_to_read_nodata_stat);
+ }
+ }
return r;
}
@@ -452,45 +439,60 @@ SSLNetVConnection::net_read_io(NetHandler *nh, EThread
*lthread)
ret = sslStartHandShake(SSL_EVENT_SERVER, err);
}
// If we have flipped to blind tunnel, don't read ahead
- if (this->handShakeReader && this->attributes !=
HttpProxyPort::TRANSPORT_BLIND_TUNNEL) {
- // Check and consume data that has been read
- if (BIO_eof(SSL_get_rbio(this->ssl))) {
- this->handShakeReader->consume(this->handShakeBioStored);
- this->handShakeBioStored = 0;
- }
- } else if (this->attributes == HttpProxyPort::TRANSPORT_BLIND_TUNNEL) {
- // Now in blind tunnel. Set things up to read what is in the buffer
- // Must send the READ_COMPLETE here before considering
- // forwarding on the handshake buffer, so the
- // SSLNextProtocolTrampoline has a chance to do its
- // thing before forwarding the buffers.
- this->readSignalDone(VC_EVENT_READ_COMPLETE, nh);
-
- // If the handshake isn't set yet, this means the tunnel
- // decision was make in the SNI callback. We must move
- // the client hello message back into the standard read.vio
- // so it will get forwarded onto the origin server
- if (!this->getSSLHandShakeComplete()) {
- this->sslHandShakeComplete = true;
-
- // Copy over all data already read in during the SSL_accept
- // (the client hello message)
- NetState *s = &this->read;
- MIOBufferAccessor &buf = s->vio.buffer;
- int64_t r = buf.writer()->write(this->handShakeHolder);
- s->vio.nbytes += r;
- s->vio.ndone += r;
-
- // Clean up the handshake buffers
- this->free_handshake_buffers();
-
- if (r > 0) {
- // Kick things again, so the data that was copied into the
- // vio.read buffer gets processed
- this->readSignalDone(VC_EVENT_READ_COMPLETE, nh);
+ if (this->handShakeReader) {
+ if (this->attributes != HttpProxyPort::TRANSPORT_BLIND_TUNNEL) {
+ // Check and consume data that has been read
+ if (BIO_eof(SSL_get_rbio(this->ssl))) {
+ this->handShakeReader->consume(this->handShakeBioStored);
+ this->handShakeBioStored = 0;
+ // Load up the next block if present
+ if (this->handShakeReader->is_read_avail_more_than(0)) {
+ // Setup the next iobuffer block to drain
+ char *start = this->handShakeReader->start();
+ char *end = this->handShakeReader->end();
+ this->handShakeBioStored = end - start;
+
+ // Sets up the buffer as a read only bio target
+ // Must be reset on each read
+ BIO *rbio = BIO_new_mem_buf(start, this->handShakeBioStored);
+ BIO_set_mem_eof_return(rbio, -1);
+ SSL_set0_rbio(this->ssl, rbio);
+ }
+ }
+ } else {
+ // Now in blind tunnel. Set things up to read what is in the buffer
+ // Must send the READ_COMPLETE here before considering
+ // forwarding on the handshake buffer, so the
+ // SSLNextProtocolTrampoline has a chance to do its
+ // thing before forwarding the buffers.
+ this->readSignalDone(VC_EVENT_READ_COMPLETE, nh);
+
+ // If the handshake isn't set yet, this means the tunnel
+ // decision was make in the SNI callback. We must move
+ // the client hello message back into the standard read.vio
+ // so it will get forwarded onto the origin server
+ if (!this->getSSLHandShakeComplete()) {
+ this->sslHandShakeComplete = 1;
+
+ // Copy over all data already read in during the SSL_accept
+ // (the client hello message)
+ NetState *s = &this->read;
+ MIOBufferAccessor &buf = s->vio.buffer;
+ int64_t r = buf.writer()->write(this->handShakeHolder);
+ s->vio.nbytes += r;
+ s->vio.ndone += r;
+
+ // Clean up the handshake buffers
+ this->free_handshake_buffers();
+
+ if (r > 0) {
+ // Kick things again, so the data that was copied into the
+ // vio.read buffer gets processed
+ this->readSignalDone(VC_EVENT_READ_COMPLETE, nh);
+ }
}
+ return; // Leave if we are tunneling
}
- return;
}
if (ret == EVENT_ERROR) {
this->read.triggered = 0;
@@ -508,6 +510,17 @@ SSLNetVConnection::net_read_io(NetHandler *nh, EThread
*lthread)
return;
}
}
+ // move over to the socket if we haven't already
+ if (this->handShakeBuffer) {
+ ink_release_assert(BIO_eof(SSL_get_rbio(this->ssl)) &&
!handShakeReader->is_read_avail_more_than(0));
+ // Done with the buffer after the first exchange, convert over to the
socket buffer
+ BIO *rbio = BIO_new_fd(this->get_socket(), BIO_NOCLOSE);
+ BIO_set_mem_eof_return(rbio, -1);
+ SSL_set0_rbio(this->ssl, rbio);
+ free_handshake_buffers();
+ } else {
+ Debug("ssl", "Want read from socket");
+ }
read.triggered = 0;
nh->read_ready_list.remove(this);
readReschedule(nh);
@@ -541,36 +554,7 @@ SSLNetVConnection::net_read_io(NetHandler *nh, EThread
*lthread)
}
// At this point we are at the post-handshake SSL processing
- // If the read BIO is not already a socket, consider changing it
- if (this->handShakeReader) {
- // Check out if there is anything left in the current bio
- if (!BIO_eof(SSL_get_rbio(this->ssl))) {
- // Still data remaining in the current BIO block
- } else {
- // Consume what SSL has read so far.
- this->handShakeReader->consume(this->handShakeBioStored);
-
- // If we are empty now, switch over
- if (this->handShakeReader->read_avail() <= 0) {
- // Switch the read bio over to a socket bio
- SSL_set_rfd(this->ssl, this->get_socket());
- this->free_handshake_buffers();
- } else {
- // Setup the next iobuffer block to drain
- char *start = this->handShakeReader->start();
- char *end = this->handShakeReader->end();
- this->handShakeBioStored = end - start;
-
- // Sets up the buffer as a read only bio target
- // Must be reset on each read
- BIO *rbio = BIO_new_mem_buf(start, this->handShakeBioStored);
- BIO_set_mem_eof_return(rbio, -1);
- SSL_set0_rbio(this->ssl, rbio);
- }
- }
- }
- // Otherwise, we already replaced the buffer bio with a socket bio
-
+ //
// not sure if this do-while loop is really needed here, please replace
// this comment if you know
do {
@@ -1075,19 +1059,47 @@ SSLNetVConnection::sslServerHandShakeEvent(int &err)
sslHandShakeComplete = true;
return EVENT_DONE;
}
-
- int retval = 1; // Initialze with a non-error value
-
// All the pre-accept hooks have completed, proceed with the actual accept.
- if (BIO_eof(SSL_get_rbio(this->ssl))) { // No more data in the buffer
- // Read from socket to fill in the BIO buffer with the
- // raw handshake data before calling the ssl accept calls.
- retval = this->read_raw_data();
- if (retval == 0) {
- // EOF, go away, we stopped in the handshake
- SSLDebugVC(this, "SSL handshake error: EOF");
- return EVENT_ERROR;
- }
+ if (this->handShakeReader) {
+ if (BIO_eof(SSL_get_rbio(this->ssl))) { // No more data in the buffer
+ // Is this the first read?
+ if (!this->handShakeReader->is_read_avail_more_than(0)) {
+ Debug("ssl", "%p first read\n", this);
+ // Read from socket to fill in the BIO buffer with the
+ // raw handshake data before calling the ssl accept calls.
+ int retval = this->read_raw_data();
+ if (retval < 0) {
+ if (retval == -EAGAIN) {
+ // No data at the moment, hang tight
+ // SSLDebugVC(this, "SSL handshake: EAGAIN");
+ return SSL_HANDSHAKE_WANT_READ;
+ } else {
+ // An error, make us go away
+ SSLDebugVC(this, "SSL handshake error: read_retval=%d", retval);
+ return EVENT_ERROR;
+ }
+ } else if (retval == 0) {
+ // EOF, go away, we stopped in the handshake
+ SSLDebugVC(this, "SSL handshake error: EOF");
+ return EVENT_ERROR;
+ }
+ } else {
+ this->handShakeReader->consume(this->handShakeBioStored);
+ this->handShakeBioStored = 0;
+ // There is more data in the buffer, reset the memory buffer
+ if (this->handShakeReader->is_read_avail_more_than(0)) {
+ char *start = this->handShakeReader->start();
+ char *end = this->handShakeReader->end();
+ this->handShakeBioStored = end - start;
+
+ // Sets up the buffer as a read only bio target
+ // Must be reset on each read
+ BIO *rbio = BIO_new_mem_buf(start, this->handShakeBioStored);
+ BIO_set_mem_eof_return(rbio, -1);
+ SSL_set0_rbio(this->ssl, rbio);
+ }
+ }
+ } // Still data in the BIO
}
ssl_error_t ssl_error = SSLAccept(ssl);
@@ -1098,7 +1110,7 @@ SSLNetVConnection::sslServerHandShakeEvent(int &err)
SSLDebugVC(this, "SSL handshake error: %s (%d), errno=%d",
SSLErrorName(ssl_error), ssl_error, err);
// start a blind tunnel if tr-pass is set and data does not look like
ClientHello
- char *buf = handShakeBuffer->buf();
+ char *buf = handShakeBuffer ? handShakeBuffer->buf() : NULL;
if (getTransparentPassThrough() && buf && *buf != SSL_OP_HANDSHAKE) {
SSLDebugVC(this, "Data does not look like SSL handshake, starting blind
tunnel");
this->attributes = HttpProxyPort::TRANSPORT_BLIND_TUNNEL;
@@ -1184,15 +1196,6 @@ SSLNetVConnection::sslServerHandShakeEvent(int &err)
case SSL_ERROR_WANT_READ:
TraceIn(trace, get_remote_addr(), get_remote_port(), "SSL server handshake
ERROR_WANT_READ");
- if (retval == -EAGAIN) {
- // No data at the moment, hang tight
- SSLDebugVC(this, "SSL handshake: EAGAIN");
- return SSL_HANDSHAKE_WANT_READ;
- } else if (retval < 0) {
- // An error, make us go away
- SSLDebugVC(this, "SSL handshake error: read_retval=%d", retval);
- return EVENT_ERROR;
- }
return SSL_HANDSHAKE_WANT_READ;
// This value is only defined in openssl has been patched to
diff --git a/tests/gold_tests/tls/gold/ssl-post.gold
b/tests/gold_tests/tls/gold/ssl-post.gold
new file mode 100644
index 0000000..b71b025
--- /dev/null
+++ b/tests/gold_tests/tls/gold/ssl-post.gold
@@ -0,0 +1,2 @@
+Sent request
+All threads finished
diff --git a/tests/gold_tests/tls/ssl-post.c b/tests/gold_tests/tls/ssl-post.c
new file mode 100644
index 0000000..e4c9f4f
--- /dev/null
+++ b/tests/gold_tests/tls/ssl-post.c
@@ -0,0 +1,330 @@
+/** @file
+
+ SSL post test client
+
+ @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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <openssl/ssl.h>
+#include <fcntl.h>
+#include <netinet/tcp.h>
+#include <pthread.h>
+
+#define NUM_THREADS 10
+
+char req_buf[10000];
+char post_buf[1000];
+
+pthread_mutex_t *mutex_buf = NULL;
+
+struct thread_info
+{
+ struct addrinfo *result, *rp;
+ SSL_SESSION *session;
+};
+
+void
+SSL_locking_callback(int mode, int type, const char *file, int line)
+{
+ 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(CRYPTO_THREADID *id)
+{
+ CRYPTO_THREADID_set_numeric(id, (unsigned long)pthread_self());
+}
+
+void *spawn_same_session_send(void *arg)
+{
+ struct thread_info *tinfo = (struct thread_info *)arg;
+
+ // Start again, but with the session set this time
+ int sfd = socket(tinfo->rp->ai_family, tinfo->rp->ai_socktype,
+ tinfo->rp->ai_protocol);
+ if (sfd == -1)
+ {
+ printf("Failed to get socket");
+ perror("Failed");
+ pthread_exit((void *)1);
+ }
+ if (connect(sfd, tinfo->rp->ai_addr, tinfo->rp->ai_addrlen) < 0)
+ {
+ printf("Failed to connect %d\n", sfd);
+ perror("Failed");
+ pthread_exit((void *)1);
+ }
+
+ fcntl(sfd, F_SETFL, O_NONBLOCK);
+ // Make sure we are nagling
+ int one = 0;
+ setsockopt(sfd, SOL_TCP, TCP_NODELAY, &one, sizeof(one));
+
+ SSL_CTX *client_ctx = SSL_CTX_new(SSLv23_client_method());
+ SSL *ssl = SSL_new(client_ctx);
+ SSL_set_session(ssl, tinfo->session);
+
+ SSL_set_fd(ssl, sfd);
+ int ret = SSL_connect(ssl);
+ int read_count = 0;
+ int write_count = 1;
+ int write_ret = -1;
+ int post_write_ret = -1;
+
+ while (ret < 0) {
+ int error = SSL_get_error(ssl, ret);
+ fd_set reads;
+ fd_set writes;
+ FD_ZERO(&reads);
+ FD_ZERO(&writes);
+ switch (error) {
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_ACCEPT:
+ FD_SET(sfd, &reads);
+ read_count++;
+ break;
+ case SSL_ERROR_WANT_CONNECT:
+ case SSL_ERROR_WANT_WRITE:
+ FD_SET(sfd, &writes);
+ write_count++;
+ break;
+ case SSL_ERROR_SYSCALL:
+ case SSL_ERROR_SSL:
+ case SSL_ERROR_ZERO_RETURN:
+ printf("Error %d\n", error);
+ pthread_exit((void *)1);
+ break;
+ default:
+ //printf("Unknown error is %d", error);
+ FD_SET(sfd, &reads);
+ FD_SET(sfd, &writes);
+ break;
+ }
+ ret = select(sfd+1, &reads, &writes, NULL, NULL);
+ if (FD_ISSET(sfd, &reads) || FD_ISSET(sfd, &writes)) {
+ ret = write_ret = SSL_write(ssl, req_buf, strlen(req_buf));
+ if (write_ret >= 0)
+ post_write_ret = SSL_write(ssl, post_buf, sizeof(post_buf));
+ }
+ }
+
+ while (write_ret < 0) {
+ write_ret = SSL_write(ssl, req_buf, strlen(req_buf));
+ }
+ while (post_write_ret < 0) {
+ post_write_ret = SSL_write(ssl, post_buf, sizeof(post_buf));
+ }
+
+ // Have to do the shutdown so the data packet is sent out fast enough
+ // so it might be read with the last handshake packet
+ shutdown(sfd, SHUT_WR);
+
+ char input_buf[1024];
+ int read_bytes = SSL_read(ssl, input_buf, sizeof(input_buf));
+ int total_read = 0;
+ while (read_bytes != 0) {
+ fd_set reads;
+ fd_set writes;
+ FD_ZERO(&reads);
+ FD_ZERO(&writes);
+ if (read_bytes > 0) {
+ total_read += read_bytes;
+ FD_SET(sfd, &reads);
+ }
+ else {
+ int error = SSL_get_error(ssl, read_bytes);
+ switch (error) {
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_ACCEPT:
+ FD_SET(sfd, &reads);
+ break;
+ case SSL_ERROR_WANT_CONNECT:
+ case SSL_ERROR_WANT_WRITE:
+ printf("Unexpected write\n");
+ pthread_exit((void *)1);
+ break;
+ case SSL_ERROR_SYSCALL:
+ case SSL_ERROR_SSL:
+ case SSL_ERROR_ZERO_RETURN:
+ printf("Error Read\n");
+ pthread_exit((void *)1);
+ break;
+ default:
+ FD_SET(sfd, &reads);
+ FD_SET(sfd, &writes);
+ break;
+ }
+ }
+ select(sfd+1, &reads, &writes, NULL, NULL);
+ if (FD_ISSET(sfd, &reads)) {
+ read_bytes = SSL_read(ssl, input_buf, sizeof(input_buf));
+ }
+ }
+ if (read_bytes > 0 && read_bytes < 1024) input_buf[read_bytes] = '\0';
+ else input_buf[1023] = '\0';
+ //printf("total_bytes=%d Received bytes=%d handshake writes=%d handshake
reads=%d\n", total_read, read_bytes, write_count, read_count);
+
+ // Leaking the socket, so that the EOS does not wake up a potentially
+ // stalled ATS connection. Want to wait for the inactivity timeout
+ // to make it clear that there was a stalling problem
+ //close(sfd);
+ pthread_exit(NULL);
+}
+
+/**
+ * Connect to a server.
+ * Handshake
+ * Exit immediatesly
+ */
+int
+main(int argc, char *argv[])
+{
+ struct addrinfo hints;
+ struct addrinfo *result, *rp;
+ int sfd, s, j;
+ size_t len;
+ ssize_t nread;
+
+ if (argc < 4) {
+ fprintf(stderr, "Usage: %s host thread-count header-count [port]\n",
argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ char *host = argv[1];
+ int header_count = atoi(argv[3]);
+ snprintf(req_buf, sizeof(req_buf), "POST /post HTTP/1.1\r\nHost:
%s\r\nConnection: close\r\nContent-length:%d\r\n", host, sizeof(post_buf));
+ int i;
+ for (i = 0; i < header_count; i++) {
+ sprintf(req_buf + strlen(req_buf), "header%d:%d\r\n", i, i);
+ }
+ strcat(req_buf, "\r\n");
+ memset(post_buf, '0', sizeof(post_buf));
+
+ int thread_count = atoi(argv[2]);
+
+ char *port = argc == 5 ? argv[4] : "443";
+
+ /* Obtain address(es) matching host/port */
+
+ memset(&hints, 0, sizeof(struct addrinfo));
+ hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
+ hints.ai_socktype = SOCK_STREAM; /* Datagram socket */
+ hints.ai_flags = 0;
+ hints.ai_protocol = 0; /* Any protocol */
+
+ s = getaddrinfo(host, port, &hints, &result);
+ if (s != 0) {
+ fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
+ exit(EXIT_FAILURE);
+ }
+
+ /* getaddrinfo() returns a list of address structures.
+ * Try each address until we successfully connect(2).
+ * socket(2) (or connect(2)) fails, we (close the socket
+ and) try the next address. */
+
+ for (rp = result; rp != NULL; rp = rp->ai_next) {
+ sfd = socket(rp->ai_family, rp->ai_socktype,
+ rp->ai_protocol);
+ if (sfd == -1)
+ continue;
+ if (connect(sfd, rp->ai_addr, rp->ai_addrlen) != -1)
+ break; /* Success */
+
+ close(sfd);
+ }
+
+ if (rp == NULL) { /* No address succeeded */
+ fprintf(stderr, "Could not connect\n");
+ exit(EXIT_FAILURE);
+ }
+
+
+ //fcntl(sfd, F_SETFL, O_NONBLOCK);
+
+ SSL_load_error_strings();
+ SSL_library_init();
+
+ mutex_buf = (pthread_mutex_t *)OPENSSL_malloc(CRYPTO_num_locks() *
sizeof(pthread_mutex_t));
+ for (i = 0; i < CRYPTO_num_locks(); i++) {
+ pthread_mutex_init(&mutex_buf[i], NULL);
+ }
+
+ CRYPTO_set_locking_callback(SSL_locking_callback);
+ CRYPTO_THREADID_set_callback(SSL_pthreads_thread_id);
+
+
+ SSL_CTX *client_ctx = SSL_CTX_new(SSLv23_client_method());
+ SSL *ssl = SSL_new(client_ctx);
+
+ SSL_set_fd(ssl, sfd);
+ int ret = SSL_connect(ssl);
+ int read_count = 0;
+ int write_count = 1;
+
+ printf("Sent request\n");
+ if ((ret = SSL_write(ssl, req_buf, strlen(req_buf))) <= 0) {
+ int error = SSL_get_error(ssl, ret);
+ printf("SSL_write failed %d", error);
+ exit(1);
+ }
+ SSL_write(ssl, post_buf, sizeof(post_buf));
+
+ char input_buf[1024];
+ int read_bytes = SSL_read(ssl, input_buf, sizeof(input_buf));
+ if (read_bytes > 0 && read_bytes < 1024) input_buf[read_bytes] = '\0';
+ else input_buf[1023] = '\0';
+ //printf("Received %d bytes %s\n", read_bytes, input_buf);
+ SSL_SESSION *session = SSL_get_session(ssl);
+ close(sfd);
+ struct thread_info tinfo;
+ tinfo.rp =rp;
+ tinfo.session = session;
+ pthread_t * threads = malloc(thread_count *sizeof(pthread_t));
+ for (i= 0; i < thread_count; i++) {
+ pthread_create(threads + i, NULL, spawn_same_session_send, &tinfo);
+ }
+
+ void *retval;
+ for (i = 0; i < thread_count; i++) {
+ retval = NULL;
+ pthread_join(threads[i], &retval);
+ if (retval != NULL) {
+ printf("Thread %d failed 0x%x\n", i, retval);
+ }
+ }
+
+ printf("All threads finished\n");
+
+ exit(0);
+}
+
diff --git a/tests/gold_tests/tls/ssl/server.key
b/tests/gold_tests/tls/ssl/server.key
new file mode 100644
index 0000000..4c7a661
--- /dev/null
+++ b/tests/gold_tests/tls/ssl/server.key
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQDWMHOiUF+ORmZjAxI8MWE9dblb7gQSJ36WCXlPFiFx6ynF+S1E
+kXAYpIip5X0pzDUaIbLukxJUAAnOtMEO0PCgxJQUrEtRWh8wiJdbdQJF0Zs/9R+u
+SUgb61f+mdTQvhqefBGx+xrpfAcgtcWiZuSA9Q3fvpDj5WOWSPWXBUuxywIDAQAB
+AoGBAJPxRX2gjFAGWmQbU/YVmXfNH6navh8X/nx9sLeqrpE0AFeJI/ZPiqDKzMal
+B43eSfNxwVi+ZxN0L1ICUbL9KKZvHs/QBxWLA1fGVAXrz7sRplEVvakPpTfHoEnv
+sKaMWVKaK/S5WGbDhElb6zb/Lwo19DsIAPjGYqFvzFJBmobJAkEA9iSeTGkR9X26
+GywZoYrIMlRh34htOIRx1UUq88rFzdrCF21kQ4lhBIkX5OZMMy652i2gyak4OZTe
+YewIv8jw9QJBAN7EQNHG8jPwXfVp91/fqxVQEfumuP2i6uiWWYQgZCmla2+0xcLZ
+pMQ6sQEe10hhTrVnzHgAUVp50Ntn2jwBX78CQF09veGAI9d1Cxzj9cmmAvRd1r2Q
+tp8kPOLnUsALXib+6WtqewLCdcf8DtsdClyRJMIraq85tRzK8fryKNZNzkkCQEgA
+yS7FDj5JgCU15hZgFk1iPx3HCt44jZM2HaL+UUHAzRQjKxTLAl3G1rWVAWLMyQML
+lORoveLvotl4HOruSsMCQQCAx9dV9JUSFoyc1CWILp/FgUH/se4cjQCThGO0DoQQ
+vGTYmntY7j9WRJ9esQrjdD6Clw8zM/45GIBNwnXzqo7Z
+-----END RSA PRIVATE KEY-----
diff --git a/tests/gold_tests/tls/ssl/server.pem
b/tests/gold_tests/tls/ssl/server.pem
new file mode 100644
index 0000000..a1de94f
--- /dev/null
+++ b/tests/gold_tests/tls/ssl/server.pem
@@ -0,0 +1,32 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQDWMHOiUF+ORmZjAxI8MWE9dblb7gQSJ36WCXlPFiFx6ynF+S1E
+kXAYpIip5X0pzDUaIbLukxJUAAnOtMEO0PCgxJQUrEtRWh8wiJdbdQJF0Zs/9R+u
+SUgb61f+mdTQvhqefBGx+xrpfAcgtcWiZuSA9Q3fvpDj5WOWSPWXBUuxywIDAQAB
+AoGBAJPxRX2gjFAGWmQbU/YVmXfNH6navh8X/nx9sLeqrpE0AFeJI/ZPiqDKzMal
+B43eSfNxwVi+ZxN0L1ICUbL9KKZvHs/QBxWLA1fGVAXrz7sRplEVvakPpTfHoEnv
+sKaMWVKaK/S5WGbDhElb6zb/Lwo19DsIAPjGYqFvzFJBmobJAkEA9iSeTGkR9X26
+GywZoYrIMlRh34htOIRx1UUq88rFzdrCF21kQ4lhBIkX5OZMMy652i2gyak4OZTe
+YewIv8jw9QJBAN7EQNHG8jPwXfVp91/fqxVQEfumuP2i6uiWWYQgZCmla2+0xcLZ
+pMQ6sQEe10hhTrVnzHgAUVp50Ntn2jwBX78CQF09veGAI9d1Cxzj9cmmAvRd1r2Q
+tp8kPOLnUsALXib+6WtqewLCdcf8DtsdClyRJMIraq85tRzK8fryKNZNzkkCQEgA
+yS7FDj5JgCU15hZgFk1iPx3HCt44jZM2HaL+UUHAzRQjKxTLAl3G1rWVAWLMyQML
+lORoveLvotl4HOruSsMCQQCAx9dV9JUSFoyc1CWILp/FgUH/se4cjQCThGO0DoQQ
+vGTYmntY7j9WRJ9esQrjdD6Clw8zM/45GIBNwnXzqo7Z
+-----END RSA PRIVATE KEY-----
+-----BEGIN CERTIFICATE-----
+MIICszCCAhwCCQCRJsJJ+mTsdDANBgkqhkiG9w0BAQsFADCBnTELMAkGA1UEBhMC
+VVMxCzAJBgNVBAgMAklMMRIwEAYDVQQHDAlDaGFtcGFpZ24xDjAMBgNVBAoMBVlh
+aG9vMQ0wCwYDVQQLDARFZGdlMSgwJgYDVQQDDB9qdWljZXByb2R1Y2UuY29ycC5u
+ZTEueWFob28uY29tMSQwIgYJKoZIhvcNAQkBFhVwZXJzaWEuYXppekB5YWhvby5j
+b20wHhcNMTYwODI1MjI1NzIxWhcNMTcwODI1MjI1NzIxWjCBnTELMAkGA1UEBhMC
+VVMxCzAJBgNVBAgMAklMMRIwEAYDVQQHDAlDaGFtcGFpZ24xDjAMBgNVBAoMBVlh
+aG9vMQ0wCwYDVQQLDARFZGdlMSgwJgYDVQQDDB9qdWljZXByb2R1Y2UuY29ycC5u
+ZTEueWFob28uY29tMSQwIgYJKoZIhvcNAQkBFhVwZXJzaWEuYXppekB5YWhvby5j
+b20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANYwc6JQX45GZmMDEjwxYT11
+uVvuBBInfpYJeU8WIXHrKcX5LUSRcBikiKnlfSnMNRohsu6TElQACc60wQ7Q8KDE
+lBSsS1FaHzCIl1t1AkXRmz/1H65JSBvrV/6Z1NC+Gp58EbH7Gul8ByC1xaJm5ID1
+Dd++kOPlY5ZI9ZcFS7HLAgMBAAEwDQYJKoZIhvcNAQELBQADgYEAXSVfZ5p1TkhW
+QiYq9nfQlBnX2NVaf8ymA8edQR0qH/QBv4/52bNNXC7V/V+ev9LCho2iRMeYYyXB
+yo1wBAGR83lS9cF/tOABcYrxjdP54Sfkyh5fomcg8SV7zap6C8mhbV8r3EujbKCx
+igH3fMX5F/eRwNCzaMMyQsXaxTJ3trk=
+-----END CERTIFICATE-----
diff --git a/tests/gold_tests/tls/tls.test.py b/tests/gold_tests/tls/tls.test.py
new file mode 100644
index 0000000..04588fa
--- /dev/null
+++ b/tests/gold_tests/tls/tls.test.py
@@ -0,0 +1,101 @@
+'''
+'''
+# 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.
+
+import os
+Test.Summary = '''
+Test tls
+'''
+
+def Build(Test, filename, host):
+ tr = Test.AddTestRun("Build", "Build test file: {0}".format(filename))
+ tr.Command = 'gcc -o ssl-post -O2 -g {0} -lssl -lpthread
-lcrypto'.format(filename)
+ tr.ReturnCode = 0
+ tr = Test.addTestRun("Run-Test")
+ tr.Command = './ssl-post {0} 40 378'.format(host)
+
+# ExtendTest(Build)
+
+
+# need Curl
+Test.SkipUnless(
+ Condition.HasProgram("curl","Curl need to be installed on system for this
test to work")
+ )
+Test.ContinueOnFail=True
+# Define default ATS
+ts=Test.MakeATSProcess("ts",select_ports=False)
+server=Test.MakeOriginServer("server")
+
+
+tr=Test.AddTestRun("Build-Test","build test file: ssl-post.c")
+tr.Command = 'gcc -o ssl-post -O2 -g {0}/ssl-post.c -lssl -lpthread
-lcrypto'.format(Test.RunDirectory)
+tr.ReturnCode=0
+tr.Setup.CopyAs('ssl-post.c',Test.RunDirectory)
+
+requestLocation = "test2"
+reHost = "www.example.com"
+
+testName = ""
+
+header_count = 378
+
+header_string = "POST /post HTTP/1.1\r\nHost:
www.example.com\r\nContent-Length:1000\r\n"
+
+for i in range (0, 378):
+ header_string = "{1}header{0}:{0}\r\n".format(i,header_string)
+header_string = "{0}\r\n".format(header_string)
+
+post_body = ""
+for i in range (0, 1000):
+ post_body = "{0}0".format(post_body)
+
+# Add info the origin server responses
+server.addResponse("sessionlog.json",
+ {"headers": header_string, "timestamp": "1469733493.993", "body":
post_body},
+ {"headers": "HTTP/1.1 200 OK\r\nServer: microserver\r\nConnection:
close\r\nCache-Control: max-age=3600\r\nContent-Length: 2\r\n\r\n",
"timestamp": "1469733493.993", "body": "ok" })
+
+#add ssl materials like key, certificates for the server
+ts.addSSLfile("ssl/server.pem")
+ts.addSSLfile("ssl/server.key")
+
+ts.Variables.ssl_port = 4443
+ts.Disk.remap_config.AddLine(
+ 'map / http://127.0.0.1:{0}'.format(server.Variables.Port)
+)
+
+ts.Disk.ssl_multicert_config.AddLine(
+ 'dest_ip=* ssl_cert_name=server.pem ssl_key_name=server.key'
+)
+ts.Disk.records_config.update({
+ 'proxy.config.ssl.server.cert.path':
'{0}'.format(ts.Variables.SSLDir),
+ 'proxy.config.ssl.server.private_key.path':
'{0}'.format(ts.Variables.SSLDir),
+ 'proxy.config.http.server_ports': '{0}
{1}:proto=http2;http:ssl'.format(ts.Variables.port,ts.Variables.ssl_port), #
enable ssl port
+ 'proxy.config.ssl.client.verify.server': 0,
+ 'proxy.config.ssl.server.cipher_suite' :
'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:AES128-GCM-SHA256:AES256-GCM-SHA384:ECDHE-RSA-RC4-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:RC4-SHA:RC4-MD5:AES128-SHA:AES256-SHA:DES-CBC3-SHA!SRP:!DSS:!PSK:!aNULL:!eNULL:!SSLv2',
+ })
+
+tr=Test.AddTestRun("Run-Test")
+tr.Command = './ssl-post 127.0.0.1 40 378 4443'
+tr.ReturnCode=0
+# time delay as proxy.config.http.wait_for_cache could be broken
+tr.Processes.Default.StartBefore(server)
+tr.Processes.Default.StartBefore(Test.Processes.ts,
ready=When.PortOpen(ts.Variables.ssl_port))
+tr.Processes.Default.Streams.stdout="gold/ssl-post.gold"
+tr.StillRunningAfter=server
+tr.Processes.Default.TimeOut = 5
+tr.TimeOut = 5
+
--
To stop receiving notification emails like this one, please contact
['"[email protected]" <[email protected]>'].