Fix duplicate code and typo.
[openssl.git] / apps / s_server.c
index 3b7fb0776524c226f307297a4844112af4b347bb..f5d313ceb52f3fa6b0620a6f20a95ba29ced14f3 100644 (file)
@@ -201,6 +201,7 @@ typedef unsigned int u_int;
 #ifndef OPENSSL_NO_RSA
 static RSA MS_CALLBACK *tmp_rsa_cb(SSL *s, int is_export, int keylength);
 #endif
+static int not_resumable_sess_cb(SSL *s, int is_forward_secure);
 static int sv_body(char *hostname, int s, unsigned char *context);
 static int www_body(char *hostname, int s, unsigned char *context);
 static void close_accept_socket(void );
@@ -209,6 +210,8 @@ static int init_ssl_connection(SSL *s);
 static void print_stats(BIO *bp,SSL_CTX *ctx);
 static int generate_session_id(const SSL *ssl, unsigned char *id,
                                unsigned int *id_len);
+static void init_session_cache_ctx(SSL_CTX *sctx);
+static void free_sessions(void);
 #ifndef OPENSSL_NO_DH
 static DH *load_dh_param(const char *dhfile);
 static DH *get_dh512(void);
@@ -287,6 +290,7 @@ static int s_tlsextdebug=0;
 static int s_tlsextstatus=0;
 static int cert_status_cb(SSL *s, void *arg);
 #endif
+static int no_resume_ephemeral = 0;
 static int s_msg=0;
 static int s_quiet=0;
 
@@ -298,7 +302,9 @@ static const char *session_id_prefix=NULL;
 
 static int enable_timeouts = 0;
 static long socket_mtu;
+#ifndef OPENSSL_NO_DTLS1
 static int cert_chain = 0;
+#endif
 
 #ifndef OPENSSL_NO_PSK
 static char *psk_identity="Client_identity";
@@ -456,20 +462,23 @@ static void sv_usage(void)
 #endif
        BIO_printf(bio_err," -ssl2         - Just talk SSLv2\n");
        BIO_printf(bio_err," -ssl3         - Just talk SSLv3\n");
+       BIO_printf(bio_err," -tls1_1       - Just talk TLSv1.1\n");
        BIO_printf(bio_err," -tls1         - Just talk TLSv1\n");
        BIO_printf(bio_err," -dtls1        - Just talk DTLSv1\n");
        BIO_printf(bio_err," -timeout      - Enable timeouts\n");
-       BIO_printf(bio_err," -mtu          - Set MTU\n");
+       BIO_printf(bio_err," -mtu          - Set link layer MTU\n");
        BIO_printf(bio_err," -chain        - Read a certificate chain\n");
        BIO_printf(bio_err," -no_ssl2      - Just disable SSLv2\n");
        BIO_printf(bio_err," -no_ssl3      - Just disable SSLv3\n");
        BIO_printf(bio_err," -no_tls1      - Just disable TLSv1\n");
+       BIO_printf(bio_err," -no_tls1_1    - Just disable TLSv1.1\n");
 #ifndef OPENSSL_NO_DH
        BIO_printf(bio_err," -no_dhe       - Disable ephemeral DH\n");
 #endif
 #ifndef OPENSSL_NO_ECDH
        BIO_printf(bio_err," -no_ecdhe     - Disable ephemeral ECDH\n");
 #endif
+       BIO_printf(bio_err, "-no_resume_ephemeral - Disable caching and tickets if ephemeral (EC)DH is used\n");
        BIO_printf(bio_err," -bugs         - Turn on SSL bug compatibility\n");
        BIO_printf(bio_err," -www          - Respond to a 'GET /' with a status page\n");
        BIO_printf(bio_err," -WWW          - Respond to a 'GET /<path> HTTP/1.0' with file ./<path>\n");
@@ -489,6 +498,10 @@ static void sv_usage(void)
        BIO_printf(bio_err,"                 not specified (default is %s)\n",TEST_CERT2);
        BIO_printf(bio_err," -tlsextdebug  - hex dump of all TLS extensions received\n");
        BIO_printf(bio_err," -no_ticket    - disable use of RFC4507bis session tickets\n");
