Make s_server, s_client check cipher list return codes.
[openssl.git] / apps / s_server.c
index d1e406c7b47e2501bbb8cc270337c113fd2ee48a..bbb651b6eaa8d370ec787caaeb26d61b175fd9f0 100644 (file)
@@ -1,5 +1,5 @@
 /* apps/s_server.c */
-/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com)
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
  * All rights reserved.
  *
  * This package is an SSL implementation written
  * [including the GNU Public Licence.]
  */
 
+#include <assert.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/types.h>
 #include <sys/stat.h>
-#ifdef WIN16
+#ifdef NO_STDIO
 #define APPS_WIN16
 #endif
-#include "lhash.h"
-#include "bn.h"
+
+/* With IPv6, it looks like Digital has mixed up the proper order of
+   recursive header file inclusion, resulting in the compiler complaining
+   that u_int isn't defined, but only if _POSIX_C_SOURCE is defined, which
+   is needed to have fileno() declared correctly...  So let's define u_int */
+#if defined(VMS) && defined(__DECC) && !defined(__U_INT)
+#define __U_INT
+typedef unsigned int u_int;
+#endif
+
+#include <openssl/lhash.h>
+#include <openssl/bn.h>
 #define USE_SOCKETS
 #include "apps.h"
-#include "err.h"
-#include "pem.h"
-#include "x509.h"
-#include "ssl.h"
+#include <openssl/err.h>
+#include <openssl/pem.h>
+#include <openssl/x509.h>
+#include <openssl/ssl.h>
 #include "s_apps.h"
 
-#ifndef NOPROTO
-static RSA MS_CALLBACK *tmp_rsa_cb(SSL *s, int export);
-static int sv_body(char *hostname, int s);
-static int www_body(char *hostname, int s);
+#ifdef WINDOWS
+#include <conio.h>
+#endif
+
+#if (defined(VMS) && __VMS_VER < 70000000)
+/* FIONBIO used as a switch to enable ioctl, and that isn't in VMS < 7.0 */
+#undef FIONBIO
+#endif
+
+#ifndef NO_RSA
+static RSA MS_CALLBACK *tmp_rsa_cb(SSL *s, int is_export, int keylength);
+#endif
+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 );
 static void sv_usage(void);
 static int init_ssl_connection(SSL *s);
 static void print_stats(BIO *bp,SSL_CTX *ctx);
-static DH *load_dh_param(void );
+#ifndef NO_DH
+static DH *load_dh_param(char *dhfile);
 static DH *get_dh512(void);
-#else
-static RSA MS_CALLBACK *tmp_rsa_cb();
-static int sv_body();
-static int www_body();
-static void close_accept_socket();
-static void sv_usage();
-static int init_ssl_connection();
-static void print_stats();
-static DH *load_dh_param();
-static DH *get_dh512();
 #endif
-
+#ifdef MONOLITH
+static void s_server_init(void);
+#endif
 
 #ifndef S_ISDIR
-#define S_ISDIR(a)     (((a) & _S_IFMT) == _S_IFDIR)
+# if defined(_S_IFMT) && defined(_S_IFDIR)
+#  define S_ISDIR(a)   (((a) & _S_IFMT) == _S_IFDIR)
+# else
+#  define S_ISDIR(a)   (((a) & S_IFMT) == S_IFDIR)
+# endif
 #endif
 
