From 0a9ec5fc896c0fdc417e60366d03c1d95cc53033 Mon Sep 17 00:00:00 2001
From: Todd Short <tshort@akamai.com>
Date: Wed, 1 Apr 2015 11:56:42 -0400
Subject: [PATCH 23/26] RT3883 Add IPv4/IPv6:port-based client cache

Update client cache to use IPv4/v6 addresses via sockaddr_storage.
Add unit tests for client cache

(cherry picked from commit 5f6500c4da4746e32db601edcfc0dff20d6f1aa3)

Conflicts:
	include/openssl/ssl.h
	ssl/Makefile
	test/Makefile
---
 doc/ssl/SSL_CTX_set_client_session_cache.pod | 119 +++++
 doc/ssl/SSL_CTX_set_session_cache_mode.pod   |   7 +-
 doc/ssl/SSL_SESSION_get_time.pod             |  12 +-
 include/openssl/ssl.h                        |  23 +
 ssl/Makefile                                 |  28 +-
 ssl/ssl_client_cache.c                       | 727 +++++++++++++++++++++++++++
 ssl/ssl_lib.c                                |   7 +
 ssl/ssl_locl.h                               |   3 +
 ssl/ssl_sess.c                               |   5 +-
 test/Makefile                                |  52 +-
 test/client_cache_test.c                     | 723 ++++++++++++++++++++++++++
 test/clientcache2test.c                      | 195 +++++++
 test/testclientcache2                        |  18 +
 13 files changed, 1908 insertions(+), 11 deletions(-)
 create mode 100644 doc/ssl/SSL_CTX_set_client_session_cache.pod
 create mode 100644 ssl/ssl_client_cache.c
 create mode 100644 test/client_cache_test.c
 create mode 100644 test/clientcache2test.c
 create mode 100755 test/testclientcache2

diff --git a/doc/ssl/SSL_CTX_set_client_session_cache.pod b/doc/ssl/SSL_CTX_set_client_session_cache.pod
new file mode 100644
index 0000000..3459e73
--- /dev/null
+++ b/doc/ssl/SSL_CTX_set_client_session_cache.pod
@@ -0,0 +1,119 @@
+=pod
+
+=head1 NAME
+
+SSL_CTX_set_client_session_cache, SSL_set_remote_addr, SSL_get_remote_addr, SSL_set_remote_port, SSL_get_remote_port, SSL_set_remote_addr_ex, SSL_get_remote_addr_ex, SSL_get_prev_client_session - use client IP addresses for session caching
+
+=head1 SYNOPSIS
+
+ #include <openssl/ssl.h>
+
+ int SSL_CTX_set_client_session_cache(SSL_CTX ctx);
+
+ void SSL_set_remote_addr(SSL *s, unsigned int addr);
+ unsigned int SSL_get_remote_addr(SSL *s);
+
+ void SSL_set_remote_port(SSL *s, unsigned int port);
+ unsigned int SSL_get_remote_port(SSL *s);
+
+ int SSL_set_remote_addr_ex(SSL *s, struct sockaddr_storage *addr);
+ int SSL_get_remote_addr_ex(SSL *s, struct sockaddr_storage *addr);
+
+ int SSL_get_prev_client_session(SSL *s, int flags);
+
+=head1 DESCRIPTION
+
+SSL_CTX_set_client_session_cache() enables session caching for clients using
+IP addresses, by setting the cache operational mode for B<ctx> to
+SSL_SESS_CACHE_CLIENT and replacing the cache compare and hash functions to
+evaluate IP addresses rather than session IDs.
+
+SSL_set_remote_addr() sets an IPv4 B<addr> into B<s>.
+
+SSL_get_remote_addr() returns the current IPv4 address from B<s>.
+
+SSL_set_remote_port() sets a TCP/UDP B<port> into B<s>.
+
+SSL_set_remote_port() returns the current TCP/UDP port from B<s>.
+
+SSL_set_remote_addr_ex() sets an IPv4 or IPv6 address and TCP/UDP port
+into B<s> using a sockaddr_storage B<addr>.
+
+SSL_get_remote_addr_ex() returns the current IP address and TCP/UDP port
+from B<s> into the sockaddr_storage B<addr>.
+
+SSL_get_prev_client_session() is used to search for and assign a client
+session to the B<s> based on previously set IP address and port. The
+B<flags> may be a bitwise combination of MUST_HAVE_APP_DATA or
+MUST_COPY_SESSION.
+
+=head1 NOTES
+
+The OpenSSL library can store/retrieve SSL/TLS sessions for later reuse.
+The sessions can be held in memory for each B<ctx>, if more than one
+SSL_CTX object is being maintained, the sessions are unique for each SSL_CTX
+object.
+
+In order to reuse a session, a client must send the session's id to the
+server. It can only send exactly one id.  The server then either
+agrees to reuse the session or it starts a full handshake (to create a new
+session).
+
+To reuse a session, use the SSL_set_remote_addr() and SSL_set_remote_port() or
+SSL_set_remote_addr_ex() functions to specify the IP address/port combination
+that the client is attempting to connect to. Set the address and port before
+calling SSL_get_prev_client_session(). Call SSL_get_prev_client_session() before
+calling L<SSL_connect(3)|SSL_connect(3)>.
+
+The B<flags> argument to SSL_get_prev_client_session() is used to control
+how a session is found and assigned.
+
+The port value used in SSL_set_remote_port() and SSL_set_remote_addr_ex()
+should be specified consistently, either in network or host order. The values
+are only used for comparison, not for connection establishment.
+
+=over
+
+=item MUST_HAVE_APP_DATA
+
+This flag means that the session will only be assigned if there is app data
+(see  L<SSL_SESSION_get_app_data(3)|SSL_SESSION_get_app_data(3)>) set for the
+SSL_SESSION.
+
+=item MUST_COPY_SESSION
+
+This flag duplicates the session before assigning it to the B<s>.
+
+=back
+
+=head1 RETURN VALUES
+
+SSL_CTX_set_client_session_cache() returns 1 on success, 0 on failure.
+
+SSL_get_remote_addr() returns the last set IPv4 address, 0 otherwise.
+
+SSL_get_remote_port() returns the last set port, 0 otherwise.
+
+SSL_set_remote_addr_ex() returns 0 on success, -1 on failure.
+
+SSL_get_remote_addr_ex() returns 0 on success, -1 on failure. The addr
+structrure is filled in with the address and port previously set. The
+family may be set to AF_INET and the IPv4 address set to 0 if only the
+port has been set.
+
+SSL_get_prev_client_session() returns 1 if a previous session was found,
+0 if no previous session was found, and -1 on error.
+
+=head1 SEE ALSO
+
+L<ssl(3)|ssl(3)>, L<SSL_set_session(3)|SSL_set_session(3)>,
+L<SSL_session_reused(3)|SSL_session_reused(3)>,
+L<SSL_CTX_add_session(3)|SSL_CTX_add_session(3)>,
+L<SSL_CTX_sess_number(3)|SSL_CTX_sess_number(3)>,
+L<SSL_CTX_sess_set_cache_size(3)|SSL_CTX_sess_set_cache_size(3)>,
+L<SSL_CTX_sess_set_get_cb(3)|SSL_CTX_sess_set_get_cb(3)>,
+L<SSL_CTX_set_session_id_context(3)|SSL_CTX_set_session_id_context(3)>,
+L<SSL_CTX_set_timeout(3)|SSL_CTX_set_timeout(3)>,
+L<SSL_CTX_flush_sessions(3)|SSL_CTX_flush_sessions(3)>,
+L<SSL_CTX_set_session_cache_mode(3)|SSL_CTX_set_session_cache_mode(3)>,
+=cut
diff --git a/doc/ssl/SSL_CTX_set_session_cache_mode.pod b/doc/ssl/SSL_CTX_set_session_cache_mode.pod
index c5d2f43..5d2cc68 100644
--- a/doc/ssl/SSL_CTX_set_session_cache_mode.pod
+++ b/doc/ssl/SSL_CTX_set_session_cache_mode.pod
@@ -56,6 +56,10 @@ have details about the connection), the application must select the session
 to be reused by using the L<SSL_set_session(3)|SSL_set_session(3)>
 function. This option is not activated by default.
 
+This mode is automatically enabled by the
+L<SSL_CTX_set_client_session_cache(3),SSL_CTX_set_client_session_cache(3)>
+function to manage the cache via IP address rather than session id.
+
 =item SSL_SESS_CACHE_SERVER
 
 Server sessions are added to the session cache. When a client proposes a
@@ -127,7 +131,8 @@ L<SSL_CTX_sess_set_cache_size(3)|SSL_CTX_sess_set_cache_size(3)>,
 L<SSL_CTX_sess_set_get_cb(3)|SSL_CTX_sess_set_get_cb(3)>,
 L<SSL_CTX_set_session_id_context(3)|SSL_CTX_set_session_id_context(3)>,
 L<SSL_CTX_set_timeout(3)|SSL_CTX_set_timeout(3)>,
-L<SSL_CTX_flush_sessions(3)|SSL_CTX_flush_sessions(3)>
+L<SSL_CTX_flush_sessions(3)|SSL_CTX_flush_sessions(3)>,
+L<SSL_CTX_set_client_session_cache(3)|SSL_CTX_set_client_session_cache(3)>
 
 =head1 HISTORY
 
diff --git a/doc/ssl/SSL_SESSION_get_time.pod b/doc/ssl/SSL_SESSION_get_time.pod
index 490337a..f8cbd7a 100644
--- a/doc/ssl/SSL_SESSION_get_time.pod
+++ b/doc/ssl/SSL_SESSION_get_time.pod
@@ -2,7 +2,7 @@
 
 =head1 NAME
 
-SSL_SESSION_get_time, SSL_SESSION_set_time, SSL_SESSION_get_timeout, SSL_SESSION_set_timeout - retrieve and manipulate session time and timeout settings
+SSL_SESSION_get_time, SSL_SESSION_set_time, SSL_SESSION_get_timeout, SSL_SESSION_set_timeout SSL_set_timeout_update_cache - retrieve and manipulate session time and timeout settings
 
 =head1 SYNOPSIS
 
@@ -13,11 +13,14 @@ SSL_SESSION_get_time, SSL_SESSION_set_time, SSL_SESSION_get_timeout, SSL_SESSION
  long SSL_SESSION_get_timeout(const SSL_SESSION *s);
  long SSL_SESSION_set_timeout(SSL_SESSION *s, long tm);
 