+       BIO_printf(bio_err," -legacy_renegotiation - enable use of legacy renegotiation (dangerous)\n");
+# ifndef OPENSSL_NO_NEXTPROTONEG
+       BIO_printf(bio_err," -nextprotoneg arg - set the advertised protocols for the NPN extension (comma-separated list)\n");
+# endif
 #endif
        }
 
@@ -671,7 +684,7 @@ static int MS_CALLBACK ssl_servername_cb(SSL *s, int *ad, void *arg)
                        return p->extension_error;
                if (ctx2)
                        {
-                       BIO_printf(p->biodebug,"Swiching server context.\n");
+                       BIO_printf(p->biodebug,"Switching server context.\n");
                        SSL_set_SSL_CTX(s,ctx2);
                        }     
                }
@@ -710,7 +723,7 @@ static int cert_status_cb(SSL *s, void *arg)
        int use_ssl;
        unsigned char *rspder = NULL;
        int rspderlen;
-       STACK_OF(STRING) *aia = NULL;
+       STACK_OF(OPENSSL_STRING) *aia = NULL;
        X509 *x = NULL;
        X509_STORE_CTX inctx;
        X509_OBJECT obj;
@@ -732,7 +745,7 @@ BIO_printf(err, "cert_status: received %d ids\n", sk_OCSP_RESPID_num(ids));
        aia = X509_get1_ocsp(x);
        if (aia)
                {
-               if (!OCSP_parse_url(sk_STRING_value(aia, 0),
+               if (!OCSP_parse_url(sk_OPENSSL_STRING_value(aia, 0),
                        &host, &port, &path, &use_ssl))
                        {
                        BIO_puts(err, "cert_status: can't parse AIA URL\n");
@@ -740,7 +753,7 @@ BIO_printf(err, "cert_status: received %d ids\n", sk_OCSP_RESPID_num(ids));
                        }
                if (srctx->verbose)
                        BIO_printf(err, "cert_status: AIA URL: %s\n",
-                                       sk_STRING_value(aia, 0));
+                                       sk_OPENSSL_STRING_value(aia, 0));
                }
        else
                {
@@ -785,7 +798,7 @@ BIO_printf(err, "cert_status: received %d ids\n", sk_OCSP_RESPID_num(ids));
                if (!OCSP_REQUEST_add_ext(req, ext, -1))
                        goto err;
                }
-       resp = process_responder(err, req, host, path, port, use_ssl,
+       resp = process_responder(err, req, host, path, port, use_ssl, NULL,
                                        srctx->timeout);
        if (!resp)
                {
@@ -823,8 +836,32 @@ BIO_printf(err, "cert_status: received %d ids\n", sk_OCSP_RESPID_num(ids));
        ret = SSL_TLSEXT_ERR_ALERT_FATAL;
        goto done;
        }
+
+# ifndef OPENSSL_NO_NEXTPROTONEG
+/* This is the context that we pass to next_proto_cb */
+typedef struct tlsextnextprotoctx_st {
+       unsigned char *data;
+       unsigned int len;
+} tlsextnextprotoctx;
+
+static int next_proto_cb(SSL *s, const unsigned char **data, unsigned int *len, void *arg)
+       {
+       tlsextnextprotoctx *next_proto = arg;
+
+       *data = next_proto->data;
+       *len = next_proto->len;
+
+       return SSL_TLSEXT_ERR_OK;
+       }
+# endif  /* ndef OPENSSL_NO_NPN */
 #endif
 
+static int not_resumable_sess_cb(SSL *s, int is_forward_secure)
+       {
+       /* disable resumption for sessions with forward secure ciphers */
+       return is_forward_secure;
+       }
+
 int MAIN(int, char **);
 
 #ifndef OPENSSL_NO_JPAKE
@@ -833,8 +870,8 @@ static char *jpake_secret = NULL;
 
 int MAIN(int argc, char *argv[])
        {
-       X509_STORE *store = NULL;
-       int vflags = 0;
+       X509_VERIFY_PARAM *vpm = NULL;
+       int badarg = 0;
        short port=PORT;
        char *CApath=NULL,*CAfile=NULL;
        unsigned char *context = NULL;
@@ -849,9 +886,7 @@ int MAIN(int argc, char *argv[])
        int state=0;
        const SSL_METHOD *meth=NULL;
        int socket_type=SOCK_STREAM;
-#ifndef OPENSSL_NO_ENGINE
        ENGINE *e=NULL;
-#endif
        char *inrand=NULL;
        int s_cert_format = FORMAT_PEM, s_key_format = FORMAT_PEM;
        char *passarg = NULL, *pass = NULL;
@@ -859,12 +894,17 @@ int MAIN(int argc, char *argv[])
        int s_dcert_format = FORMAT_PEM, s_dkey_format = FORMAT_PEM;
        X509 *s_cert = NULL, *s_dcert = NULL;
        EVP_PKEY *s_key = NULL, *s_dkey = NULL;
+       int no_cache = 0, ext_cache = 0;
 #ifndef OPENSSL_NO_TLSEXT
        EVP_PKEY *s_key2 = NULL;
        X509 *s_cert2 = NULL;
 #endif
 #ifndef OPENSSL_NO_TLSEXT
         tlsextctx tlsextcbp = {NULL, NULL, SSL_TLSEXT_ERR_ALERT_WARNING};
+# ifndef OPENSSL_NO_NEXTPROTONEG
+       const char *next_proto_neg_in = NULL;
+       tlsextnextprotoctx next_proto;
+# endif
 #endif
 #ifndef OPENSSL_NO_PSK
        /* by default do not send a PSK identity hint */
@@ -1001,18 +1041,22 @@ int MAIN(int argc, char *argv[])
                        if (--argc < 1) goto bad;
                        CApath= *(++argv);
                        }
-               else if (strcmp(*argv,"-crl_check") == 0)
+               else if (strcmp(*argv,"-no_cache") == 0)
+                       no_cache = 1;
+               else if (strcmp(*argv,"-ext_cache") == 0)
+                       ext_cache = 1;
+               else if (args_verify(&argv, &argc, &badarg, bio_err, &vpm))
                        {
-                       vflags |= X509_V_FLAG_CRL_CHECK;
-                       }
-               else if (strcmp(*argv,"-crl_check_all") == 0)
-                       {
-                       vflags |= X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL;
+                       if (badarg)
+                               goto bad;
+                       continue;
                        }
                else if (strcmp(*argv,"-verify_return_error") == 0)
                        verify_return_error = 1;
                else if (strcmp(*argv,"-serverpref") == 0)
                        { off|=SSL_OP_CIPHER_SERVER_PREFERENCE; }
+               else if (strcmp(*argv,"-legacy_renegotiation") == 0)
+                       off|=SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION;
                else if (strcmp(*argv,"-cipher") == 0)
                        {
                        if (--argc < 1) goto bad;
@@ -1085,6 +1129,8 @@ int MAIN(int argc, char *argv[])
                        { no_dhe=1; }
                else if (strcmp(*argv,"-no_ecdhe") == 0)
                        { no_ecdhe=1; }
+               else if (strcmp(*argv,"-no_resume_ephemeral") == 0)
+                       { no_resume_ephemeral = 1; }
 #ifndef OPENSSL_NO_PSK
                 else if (strcmp(*argv,"-psk_hint") == 0)
                        {
@@ -1116,6 +1162,8 @@ int MAIN(int argc, char *argv[])
                        { off|=SSL_OP_NO_SSLv2; }
                else if (strcmp(*argv,"-no_ssl3") == 0)
                        { off|=SSL_OP_NO_SSLv3; }
+               else if (strcmp(*argv,"-no_tls1_1") == 0)
+                       { off|=SSL_OP_NO_TLSv1_1; }
                else if (strcmp(*argv,"-no_tls1") == 0)
                        { off|=SSL_OP_NO_TLSv1; }
                else if (strcmp(*argv,"-no_comp") == 0)
@@ -1133,8 +1181,12 @@ int MAIN(int argc, char *argv[])
                        { meth=SSLv3_server_method(); }
 #endif
 #ifndef OPENSSL_NO_TLS1
+               else if (strcmp(*argv,"-tls1_1") == 0)
+                       { meth=TLSv1_1_server_method(); }
                else if (strcmp(*argv,"-tls1") == 0)
                        { meth=TLSv1_server_method(); }
+               else if (strcmp(*argv,"-tls1_1") == 0)
+                       { meth=TLSv1_1_server_method(); }
 #endif
 #ifndef OPENSSL_NO_DTLS1
                else if (strcmp(*argv,"-dtls1") == 0)
@@ -1187,7 +1239,13 @@ int MAIN(int argc, char *argv[])
                        if (--argc < 1) goto bad;
                        s_key_file2= *(++argv);
                        }
-                       
+# ifndef OPENSSL_NO_NEXTPROTONEG
+               else if (strcmp(*argv,"-nextprotoneg") == 0)
+                       {
+                       if (--argc < 1) goto bad;
+                       next_proto_neg_in = *(++argv);
+                       }
+# endif
 #endif
 #if !defined(OPENSSL_NO_JPAKE) && !defined(OPENSSL_NO_PSK)
                else if (strcmp(*argv,"-jpake") == 0)
@@ -1222,16 +1280,16 @@ bad:
                        goto end;
                        }
                psk_identity = "JPAKE";
+               if (cipher)
+                       {
+                       BIO_printf(bio_err, "JPAKE sets cipher to PSK\n");
+                       goto end;
+                       }
+               cipher = "PSK";
                }
 
-       if (cipher)
-               {
-               BIO_printf(bio_err, "JPAKE sets cipher to PSK\n");
-               goto end;
-               }
-       cipher = "PSK";
 #endif
-               
+
        SSL_load_error_strings();
        OpenSSL_add_ssl_algorithms();
 
@@ -1292,6 +1350,21 @@ bad:
                                goto end;
                                }
                        }