+#ifndef NO_DH
 static unsigned char dh512_p[]={
        0xDA,0x58,0x3C,0x16,0xD9,0x85,0x22,0x89,0xD0,0xE4,0xAF,0x75,
        0x6F,0x4C,0xCA,0x92,0xDD,0x4B,0xE5,0x33,0xB8,0x04,0xFB,0x0F,
@@ -113,41 +132,42 @@ static unsigned char dh512_g[]={
        0x02,
        };
 
-static DH *get_dh512()
+static DH *get_dh512(void)
        {
        DH *dh=NULL;
 
-#ifndef NO_DH
        if ((dh=DH_new()) == NULL) return(NULL);
        dh->p=BN_bin2bn(dh512_p,sizeof(dh512_p),NULL);
        dh->g=BN_bin2bn(dh512_g,sizeof(dh512_g),NULL);
        if ((dh->p == NULL) || (dh->g == NULL))
                return(NULL);
-#endif
        return(dh);
        }
+#endif
 
 /* static int load_CA(SSL_CTX *ctx, char *file);*/
 
 #undef BUFSIZZ
-#define BUFSIZZ        8*1024
+#define BUFSIZZ        16*1024
+static int bufsize=BUFSIZZ;
 static int accept_socket= -1;
 
 #define TEST_CERT      "server.pem"
 #undef PROG
 #define PROG           s_server_main
 
-#define DH_PARAM       "server.pem"
-
 extern int verify_depth;
 
 static char *cipher=NULL;
-int verify=SSL_VERIFY_NONE;
-char *s_cert_file=TEST_CERT,*s_key_file=NULL;
+static int s_server_verify=SSL_VERIFY_NONE;
+static int s_server_session_id_context = 1; /* anything will do */
+static char *s_cert_file=TEST_CERT,*s_key_file=NULL;
+static char *s_dcert_file=NULL,*s_dkey_file=NULL;
 #ifdef FIONBIO
 static int s_nbio=0;
 #endif
 static int s_nbio_test=0;
+int s_crlf=0;
 static SSL_CTX *ctx=NULL;
 static int www=0;
 
@@ -155,52 +175,243 @@ static BIO *bio_s_out=NULL;
 static int s_debug=0;
 static int s_quiet=0;
 
-static void sv_usage()
+static int hack=0;
+
+#ifdef MONOLITH
+static void s_server_init(void)
+       {
+       accept_socket=-1;
+       cipher=NULL;
+       s_server_verify=SSL_VERIFY_NONE;
+       s_dcert_file=NULL;
+       s_dkey_file=NULL;
+       s_cert_file=TEST_CERT;
+       s_key_file=NULL;
+#ifdef FIONBIO
+       s_nbio=0;
+#endif
+       s_nbio_test=0;
+       ctx=NULL;
+       www=0;
+
+       bio_s_out=NULL;
+       s_debug=0;
+       s_quiet=0;
+       hack=0;
+       }
+#endif
+
+static void sv_usage(void)
        {
        BIO_printf(bio_err,"usage: s_server [args ...]\n");
        BIO_printf(bio_err,"\n");
-       BIO_printf(bio_err," -accpet arg   - port to accept on (default is %d\n",PORT);
+       BIO_printf(bio_err," -accept arg   - port to accept on (default is %d)\n",PORT);
+       BIO_printf(bio_err," -context arg  - set session ID context\n");
        BIO_printf(bio_err," -verify arg   - turn on peer certificate verification\n");
        BIO_printf(bio_err," -Verify arg   - turn on peer certificate verification, must have a cert.\n");
        BIO_printf(bio_err," -cert arg     - certificate file to use, PEM format assumed\n");
        BIO_printf(bio_err,"                 (default is %s)\n",TEST_CERT);
-       BIO_printf(bio_err," -key arg      - RSA file to use, PEM format assumed, in cert file if\n");
+       BIO_printf(bio_err," -key arg      - Private Key file to use, PEM format assumed, in cert file if\n");
        BIO_printf(bio_err,"                 not specified (default is %s)\n",TEST_CERT);
+       BIO_printf(bio_err," -dcert arg    - second certificate file to use (usually for DSA)\n");
+       BIO_printf(bio_err," -dkey arg     - second private key file to use (usually for DSA)\n");
+       BIO_printf(bio_err," -dhparam arg  - DH parameter file to use, in cert file if not specified\n");
+       BIO_printf(bio_err,"                 or a default set of parameters is used\n");
 #ifdef FIONBIO
        BIO_printf(bio_err," -nbio         - Run with non-blocking IO\n");
 #endif
        BIO_printf(bio_err," -nbio_test    - test with the non-blocking test bio\n");
+       BIO_printf(bio_err," -crlf         - convert LF from terminal into CRLF\n");
        BIO_printf(bio_err," -debug        - Print more output\n");
        BIO_printf(bio_err," -state        - Print the SSL states\n");
        BIO_printf(bio_err," -CApath arg   - PEM format directory of CA's\n");
        BIO_printf(bio_err," -CAfile arg   - PEM format file of CA's\n");
        BIO_printf(bio_err," -nocert       - Don't use any certificates (Anon-DH)\n");
-       BIO_printf(bio_err," -cipher arg   - play with 'ssleay ciphers' to see what goes here\n");
+       BIO_printf(bio_err," -cipher arg   - play with 'openssl ciphers' to see what goes here\n");
        BIO_printf(bio_err," -quiet        - No server output\n");
        BIO_printf(bio_err," -no_tmp_rsa   - Do not generate a tmp RSA key\n");
        BIO_printf(bio_err," -ssl2         - Just talk SSLv2\n");
        BIO_printf(bio_err," -ssl3         - Just talk SSLv3\n");
+       BIO_printf(bio_err," -tls1         - Just talk TLSv1\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");
+#ifndef NO_DH
+       BIO_printf(bio_err," -no_dhe       - Disable ephemeral DH\n");
+#endif
        BIO_printf(bio_err," -bugs         - Turn on SSL bug compatability\n");
        BIO_printf(bio_err," -www          - Respond to a 'GET /' with a status page\n");
-       BIO_printf(bio_err," -WWW          - Returns requested page from to a 'GET <path> HTTP/1.0'\n");
+       BIO_printf(bio_err," -WWW          - Respond to a 'GET /<path> HTTP/1.0' with file ./<path>\n");
        }
 
-static int local_argc;
+static int local_argc=0;
 static char **local_argv;
-static int hack;
 
-int MAIN(argc, argv)
-int argc;
-char *argv[];
+#ifdef CHARSET_EBCDIC
+static int ebcdic_new(BIO *bi);
+static int ebcdic_free(BIO *a);
+static int ebcdic_read(BIO *b, char *out, int outl);
+static int ebcdic_write(BIO *b, char *in, int inl);
+static long ebcdic_ctrl(BIO *b, int cmd, long num, char *ptr);
+static int ebcdic_gets(BIO *bp, char *buf, int size);
+static int ebcdic_puts(BIO *bp, char *str);
+
+#define BIO_TYPE_EBCDIC_FILTER (18|0x0200)
+static BIO_METHOD methods_ebcdic=
+       {
+       BIO_TYPE_EBCDIC_FILTER,
+       "EBCDIC/ASCII filter",
+       ebcdic_write,
+       ebcdic_read,
+       ebcdic_puts,
+       ebcdic_gets,
+       ebcdic_ctrl,
+       ebcdic_new,
+       ebcdic_free,
+       };
+
+typedef struct
+{
+       size_t  alloced;
+       char    buff[1];
+} EBCDIC_OUTBUFF;
+
+BIO_METHOD *BIO_f_ebcdic_filter()
+{
+       return(&methods_ebcdic);
+}
+
+static int ebcdic_new(BIO *bi)
+{
+       EBCDIC_OUTBUFF *wbuf;
+
+       wbuf = (EBCDIC_OUTBUFF *)Malloc(sizeof(EBCDIC_OUTBUFF) + 1024);
+       wbuf->alloced = 1024;
+       wbuf->buff[0] = '\0';
+
+       bi->ptr=(char *)wbuf;
+       bi->init=1;
+       bi->flags=0;
+       return(1);
+}
+
+static int ebcdic_free(BIO *a)
+{
+       if (a == NULL) return(0);
+       if (a->ptr != NULL)
+               Free(a->ptr);
+       a->ptr=NULL;
+       a->init=0;
+       a->flags=0;
+       return(1);
+}
+       
+static int ebcdic_read(BIO *b, char *out, int outl)
+{
+       int ret=0;
+
+       if (out == NULL || outl == 0) return(0);
+       if (b->next_bio == NULL) return(0);
+
+       ret=BIO_read(b->next_bio,out,outl);
+       if (ret > 0)
+               ascii2ebcdic(out,out,ret);
+       return(ret);
+}
+
+static int ebcdic_write(BIO *b, char *in, int inl)
+{
+       EBCDIC_OUTBUFF *wbuf;
+       int ret=0;
+       int num;
+       unsigned char n;
+
+       if ((in == NULL) || (inl <= 0)) return(0);
+       if (b->next_bio == NULL) return(0);
+
+       wbuf=(EBCDIC_OUTBUFF *)b->ptr;
+
+       if (inl > (num = wbuf->alloced))
+       {
+               num = num + num;  /* double the size */
+               if (num < inl)
+                       num = inl;
+               Free((char*)wbuf);
+               wbuf=(EBCDIC_OUTBUFF *)Malloc(sizeof(EBCDIC_OUTBUFF) + num);
+
+               wbuf->alloced = num;
+               wbuf->buff[0] = '\0';
+
+               b->ptr=(char *)wbuf;
+       }
+
+       ebcdic2ascii(wbuf->buff, in, inl);
+
+       ret=BIO_write(b->next_bio, wbuf->buff, inl);
+
+       return(ret);
+}
+
+static long ebcdic_ctrl(BIO *b, int cmd, long num, char *ptr)
+{
+       long ret;
+
+       if (b->next_bio == NULL) return(0);
+       switch (cmd)
+       {
+       case BIO_CTRL_DUP:
+               ret=0L;
+               break;
+       default:
+               ret=BIO_ctrl(b->next_bio,cmd,num,ptr);
+               break;
+       }
+       return(ret);
+}
+
+static int ebcdic_gets(BIO *bp, char *buf, int size)
+{
+       int i, ret;
+       if (bp->next_bio == NULL) return(0);
+/*     return(BIO_gets(bp->next_bio,buf,size));*/
+       for (i=0; i<size-1; ++i)
+       {
+               ret = ebcdic_read(bp,&buf[i],1);
+               if (ret <= 0)
+                       break;
+               else if (buf[i] == '\n')
+               {
+                       ++i;
+                       break;
+               }
+       }
+       if (i < size)
+               buf[i] = '\0';
+       return (ret < 0 && i == 0) ? ret : i;
+}
+
+static int ebcdic_puts(BIO *bp, char *str)
+{
+       if (bp->next_bio == NULL) return(0);
+       return ebcdic_write(bp, str, strlen(str));
+}
+#endif
+
+int MAIN(int argc, char *argv[])
        {
        short port=PORT;
        char *CApath=NULL,*CAfile=NULL;
+       char *context = NULL;
+       char *dhfile = NULL;
        int badop=0,bugs=0;
        int ret=1;
-       int no_tmp_rsa=0,nocert=0;
+       int off=0;
+       int no_tmp_rsa=0,no_dhe=0,nocert=0;
        int state=0;
        SSL_METHOD *meth=NULL;
+#ifndef NO_DH
        DH *dh=NULL;
+#endif
 
 #if !defined(NO_SSL2) && !defined(NO_SSL3)
        meth=SSLv23_server_method();
@@ -214,8 +425,9 @@ char *argv[];
        local_argv=argv;
 
        apps_startup();
-       s_quiet=0;
-       s_debug=0;
+#ifdef MONOLITH
+       s_server_init();
+#endif
 
        if (bio_err == NULL)
                bio_err=BIO_new_fp(stderr,BIO_NOCLOSE);
@@ -240,19 +452,24 @@ char *argv[];
                        }
                else if (strcmp(*argv,"-verify") == 0)
                        {
-                       verify=SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE;
+                       s_server_verify=SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE;
                        if (--argc < 1) goto bad;
                        verify_depth=atoi(*(++argv));
                        BIO_printf(bio_err,"verify depth is %d\n",verify_depth);
                        }
                else if (strcmp(*argv,"-Verify") == 0)
                        {
-                       verify=SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT|
+                       s_server_verify=SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT|
                                SSL_VERIFY_CLIENT_ONCE;
                        if (--argc < 1) goto bad;
                        verify_depth=atoi(*(++argv));
                        BIO_printf(bio_err,"verify depth is %d, must return a certificate\n",verify_depth);
                        }
+               else if (strcmp(*argv,"-context") == 0)
+                       {
+                       if (--argc < 1) goto bad;
+                       context= *(++argv);
+                       }
                else if (strcmp(*argv,"-cert") == 0)
                        {
                        if (--argc < 1) goto bad;
@@ -263,6 +480,21 @@ char *argv[];
                        if (--argc < 1) goto bad;
                        s_key_file= *(++argv);
                        }
+               else if (strcmp(*argv,"-dhparam") == 0)
+                       {
+                       if (--argc < 1) goto bad;
+                       dhfile = *(++argv);
+                       }
+               else if (strcmp(*argv,"-dcert") == 0)
+                       {
+                       if (--argc < 1) goto bad;
+                       s_dcert_file= *(++argv);
+                       }
+               else if (strcmp(*argv,"-dkey") == 0)
+                       {
+                       if (--argc < 1) goto bad;
+                       s_dkey_file= *(++argv);
+                       }
                else if (strcmp(*argv,"-nocert") == 0)
                        {
                        nocert=1;
@@ -299,16 +531,26 @@ char *argv[];
                        { hack=1; }
                else if (strcmp(*argv,"-state") == 0)
                        { state=1; }
+               else if (strcmp(*argv,"-crlf") == 0)
+                       { s_crlf=1; }
                else if (strcmp(*argv,"-quiet") == 0)
                        { s_quiet=1; }
                else if (strcmp(*argv,"-bugs") == 0)
                        { bugs=1; }
                else if (strcmp(*argv,"-no_tmp_rsa") == 0)
                        { no_tmp_rsa=1; }
+               else if (strcmp(*argv,"-no_dhe") == 0)
+                       { no_dhe=1; }
                else if (strcmp(*argv,"-www") == 0)
                        { www=1; }
                else if (strcmp(*argv,"-WWW") == 0)
                        { www=2; }
+               else if (strcmp(*argv,"-no_ssl2") == 0)
+                       { off|=SSL_OP_NO_SSLv2; }
+               else if (strcmp(*argv,"-no_ssl3") == 0)
+                       { off|=SSL_OP_NO_SSLv3; }
+               else if (strcmp(*argv,"-no_tls1") == 0)
+                       { off|=SSL_OP_NO_TLSv1; }
 #ifndef NO_SSL2
                else if (strcmp(*argv,"-ssl2") == 0)
                        { meth=SSLv2_server_method(); }
@@ -316,6 +558,10 @@ char *argv[];
 #ifndef NO_SSL3
                else if (strcmp(*argv,"-ssl3") == 0)
                        { meth=SSLv3_server_method(); }
+#endif
+#ifndef NO_TLS1
+               else if (strcmp(*argv,"-tls1") == 0)
+                       { meth=TLSv1_server_method(); }
 #endif
                else
                        {
@@ -333,6 +579,8 @@ bad:
                goto end;
                }
 
+       app_RAND_load_file(NULL, bio_err, 0);
+
        if (bio_s_out == NULL)
                {
                if (s_quiet && !s_debug)
@@ -352,6 +600,8 @@ bad:
                {
                s_cert_file=NULL;
                s_key_file=NULL;
+               s_dcert_file=NULL;
+               s_dkey_file=NULL;
                }
 
        SSL_load_error_strings();
@@ -364,12 +614,16 @@ bad:
                goto end;
                }
 
+       SSL_CTX_set_quiet_shutdown(ctx,1);
        if (bugs) SSL_CTX_set_options(ctx,SSL_OP_ALL);
        if (hack) SSL_CTX_set_options(ctx,SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG);
+       SSL_CTX_set_options(ctx,off);
        if (hack) SSL_CTX_set_options(ctx,SSL_OP_NON_EXPORT_FIRST);
 
        if (state) SSL_CTX_set_info_callback(ctx,apps_ssl_info_callback);
 
+       SSL_CTX_sess_set_cache_size(ctx,128);
+
 #if 0
        if (cipher == NULL) cipher=getenv("SSL_CIPHER");
 #endif
@@ -385,32 +639,40 @@ bad:
        if ((!SSL_CTX_load_verify_locations(ctx,CAfile,CApath)) ||
                (!SSL_CTX_set_default_verify_paths(ctx)))
                {
-               BIO_printf(bio_err,"X509_load_verify_locations\n");
+               /* BIO_printf(bio_err,"X509_load_verify_locations\n"); */
                ERR_print_errors(bio_err);
-               goto end;
+               /* goto end; */
                }
 
 #ifndef NO_DH
-       /* EAY EAY EAY evil hack */
-       dh=load_dh_param();
-       if (dh != NULL)
-               {
-               BIO_printf(bio_s_out,"Setting temp DH parameters\n");
-               }
-       else
+       if (!no_dhe)
                {
-               BIO_printf(bio_s_out,"Using default temp DH parameters\n");
-               dh=get_dh512();
-               }
-       BIO_flush(bio_s_out);
+               dh=load_dh_param(dhfile ? dhfile : s_cert_file);
+               if (dh != NULL)
+                       {
+                       BIO_printf(bio_s_out,"Setting temp DH parameters\n");
+                       }
+               else
+                       {
+                       BIO_printf(bio_s_out,"Using default temp DH parameters\n");
+                       dh=get_dh512();
+                       }
+               (void)BIO_flush(bio_s_out);
 
-       SSL_CTX_set_tmp_dh(ctx,dh);
-       DH_free(dh);
+               SSL_CTX_set_tmp_dh(ctx,dh);
+               DH_free(dh);
+               }
 #endif
        
        if (!set_cert_stuff(ctx,s_cert_file,s_key_file))
                goto end;
+       if (s_dcert_file != NULL)
+               {
+               if (!set_cert_stuff(ctx,s_dcert_file,s_dkey_file))
+                       goto end;
+               }
 
+#ifndef NO_RSA
 #if 1
        SSL_CTX_set_tmp_rsa_callback(ctx,tmp_rsa_cb);
 #else
@@ -431,19 +693,27 @@ bad:
                RSA_free(rsa);
                BIO_printf(bio_s_out,"\n");
                }