+ long SSL_SESSION_set_timeout_update_cache(const SSL *s, long tm);
+
  long SSL_get_time(const SSL_SESSION *s);
  long SSL_set_time(SSL_SESSION *s, long tm);
  long SSL_get_timeout(const SSL_SESSION *s);
  long SSL_set_timeout(SSL_SESSION *s, long tm);
 
+
 =head1 DESCRIPTION
 
 SSL_SESSION_get_time() returns the time at which the session B<s> was
@@ -36,6 +39,10 @@ to B<tm>.
 The SSL_get_time(), SSL_set_time(), SSL_get_timeout(), and SSL_set_timeout()
 functions are synonyms for the SSL_SESSION_*() counterparts.
 
+The SSL_SESSION_set_timeout_update_cache() sets the timeout value for the
+session assigned to B<SSL* s>, and updates the value in the associated
+SSL_CTX session cache.
+
 =head1 NOTES
 
 Sessions are expired by examining the creation time and the timeout value.
@@ -50,7 +57,8 @@ of the session.
 SSL_SESSION_get_time() and SSL_SESSION_get_timeout() return the currently
 valid values.
 
-SSL_SESSION_set_time() and SSL_SESSION_set_timeout() return 1 on success.
+SSL_SESSION_set_time() and SSL_SESSION_set_timeout() and
+SSL_set_timeout_update_cache() return 1 on success.
 
 If any of the function is passed the NULL pointer for the session B<s>, 
 0 is returned.
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 731d0a4..b47affe 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -1524,6 +1524,7 @@ __owur long SSL_SESSION_get_time(const SSL_SESSION *s);
 __owur long SSL_SESSION_set_time(SSL_SESSION *s, long t);
 __owur long SSL_SESSION_get_timeout(const SSL_SESSION *s);
 __owur long SSL_SESSION_set_timeout(SSL_SESSION *s, long t);