+# ifndef OPENSSL_NO_NEXTPROTONEG
+               if (next_proto_neg_in)
+                       {
+                       unsigned short len;
+                       next_proto.data = next_protos_parse(&len,
+                               next_proto_neg_in);
+                       if (next_proto.data == NULL)
+                               goto end;
+                       next_proto.len = len;
+                       }
+               else
+                       {
+                       next_proto.data = NULL;
+                       }
+# endif
 #endif
                }
 
@@ -1390,8 +1463,12 @@ bad:
        if (socket_type == SOCK_DGRAM) SSL_CTX_set_read_ahead(ctx, 1);
 
        if (state) SSL_CTX_set_info_callback(ctx,apps_ssl_info_callback);
-
-       SSL_CTX_sess_set_cache_size(ctx,128);
+       if (no_cache)
+               SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
+       else if (ext_cache)
+               init_session_cache_ctx(ctx);
+       else
+               SSL_CTX_sess_set_cache_size(ctx,128);
 
 #if 0
        if (cipher == NULL) cipher=getenv("SSL_CIPHER");
@@ -1412,8 +1489,8 @@ bad:
                ERR_print_errors(bio_err);
                /* goto end; */
                }
-       store = SSL_CTX_get_cert_store(ctx);
-       X509_STORE_set_flags(store, vflags);
+       if (vpm)
+               SSL_CTX_set1_param(ctx, vpm);
 
 #ifndef OPENSSL_NO_TLSEXT
        if (s_cert2)