+#endif
 #endif
 
        if (cipher != NULL)
-               SSL_CTX_set_cipher_list(ctx,cipher);
-       SSL_CTX_set_verify(ctx,verify,verify_callback);
+               if(!SSL_CTX_set_cipher_list(ctx,cipher)) {
+               BIO_printf(bio_err,"error seting cipher list\n");
+               ERR_print_errors(bio_err);
+               goto end;
+       }
+       SSL_CTX_set_verify(ctx,s_server_verify,verify_callback);
+       SSL_CTX_set_session_id_context(ctx,(void*)&s_server_session_id_context,
+               sizeof s_server_session_id_context);
 
-       SSL_CTX_set_client_CA_list(ctx,SSL_load_client_CA_file(s_cert_file));
+       if (CAfile != NULL)
+           SSL_CTX_set_client_CA_list(ctx,SSL_load_client_CA_file(CAfile));
 
        BIO_printf(bio_s_out,"ACCEPT\n");
        if (www)
-               do_server(port,&accept_socket,www_body);
+               do_server(port,&accept_socket,www_body, context);
        else
-               do_server(port,&accept_socket,sv_body);
+               do_server(port,&accept_socket,sv_body, context);
        print_stats(bio_s_out,ctx);
        ret=0;
 end:
@@ -456,29 +726,32 @@ end:
        EXIT(ret);
        }
 