+__owur long SSL_SESSION_set_timeout_update_cache(const SSL *s, long t);
 __owur int SSL_SESSION_has_ticket(const SSL_SESSION *s);
 __owur unsigned long SSL_SESSION_get_ticket_lifetime_hint(const SSL_SESSION *s);
 void SSL_SESSION_get0_ticket(const SSL_SESSION *s, unsigned char **tick,
@@ -2006,6 +2007,28 @@ void SSL_CTX_share_session_cache(SSL_CTX *a, SSL_CTX *b);
 /* The int argument is 1 for read buffers, 0 for write buffers */
 void SSL3_BUFFER_set_mem_functions(void* (*m)(int, size_t), void(*f)(int, size_t, void*));
 
+/* Support for client cache */
+# ifdef OPENSSL_SYS_WINDOWS
+#  include <winsock.h>
+# else
+#  include <sys/socket.h>
+# endif
+
+/* IPv4 legacy functions */
+void SSL_set_remote_addr(SSL *s, unsigned int addr);
+void SSL_set_remote_port(SSL *s, unsigned int port);
+unsigned int SSL_get_remote_addr(const SSL *s);
+unsigned int SSL_get_remote_port(const SSL *s);
+
+/* IPv4/6 versions */
+int SSL_set_remote_addr_ex(SSL *s, struct sockaddr_storage* addr);
+int SSL_get_remote_addr_ex(const SSL *s, struct sockaddr_storage* addr);
+
+#define MUST_HAVE_APP_DATA 0x1
+#define MUST_COPY_SESSION  0x2
+int     SSL_get_prev_client_session(SSL *s, int flags);
+int     SSL_CTX_set_client_session_cache(SSL_CTX *ctx);
+
 /* BEGIN ERROR CODES */
 /*
  * The following lines are auto generated by the script mkerr.pl. Any changes
diff --git a/ssl/Makefile b/ssl/Makefile
index 00b5cf7..57df6ad 100644
--- a/ssl/Makefile
+++ b/ssl/Makefile
@@ -26,7 +26,8 @@ LIBSRC=	\
 	ssl_ciph.c ssl_stat.c ssl_rsa.c \
 	ssl_asn1.c ssl_txt.c ssl_algs.c ssl_conf.c ssl_bucket.c \
 	bio_ssl.c ssl_err.c t1_reneg.c tls_srp.c t1_trce.c ssl_utst.c \
-	record/ssl3_buffer.c record/ssl3_record.c record/dtls1_bitmap.c
+	record/ssl3_buffer.c record/ssl3_record.c record/dtls1_bitmap.c \
+	ssl_client_cache.c
 LIBOBJ= \
 	s3_srvr.o  s3_clnt.o  s3_lib.o  s3_enc.o record/rec_layer_s3.o \
 	s3_both.o s3_cbc.o s3_msg.o \
@@ -37,7 +38,8 @@ LIBOBJ= \
 	ssl_ciph.o ssl_stat.o ssl_rsa.o \
 	ssl_asn1.o ssl_txt.o ssl_algs.o ssl_conf.o ssl_bucket.o \
 	bio_ssl.o ssl_err.o t1_reneg.o tls_srp.o t1_trce.o ssl_utst.o \
-	record/ssl3_buffer.o record/ssl3_record.o record/dtls1_bitmap.o
+	record/ssl3_buffer.o record/ssl3_record.o record/dtls1_bitmap.o \
+	ssl_client_cache.o
 
 SRC= $(LIBSRC)
 
@@ -587,6 +589,28 @@ ssl_ciph.o: ../include/openssl/ssl2.h ../include/openssl/ssl3.h
 ssl_ciph.o: ../include/openssl/stack.h ../include/openssl/symhacks.h
 ssl_ciph.o: ../include/openssl/tls1.h ../include/openssl/x509.h
 ssl_ciph.o: ../include/openssl/x509_vfy.h record/record.h ssl_ciph.c ssl_locl.h
+ssl_client_cache.o: ../e_os.h ../include/openssl/asn1.h
+ssl_client_cache.o: ../include/openssl/bio.h ../include/openssl/buffer.h
+ssl_client_cache.o: ../include/openssl/comp.h ../include/openssl/crypto.h
+ssl_client_cache.o: ../include/openssl/dsa.h ../include/openssl/dtls1.h
+ssl_client_cache.o: ../include/openssl/e_os2.h ../include/openssl/ec.h
+ssl_client_cache.o: ../include/openssl/ecdh.h ../include/openssl/ecdsa.h
+ssl_client_cache.o: ../include/openssl/engine.h ../include/openssl/err.h
+ssl_client_cache.o: ../include/openssl/evp.h ../include/openssl/hmac.h
+ssl_client_cache.o: ../include/openssl/lhash.h ../include/openssl/obj_mac.h
+ssl_client_cache.o: ../include/openssl/objects.h
+ssl_client_cache.o: ../include/openssl/opensslconf.h
+ssl_client_cache.o: ../include/openssl/opensslv.h ../include/openssl/ossl_typ.h
+ssl_client_cache.o: ../include/openssl/pem.h ../include/openssl/pem2.h
+ssl_client_cache.o: ../include/openssl/pkcs7.h ../include/openssl/pqueue.h
+ssl_client_cache.o: ../include/openssl/rand.h ../include/openssl/rsa.h
+ssl_client_cache.o: ../include/openssl/safestack.h ../include/openssl/sha.h
+ssl_client_cache.o: ../include/openssl/srtp.h ../include/openssl/ssl.h
+ssl_client_cache.o: ../include/openssl/ssl2.h ../include/openssl/ssl3.h
+ssl_client_cache.o: ../include/openssl/stack.h ../include/openssl/symhacks.h
+ssl_client_cache.o: ../include/openssl/tls1.h ../include/openssl/x509.h
+ssl_client_cache.o: ../include/openssl/x509_vfy.h record/record.h
+ssl_client_cache.o: ssl_client_cache.c ssl_locl.h
 ssl_conf.o: ../e_os.h ../include/openssl/asn1.h ../include/openssl/bio.h
 ssl_conf.o: ../include/openssl/buffer.h ../include/openssl/comp.h
 ssl_conf.o: ../include/openssl/conf.h ../include/openssl/crypto.h
diff --git a/ssl/ssl_client_cache.c b/ssl/ssl_client_cache.c
new file mode 100644
index 0000000..21ee4be
--- /dev/null
+++ b/ssl/ssl_client_cache.c
@@ -0,0 +1,727 @@
+/* ssl/ssl_client_cache.c */
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 AUTHOR 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.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+/* ====================================================================
+ * Copyright (c) 1998-2002 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. 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.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED 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 OpenSSL PROJECT OR
+ * ITS 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.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
+/*
+ * Copyright (C) 2015 Akamai Technologies. ALL RIGHTS RESERVED.
+ * This code was originally developed by Akamai Technologies and
+ * contributed to the OpenSSL project under the terms of the Corporate
+ * Contributor License Agreement v1.0
+ */
+
+#include <stdio.h>
+#include <assert.h>
+#include <openssl/lhash.h>
+#include <openssl/rand.h>
+#ifndef OPENSSL_NO_ENGINE
+#include <openssl/engine.h>
+#endif
+#include "ssl_locl.h"
+#ifndef OPENSSL_SYS_WINDOWS
+#include <netinet/in.h>
+#endif
+
+static volatile int SSL_SESSION_SOCKADDR_IDX = -1; /**< EX_DATA index for SSL_SESSION sockaddr data */
+static volatile int SSL_SOCKADDR_IDX = -1;         /**< EX_DATA index for SSL sockaddr data */
+
+/**
+ * @brief Allocates sockaddr for EX_DATA
+ * This function is used to add sockaddr data to be put into an OpenSSL data-structure's EX_DATA
+ * It is assigned via CRYPTO_get_ex_new_index.
+ *
+ * @see CRYPTO_get_ex_new_index
+ *
+ * @param @c parent The data structure the sockaddr is for, it is generic so it can't be used to add the ex_data
+ * @param @c ptr The existing value of the sockaddr (invariably NULL)
+ * @param @c ad The EX_DATA structure for adding the sockaddr
+ * @param @c idx The index used for adding sockaddr to @c ad, passed to CRYPTO_get_ex_new_index
+ * @param @c argl @c long value passed to CRYPTO_get_ex_new_index
+ * @param @c argp pointer value passed to CRYPTO_get_ex_new_index
+ * @return 1 on success, 0 on failure
+ */
+static int ssl_sockaddr_new(void* parent, void* ptr, CRYPTO_EX_DATA* ad,
+                            int idx, long argl, void* argp)
+{
+    struct sockaddr_storage* saddr = OPENSSL_malloc(sizeof(*saddr));
+    if (saddr == NULL)
+        return 0;
+    memset(saddr, 0, sizeof(*saddr));
+    return CRYPTO_set_ex_data(ad, idx, saddr);
+}
+
+/**
+ * @brief Frees sockaddr from EX_DATA
+ * This function is used to free sockaddr data that is in an OpenSSL data-structure's EX_DATA
+ * It is assigned via CRYPTO_get_ex_new_index.
+ *
+ * @see CRYPTO_get_ex_new_index
+ *
+ * @param @c parent The data structure the sockaddr is for, it is generic so it can't be used to add the ex_data
+ * @param @c ptr The existing value of the sockaddr (to be freed)
+ * @param @c ad The EX_DATA structure for adding the sockaddr
+ * @param @c idx The index used for adding sockaddr to @c ad, passed to CRYPTO_get_ex_new_index
+ * @param @c argl @c long value passed to CRYPTO_get_ex_new_index
+ * @param @c argp pointer value passed to CRYPTO_get_ex_new_index
+ * @return @c void
+ */
+static void ssl_sockaddr_free(void* parent, void* ptr, CRYPTO_EX_DATA* ad,
+                              int idx, long arlg, void* argp)
+{
+    if (ptr)
+        OPENSSL_free(ptr);
+    CRYPTO_set_ex_data(ad, idx, NULL);
+}
+
+/**
+ * @brief Duplicates sockaddr from EX_DATA
+ * This function is used to duplicate data that is in an OpenSSL data-structure's EX_DATA
+ * It is assigned via CRYPTO_get_ex_new_index. This function passes in the address of value
+ * of the original EX_DATA (be it a pointer or integral type). The caller will take the value
+ * from the @p from EX_DATA and assign it to the @p to EX_DATA. For integral types, nothing
+ * really needs to be done. For pointers, we can't have two EX_DATA's pointing to the same
+ * memory location. So, we need to get the new data, copy from the original data (@p from_d),
+ * then assign the new data's address into @p from_d so that value is copied into the
+ * EX_DATA of @p to. It's a little convoluted, but it's how OpenSSL works.
+ *
+ * @see CRYPTO_get_ex_new_index
+ *
+ * @param @c to The EX_DATA structure that has the original data
+ * @param @c from The EX_DATA structure that has the new ata
+ * @param @c from_d The address of the original data, this is an input/output parameter
+ * @param @c idx The index used for adding sockaddr to @c ad, passed to CRYPTO_get_ex_new_index
+ * @param @c argl @c long value passed to CRYPTO_get_ex_new_index
+ * @param @c argp pointer value passed to CRYPTO_get_ex_new_index
+ * @return 1 on success, 0 on failure
+ */
+static int ssl_sockaddr_dup(CRYPTO_EX_DATA* to, CRYPTO_EX_DATA* from, void* from_d,
+                            int idx, long arlg, void* argp)
+{
+    /**
+     * from_d is actually the address of the pointer put into the ex_data,
+     * we want a different pointer put into the destination
+     **/
+    struct sockaddr_storage** orig = (struct sockaddr_storage**)from_d;
+    struct sockaddr_storage* new = CRYPTO_get_ex_data(to, idx);
+    if (orig == NULL)
+        return 0;
+    if (*orig == NULL) {
+        *orig = new;
+        return 1;
+    }
+    if (new == NULL)
+        return 0;
+    memcpy(new, *orig, sizeof(*new));
+    *orig = new;
+    return 1;
+}
+
+/**
+ * @brief Legacy IPv4 function to assign an IPv4 address to an SSL structure
+ * This function saves the given @p addr into the OpenSSL SSL @p s.
+ * The byte-order doesn't matter; the get routines will return the value as
+ * it was originally given. It is up to the caller to keep the byte-order
+ * consistent.
+ *
+ * @param @c s The SSL structure to assign the IPv4 address
+ * @param @c addr The IPv4 to assign, byte-order doesn't matter
+ * @return @c void
+ */
+void SSL_set_remote_addr(SSL *s, unsigned int addr)
+{
+    struct sockaddr_storage* sstorage = SSL_get_ex_data(s, SSL_SOCKADDR_IDX);
+    if (sstorage != NULL) {
+        struct sockaddr_in* sin = (struct sockaddr_in*)sstorage;
+        sin->sin_family = AF_INET;
+        sin->sin_addr.s_addr = addr;
+    }
+}
+
+/**
+ * @brief Legacy IPv4 function to assign a port to an SSL structure
+ * This function saves the given @p port into the OpenSSL SSL @p s.
+ * The byte-order doesn't matter; the get routines will return the value as
+ * it was originally given. It is up to the caller to keep the byte-order
+ * consistent. Only the lower 16-bits are significant.
+ *
+ * @param @c s The SSL structure to assign the port
+ * @param @c port The port, byte-order doesn't matter, 16-bits are significant
+ * @return @c void
+ */
+void SSL_set_remote_port(SSL *s, unsigned int port)
+{
+    struct sockaddr_storage* sstorage = SSL_get_ex_data(s, SSL_SOCKADDR_IDX);
+    if (sstorage != NULL) {
+        if (sstorage->ss_family == AF_INET6) {
+            struct sockaddr_in6* sin6 = (struct sockaddr_in6*)sstorage;
+            sin6->sin6_port = port;
+        } else {
+            struct sockaddr_in* sin = (struct sockaddr_in*)sstorage;
+            sin->sin_family = AF_INET; /* may not be initialized */
+            sin->sin_port = port;
+        }
+    }
+}
+
+/**
+ * @brief Legacy IPv4 function to retreive an IPv4 address from an SSL structure
+ * This function returns the saved IPv4 address from the OpenSSL SSL @p s.
+ * The byte-order doesn't matter; the get routines will return the value as
+ * it was originally given. It is up to the caller to keep the byte-order
+ * consistent.
+ *
+ * @param @c s The SSL structure from which to retrieve the IPv4 address
+ * @return @<unsigned int> IPv4 address
+ */
+unsigned int SSL_get_remote_addr(const SSL *s)
+{
+    struct sockaddr_storage* sstorage = SSL_get_ex_data(s, SSL_SOCKADDR_IDX);
+    if (sstorage != NULL && sstorage->ss_family == AF_INET) {
+        struct sockaddr_in* sin = (struct sockaddr_in*)sstorage;
+        return sin->sin_addr.s_addr;
+    }
+    return 0;
+}
+
+/**
+ * @brief Legacy IPv4 function to retrieve an port from an SSL structure
+ * This function returns the saved port from the OpenSSL SSL @p s.
+ * The byte-order doesn't matter; this routines will return the value as
+ * it was originally set,  is up to the caller to keep the byte-order
+ * consistent. Only the lower 16-bits are significant.
+ *
+ * @param @c s The SSL structure from which to retreive the port
+ * @return @<unsigned int@> 16-bit port value
+ */
+unsigned int SSL_get_remote_port(const SSL *s)
+{
+    struct sockaddr_storage* sstorage = SSL_get_ex_data(s, SSL_SOCKADDR_IDX);
+    if (sstorage != NULL) {
+        if (sstorage->ss_family == AF_INET6) {
+            struct sockaddr_in6* sin6 = (struct sockaddr_in6*)sstorage;
+            return sin6->sin6_port;
+        } else if (sstorage->ss_family == AF_INET) {
+            struct sockaddr_in* sin = (struct sockaddr_in*)sstorage;
+            return sin->sin_port;
+        }
+    }
+    return 0;
+}
+
+/**
+ * @brief IPv4/v6 function to assign an address and port to an SSL structure
+ * This function saves a copy of the given @p addr into the OpenSSL SSL @p s.
+ * The byte-order doesn't matter; the get routines will return the value as
+ * it was originally given. It is up to the caller to keep the byte-order
+ * consistent. The @c ss_family field of @p addr must be assigned to either
+ * AF_INET or AF_INET6. If set to AF_INET, then a @c sockaddr_in structure
+ * must be passed in. If set to AF_INET6, then a @c sockaddr_in6 structure
+ * must be passed in. Both the address and the port are set in the same
+ * function call.
+ *
+ * @param @c s The SSL structure to assign the address/port.
+ * @param @c addr The address/port to be assigned.
+ * @return 0 on success, -1 on failure
+ */
+int SSL_set_remote_addr_ex(SSL *s, struct sockaddr_storage* addr)
+{
+    struct sockaddr_storage* sstorage = SSL_get_ex_data(s, SSL_SOCKADDR_IDX);
+    if (addr != NULL && sstorage != NULL) {
+        if (addr->ss_family == AF_INET6) {
+            memcpy(sstorage, addr, sizeof(struct sockaddr_in6));
+            return 0;
+        } else if (addr->ss_family == AF_INET) {
+            memcpy(sstorage, addr, sizeof(struct sockaddr_in));
+            return 0;
+        }
+    }
+    return -1;
+}
+
+/**
+ * @brief IPv4/v6 function to retrieve an address and port from an SSL structure
+ * This function copies the address/port of the given @p s into the @p addr.
+ * The byte-order doesn't matter; the get routines will return the value as
+ * it was originally given. It is up to the caller to keep the byte-order
+ * consistent. The caller must pass in a @<struct sockaddr_storage@>.
+ * The @c ss_family field of @p addr will be assigned to either AF_INET or
+ * AF_INET6. Both the address and the port are returned in the same function
+ * call.
+ *
+ * @param @c s The SSL structure to assign the address/port.
+ * @param @c addr Output parameter to receive the address/port.
+ * @return 0 on success, -1 on failure
+ */
+int SSL_get_remote_addr_ex(const SSL *s, struct sockaddr_storage* addr)
+{
+    struct sockaddr_storage* sstorage = SSL_get_ex_data(s, SSL_SOCKADDR_IDX);
+    if (addr != NULL && sstorage != NULL) {
+        if (sstorage->ss_family == AF_INET6)
+            memcpy(addr, sstorage, sizeof(struct sockaddr_in6));
+        else if (sstorage->ss_family == AF_INET)
+            memcpy(addr, sstorage, sizeof(struct sockaddr_in));
+        else
+            addr->ss_family = 0;
+        return 0;
+    }
+    return -1;
+}
+
+/**
+ * @brief Copies a sockaddr from an SSL to an SSL_SESSION
+ * This function copies the address/port from an SSL structure into an
+ * SSL_SESSION structure. Called in SSL_get_prev_client_session() and
+ * ssl_get_new_session().
+ * Library internal use only - not static, however.
+ *
+ * @see SSL_get_prev_client_session
+ * @see ssl_get_new_session
+ *
+ * @param @c ss Destination SSL_SESSION
+ * @param @c s Source SSL
+ * @return @c void
+ */
+void SSL_SESSION_copy_remote_addr(SSL_SESSION* ss, SSL* s)
+{
+    /* Looks weird, but it's right: grab the desintation, and then
+       copy to the destination. */
+    void* p = SSL_SESSION_get_ex_data(ss, SSL_SESSION_SOCKADDR_IDX);
+    if (p != NULL)
+        SSL_get_remote_addr_ex(s, p);
+}
+
+/**
+ * @brief Assigns an SSL_SESSION based on an SSL's client address
+ * This function is designed to find an SSL_SESSION with which to
+ * establish a new connection as a client. It is similar to the
+ * the server-side SSL_get_prev_session() function, but instead
+ * of searching for a session ID, the search is for the server's
+ * address and port.
+ * This function will search for an SSL_SESSION based on SSL @p s
+ * IP address specified via SSL_set_remote_addr_ex. When found,
+ * it assigns the SSL_SESSION to the SSL structure, based on the
+ * passed @p flags. This function does not work until the SSL_CTX
+ * that was used to create the SSL is configured with
+ * SSL_CTX_set_client_session_cache().
+ *
+ * @see SSL_get_prev_session
+ * @see SSL_CTX_set_client_session_cache
+ * @see SSL_set_remote_addr_ex
+ *
+ * @param @c s SSL structure
+ * @param @c flags May include MUST_COPY_SESSION and/or MUST_HAVE_APP_DATA
+ * @return @c int 1 = found, 0 = not found, -1 = error
+ */
+int SSL_get_prev_client_session(SSL *s, int flags)
+{
+    /* This is used only by clients.
+     * It's a replica of ssl_get_prev_session() with some modifications.
+     * Call it after finishing with SSL_new(), and either:
+     ** SSL_set_remote_addr() and SSL_set_remote_port()
+     * OR
+     ** SSL_set_remote_addr_ex()
+     * to attach a previous session (if any) to the SSL structure.
+     * This originally used ssl->ctx, but was changed to ssl->session_ctx
+     */
+
+    SSL_SESSION *ret = NULL, *ss = NULL, data;
+    int fatal = 0;
+
+    /* initialize the EX_DATA */
+    memset(&data, 0, sizeof(SSL_SESSION));
+    CRYPTO_new_ex_data(CRYPTO_EX_INDEX_SSL_SESSION, &data, &data.ex_data);
+    SSL_SESSION_copy_remote_addr(&data, s);
+    data.sid_ctx_length = s->sid_ctx_length;
+
+    if (s->sid_ctx_length > 0)
+        memcpy(data.sid_ctx, s->sid_ctx, s->sid_ctx_length);
+
+    /* we don't support get_session_cb() on the client side because that
+     * takes the session ID rather than the server address & port
+     * + we don't need it
+     */
+
+    CRYPTO_r_lock(CRYPTO_LOCK_SSL_CTX);
+    /* was originally ctx, but should probably be session_ctx */
+    ret = (SSL_SESSION *)lh_retrieve((_LHASH *)s->session_ctx->sessions, (char *)&data);
+    /* clean up the EX_DATA */
+    CRYPTO_free_ex_data(CRYPTO_EX_INDEX_SSL_SESSION, &data, &data.ex_data);
+    if (ret != NULL) {
+        /* don't allow other threads to destroy it */
+        CRYPTO_add(&ret->references, 1, CRYPTO_LOCK_SSL_SESSION);
+    }
+    CRYPTO_r_unlock(CRYPTO_LOCK_SSL_CTX);
+
+    if (ret == NULL)
+        goto err;
+
+    if ((flags & MUST_HAVE_APP_DATA) && !SSL_SESSION_get_app_data(ret)) {
+        s->session_ctx->stats.sess_miss++;
+        SSL_CTX_remove_session(s->session_ctx, ret);
+        goto err;
+    }
+
+    if ((long)(ret->time+ret->timeout) < (long)time(NULL)) { /* timeout */
+        s->session_ctx->stats.sess_timeout++;
+        /* remove it from the cache */
+        SSL_CTX_remove_session(s->session_ctx, ret);
+        goto err;
+    }
+
+    /*
+     * At this point we are going to use the session from cache.
+     * This function needs to be reentrant, and should prohibit giving out
+     * the same session instance from cache to different threads simultaneously.
+     * Thus, we normally create a copy of the cached session.
+     * Important !!! Mark s->hit field with 1 if cached session is used,
+     * so it is not attempted to be put back in cache.
+     */
+
+    if (flags & MUST_COPY_SESSION) {
+        ss = ssl_session_dup(ret, 1);
+        if (ss == NULL) {
+            /* we could not duplicate the session we got from cache */
+            SSL_CTX_remove_session(s->session_ctx, ret);
+            fatal = -1;
+            goto err;
+        }
+    } else
+        ss = ret;
+
+    if (!SSL_set_session(s, ss)) {
+        SSL_CTX_remove_session(s->session_ctx, ret);
+        if ((flags & MUST_COPY_SESSION))
+            SSL_SESSION_free(ss);
+        fatal = -1;
+        goto err;
+    }
+
+    s->hit = 1;
+
+    CRYPTO_add(&ss->references, -1, CRYPTO_LOCK_SSL_SESSION); /* SSL_set_session() incremented it again */
+    if ((flags & MUST_COPY_SESSION))
+        SSL_SESSION_free(ret); /* This would usually just decrease its ref counter we incremented above. */
+    return(1);
+
+ err:
+    if (ret != NULL)
+        SSL_SESSION_free(ret);
+    return fatal;
+}
+
+/**
+ * @brief Updates the SSL_SESSION timeout of an SSL.
+ * This function will update an SSL_SESSION in the SSL_CTX
+ * cache, based on the SSL_SESSION of an SSL structure.
+ * This function works if the SSL_SESSION actually present
+ * in the cache, or is a copy. This does not update the
+ * last-used time of a session, just the session time-out.
+ *
+ * Note: if the session exists on the SSL, 1 is returned whether
+ * or not the session is updated on the SSL_CTX cache.
+ *
+ * @param @c s SSL structure
+ * @param @c t Timeout period in seconds.
+ * @return @c long 1 = if session exists in SSL, 0 otherwise.
+ */
+long SSL_SESSION_set_timeout_update_cache(const SSL *s, long t)
+{
+  SSL_SESSION *ss, *ret;
+
+  if (s == NULL)
+    return 0;
+
+  ss = s->session;
+
+  if (ss == NULL)
+    return 0;
+
+  ss->timeout = t;
+
+  CRYPTO_r_lock(CRYPTO_LOCK_SSL_CTX);
+  ret =(SSL_SESSION *)lh_retrieve((_LHASH *)s->session_ctx->sessions, ss);
+  if ((ret != NULL) && (ret != ss)) {
+     /* our session is a copy of the one in cache */
+     ret->timeout = t;
+  }
+  CRYPTO_r_unlock(CRYPTO_LOCK_SSL_CTX);
+
+  return 1;
+}
+
+/**
+ * @brief Generate a hash from a client SSL_SESSION
+ * Given an SSL_SESSION structure, this function generates
+ * a hash of the address/port value for an lhash structure.
+ * Used as a hash key generation callback for the client
+ * session cache.
+ *
+ * @see SSL_CTX_set_client_session_cache
+ *
+ * @param @c data SSL_SESSION to hash
+ * @return @<unsigned long@> hash value
+ */
+static unsigned long SSL_SESSION_client_hash(const void *data)
+{
+    unsigned long hash = 0;
+    const SSL_SESSION *a = (const SSL_SESSION*)data;
+    struct sockaddr_storage* sstorage = SSL_SESSION_get_ex_data(a, SSL_SESSION_SOCKADDR_IDX);
+
+    /* There's usually just 1 sid_ctx (sometimes 2) so considering only its */
+    /* length (but not its content) should be sufficient here */
+    hash ^= a->sid_ctx_length;
+    if (sstorage != NULL) {
+        hash ^= sstorage->ss_family;
+        if (sstorage->ss_family == AF_INET) {
+            struct sockaddr_in* sin = (struct sockaddr_in*)sstorage;
+            hash ^= sin->sin_addr.s_addr;
+            hash ^= sin->sin_port;
+        } else if (sstorage->ss_family == AF_INET6) {
+            struct sockaddr_in6* sin6 = (struct sockaddr_in6*)sstorage;
+#if defined(OPENSSL_SYS_LINUX) || defined(OPENSSL_SYS_MACOSX)
+# ifdef OPENSSL_SYS_MACOSX
+#  define s6_addr32 __u6_addr.__u6_addr32
+# endif
+            hash ^= sin6->sin6_addr.s6_addr32[0];
+            hash ^= sin6->sin6_addr.s6_addr32[1];
+            hash ^= sin6->sin6_addr.s6_addr32[2];
+            hash ^= sin6->sin6_addr.s6_addr32[3];
+#else
+            /* Windows, (and BSDs?) do not have s6_addr32 */
+            int i;
+            for (i = 0; i < 16; i++) {
+                /* take each byte and shift it over by a bit */
+                hash ^= sin6->sin6_addr.s6_addr[i] << i;
+            }
+#endif
+            hash ^= sin6->sin6_port;
+        }
+    }
+    return hash;
+}
+
+/**
+ * @brief Compare two client SSL_SESSIONs
+ * Given an SSL_SESSION structure, this function compares
+ * the address/port value of two SSL_SESSION structures.
+ * Used as a hash key compatiston callback for the client
+ * session cache.
+ * Does not behave like strcmp/memcmp; only does equality.
+ *
+ * @see SSL_CTX_set_client_session_cache
+ *
+ * @param @c data1 first SSL_SESSION to compare
+ * @param @c data2 second SSL_SESSION to compare
+ * @return @c int 0 if equal, 1 if non-equal
+ */
+static int SSL_SESSION_client_cmp(const void *data1, const void *data2)
+{
+    SSL_SESSION *a = (SSL_SESSION*)data1;
+    SSL_SESSION *b = (SSL_SESSION*)data2;
+    struct sockaddr_storage* sstorage_a;
+    struct sockaddr_storage* sstorage_b;
+    if (a == b)
+        return 0; /* same object, so they must be equal */
+    if (a->sid_ctx_length != b->sid_ctx_length)
+        return 1; /* cannot be equal */
+    sstorage_a = (struct sockaddr_storage*)SSL_SESSION_get_ex_data(a, SSL_SESSION_SOCKADDR_IDX);
+    if (sstorage_a == NULL)
+        return 1; /* cannot be equal */
+    sstorage_b = (struct sockaddr_storage*)SSL_SESSION_get_ex_data(b, SSL_SESSION_SOCKADDR_IDX);
+    if (sstorage_b == NULL)
+        return 1; /* cannot be equal */
+    if (sstorage_a->ss_family != sstorage_b->ss_family)
+        return 1; /* cannot be equal */
+    if (sstorage_a->ss_family == AF_INET) {
+        struct sockaddr_in* sin_a = (struct sockaddr_in*)sstorage_a;
+        struct sockaddr_in* sin_b = (struct sockaddr_in*)sstorage_b;
+        if (sin_a->sin_addr.s_addr != sin_b->sin_addr.s_addr)
+            return 1; /* cannot be equal */
+        if (sin_a->sin_port != sin_b->sin_port)
+            return 1; /* cannot be equal */
+    } else if (sstorage_a->ss_family == AF_INET6) {
+        struct sockaddr_in6* sin6_a = (struct sockaddr_in6*)sstorage_a;
+        struct sockaddr_in6* sin6_b = (struct sockaddr_in6*)sstorage_b;
+        if (memcmp(&sin6_a->sin6_addr, &sin6_b->sin6_addr, sizeof(struct in6_addr)))
+            return 1; /* cannot be equal */
+        if (sin6_a->sin6_port != sin6_b->sin6_port)
+            return 1; /* cannot be equal */
+    } else
+        return 1; /* not set cannot be equal */
+
+    if (a->sid_ctx_length > 0 && memcmp(a->sid_ctx, b->sid_ctx, a->sid_ctx_length))
+        return 1; /* they are not equal */
+
+    return 0; /* they are equal */
+}
+
+/**
+ * @brief Update an SSL_CTX structure to cache client sessions
+ * Given an SSL_CTX structure, clear out the cache, and override
+ * the default cache settings to use client-mode caching based
+ * on the IP address/port combination.
+ * Initializes the SSL_SOCKADDR_IDX and SSL_SESSION_SOCKADDR_IDX
+ * as needed.
+ *
+ * @param @c ctx SSL_CTX to update
+ * @return @c int 1 on success, 0 on failure
+ */
+int SSL_CTX_set_client_session_cache(SSL_CTX *ctx)
+{
+    /*
+     * Zero is a valid index value, BUT is used for "app_data",
+     * and it's never initially reserved for that purpose. So,
+     * always make a fake reservation that will eat up 0 (if
+     * something has already reserved 0, this will leave an
+     * unused hole, oh well. Remove the extra new_index()s
+     * when/if this is fixed.
+     */
+    if (SSL_SOCKADDR_IDX == -1) {
+        CRYPTO_w_lock(CRYPTO_LOCK_SSL);
+        if (SSL_SOCKADDR_IDX == -1) {
+            /* leave space for appdata */
+            SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
+            SSL_SOCKADDR_IDX = SSL_get_ex_new_index(0, NULL,
+                                                    ssl_sockaddr_new,
+                                                    ssl_sockaddr_dup,
+                                                    ssl_sockaddr_free);
+        }
+        CRYPTO_w_unlock(CRYPTO_LOCK_SSL);
+    }
+    if (SSL_SESSION_SOCKADDR_IDX == -1) {
+        CRYPTO_w_lock(CRYPTO_LOCK_SSL_SESSION);
+        if (SSL_SESSION_SOCKADDR_IDX == -1) {
+            /* leave space for appdata */
+            SSL_SESSION_get_ex_new_index(0, NULL, NULL, NULL, NULL);
+            SSL_SESSION_SOCKADDR_IDX =
+                SSL_SESSION_get_ex_new_index(0, NULL,
+                                             ssl_sockaddr_new,
+                                             ssl_sockaddr_dup,
+                                             ssl_sockaddr_free);
+        }
+        CRYPTO_w_unlock(CRYPTO_LOCK_SSL_SESSION);
+    }
+    CRYPTO_w_lock(CRYPTO_LOCK_SSL_CTX);
+    if (ctx->sessions != NULL)
+        lh_SSL_SESSION_free(ctx->sessions);
+
+    /* Force client-side caching */
+    ctx->session_cache_mode |= SSL_SESS_CACHE_CLIENT;
+    ctx->session_cache_mode &= ~SSL_SESS_CACHE_SERVER;
+
+    if ((ctx->sessions = (LHASH_OF(SSL_SESSION)*)lh_new(SSL_SESSION_client_hash,
+                                                        SSL_SESSION_client_cmp))) {
+        CRYPTO_w_unlock(CRYPTO_LOCK_SSL_CTX);
+        return(1);
+    }
+
+    CRYPTO_w_unlock(CRYPTO_LOCK_SSL_CTX);
+
+    SSLerr(SSL_F_SSL_CTX_NEW, ERR_R_MALLOC_FAILURE);
+    return(0);
+}
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index cd83393..4aa8e01 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -456,6 +456,13 @@ int SSL_has_matching_session_id(const SSL *ssl, const unsigned char *id,
 
     if (id_len > sizeof r.session_id)
         return 0;
+    /*
+     * SSL_SESSION r may be completely uninitialized, if we override
+     * the hashing functions to look at EX_DATA, then the code will
+     * crash without this memeset()
+     */
+    memset(&r, 0, sizeof(r));
+
 
     r.ssl_version = ssl->version;
     r.session_id_length = id_len;
diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h
index 7944b6c..e259414 100644
--- a/ssl/ssl_locl.h
+++ b/ssl/ssl_locl.h
@@ -2298,6 +2298,9 @@ __owur int custom_ext_add(SSL *s, int server,
 __owur int custom_exts_copy(custom_ext_methods *dst, const custom_ext_methods *src);
 void custom_exts_free(custom_ext_methods *exts);
 
+/* ssl_client_cache.c */
+void SSL_SESSION_copy_remote_addr(SSL_SESSION*, SSL*);
+
 # else
 
 #  define ssl_init_wbio_buffer SSL_test_functions()->p_ssl_init_wbio_buffer
diff --git a/ssl/ssl_sess.c b/ssl/ssl_sess.c
index 2da8d34..499f58b 100644
--- a/ssl/ssl_sess.c
+++ b/ssl/ssl_sess.c
@@ -257,7 +257,7 @@ SSL_SESSION *ssl_session_dup(SSL_SESSION *src, int ticket)
 #ifndef OPENSSL_NO_SRP
     dest->srp_username = NULL;
 #endif
-    memset(&dest->ex_data, 0, sizeof(dest->ex_data));
+    CRYPTO_new_ex_data(CRYPTO_EX_INDEX_SSL_SESSION, dest, &dest->ex_data);
 
     /* We deliberately don't copy the prev and next pointers */
     dest->prev = NULL;
@@ -320,7 +320,7 @@ SSL_SESSION *ssl_session_dup(SSL_SESSION *src, int ticket)
     }
 #endif
 
-    if (ticket != 0) {
+    if (ticket != 0 && src->tlsext_tick) {
         dest->tlsext_tick = BUF_memdup(src->tlsext_tick, src->tlsext_ticklen);
         if(dest->tlsext_tick == NULL)
             goto err;
@@ -515,6 +515,7 @@ int ssl_get_new_session(SSL *s, int session)
         SSL_SESSION_free(ss);
         return 0;
     }
+    SSL_SESSION_copy_remote_addr(ss, s);
     memcpy(ss->sid_ctx, s->sid_ctx, s->sid_ctx_length);
     ss->sid_ctx_length = s->sid_ctx_length;
     s->session = ss;
diff --git a/test/Makefile b/test/Makefile
index bef9f0a..070c043 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -69,6 +69,8 @@ HEARTBEATTEST=  heartbeat_test
 CONSTTIMETEST=  constant_time_test
 TASKTEST=       tasktest
 TLSEXTTEST=     tlsexttest
+CLIENTCACHETEST= client_cache_test
+CLIENTCACHE2TEST= clientcache2test
 
 TESTS=		alltests
 
@@ -85,7 +87,8 @@ EXE=	$(BNTEST)$(EXE_EXT) $(ECTEST)$(EXE_EXT)  $(ECDSATEST)$(EXE_EXT) $(ECDHTEST)
 	$(JPAKETEST)$(EXE_EXT) $(SRPTEST)$(EXE_EXT) $(V3NAMETEST)$(EXE_EXT) \
 	$(HEARTBEATTEST)$(EXE_EXT) $(P5_CRPT2_TEST)$(EXE_EXT) \
 	$(CONSTTIMETEST)$(EXE_EXT) \
-	$(TASKTEST)$(EXE_EXT) $(TLSEXTTEST)$(EXE_EXT)
+	$(TASKTEST)$(EXE_EXT) $(TLSEXTTEST)$(EXE_EXT) \
+	$(CLIENTCACHETEST)$(EXE_EXT) $(CLIENTCACHE2TEST)$(EXE_EXT)
 
 # $(METHTEST)$(EXE_EXT)
 
@@ -100,7 +103,8 @@ OBJ=	$(BNTEST).o $(ECTEST).o  $(ECDSATEST).o $(ECDHTEST).o $(IDEATEST).o \
 	$(EVPTEST).o $(EVPEXTRATEST).o $(IGETEST).o $(JPAKETEST).o $(V3NAMETEST).o \
 	$(GOST2814789TEST).o $(HEARTBEATTEST).o $(P5_CRPT2_TEST).o \
 	$(CONSTTIMETEST).o testutil.o \
-	$(TASKTEST).o $(TLSEXTTEST).o
+	$(TASKTEST).o $(TLSEXTTEST).o \
+	$(CLIENTCACHETEST).o $(CLIENTCACHE2TEST).o
 
 SRC=	$(BNTEST).c $(ECTEST).c  $(ECDSATEST).c $(ECDHTEST).c $(IDEATEST).c \
 	$(MD2TEST).c  $(MD4TEST).c $(MD5TEST).c \
@@ -112,7 +116,8 @@ SRC=	$(BNTEST).c $(ECTEST).c  $(ECDSATEST).c $(ECDHTEST).c $(IDEATEST).c \
 	$(EVPTEST).c $(EVPEXTRATEST).c $(IGETEST).c $(JPAKETEST).c $(V3NAMETEST).c \
 	$(GOST2814789TEST).c $(HEARTBEATTEST).c $(P5_CRPT2_TEST).c \
 	$(CONSTTIMETEST).c testutil.c \
-	$(TASKTEST).c $(TLSEXTTEST).c
+	$(TASKTEST).c $(TLSEXTTEST).c \
+	$(CLIENTCACHETEST).c $(CLIENTCACHE2TEST).c
 
 HEADER=	testutil.h
 
@@ -153,7 +158,8 @@ alltests: \
 	test_ige test_jpake test_srp test_cms test_v3name test_ocsp \
 	test_gost2814789 test_heartbeat test_p5_crpt2 \
 	test_constant_time \
-	test_task test_tlsext
+	test_task test_tlsext \
+	test_client_cache test_clientcache2
 
 test_evp: $(EVPTEST)$(EXE_EXT) evptests.txt
 	@echo $(START) $@
@@ -404,6 +410,13 @@ test_task: $(TASKTEST)$(EXE_EXT) testtask
 test_tlsext: $(TLSEXTTEST)$(EXE_EXT) testtlsext
 	@sh ./testtlsext
 
+test_client_cache: $(CLIENTCACHETEST)$(EXE_EXT)
+	@echo "Test client caching"
+	../util/shlib_wrap.sh ./$(CLIENTCACHETEST)
+
+test_clientcache2: $(CLIENTCACHE2TEST)$(EXE_EXT) testclientcache2
+	@sh ./testclientcache2
+
 update: local_depend
 	@if [ -z "$(THIS)" ]; then $(MAKE) -f $(TOP)/Makefile reflect THIS=$@; fi
 
@@ -595,6 +608,12 @@ $(TASKTEST)$(EXE_EXT): $(TASKTEST).o
 $(TLSEXTTEST)$(EXE_EXT): $(TLSEXTTEST).o
 	@target=$(TLSEXTTEST) $(BUILD_CMD)
 
+$(CLIENTCACHETEST)$(EXE_EXT): $(CLIENTCACHETEST).o
+	@target=$(CLIENTCACHETEST) $(BUILD_CMD)
+
+$(CLIENTCACHE2TEST)$(EXE_EXT): $(CLIENTCACHE2TEST).o
+	@target=$(CLIENTCACHE2TEST) $(BUILD_CMD)
+
 #$(AESTEST).o: $(AESTEST).c
 #	$(CC) -c $(CFLAGS) -DINTERMEDIATE_VALUE_KAT -DTRACE_KAT_MCT $(AESTEST).c
 
@@ -628,6 +647,31 @@ bntest.o: ../include/openssl/symhacks.h ../include/openssl/x509.h
 bntest.o: ../include/openssl/x509_vfy.h bntest.c
 casttest.o: ../e_os.h ../include/openssl/cast.h ../include/openssl/e_os2.h
 casttest.o: ../include/openssl/opensslconf.h casttest.c
+client_cache_test.o: ../e_os.h ../include/openssl/asn1.h
+client_cache_test.o: ../include/openssl/bio.h ../include/openssl/bn.h
+client_cache_test.o: ../include/openssl/buffer.h ../include/openssl/comp.h
+client_cache_test.o: ../include/openssl/conf.h ../include/openssl/crypto.h
+client_cache_test.o: ../include/openssl/dh.h ../include/openssl/dsa.h
+client_cache_test.o: ../include/openssl/dtls1.h ../include/openssl/e_os2.h
+client_cache_test.o: ../include/openssl/ec.h ../include/openssl/ecdh.h
+client_cache_test.o: ../include/openssl/ecdsa.h ../include/openssl/engine.h
+client_cache_test.o: ../include/openssl/err.h ../include/openssl/evp.h
+client_cache_test.o: ../include/openssl/hmac.h ../include/openssl/lhash.h
+client_cache_test.o: ../include/openssl/obj_mac.h ../include/openssl/objects.h
+client_cache_test.o: ../include/openssl/opensslconf.h
+client_cache_test.o: ../include/openssl/opensslv.h
+client_cache_test.o: ../include/openssl/ossl_typ.h ../include/openssl/pem.h
+client_cache_test.o: ../include/openssl/pem2.h ../include/openssl/pkcs7.h
+client_cache_test.o: ../include/openssl/pqueue.h ../include/openssl/rand.h
+client_cache_test.o: ../include/openssl/rsa.h ../include/openssl/safestack.h
+client_cache_test.o: ../include/openssl/sha.h ../include/openssl/srp.h
+client_cache_test.o: ../include/openssl/srtp.h ../include/openssl/ssl.h
+client_cache_test.o: ../include/openssl/ssl2.h ../include/openssl/ssl3.h
+client_cache_test.o: ../include/openssl/stack.h ../include/openssl/symhacks.h
+client_cache_test.o: ../include/openssl/tls1.h ../include/openssl/x509.h
+client_cache_test.o: ../include/openssl/x509_vfy.h ../include/openssl/x509v3.h
+client_cache_test.o: ../ssl/record/record.h ../ssl/ssl_locl.h
+client_cache_test.o: client_cache_test.c
 constant_time_test.o: ../e_os.h ../include/internal/constant_time_locl.h
 constant_time_test.o: ../include/openssl/e_os2.h
 constant_time_test.o: ../include/openssl/opensslconf.h constant_time_test.c
diff --git a/test/client_cache_test.c b/test/client_cache_test.c
new file mode 100644
index 0000000..92b1ddf
--- /dev/null
+++ b/test/client_cache_test.c
@@ -0,0 +1,723 @@
+/* test/client_cache_test.c */
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 AUTHOR 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.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+/* ====================================================================
+ * Copyright (c) 1998-2002 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. 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.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED 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 OpenSSL PROJECT OR
+ * ITS 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.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
+/*
+ * Copyright (C) 2015 Akamai Technologies. ALL RIGHTS RESERVED.
+ * This code was originally developed by Akamai Technologies and
+ * contributed to the OpenSSL project under the terms of the Corporate
+ * Contributor License Agreement v1.0
+ */
+/*
+ * This file contains routines to test routines in ssl_client_cache.c
+ * Specifically:
+ * - Caching of IPv4/IPv6 server addresses for client connections
+ */
+
+#define _BSD_SOURCE 1		/* Or gethostname won't be declared properly
+                                    on Linux and GNU platforms. */
+
+#include <assert.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#define USE_SOCKETS
+#include "e_os.h"
+
+#ifdef OPENSSL_SYS_VMS
+#define _XOPEN_SOURCE 500	/* Or isascii won't be declared properly on
+                                    VMS (at least with DECompHP C).  */
+#endif
+
+#include <ctype.h>
+
+#include <openssl/bio.h>
+#include <openssl/crypto.h>
+#include <openssl/evp.h>
+#include "../ssl/ssl_locl.h"
+#include <openssl/ssl.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+#ifndef OPENSSL_NO_ENGINE
+#include <openssl/engine.h>
+#endif
+#include <openssl/err.h>
+#include <openssl/rand.h>
+#ifndef OPENSSL_NO_RSA
+#include <openssl/rsa.h>
+#endif
+#ifndef OPENSSL_NO_DSA
+#include <openssl/dsa.h>
+#endif
+#ifndef OPENSSL_NO_DH
+#include <openssl/dh.h>
+#endif
+#ifndef OPENSSL_NO_SRP
+#include <openssl/srp.h>
+#endif
+#include <openssl/bn.h>
+
+#define _XOPEN_SOURCE_EXTENDED	1 /* Or gethostname won't be declared properly
+                                      on Compaq platforms (at least with DEC C).
+                                      Do not try to put it earlier, or IPv6 includes
+                                      get screwed... */
+
+#ifdef OPENSSL_SYS_WINDOWS
+#include <winsock.h>
+#else
+#include OPENSSL_UNISTD
+#include <netinet/in.h>
+#endif
+
+
+static int verbose = 0;
+static int debug = 0;
+static BIO* bio_stdout = NULL;
+
+static const char rnd_seed[] = "string to make the random number generator think it has entropy";
+
+
+#ifndef HEXDUMP_COLS
+#define HEXDUMP_COLS 16
+#endif
+
+void hexdump(void *mem, size_t len)
+{
+    unsigned char* ptr = mem;
+    size_t i, j;
+
+    for(i = 0; i < len + ((len % HEXDUMP_COLS) ? (HEXDUMP_COLS - len % HEXDUMP_COLS) : 0); i++) {
+        /* print offset */
+        if (i % HEXDUMP_COLS == 0)
+            printf("0x%06x: ", (unsigned int)i);
+
+        /* print hex data */
+        if (i < len)
+            printf("%02x ", 0xFF & ptr[i]);
+        else
+            /* end of block, just aligning for ASCII dump */
+            printf("   ");
+
+        /* print ASCII dump */
+        if (i % HEXDUMP_COLS == (HEXDUMP_COLS - 1)) {
+            for (j = i - (HEXDUMP_COLS - 1); j <= i; j++) {
+                if (j >= len) {
+                    /* end of block, not really printing */
+                    printf(" ");
+                } else if(isprint(ptr[j])) {
+                    /* printable char */
+                    printf("%c", 0xFF & ptr[j]);
+                } else {
+                    /* other char */
+                    printf(".");
+                }
+            }
+            printf("\n");
+        }
+    }
+}
+
+static void sv_usage(void)
+{
+    fprintf(stderr,"usage: akamaitest [args ...]\n");
+    fprintf(stderr,"\n");
+    fprintf(stderr," -v            - more output\n");
+    fprintf(stderr," -d            - debug output\n");
+}
+
+static void lock_dbg_cb(int mode, int type, const char *file, int line)
+{
+    static int modes[CRYPTO_NUM_LOCKS]; /* = {0, 0, ... } */
+    const char *errstr = NULL;
+    int rw;
+
+    rw = mode & (CRYPTO_READ|CRYPTO_WRITE);
+    if (!((rw == CRYPTO_READ) || (rw == CRYPTO_WRITE))) {
+        errstr = "invalid mode";
+        goto err;
+    }
+
+    if (type < 0 || type >= CRYPTO_NUM_LOCKS) {
+        errstr = "type out of bounds";
+        goto err;
+    }
+
+    if (mode & CRYPTO_LOCK) {
+        if (modes[type]) {
+            errstr = "already locked";
+            /* must not happen in a single-threaded program
+             * (would deadlock) */
+            goto err;
+        }
+
+        modes[type] = rw;
+    } else if (mode & CRYPTO_UNLOCK) {
+        if (!modes[type]) {
+            errstr = "not locked";
+            goto err;
+        }
+
+        if (modes[type] != rw) {
+            errstr = (rw == CRYPTO_READ) ?
+                "CRYPTO_r_unlock on write lock" :
+                "CRYPTO_w_unlock on read lock";
+        }
+
+        modes[type] = 0;
+    } else {
+        errstr = "invalid mode";
+        goto err;
+    }
+
+ err:
+    if (errstr)
+        printf("openssl (lock_dbg_cb): %s (mode=%d, type=%d) at %s:%d\n",
+                errstr, mode, type, file, line);
+}
+
+static int check_session_count(SSL_CTX* ctx, long expected_count)
+{
+    long sess_count = SSL_CTX_sess_number(ctx);
+    if (sess_count != expected_count) {
+        printf("wrong number of sessions: %ld expected %ld\n",
+                   sess_count, expected_count);
+        return 0;
+    }
+    return 1;
+}
+
+static int search_ip(SSL* ssl, struct sockaddr_storage* sstorage, const char* name, int flags, int expect_found)
+{
+    int err;
+    if (SSL_set_remote_addr_ex(ssl, sstorage) != 0)
+        printf("SSL_get_remote_addr_ex() failed for IPv6\n");
+    SSL_set_session(ssl, NULL);
+    err = SSL_get_prev_client_session(ssl, flags);
+    if (err == -1) {
+        printf("%s fatal\n", name);
+        return 0;
+    }
+    if (err == 1) {
+        if (expect_found) {
+            printf("%s found (OK)\n", name);
+            return 1;
+        }
+        printf("%s found (not OK)\n", name);
+        return 0;
+    }
+    if (err == 0) {
+        if (expect_found) {
+            printf("%s not found (not OK)\n", name);
+            return 0;
+        }
+        printf("%s not found (OK)\n", name);
+        return 1;
+    }
+    printf("%s unknown error\n", name);
+    return 0;
+}
+
+static int check_ip4(SSL* ssl, struct sockaddr_in* sin4, int expect_success)
+{
+    struct sockaddr_storage sstorage;
+    unsigned int ip4_result;
+    unsigned int port_result;
+
+    ip4_result = SSL_get_remote_addr(ssl);
+    if (ip4_result != sin4->sin_addr.s_addr && expect_success) {
+        printf("SSL_get_remote_addr() failure: 0x%X expected 0x%X\n",
+                   ip4_result, sin4->sin_addr.s_addr);
+        return 0;
+    }
+
+    port_result = SSL_get_remote_port(ssl);
+    if (port_result != sin4->sin_port && expect_success) {
+        printf("SSL_get_remote_port() failure: %d expected %d\n",
+                   port_result, sin4->sin_port);
+        return 0;
+    }
+
+    if (SSL_get_remote_addr_ex(ssl, &sstorage) != 0) {
+        printf("SSL_get_remote_addr_ex() returned failure\n");
+        /* can't continue */
+        return 0;
+    }
+    if (sstorage.ss_family != AF_INET && expect_success) {
+        printf("SSL_get_remote_addr_ex() family failure: %d expected %d\n",
+                   sstorage.ss_family, AF_INET);
+        return 0;
+    }
+    ip4_result = ((struct sockaddr_in*)&sstorage)->sin_addr.s_addr;
+    if (ip4_result != sin4->sin_addr.s_addr && expect_success) {
+        printf("SSL_get_remote_addr_ex() address failure: 0x%X expected 0x%X\n",
+                   ip4_result, sin4->sin_addr.s_addr);
+        return 0;
+    }
+    port_result = ((struct sockaddr_in*)&sstorage)->sin_port;
+    if (port_result != sin4->sin_port && expect_success) {
+        printf("SSL_get_remote_addr_ex() port failure: %d expected %d\n",
+                   port_result, sin4->sin_port);
+        return 0;
+    }
+    return 1;
+}
+
+static void print_ip6(struct in6_addr* ip6)
+{
+    int i;
+    for (i = 0; i < sizeof(struct in6_addr); i++) {
+        if (i)
+            printf(":");
+        printf("%2.2X", (unsigned char)ip6->s6_addr[i]);
+    }
+}
+
+static int check_ip6(SSL* ssl, struct sockaddr_in6* sin6, int expect_success)
+{
+    struct sockaddr_storage sstorage;
+    struct in6_addr* ip6_result;
+    unsigned int port_result;
+
+    if (SSL_get_remote_addr_ex(ssl, &sstorage) != 0) {
+        printf("SSL_get_remote_addr_ex() returned failure\n");
+        /* can't continue */
+        return 0;
+    }
+    if (sstorage.ss_family != AF_INET6 && expect_success) {
+        printf("SSL_get_remote_addr_ex() family failure: %d expected %d\n",
+                   sstorage.ss_family, AF_INET6);
+        return 0;
+    }
+    ip6_result = &((struct sockaddr_in6*)&sstorage)->sin6_addr;
+    if (memcmp(ip6_result, &sin6->sin6_addr, sizeof(struct in6_addr)) && expect_success) {
+        printf("SSL_get_remote_addr_ex() address failure: ");
+        print_ip6(ip6_result);
+        printf(" expected ");
+        print_ip6(&sin6->sin6_addr);
+        printf("\n");
+        return 0;
+    }
+    port_result = ((struct sockaddr_in6*)&sstorage)->sin6_port;
+    if (port_result != sin6->sin6_port && expect_success) {
+        printf("SSL_get_remote_addr_ex() port failure: %d expected %d\n",
+                   port_result, sin6->sin6_port);
+        return 0;
+    }
+    return 1;
+}
+
+int add_ip_to_cache(SSL_CTX* ctx, struct sockaddr_storage* sstorage)
+{
+    int ret = 0;
+    int err;
+    SSL* ssl = SSL_new(ctx);
+    if (ssl == NULL) {
+        printf("add_ip_to_cache() failed to allocate SSL\n");
+        return 0;
+    }
+    if (SSL_set_remote_addr_ex(ssl, sstorage) != 0) {
+        printf("SSL_set_remote_addr_ex() returned failure\n");
+        goto end;
+    }
+
+    /* create the session in the SSL - copies SSL's address */
+    if (!ssl_get_new_session(ssl, 1)) {
+        printf("ssl_get_new_session() returned failure\n");
+        goto end;
+    }
+
+    if (ssl->tlsext_ticket_expected) {
+        printf("tlsext_ticket_exptected is true\n");
+        goto end;
+    }
+
+    if (ssl->session == NULL) {
+        printf("no session\n");
+        goto end;
+    }
+
+    if (ssl->session->session_id_length == 0) {
+        printf("no session length\n");
+        goto end;
+    }
+
+    if (ssl->session_ctx != ctx) {
+        printf("ssl->session_ctx=%p != ctx=%p\n", ssl->session_ctx, ctx);
+        goto end;
+    }
+
+    /* ssl_update_cache() not working as expected, use this */
+    SSL_CTX_add_session(ctx, ssl->session);
+
+    SSL_SESSION_set_timeout_update_cache(ssl, 1);
+
+    SSL_set_session(ssl, NULL);
+
+    /* make sure it's there! */
+    if (SSL_set_remote_addr_ex(ssl, sstorage) != 0) {
+        printf("SSL_get_remote_addr_ex() failed\n");
+    }
+    err = SSL_get_prev_client_session(ssl, 0);
+    if (err != 1) {
+        printf("SSL_get_prev_client_session() error: %d\n", err);
+        goto end;
+    }
+
+    ret = 1;
+ end:
+    SSL_free(ssl);
+    return ret;
+}
+
+void random_ip6(struct sockaddr_in6* sin6)
+{
+    RAND_bytes((void*)&sin6->sin6_addr, sizeof(sin6->sin6_addr));
+    RAND_bytes((void*)&sin6->sin6_port, sizeof(sin6->sin6_port));
+    sin6->sin6_family = AF_INET6;
+}
+
+void zero_ip6(struct sockaddr_in6* sin6)
+{
+    memset(sin6, 0, sizeof(struct sockaddr_in6));
+    sin6->sin6_family = AF_INET6;
+}
+
+void random_ip4(struct sockaddr_in* sin4)
+{
+    RAND_bytes((void*)&sin4->sin_addr, sizeof(sin4->sin_addr));
+    RAND_bytes((void*)&sin4->sin_port, sizeof(sin4->sin_port));
+    sin4->sin_family = AF_INET;
+}
+
+void zero_ip4(struct sockaddr_in* sin4)
+{
+    memset(sin4, 0, sizeof(struct sockaddr_in));
+    sin4->sin_family = AF_INET;
+}
+
+int main(int argc, char *argv[])
+{
+    int badop=0;
+    int ret=1;
+    SSL_CTX *ctx = NULL;
+    SSL *ssl = NULL;
+    char *debug_mem;
+    int session_id_context = 0;
+    struct sockaddr_in6 sin6;
+    struct sockaddr_in6 sin6a;
+    struct sockaddr_in  sin4;
+    struct sockaddr_in  sin4a;
+
+    CRYPTO_set_locking_callback(lock_dbg_cb);
+
+    /* enable memory leak checking unless explicitly disabled */
+    debug_mem = getenv("OPENSSL_DEBUG_MEMORY");
+    if (debug_mem && strcmp(debug_mem, "on") == 0) {
+        CRYPTO_malloc_debug_init();
+        CRYPTO_set_mem_debug_options(V_CRYPTO_MDEBUG_ALL);
+    } else {
+        /* OPENSSL_DEBUG_MEMORY=off */
+        CRYPTO_set_mem_debug_functions(0, 0, 0, 0, 0);
+    }
+    CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON);
+
+    RAND_seed(rnd_seed, sizeof rnd_seed);
+
+    bio_stdout=BIO_new_fp(stdout,BIO_NOCLOSE|BIO_FP_TEXT);
+
+    argc--;
+    argv++;
+
+    while (argc >= 1) {
+        if (strcmp(*argv, "-v") == 0)
+            verbose = 1;
+        else if (strcmp(*argv, "-d") == 0)
+            debug = 1;
+        else {
+            fprintf(stderr, "unknown option %s\n", *argv);
+            badop = 1;
+            break;
+        }
+        argc--;
+        argv++;
+    }
+    if (badop) {
+        sv_usage();
+        goto end;
+    }
+
+    SSL_library_init();
+    SSL_load_error_strings();
+
+    ctx = SSL_CTX_new(SSLv3_method());
+    if (ctx == NULL) {
+        ERR_print_errors(bio_stdout);
+        goto end;
+    }
+
+    SSL_CTX_set_session_id_context(ctx, (void *)&session_id_context, sizeof session_id_context);
+
+    /* This is where the magic begins */
+    SSL_CTX_set_client_session_cache(ctx);
+
+    ssl = SSL_new(ctx);
+
+    /* We really don't care if the values are host or network in this test,
+       just as long as they match the expected result! */
+
+    printf("Testing get/set interface\n");
+
+    printf("Testing with nothing set\n");
+    random_ip4(&sin4);
+    random_ip6(&sin6);
+
+    if (!check_ip4(ssl, &sin4, 0))
+        goto end;
+
+    if (!check_ip6(ssl, &sin6, 0))
+        goto end;
+
+    printf("Testing legacy API\n");
+    random_ip4(&sin4);
+    zero_ip6(&sin6);
+
+    SSL_set_remote_addr(ssl, sin4.sin_addr.s_addr);
+    SSL_set_remote_port(ssl, sin4.sin_port);
+
+    if (!check_ip4(ssl, &sin4, 1))
+        goto end;
+
+    if (!check_ip6(ssl, &sin6, 0))
+        goto end;
+
+    printf("Testing IPv4 with new API\n");
+    random_ip4(&sin4);
+    zero_ip6(&sin6);
+
+    SSL_set_remote_addr_ex(ssl, (struct sockaddr_storage*)&sin4);
+
+    if (!check_ip4(ssl, &sin4, 1))
+        goto end;
+
+    if (!check_ip6(ssl, &sin6, 0))
+        goto end;
+
+    printf("Testing IPv6 with new API\n");
+    random_ip6(&sin6);
+    zero_ip4(&sin4);
+
+    SSL_set_remote_addr_ex(ssl, (struct sockaddr_storage*)&sin6);
+
+    if (!check_ip4(ssl, &sin4, 0))
+        goto end;
+
+    if (!check_ip6(ssl, &sin6, 1))
+        goto end;
+
+    /* Set the port on an IPv6 address using legacy */
+    SSL_set_remote_port(ssl, sin4.sin_port);
+
+    if (!check_ip6(ssl, &sin6, 0))
+        goto end;
+
+    sin6.sin6_port = sin4.sin_port;
+    if (!check_ip6(ssl, &sin6, 1))
+        goto end;
+
+    printf("Adding entries to cache... ");
+    printf("1");
+    random_ip6(&sin6);
+    if (add_ip_to_cache(ctx, (struct sockaddr_storage*)&sin6) == 0)
+        goto end;
+    printf("2");
+    random_ip4(&sin4);
+    if (add_ip_to_cache(ctx, (struct sockaddr_storage*)&sin4) == 0)
+        goto end;
+    printf("3");
+    random_ip6(&sin6);
+    if (add_ip_to_cache(ctx, (struct sockaddr_storage*)&sin6) == 0)
+        goto end;
+    printf("4");
+    random_ip4(&sin4);
+    if (add_ip_to_cache(ctx, (struct sockaddr_storage*)&sin4) == 0)
+        goto end;
+    printf("\n");
+
+    /* Added four sessions, so this should be four */
+    if (!check_session_count(ctx, 4))
+        goto end;
+
+    /* Check to see if those last two addresses are in the database */
+
+    if (search_ip(ssl, (struct sockaddr_storage*)&sin6, "IPv6", MUST_COPY_SESSION, 1) == 0)
+        goto end;
+
+    if (search_ip(ssl, (struct sockaddr_storage*)&sin4, "IPv4", 0, 1) == 0)
+        goto end;
+
+    /* Try to remove one of them and search for it */
+    SSL_CTX_remove_session(ctx, SSL_get0_session(ssl));
+
+    if (search_ip(ssl, (struct sockaddr_storage*)&sin4, "removed IPv4", 0, 0) == 0)
+        goto end;
+
+    /* Deleted one, there should be three */
+    if (!check_session_count(ctx, 3))
+        goto end;
+
+    /* Try a random IPv6, which should not be found */
+    random_ip6(&sin6a);
+
+    if (search_ip(ssl, (struct sockaddr_storage*)&sin6a, "random IPv6", 0, 0) == 0)
+        goto end;
+
+    /* Try a random IPv4, which should not be found */
+    random_ip4(&sin4a);
+
+    if (search_ip(ssl, (struct sockaddr_storage*)&sin4a, "random IPv4", 0, 0) == 0)
+        goto end;
+
+    /* Try to remove another one of them and search for it */
+    if (search_ip(ssl, (struct sockaddr_storage*)&sin6, "IPv6", 0, 1) == 0)
+        goto end;
+
+    /* Try again with MUST_HAVE_APP_DATA (which it doesn't), it will be
+       found, but then removed because of the lack of app-data */
+    if (search_ip(ssl, (struct sockaddr_storage*)&sin6, "no app-data IPv6", MUST_HAVE_APP_DATA, 0) == 0)
+        goto end;
+
+    if (search_ip(ssl, (struct sockaddr_storage*)&sin6, "removed IPv6", 0, 0) == 0)
+        goto end;
+
+    /* Deleted another one, there should be two */
+    if (!check_session_count(ctx, 2))
+        goto end;
+
+    ret = 0; /* SUCCESS! */
+
+end:
+    SSL_free(ssl);
+    SSL_CTX_free(ctx);
+#ifndef OPENSSL_NO_ENGINE
+    ENGINE_cleanup();
+#endif
+    CRYPTO_cleanup_all_ex_data();
+    ERR_free_strings();
+    ERR_remove_thread_state(NULL);
+    EVP_cleanup();
+    CRYPTO_mem_leaks(bio_stdout);
+    if (bio_stdout != NULL)
+        BIO_free(bio_stdout);
+    EXIT(ret);
+    return ret;
+}
diff --git a/test/clientcache2test.c b/test/clientcache2test.c
new file mode 100644
index 0000000..87c311f
--- /dev/null
+++ b/test/clientcache2test.c
@@ -0,0 +1,195 @@
+/* test/clientcache2test.c */
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 AUTHOR 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.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+/* ====================================================================
+ * Copyright (c) 1998-2002 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. 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.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED 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 OpenSSL PROJECT OR
+ * ITS 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.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
+/*
+ * Copyright (C) 2015 Akamai Technologies. ALL RIGHTS RESERVED.
+ * This code was originally developed by Akamai Technologies and
+ * contributed to the OpenSSL project under the terms of the Corporate
+ * Contributor License Agreement v1.0
+ */
+/*
+ * This file contains routines to test routines in ssl_client_cache.c
+ * Specifically:
+ * - Caching of IPv4/IPv6 server addresses for client connections
+ */
+
+#include "testharness.c"
+
+static int test1(TestContext *tctx)
+{
+    SSL *s_ssl = SSL_new(tctx->s_ctx);
+    SSL *c_ssl = SSL_new(tctx->c_ctx);
+    c_ssl->debug = tctx->debug;
+        
+    SSL_set_remote_addr(c_ssl, 0x12345678);
+    SSL_set_remote_port(c_ssl, 443);
+
+    (void)TESTASSERT(tctx, SSL_get_remote_addr(c_ssl), "remote host set.\n");
+	
+    (void)TESTASSERT(tctx, chatter(tctx, s_ssl, c_ssl, 1024) == 0, "data transfered");
+    SSL_free(s_ssl);
+    SSL_free(c_ssl);
+	
+    (void)TESTEQINT(tctx, 0, tctx->c_ctx->stats.sess_hit, "session was not reused");
+    return tctx->failed;
+}
+
+static int test2(TestContext *tctx)
+{
+    SSL *s_ssl = SSL_new(tctx->s_ctx);
+    SSL *c_ssl = SSL_new(tctx->c_ctx);
+    c_ssl->debug = tctx->debug;
+	
+    SSL_set_remote_addr(c_ssl, 0x12345678);
+    SSL_set_remote_port(c_ssl, 443);
+	
+    (void)TESTASSERT(tctx, chatter(tctx, s_ssl, c_ssl, 1024) == 0, "data transfered");
+    SSL_free(s_ssl);
+    SSL_free(c_ssl);
+	
+    (void)TESTEQINT(tctx, 1, tctx->c_ctx->stats.sess_hit, "session was reused");
+    return tctx->failed;
+}
+
+static int test3(TestContext *tctx)
+{
+    SSL *s_ssl = SSL_new(tctx->s_ctx);
+    SSL *c_ssl = SSL_new(tctx->c_ctx);
+    c_ssl->debug = tctx->debug;
+	
+    SSL_set_remote_addr(c_ssl, 0x12345678);
+    SSL_set_remote_port(c_ssl, 443);
+	
+    (void)TESTASSERT(tctx, chatter(tctx, s_ssl, c_ssl, 1024) == 0, "data transfered");
+    SSL_free(s_ssl);
+    SSL_free(c_ssl);
+	
+    (void)TESTEQINT(tctx, 2, tctx->c_ctx->stats.sess_hit, "session was reused");
+    return tctx->failed;
+}
+
+
+int test_setup(void)
+{
+    return 1;
+}
+
+int test_run(TestContext *tctx)
+{
+    int ret = 0;
+
+    SSL_CTX_set_client_session_cache(tctx->c_ctx);
+	
+    ret |= TESTRUN(tctx, test1(tctx));
+    ret |= TESTRUN(tctx, test2(tctx));
+    ret |= TESTRUN(tctx, test3(tctx));
+	
+    return ret;
+}
diff --git a/test/testclientcache2 b/test/testclientcache2
new file mode 100755
index 0000000..2d6c30d
--- /dev/null
+++ b/test/testclientcache2
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+clientcache2test="../util/shlib_wrap.sh ./clientcache2test"
+
+#############################################################################
+
+echo Test Client Cache \(2\)
+if [ ! -d gen ]; then
+    ./gen_cert.sh server
+fi
+
+# test client session reuse, expected to work on all protocols except sslv23
+$clientcache2test          || exit 1
+$clientcache2test --sslv23 || exit 1
+$clientcache2test --sslv3  || exit 1
+$clientcache2test --tlsv1  || exit 1
+
+exit 0
-- 
2.3.2 (Apple Git-55)