@@ -1457,16 +1534,26 @@ bad:
 
                if (state) SSL_CTX_set_info_callback(ctx2,apps_ssl_info_callback);
 
-               SSL_CTX_sess_set_cache_size(ctx2,128);
+               if (no_cache)
+                       SSL_CTX_set_session_cache_mode(ctx2,SSL_SESS_CACHE_OFF);
+               else if (ext_cache)
+                       init_session_cache_ctx(ctx2);
+               else
+                       SSL_CTX_sess_set_cache_size(ctx2,128);
 
                if ((!SSL_CTX_load_verify_locations(ctx2,CAfile,CApath)) ||
                        (!SSL_CTX_set_default_verify_paths(ctx2)))
                        {
                        ERR_print_errors(bio_err);
                        }
-               store = SSL_CTX_get_cert_store(ctx2);
-               X509_STORE_set_flags(store, vflags);
+               if (vpm)
+                       SSL_CTX_set1_param(ctx2, vpm);
                }
+
+# ifndef OPENSSL_NO_NEXTPROTONEG
+       if (next_proto.data)
+               SSL_CTX_set_next_protos_advertised_cb(ctx, next_proto_cb, &next_proto);
+# endif
 #endif 
 
 #ifndef OPENSSL_NO_DH
@@ -1615,6 +1702,15 @@ bad:
 #endif
 #endif
 