-static void print_stats(bio,ssl_ctx)
-BIO *bio;
-SSL_CTX *ssl_ctx;
+static void print_stats(BIO *bio, SSL_CTX *ssl_ctx)
        {
        BIO_printf(bio,"%4ld items in the session cache\n",
                SSL_CTX_sess_number(ssl_ctx));
        BIO_printf(bio,"%4d client connects (SSL_connect())\n",
                SSL_CTX_sess_connect(ssl_ctx));
+       BIO_printf(bio,"%4d client renegotiates (SSL_connect())\n",
+               SSL_CTX_sess_connect_renegotiate(ssl_ctx));
        BIO_printf(bio,"%4d client connects that finished\n",
                SSL_CTX_sess_connect_good(ssl_ctx));
        BIO_printf(bio,"%4d server accepts (SSL_accept())\n",
                SSL_CTX_sess_accept(ssl_ctx));
+       BIO_printf(bio,"%4d server renegotiates (SSL_accept())\n",
+               SSL_CTX_sess_accept_renegotiate(ssl_ctx));
        BIO_printf(bio,"%4d server accepts that finished\n",
                SSL_CTX_sess_accept_good(ssl_ctx));
        BIO_printf(bio,"%4d session cache hits\n",SSL_CTX_sess_hits(ssl_ctx));
        BIO_printf(bio,"%4d session cache misses\n",SSL_CTX_sess_misses(ssl_ctx));
        BIO_printf(bio,"%4d session cache timeouts\n",SSL_CTX_sess_timeouts(ssl_ctx));
        BIO_printf(bio,"%4d callback cache hits\n",SSL_CTX_sess_cb_hits(ssl_ctx));
+       BIO_printf(bio,"%4d cache full overflows (%d allowed)\n",
+               SSL_CTX_sess_cache_full(ssl_ctx),
+               SSL_CTX_sess_get_cache_size(ssl_ctx));
        }
 