+       if (no_resume_ephemeral)
+               {
+               SSL_CTX_set_not_resumable_session_callback(ctx, not_resumable_sess_cb);
+#ifndef OPENSSL_NO_TLSEXT
+               if (ctx2)
+                       SSL_CTX_set_not_resumable_session_callback(ctx2, not_resumable_sess_cb);
+#endif
+               }
+
 #ifndef OPENSSL_NO_PSK
 #ifdef OPENSSL_NO_JPAKE
        if (psk_key != NULL)
@@ -1656,6 +1752,10 @@ bad:
        SSL_CTX_set_session_id_context(ctx,(void*)&s_server_session_id_context,
                sizeof s_server_session_id_context);
 
+       /* Set DTLS cookie generation and verification callbacks */
+       SSL_CTX_set_cookie_generate_cb(ctx, generate_cookie_callback);
+       SSL_CTX_set_cookie_verify_cb(ctx, verify_cookie_callback);
+
 #ifndef OPENSSL_NO_TLSEXT
        if (ctx2)
                {
@@ -1702,6 +1802,7 @@ end:
                OPENSSL_free(pass);
        if (dpass)
                OPENSSL_free(dpass);
+       free_sessions();
 #ifndef OPENSSL_NO_TLSEXT
        if (ctx2 != NULL) SSL_CTX_free(ctx2);
        if (s_cert2)
@@ -1752,8 +1853,11 @@ static int sv_body(char *hostname, int s, unsigned char *context)
        unsigned long l;
        SSL *con=NULL;
        BIO *sbio;
+       struct timeval timeout;
 #if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_MSDOS) || defined(OPENSSL_SYS_NETWARE) || defined(OPENSSL_SYS_BEOS_R5)
        struct timeval tv;
+#else
+       struct timeval *timeoutp;
 #endif
 
        if ((buf=OPENSSL_malloc(bufsize)) == NULL)
@@ -1810,7 +1914,6 @@ static int sv_body(char *hostname, int s, unsigned char *context)
 
        if (SSL_version(con) == DTLS1_VERSION)
                {
-               struct timeval timeout;
 
                sbio=BIO_new_dgram(s,BIO_NOCLOSE);
 
@@ -1825,10 +1928,10 @@ static int sv_body(char *hostname, int s, unsigned char *context)
                        BIO_ctrl(sbio, BIO_CTRL_DGRAM_SET_SEND_TIMEOUT, 0, &timeout);
                        }
 
-               if (socket_mtu > 0)
+               if (socket_mtu > 28)
                        {
                        SSL_set_options(con, SSL_OP_NO_QUERY_MTU);
-                       SSL_set_mtu(con, socket_mtu);
+                       SSL_set_mtu(con, socket_mtu - 28);
                        }
                else
                        /* want to do MTU discovery */
@@ -1921,7 +2024,19 @@ static int sv_body(char *hostname, int s, unsigned char *context)
                                read_from_terminal = 1;
                        (void)fcntl(fileno(stdin), F_SETFL, 0);
 #else
-                       i=select(width,(void *)&readfds,NULL,NULL,NULL);
+                       if ((SSL_version(con) == DTLS1_VERSION) &&
+                               DTLSv1_get_timeout(con, &timeout))
+                               timeoutp = &timeout;
+                       else
+                               timeoutp = NULL;
+
+                       i=select(width,(void *)&readfds,NULL,NULL,timeoutp);
+
+                       if ((SSL_version(con) == DTLS1_VERSION) && DTLSv1_handle_timeout(con) > 0)
+                               {
+                               BIO_printf(bio_err,"TIMEOUT occured\n");
+                               }
+
                        if (i <= 0) continue;
                        if (FD_ISSET(fileno(stdin),&readfds))
                                read_from_terminal = 1;
@@ -2132,6 +2247,10 @@ static int init_ssl_connection(SSL *con)
        X509 *peer;
        long verify_error;
        MS_STATIC char buf[BUFSIZ];
+#if !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG)
+       const unsigned char *next_proto_neg;
+       unsigned next_proto_neg_len;
+#endif
 
        if ((i=SSL_accept(con)) <= 0)
                {
@@ -2171,6 +2290,15 @@ static int init_ssl_connection(SSL *con)
                BIO_printf(bio_s_out,"Shared ciphers:%s\n",buf);
        str=SSL_CIPHER_get_name(SSL_get_current_cipher(con));
        BIO_printf(bio_s_out,"CIPHER is %s\n",(str != NULL)?str:"(NONE)");
+#if !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG)
+       SSL_get0_next_proto_negotiated(con, &next_proto_neg, &next_proto_neg_len);
+       if (next_proto_neg)
+               {
+               BIO_printf(bio_s_out,"NEXTPROTO is ");
+               BIO_write(bio_s_out, next_proto_neg, next_proto_neg_len);
+               BIO_printf(bio_s_out, "\n");
+               }
+#endif
        if (con->hit) BIO_printf(bio_s_out,"Reused session-id\n");
        if (SSL_ctrl(con,SSL_CTRL_GET_FLAGS,0,NULL) &
                TLS1_FLAGS_TLS_PADDING_BUG)
@@ -2182,6 +2310,8 @@ static int init_ssl_connection(SSL *con)
                        con->kssl_ctx->client_princ);
                }
 #endif /* OPENSSL_NO_KRB5 */