-static int sv_body(hostname, s)
-char *hostname;
-int s;
+static int sv_body(char *hostname, int s, unsigned char *context)
        {
        char *buf=NULL;
        fd_set readfds;
@@ -487,8 +760,11 @@ int s;
        unsigned long l;
        SSL *con=NULL;
        BIO *sbio;
+#ifdef WINDOWS
+       struct timeval tv;
+#endif
 
-       if ((buf=Malloc(BUFSIZZ)) == NULL)
+       if ((buf=Malloc(bufsize)) == NULL)
                {
                BIO_printf(bio_err,"out of memory\n");
                goto err;
@@ -500,12 +776,17 @@ int s;
 
                if (!s_quiet)
                        BIO_printf(bio_err,"turning on non blocking io\n");
-               socket_ioctl(s,FIONBIO,&sl);
+               if (BIO_socket_ioctl(s,FIONBIO,&sl) < 0)
+                       ERR_print_errors(bio_err);
                }
 #endif
 
-       if (con == NULL)
+       if (con == NULL) {
                con=(SSL *)SSL_new(ctx);
+               if(context)
+                     SSL_set_session_id_context(con, context,
+                                                strlen((char *)context));
+       }
        SSL_clear(con);
 
        sbio=BIO_new_socket(s,BIO_NOCLOSE);
@@ -535,11 +816,53 @@ int s;
                FD_SET(fileno(stdin),&readfds);
 #endif
                FD_SET(s,&readfds);
-               i=select(width,&readfds,NULL,NULL,NULL);
-               if (i <= 0) continue;
-               if (FD_ISSET(fileno(stdin),&readfds))
+               /* Note: under VMS with SOCKETSHR the second parameter is
+                * currently of type (int *) whereas under other systems
+                * it is (void *) if you don't have a cast it will choke
+                * the compiler: if you do have a cast then you can either
+                * go for (int *) or (void *).
+                */
+#ifdef WINDOWS
+               /* Under Windows we can't select on stdin: only
+                * on sockets. As a workaround we timeout the select every
+                * second and check for any keypress. In a proper Windows
+                * application we wouldn't do this because it is inefficient.
+                */
+               tv.tv_sec = 1;
+               tv.tv_usec = 0;
+               i=select(width,(void *)&readfds,NULL,NULL,&tv);
+               if((i < 0) || (!i && !_kbhit() ) )continue;
+               if(_kbhit())
+#else
+               i=select(width,(void *)&readfds,NULL,NULL,NULL);
+               if (i <= 0) continue;
+               if (FD_ISSET(fileno(stdin),&readfds))
+#endif
                        {
-                       i=read(fileno(stdin),buf,BUFSIZZ);
+                       if (s_crlf)
+                               {
+                               int j, lf_num;
+
+                               i=read(fileno(stdin), buf, bufsize/2);
+                               lf_num = 0;
+                               /* both loops are skipped when i <= 0 */
+                               for (j = 0; j < i; j++)
+                                       if (buf[j] == '\n')
+                                               lf_num++;
+                               for (j = i-1; j >= 0; j--)
+                                       {
+                                       buf[j+lf_num] = buf[j];
+                                       if (buf[j] == '\n')
+                                               {
+                                               lf_num--;
+                                               i++;
+                                               buf[j+lf_num] = '\r';
+                                               }
+                                       }
+                               assert(lf_num == 0);
+                               }
+                       else
+                               i=read(fileno(stdin),buf,bufsize);
                        if (!s_quiet)
                                {
                                if ((i <= 0) || (buf[0] == 'Q'))
@@ -558,21 +881,27 @@ int s;
                                        ret= -11;*/
                                        goto err;
                                        }
-                               if (buf[0] == 'r')
+                               if ((buf[0] == 'r') && 
+                                       ((buf[1] == '\n') || (buf[1] == '\r')))
                                        {
                                        SSL_renegotiate(con);
+                                       i=SSL_do_handshake(con);
+                                       printf("SSL_do_handshake -> %d\n",i);
                                        i=0; /*13; */
                                        continue;
-                                       strcpy(buf,"server side RE-NEGOTIATE\n");
+                                       /* strcpy(buf,"server side RE-NEGOTIATE\n"); */
                                        }
-                               if (buf[0] == 'R')
+                               if ((buf[0] == 'R') &&
+                                       ((buf[1] == '\n') || (buf[1] == '\r')))
                                        {
                                        SSL_set_verify(con,
                                                SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE,NULL);
                                        SSL_renegotiate(con);
+                                       i=SSL_do_handshake(con);
+                                       printf("SSL_do_handshake -> %d\n",i);
                                        i=0; /* 13; */
                                        continue;
-                                       strcpy(buf,"server side RE-NEGOTIATE asking for client cert\n");
+                                       /* strcpy(buf,"server side RE-NEGOTIATE asking for client cert\n"); */
                                        }
                                if (buf[0] == 'P')
                                        {
@@ -584,23 +913,34 @@ int s;
                                        print_stats(bio_s_out,SSL_get_SSL_CTX(con));
                                        }
                                }
+#ifdef CHARSET_EBCDIC
+                       ebcdic2ascii(buf,buf,i);
+#endif
                        l=k=0;
                        for (;;)
                                {
                                /* should do a select for the write */
-                               k=SSL_write(con,&(buf[l]),(unsigned int)i);
-                               if (
-#ifdef FIONBIO
-                                       s_nbio &&
+#ifdef RENEG
+{ static count=0; if (++count == 100) { count=0; SSL_renegotiate(con); } }
 #endif
-                                       BIO_sock_should_retry(k))
+                               k=SSL_write(con,&(buf[l]),(unsigned int)i);
+                               switch (SSL_get_error(con,k))
                                        {
+                               case SSL_ERROR_NONE:
+                                       break;
+                               case SSL_ERROR_WANT_WRITE:
+                               case SSL_ERROR_WANT_READ:
+                               case SSL_ERROR_WANT_X509_LOOKUP:
                                        BIO_printf(bio_s_out,"Write BLOCK\n");
-                                       continue;
-                                       }
-                               if (k <= 0)
-                                       {
+                                       break;
+                               case SSL_ERROR_SYSCALL:
+                               case SSL_ERROR_SSL:
+                                       BIO_printf(bio_s_out,"ERROR\n");
                                        ERR_print_errors(bio_err);
+                                       ret=1;
+                                       goto err;
+                                       /* break; */
+                               case SSL_ERROR_ZERO_RETURN:
                                        BIO_printf(bio_s_out,"DONE\n");
                                        ret=1;
                                        goto err;
@@ -629,25 +969,34 @@ int s;
                                }
                        else
                                {
-                               i=SSL_read(con,(char *)buf,BUFSIZZ);
-                               if ((i <= 0) &&
-#ifdef FIONBIO
-                                       s_nbio &&
-#endif
-                                       BIO_sock_should_retry(i))
+again: 
+                               i=SSL_read(con,(char *)buf,bufsize);
+                               switch (SSL_get_error(con,i))
                                        {
+                               case SSL_ERROR_NONE:
+#ifdef CHARSET_EBCDIC
+                                       ascii2ebcdic(buf,buf,i);
+#endif
+                                       write(fileno(stdout),buf,
+                                               (unsigned int)i);
+                                       if (SSL_pending(con)) goto again;
+                                       break;
+                               case SSL_ERROR_WANT_WRITE:
+                               case SSL_ERROR_WANT_READ:
+                               case SSL_ERROR_WANT_X509_LOOKUP:
                                        BIO_printf(bio_s_out,"Read BLOCK\n");
-                                       }
-                               else if (i <= 0)
-                                       {
+                                       break;
+                               case SSL_ERROR_SYSCALL:
+                               case SSL_ERROR_SSL:
+                                       BIO_printf(bio_s_out,"ERROR\n");
                                        ERR_print_errors(bio_err);
+                                       ret=1;
+                                       goto err;
+                               case SSL_ERROR_ZERO_RETURN:
                                        BIO_printf(bio_s_out,"DONE\n");
                                        ret=1;
                                        goto err;
                                        }
-                               else
-                                       write(fileno(stdout),buf,
-                                               (unsigned int)i);
                                }
                        }
                }
@@ -662,7 +1011,7 @@ err:
        BIO_printf(bio_s_out,"CONNECTION CLOSED\n");
        if (buf != NULL)
                {
-               memset(buf,0,BUFSIZZ);
+               memset(buf,0,bufsize);
                Free(buf);
                }
        if (ret >= 0)
@@ -670,7 +1019,7 @@ err:
        return(ret);
        }
 
-static void close_accept_socket()
+static void close_accept_socket(void)
        {
        BIO_printf(bio_err,"shutdown accept socket\n");
        if (accept_socket >= 0)
@@ -679,13 +1028,12 @@ static void close_accept_socket()
                }
        }
 
-static int init_ssl_connection(con)
-SSL *con;
+static int init_ssl_connection(SSL *con)
        {
        int i;
-       char *str;
+       const char *str;
        X509 *peer;
-       int verify_error;
+       long verify_error;
        MS_STATIC char buf[BUFSIZ];
 
        if ((i=SSL_accept(con)) <= 0)
@@ -727,28 +1075,30 @@ SSL *con;
        str=SSL_CIPHER_get_name(SSL_get_current_cipher(con));
        BIO_printf(bio_s_out,"CIPHER is %s\n",(str != NULL)?str:"(NONE)");
        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)
+               BIO_printf(bio_s_out,"Peer has incorrect TLSv1 block padding\n");
+
        return(1);
        }
 