+       BIO_printf(bio_s_out, "Secure Renegotiation IS%s supported\n",
+                     SSL_get_secure_renegotiation_support(con) ? "" : " NOT");
        return(1);
        }
 
@@ -2225,11 +2355,10 @@ static int www_body(char *hostname, int s, unsigned char *context)
        {
        char *buf=NULL;
        int ret=1;
-       int i,j,k,blank,dot;
+       int i,j,k,dot;
        SSL *con;
        const SSL_CIPHER *c;
        BIO *io,*ssl_bio,*sbio;
-       long total_bytes;
 
        buf=OPENSSL_malloc(bufsize);
        if (buf == NULL) return(0);
@@ -2280,7 +2409,6 @@ static int www_body(char *hostname, int s, unsigned char *context)
                }
        SSL_set_bio(con,sbio,sbio);
        SSL_set_accept_state(con);
-
        /* SSL_set_fd(con,s); */
        BIO_set_ssl(ssl_bio,con,BIO_CLOSE);
        BIO_push(io,ssl_bio);
@@ -2300,7 +2428,6 @@ static int www_body(char *hostname, int s, unsigned char *context)
                SSL_set_msg_callback_arg(con, bio_s_out);
                }
 
-       blank=0;
        for (;;)
                {
                if (hack)
@@ -2362,6 +2489,32 @@ static int www_body(char *hostname, int s, unsigned char *context)
                        STACK_OF(SSL_CIPHER) *sk;
                        static const char *space="                          ";
 
+               if (www == 1 && strncmp("GET /reneg", buf, 10) == 0)
+                       {
+                       if (strncmp("GET /renegcert", buf, 14) == 0)
+                               SSL_set_verify(con,
+                               SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE,NULL);
+                       i=SSL_renegotiate(con);
+                       BIO_printf(bio_s_out, "SSL_renegotiate -> %d\n",i);
+                       i=SSL_do_handshake(con);
+                       if (i <= 0)
+                               {
+                               BIO_printf(bio_s_out, "SSL_do_handshake() Retval %d\n", SSL_get_error(con, i));
+                               ERR_print_errors(bio_err);
+                               goto err;
+                               }
+                       /* EVIL HACK! */
+                       con->state = SSL_ST_ACCEPT;
+                       i=SSL_do_handshake(con);
+                       BIO_printf(bio_s_out, "SSL_do_handshake -> %d\n",i);
+                       if (i <= 0)
+                               {
+                               BIO_printf(bio_s_out, "SSL_do_handshake() Retval %d\n", SSL_get_error(con, i));
+                               ERR_print_errors(bio_err);
+                               goto err;
+                               }
+                       }
+
                        BIO_puts(io,"HTTP/1.0 200 ok\r\nContent-type: text/html\r\n\r\n");
                        BIO_puts(io,"<HTML><BODY BGCOLOR=\"#ffffff\">\n");
                        BIO_puts(io,"<pre>\n");
@@ -2374,6 +2527,11 @@ static int www_body(char *hostname, int s, unsigned char *context)
                                }
                        BIO_puts(io,"\n");
 
+                       BIO_printf(io,
+                               "Secure Renegotiation IS%s supported\n",
+                               SSL_get_secure_renegotiation_support(con) ?
+                                                       "" : " NOT");
+
                        /* The following is evil and should not really
                         * be done */
                        BIO_printf(io,"Ciphers supported in s_server binary\n");
@@ -2530,7 +2688,6 @@ static int www_body(char *hostname, int s, unsigned char *context)
                                         BIO_puts(io,"HTTP/1.0 200 ok\r\nContent-type: text/plain\r\n\r\n");
                                 }
                        /* send the file */
-                       total_bytes=0;
                        for (;;)
                                {
                                i=BIO_read(file,buf,bufsize);
@@ -2660,3 +2817,115 @@ static int generate_session_id(const SSL *ssl, unsigned char *id,
                return 0;
        return 1;
        }
+
+/* By default s_server uses an in-memory cache which caches SSL_SESSION
+ * structures without any serialisation. This hides some bugs which only
+ * become apparent in deployed servers. By implementing a basic external
+ * session cache some issues can be debugged using s_server.
+ */
+
+typedef struct simple_ssl_session_st
+       {
+       unsigned char *id;
+       int idlen;
+       unsigned char *der;
+       int derlen;
+       struct simple_ssl_session_st *next;
+       } simple_ssl_session;
+
+static simple_ssl_session *first = NULL;
+
+static int add_session(SSL *ssl, SSL_SESSION *session)
+       {
+       simple_ssl_session *sess;
+       unsigned char *p;
+
+       sess = OPENSSL_malloc(sizeof(simple_ssl_session));
+
+       sess->idlen = session->session_id_length;
+       sess->derlen = i2d_SSL_SESSION(session, NULL);
+
+       sess->id = BUF_memdup(session->session_id, sess->idlen);
+
+       sess->der = OPENSSL_malloc(sess->derlen);
+       p = sess->der;
+       i2d_SSL_SESSION(session, &p);
+
+       sess->next = first;
+       first = sess;
+       BIO_printf(bio_err, "New session added to external cache\n");
+       return 0;
+       }
+
+static SSL_SESSION *get_session(SSL *ssl, unsigned char *id, int idlen,
+                                       int *do_copy)
+       {
+       simple_ssl_session *sess;
+       *do_copy = 0;
+       for (sess = first; sess; sess = sess->next)
+               {
+               if (idlen == sess->idlen && !memcmp(sess->id, id, idlen))
+                       {
+                       const unsigned char *p = sess->der;
+                       BIO_printf(bio_err, "Lookup session: cache hit\n");
+                       return d2i_SSL_SESSION(NULL, &p, sess->derlen);
+                       }
+               }
+       BIO_printf(bio_err, "Lookup session: cache miss\n");
+       return NULL;
+       }
+
+static void del_session(SSL_CTX *sctx, SSL_SESSION *session)
+       {
+       simple_ssl_session *sess, *prev = NULL;
+       unsigned char *id = session->session_id;
+       int idlen = session->session_id_length;
+       for (sess = first; sess; sess = sess->next)
+               {
+               if (idlen == sess->idlen && !memcmp(sess->id, id, idlen))
+                       {
+                       if(prev)
+                               prev->next = sess->next;
+                       else
+                               first = sess->next;
+                       OPENSSL_free(sess->id);
+                       OPENSSL_free(sess->der);
+                       OPENSSL_free(sess);
+                       return;
+                       }
+               prev = sess;
+               }
+       }
+
+static void init_session_cache_ctx(SSL_CTX *sctx)
+       {
+       SSL_CTX_set_session_cache_mode(sctx,
+                       SSL_SESS_CACHE_NO_INTERNAL|SSL_SESS_CACHE_SERVER);
+       SSL_CTX_sess_set_new_cb(sctx, add_session);
+        SSL_CTX_sess_set_get_cb(sctx, get_session);
+        SSL_CTX_sess_set_remove_cb(sctx, del_session);
+       }
+
+static void free_sessions(void)
+       {
+       simple_ssl_session *sess, *tsess;
+       for (sess = first; sess;)
+               {
+               OPENSSL_free(sess->id);
+               OPENSSL_free(sess->der);
+               tsess = sess;
+               sess = sess->next;
+               OPENSSL_free(tsess);
+               }
+       first = NULL;
+       }
+       
+
+
+
+
+
+
+       
+
+