-static DH *load_dh_param()
+#ifndef NO_DH
+static DH *load_dh_param(char *dhfile)
        {
        DH *ret=NULL;
        BIO *bio;
 
-#ifndef NO_DH
-       if ((bio=BIO_new_file(DH_PARAM,"r")) == NULL)
+       if ((bio=BIO_new_file(dhfile,"r")) == NULL)
                goto err;
-       ret=PEM_read_bio_DHparams(bio,NULL,NULL);
+       ret=PEM_read_bio_DHparams(bio,NULL,NULL,NULL);
 err:
        if (bio != NULL) BIO_free(bio);
-#endif
        return(ret);
        }
+#endif
 
 #if 0
-static int load_CA(ctx,file)
-SSL_CTX *ctx;
-char *file;
+static int load_CA(SSL_CTX *ctx, char *file)
        {
        FILE *in;
        X509 *x=NULL;
@@ -768,18 +1118,19 @@ char *file;
        }
 #endif
 
-static int www_body(hostname, s)
-char *hostname;
-int s;
+static int www_body(char *hostname, int s, unsigned char *context)
        {
-       char buf[1024];
+       char *buf=NULL;
        int ret=1;
        int i,j,k,blank,dot;
        struct stat st_buf;
        SSL *con;
        SSL_CIPHER *c;
        BIO *io,*ssl_bio,*sbio;
+       long total_bytes;
 
+       buf=Malloc(bufsize);
+       if (buf == NULL) return(0);
        io=BIO_new(BIO_f_buffer());
        ssl_bio=BIO_new(BIO_f_ssl());
        if ((io == NULL) || (ssl_bio == NULL)) goto err;
@@ -787,18 +1138,21 @@ int s;
 #ifdef FIONBIO 
        if (s_nbio)
                {
-               unsigned int long sl=1;
+               unsigned long sl=1;
 
                if (!s_quiet)
                        BIO_printf(bio_err,"turning on non blocking io\n");
-               socket_ioctl(s,FIONBIO,&sl);
+               if (BIO_socket_ioctl(s,FIONBIO,&sl) < 0)
+                       ERR_print_errors(bio_err);
                }
 #endif
 
        /* lets make the output buffer a reasonable size */
-       if (!BIO_set_write_buffer_size(io,16*1024)) goto err;
+       if (!BIO_set_write_buffer_size(io,bufsize)) goto err;
 
        if ((con=(SSL *)SSL_new(ctx)) == NULL) goto err;
+       if(context) SSL_set_session_id_context(con, context,
+                                              strlen((char *)context));
 
        sbio=BIO_new_socket(s,BIO_NOCLOSE);
        if (s_nbio_test)
@@ -814,6 +1168,9 @@ int s;
        /* SSL_set_fd(con,s); */
        BIO_set_ssl(ssl_bio,con,BIO_CLOSE);
        BIO_push(io,ssl_bio);
+#ifdef CHARSET_EBCDIC
+       io = BIO_push(BIO_new(BIO_f_ebcdic_filter()),io);
+#endif
 
        if (s_debug)
                {
@@ -842,14 +1199,14 @@ int s;
                        case SSL_ERROR_ZERO_RETURN:
                                ret=1;
                                goto err;
-                               break;
+                               /* break; */
                                }
 
                        SSL_renegotiate(con);
                        SSL_write(con,NULL,0);
                        }
 
-               i=BIO_gets(io,buf,sizeof(buf)-1);
+               i=BIO_gets(io,buf,bufsize-1);
                if (i < 0) /* error */
                        {
                        if (!BIO_should_retry(io))
@@ -875,14 +1232,15 @@ int s;
 
                /* else we have data */
                if (    ((www == 1) && (strncmp("GET ",buf,4) == 0)) ||
-                       ((www == 2) && (strncmp("GET stats ",buf,10) == 0)))
+                       ((www == 2) && (strncmp("GET /stats ",buf,10) == 0)))
                        {
                        char *p;
                        X509 *peer;
-                       STACK *sk;
-                       static char *space="                ";
+                       STACK_OF(SSL_CIPHER) *sk;
+                       static char *space="                          ";
 
                        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");
 /*                     BIO_puts(io,SSLeay_version(SSLEAY_VERSION));*/
                        BIO_puts(io,"\n");
@@ -897,18 +1255,18 @@ int s;
                         * be done */
                        BIO_printf(io,"Ciphers supported in s_server binary\n");
                        sk=SSL_get_ciphers(con);
-                       j=sk_num(sk);
+                       j=sk_SSL_CIPHER_num(sk);
                        for (i=0; i<j; i++)
                                {
-                               c=(SSL_CIPHER *)sk_value(sk,i);
-                               BIO_printf(io,"%s:%-25s",
+                               c=sk_SSL_CIPHER_value(sk,i);
+                               BIO_printf(io,"%-11s:%-25s",
                                        SSL_CIPHER_get_version(c),
                                        SSL_CIPHER_get_name(c));
-                               if ((((i+1)%3) == 0) && (i+1 != j))
+                               if ((((i+1)%2) == 0) && (i+1 != j))
                                        BIO_puts(io,"\n");
                                }
                        BIO_puts(io,"\n");
-                       p=SSL_get_shared_ciphers(con,buf,sizeof(buf));
+                       p=SSL_get_shared_ciphers(con,buf,bufsize);
                        if (p != NULL)
                                {
                                BIO_printf(io,"---\nCiphers common between both SSL end points:\n");
@@ -917,7 +1275,7 @@ int s;
                                        {
                                        if (*p == ':')
                                                {
-                                               BIO_write(io,space,15-j);
+                                               BIO_write(io,space,26-j);
                                                i++;
                                                j=0;
                                                BIO_write(io,((i%3)?" ":"\n"),1);
@@ -935,7 +1293,7 @@ int s;
                                ?"---\nReused, "
                                :"---\nNew, "));
                        c=SSL_get_current_cipher(con);
-                       BIO_printf(io,"SSLv%d, Cipher is %s\n",
+                       BIO_printf(io,"%s, Cipher is %s\n",
                                SSL_CIPHER_get_version(c),
                                SSL_CIPHER_get_name(c));
                        SSL_SESSION_print(io,SSL_get_session(con));
@@ -951,9 +1309,10 @@ int s;
                                }
                        else
                                BIO_puts(io,"no client certificate available\n");
+                       BIO_puts(io,"</BODY></HTML>\r\n\r\n");
                        break;
                        }
-               else if ((www == 2) && (strncmp("GET ",buf,4) == 0))
+               else if ((www == 2) && (strncmp("GET /",buf,5) == 0))
                        {
                        BIO *file;
                        char *p,*e;
@@ -969,6 +1328,7 @@ int s;
                                        (strncmp(&(e[-1]),"/../",4) == 0))
                                        dot=1;
                                }
+                       
 
                        if (*e == '\0')
                                {
@@ -1028,18 +1388,33 @@ int s;
                        else
                                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,1024);
+                               i=BIO_read(file,buf,bufsize);
                                if (i <= 0) break;
 
+#ifdef RENEG
+                               total_bytes+=i;
+                               fprintf(stderr,"%d\n",i);
+                               if (total_bytes > 3*1024)
+                                       {
+                                       total_bytes=0;
+                                       fprintf(stderr,"RENEGOTIATE\n");
+                                       SSL_renegotiate(con);
+                                       }
+#endif
+
                                for (j=0; j<i; )
                                        {
+#ifdef RENEG
+{ static count=0; if (++count == 13) { SSL_renegotiate(con); } }
+#endif
                                        k=BIO_write(io,&(buf[j]),i-j);
                                        if (k <= 0)
                                                {
                                                if (!BIO_should_retry(io))
-                                                       break;
+                                                       goto write_error;
                                                else
                                                        {
                                                        BIO_printf(bio_s_out,"rwrite W BLOCK\n");
@@ -1051,6 +1426,7 @@ int s;
                                                }
                                        }
                                }
+write_error:
                        BIO_free(file);
                        break;
                        }
@@ -1068,12 +1444,13 @@ int s;
                        break;
                }
 end:
-#if 0
+#if 1
        /* make sure we re-use sessions */
        SSL_set_shutdown(con,SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN);
 #else
        /* This kills performace */
-       SSL_shutdown(con);
+/*     SSL_shutdown(con); A shutdown gets sent in the
+ *     BIO_free_all(io) procession */
 #endif
 
 err:
@@ -1081,14 +1458,14 @@ err:
        if (ret >= 0)
                BIO_printf(bio_s_out,"ACCEPT\n");
 
+       if (buf != NULL) Free(buf);
        if (io != NULL) BIO_free_all(io);
-/*     if (ssl_bio != NULL) BIO_free(ssl_bio); */
+/*     if (ssl_bio != NULL) BIO_free(ssl_bio);*/
        return(ret);
        }
 
-static RSA MS_CALLBACK *tmp_rsa_cb(s,export)
-SSL *s;
-int export;
+#ifndef NO_RSA
+static RSA MS_CALLBACK *tmp_rsa_cb(SSL *s, int is_export, int keylength)
        {
        static RSA *rsa_tmp=NULL;
 
@@ -1096,17 +1473,16 @@ int export;
                {
                if (!s_quiet)
                        {
-                       BIO_printf(bio_err,"Generating temp (512 bit) RSA key...");
-                       BIO_flush(bio_err);
+                       BIO_printf(bio_err,"Generating temp (%d bit) RSA key...",keylength);
+                       (void)BIO_flush(bio_err);
                        }
-#ifndef NO_RSA
-               rsa_tmp=RSA_generate_key(512,RSA_F4,NULL);
-#endif
+               rsa_tmp=RSA_generate_key(keylength,RSA_F4,NULL,NULL);
                if (!s_quiet)
                        {
                        BIO_printf(bio_err,"\n");
-                       BIO_flush(bio_err);
+                       (void)BIO_flush(bio_err);
                        }
                }
        return(rsa_tmp);
        }
+#endif