#ifndef OPENSSL_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 int sv_body(char *hostname, int s, int stype, unsigned char *context);
+static int www_body(char *hostname, int s, int stype, unsigned char *context);
+static int rev_body(char *hostname, int s, int stype, 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 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);
#undef PROG
#define PROG s_server_main
-extern int verify_depth, verify_return_error;
+extern int verify_depth, verify_return_error, verify_quiet;
static int s_server_verify=SSL_VERIFY_NONE;
static int s_server_session_id_context = 1; /* anything will do */
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;
+static int s_ign_eof=0;
+static int s_brief=0;
static char *keymatexportlabel=NULL;
static int keymatexportlen=20;
#endif
#ifndef OPENSSL_NO_TLSEXT
-static BIO *authz_in = NULL;
-static const char *s_authz_file = NULL;
static BIO *serverinfo_in = NULL;
static const char *s_serverinfo_file = NULL;
+
#endif
#ifndef OPENSSL_NO_PSK
s_debug=0;
s_msg=0;
s_quiet=0;
+ s_brief=0;
hack=0;
#ifndef OPENSSL_NO_ENGINE
engine_id=NULL;
BIO_printf(bio_err,"usage: s_server [args ...]\n");
BIO_printf(bio_err,"\n");
BIO_printf(bio_err," -accept arg - port to accept on (default is %d)\n",PORT);
+ BIO_printf(bio_err," -checkhost host - check peer certificate matches \"host\"\n");
+ BIO_printf(bio_err," -checkemail email - check peer certificate matches \"email\"\n");
+ BIO_printf(bio_err," -checkip ipaddr - check peer certificate matches \"ipaddr\"\n");
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," -verify_return_error - return verification errors\n");
BIO_printf(bio_err," -cert arg - certificate file to use\n");
BIO_printf(bio_err," (default is %s)\n",TEST_CERT);
- BIO_printf(bio_err," -authz arg - binary authz file for certificate\n");
#ifndef OPENSSL_NO_TLSEXT
BIO_printf(bio_err," -serverinfo arg - PEM serverinfo file for certificate\n");
+ BIO_printf(bio_err," -auth - send and receive RFC 5878 TLS auth extensions and supplemental data\n");
+ BIO_printf(bio_err," -auth_require_reneg - Do not send TLS auth extensions until renegotiation\n");
#endif
+ BIO_printf(bio_err," -no_resumption_on_reneg - set SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION flag\n");
BIO_printf(bio_err," -crl_check - check the peer certificate has not been revoked by its CA.\n" \
" The CRL(s) are appended to the certificate file\n");
BIO_printf(bio_err," -crl_check_all - check the peer certificate has not been revoked by its CA\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," -dtls1_2 - Just talk DTLSv1.2\n");
BIO_printf(bio_err," -timeout - Enable timeouts\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_ecdhe - Disable ephemeral ECDH\n");
#endif
BIO_printf(bio_err," -bugs - Turn on SSL bug compatibility\n");
+ BIO_printf(bio_err," -hack - workaround for early Netscape code\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");
BIO_printf(bio_err," -HTTP - Respond to a 'GET /<path> HTTP/1.0' with file ./<path>\n");
BIO_printf(bio_err," -nextprotoneg arg - set the advertised protocols for the NPN extension (comma-separated list)\n");
# endif
BIO_printf(bio_err," -use_srtp profiles - Offer SRTP key management with a colon-separated profile list\n");
+ BIO_printf(bio_err," -alpn arg - set the advertised protocols for the ALPN extension (comma-separated list)\n");
#endif
BIO_printf(bio_err," -keymatexport label - Export keying material using label\n");
BIO_printf(bio_err," -keymatexportlen len - Export len bytes of keying material (default 20)\n");
+ BIO_printf(bio_err," -status - respond to certificate status requests\n");
+ BIO_printf(bio_err," -status_verbose - enable status request verbose printout\n");
+ BIO_printf(bio_err," -status_timeout n - status request responder timeout\n");
+ BIO_printf(bio_err," -status_url URL - status request fallback URL\n");
}
static int local_argc=0;
if (servername)
{
- if (strcmp(servername,p->servername))
+ if (strcasecmp(servername,p->servername))
return p->extension_error;
if (ctx2)
{
}
# endif /* ndef OPENSSL_NO_NEXTPROTONEG */
+/* This the context that we pass to alpn_cb */
+typedef struct tlsextalpnctx_st {
+ unsigned char *data;
+ unsigned short len;
+} tlsextalpnctx;
-#endif
+static int alpn_cb(SSL *s, const unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg)
+ {
+ tlsextalpnctx *alpn_ctx = arg;
+
+ if (!s_quiet)
+ {
+ /* We can assume that |in| is syntactically valid. */
+ unsigned i;
+ BIO_printf(bio_s_out, "ALPN protocols advertised by the client: ");
+ for (i = 0; i < inlen; )
+ {
+ if (i)
+ BIO_write(bio_s_out, ", ", 2);
+ BIO_write(bio_s_out, &in[i + 1], in[i]);
+ i += in[i] + 1;
+ }
+ BIO_write(bio_s_out, "\n", 1);
+ }
+
+ if (SSL_select_next_proto((unsigned char**) out, outlen, alpn_ctx->data, alpn_ctx->len, in, inlen) !=
+ OPENSSL_NPN_NEGOTIATED)
+ {
+ return SSL_TLSEXT_ERR_NOACK;
+ }
+
+ if (!s_quiet)
+ {
+ BIO_printf(bio_s_out, "ALPN protocols selected: ");
+ BIO_write(bio_s_out, *out, *outlen);
+ BIO_write(bio_s_out, "\n", 1);
+ }
+
+ return SSL_TLSEXT_ERR_OK;
+ }
+#endif /* ndef OPENSSL_NO_TLSEXT */
int MAIN(int, char **);
X509 *s_cert = NULL, *s_dcert = NULL;
STACK_OF(X509) *s_chain = NULL, *s_dchain = NULL;
EVP_PKEY *s_key = NULL, *s_dkey = NULL;
- int no_cache = 0;
+ int no_cache = 0, ext_cache = 0;
+ int rev = 0, naccept = -1;
#ifndef OPENSSL_NO_TLSEXT
EVP_PKEY *s_key2 = NULL;
X509 *s_cert2 = NULL;
tlsextctx tlsextcbp = {NULL, NULL, SSL_TLSEXT_ERR_ALERT_WARNING};
# ifndef OPENSSL_NO_NEXTPROTONEG
const char *next_proto_neg_in = NULL;
- tlsextnextprotoctx next_proto;
+ tlsextnextprotoctx next_proto = { NULL, 0};
# endif
+ const char *alpn_in = NULL;
+ tlsextalpnctx alpn_ctx = { NULL, 0};
#endif
#ifndef OPENSSL_NO_PSK
/* by default do not send a PSK identity hint */
if (!extract_port(*(++argv),&port))
goto bad;
}
+ else if (strcmp(*argv,"-naccept") == 0)
+ {
+ if (--argc < 1) goto bad;
+ naccept = atol(*(++argv));
+ if (naccept <= 0)
+ {
+ BIO_printf(bio_err, "bad accept value %s\n",
+ *argv);
+ goto bad;
+ }
+ }
else if (strcmp(*argv,"-verify") == 0)
{
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);
+ if (!s_quiet)
+ BIO_printf(bio_err,"verify depth is %d\n",verify_depth);
}
else if (strcmp(*argv,"-Verify") == 0)
{
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);
+ if (!s_quiet)
+ BIO_printf(bio_err,"verify depth is %d, must return a certificate\n",verify_depth);
}
else if (strcmp(*argv,"-context") == 0)
{
else if (strcmp(*argv,"-crl_download") == 0)
crl_download = 1;
#ifndef OPENSSL_NO_TLSEXT
- else if (strcmp(*argv,"-authz") == 0)
- {
- if (--argc < 1) goto bad;
- s_authz_file = *(++argv);
- }
else if (strcmp(*argv,"-serverinfo") == 0)
{
if (--argc < 1) goto bad;
}
else if (strcmp(*argv,"-no_cache") == 0)
no_cache = 1;
+ else if (strcmp(*argv,"-ext_cache") == 0)
+ ext_cache = 1;
else if (strcmp(*argv,"-CRLform") == 0)
{
if (--argc < 1) goto bad;
}
else if (strcmp(*argv,"-verify_return_error") == 0)
verify_return_error = 1;
+ else if (strcmp(*argv,"-verify_quiet") == 0)
+ verify_quiet = 1;
else if (strcmp(*argv,"-build_chain") == 0)
build_chain = 1;
else if (strcmp(*argv,"-CAfile") == 0)
#endif
s_nbio_test=1;
}
+ else if (strcmp(*argv,"-ign_eof") == 0)
+ s_ign_eof=1;
+ else if (strcmp(*argv,"-no_ign_eof") == 0)
+ s_ign_eof=0;
else if (strcmp(*argv,"-debug") == 0)
{ s_debug=1; }
#ifndef OPENSSL_NO_TLSEXT
{ s_crlf=1; }
else if (strcmp(*argv,"-quiet") == 0)
{ s_quiet=1; }
+ else if (strcmp(*argv,"-brief") == 0)
+ {
+ s_quiet=1;
+ s_brief=1;
+ verify_quiet=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,"-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)
{
meth = TLSv1_server_method();
}
#endif
+ else if (strcmp(*argv,"-rev") == 0)
+ { rev=1; }
else if (strcmp(*argv,"-www") == 0)
{ www=1; }
else if (strcmp(*argv,"-WWW") == 0)
{ meth=TLSv1_2_server_method(); }
#endif
#ifndef OPENSSL_NO_DTLS1
+ else if (strcmp(*argv,"-dtls") == 0)
+ {
+ meth=DTLS_server_method();
+ socket_type = SOCK_DGRAM;
+ }
else if (strcmp(*argv,"-dtls1") == 0)
{
meth=DTLSv1_server_method();
socket_type = SOCK_DGRAM;
}
+ else if (strcmp(*argv,"-dtls1_2") == 0)
+ {
+ meth=DTLSv1_2_server_method();
+ socket_type = SOCK_DGRAM;
+ }
else if (strcmp(*argv,"-timeout") == 0)
enable_timeouts = 1;
else if (strcmp(*argv,"-mtu") == 0)
next_proto_neg_in = *(++argv);
}
# endif
+ else if (strcmp(*argv,"-alpn") == 0)
+ {
+ if (--argc < 1) goto bad;
+ alpn_in = *(++argv);
+ }
#endif
#if !defined(OPENSSL_NO_JPAKE) && !defined(OPENSSL_NO_PSK)
else if (strcmp(*argv,"-jpake") == 0)
sv_usage();
goto end;
}
+#ifndef OPENSSL_NO_DTLS1
+ if (www && socket_type == SOCK_DGRAM)
+ {
+ BIO_printf(bio_err,
+ "Can't use -HTTP, -www or -WWW with DTLS\n");
+ goto end;
+ }
+#endif
#if !defined(OPENSSL_NO_JPAKE) && !defined(OPENSSL_NO_PSK)
if (jpake_secret)
#endif /* OPENSSL_NO_TLSEXT */
}
-#if !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG)
+#if !defined(OPENSSL_NO_TLSEXT)
+# if !defined(OPENSSL_NO_NEXTPROTONEG)
if (next_proto_neg_in)
{
unsigned short len;
{
next_proto.data = NULL;
}
+# endif
+ alpn_ctx.data = NULL;
+ if (alpn_in)
+ {
+ unsigned short len;
+ alpn_ctx.data = next_protos_parse(&len, alpn_in);
+ if (alpn_ctx.data == NULL)
+ goto end;
+ alpn_ctx.len = len;
+ }
#endif
if (crl_file)
if (bio_s_out == NULL)
{
- if (s_quiet && !s_debug && !s_msg)
+ if (s_quiet && !s_debug)
{
bio_s_out=BIO_new(BIO_s_null());
if (s_msg && !bio_s_msg)
if (state) SSL_CTX_set_info_callback(ctx,apps_ssl_info_callback);
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 (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 (next_proto.data)
SSL_CTX_set_next_protos_advertised_cb(ctx, next_proto_cb, &next_proto);
# endif
+ if (alpn_ctx.data)
+ SSL_CTX_set_alpn_select_cb(ctx, alpn_cb, &alpn_ctx);
#endif
#ifndef OPENSSL_NO_DH
if (!set_cert_key_stuff(ctx, s_cert, s_key, s_chain, build_chain))
goto end;
#ifndef OPENSSL_NO_TLSEXT
- if (s_authz_file != NULL && !SSL_CTX_use_authz_file(ctx, s_authz_file))
- goto end;
if (s_serverinfo_file != NULL
&& !SSL_CTX_use_serverinfo_file(ctx, s_serverinfo_file))
+ {
+ ERR_print_errors(bio_err);
goto end;
+ }
#endif
#ifndef OPENSSL_NO_TLSEXT
if (ctx2 && !set_cert_key_stuff(ctx2,s_cert2,s_key2, NULL, build_chain))
BIO_printf(bio_s_out,"ACCEPT\n");
(void)BIO_flush(bio_s_out);
- if (www)
- do_server(port,socket_type,&accept_socket,www_body, context);
+ if (rev)
+ do_server(port,socket_type,&accept_socket,rev_body, context, naccept);
+ else if (www)
+ do_server(port,socket_type,&accept_socket,www_body, context, naccept);
else
- do_server(port,socket_type,&accept_socket,sv_body, context);
+ do_server(port,socket_type,&accept_socket,sv_body, context, naccept);
print_stats(bio_s_out,ctx);
ret=0;
end:
OPENSSL_free(dpass);
if (vpm)
X509_VERIFY_PARAM_free(vpm);
+ free_sessions();
#ifndef OPENSSL_NO_TLSEXT
if (tlscstatp.host)
OPENSSL_free(tlscstatp.host);
X509_free(s_cert2);
if (s_key2)
EVP_PKEY_free(s_key2);
- if (authz_in != NULL)
- BIO_free(authz_in);
if (serverinfo_in != NULL)
BIO_free(serverinfo_in);
+# ifndef OPENSSL_NO_NEXTPROTONEG
+ if (next_proto.data)
+ OPENSSL_free(next_proto.data);
+# endif
+ if (alpn_ctx.data)
+ OPENSSL_free(alpn_ctx.data);
#endif
ssl_excert_free(exc);
if (ssl_args)
SSL_CTX_sess_get_cache_size(ssl_ctx));
}
-static int sv_body(char *hostname, int s, unsigned char *context)
+static int sv_body(char *hostname, int s, int stype, unsigned char *context)
{
char *buf=NULL;
fd_set readfds;
#endif
#endif
- if (SSL_version(con) == DTLS1_VERSION)
+ if (stype == SOCK_DGRAM)
{
sbio=BIO_new_dgram(s,BIO_NOCLOSE);
}
else
i=raw_read_stdin(buf,bufsize);
- if (!s_quiet)
+ if (!s_quiet && !s_brief)
{
if ((i <= 0) || (buf[0] == 'Q'))
{
i=SSL_accept(con);
+#ifdef CERT_CB_TEST_RETRY
+ {
+ while (i <= 0 && SSL_get_error(con,i) == SSL_ERROR_WANT_X509_LOOKUP && SSL_state(con) == SSL3_ST_SR_CLNT_HELLO_C)
+ {
+ fprintf(stderr, "LOOKUP from certificate callback during accept\n");
+ i=SSL_accept(con);
+ }
+ }
+#endif
#ifndef OPENSSL_NO_SRP
while (i <= 0 && SSL_get_error(con,i) == SSL_ERROR_WANT_X509_LOOKUP)
{
i=SSL_accept(con);
}
#endif
+
if (i <= 0)
{
if (BIO_sock_should_retry(i))
return(0);
}
+ if (s_brief)
+ print_ssl_summary(bio_err, con);
+
PEM_write_bio_SSL_SESSION(bio_s_out,SSL_get_session(con));
peer=SSL_get_peer_certificate(con);
BIO_printf(bio_s_out,"Shared ciphers:%s\n",buf);
str=SSL_CIPHER_get_name(SSL_get_current_cipher(con));
ssl_print_sigalgs(bio_s_out, con);
- ssl_print_curves(bio_s_out, con);
+#ifndef OPENSSL_NO_EC
+ ssl_print_point_formats(bio_s_out, con);
+ ssl_print_curves(bio_s_out, con, 0);
+#endif
BIO_printf(bio_s_out,"CIPHER is %s\n",(str != NULL)?str:"(NONE)");
#if !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG)
}
#endif
-static int www_body(char *hostname, int s, unsigned char *context)
+static int www_body(char *hostname, int s, int stype, unsigned char *context)
{
char *buf=NULL;
int ret=1;
BIO_puts(io,"\n");
}
ssl_print_sigalgs(io, con);
- ssl_print_curves(io, con);
+#ifndef OPENSSL_NO_EC
+ ssl_print_curves(io, con, 0);
+#endif
BIO_printf(io,(SSL_cache_hit(con)
?"---\nReused, "
:"---\nNew, "));
return(ret);
}
+static int rev_body(char *hostname, int s, int stype, unsigned char *context)
+ {
+ char *buf=NULL;
+ int i;
+ int ret=1;
+ SSL *con;
+ BIO *io,*ssl_bio,*sbio;
+#ifndef OPENSSL_NO_KRB5
+ KSSL_CTX *kctx;
+#endif
+
+ buf=OPENSSL_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;
+
+ /* lets make the output buffer a reasonable size */
+ if (!BIO_set_write_buffer_size(io,bufsize)) goto err;
+
+ if ((con=SSL_new(ctx)) == NULL) goto err;
+#ifndef OPENSSL_NO_TLSEXT
+ if (s_tlsextdebug)
+ {
+ SSL_set_tlsext_debug_callback(con, tlsext_cb);
+ SSL_set_tlsext_debug_arg(con, bio_s_out);
+ }
+#endif
+#ifndef OPENSSL_NO_KRB5
+ if ((kctx = kssl_ctx_new()) != NULL)
+ {
+ kssl_ctx_setstring(kctx, KSSL_SERVICE, KRB5SVC);
+ kssl_ctx_setstring(kctx, KSSL_KEYTAB, KRB5KEYTAB);
+ }
+#endif /* OPENSSL_NO_KRB5 */
+ if(context) SSL_set_session_id_context(con, context,
+ strlen((char *)context));
+
+ sbio=BIO_new_socket(s,BIO_NOCLOSE);
+ SSL_set_bio(con,sbio,sbio);
+ SSL_set_accept_state(con);
+
+ 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)
+ {
+ SSL_set_debug(con, 1);
+ BIO_set_callback(SSL_get_rbio(con),bio_dump_callback);
+ BIO_set_callback_arg(SSL_get_rbio(con),(char *)bio_s_out);
+ }
+ if (s_msg)
+ {
+#ifndef OPENSSL_NO_SSL_TRACE
+ if (s_msg == 2)
+ SSL_set_msg_callback(con, SSL_trace);
+ else
+#endif
+ SSL_set_msg_callback(con, msg_cb);
+ SSL_set_msg_callback_arg(con, bio_s_msg ? bio_s_msg : bio_s_out);
+ }
+
+ for (;;)
+ {
+ i = BIO_do_handshake(io);
+ if (i > 0)
+ break;
+ if (!BIO_should_retry(io))
+ {
+ BIO_puts(bio_err, "CONNECTION FAILURE\n");
+ ERR_print_errors(bio_err);
+ goto end;
+ }
+ }
+ BIO_printf(bio_err, "CONNECTION ESTABLISHED\n");
+ print_ssl_summary(bio_err, con);
+
+ for (;;)
+ {
+ i=BIO_gets(io,buf,bufsize-1);
+ if (i < 0) /* error */
+ {
+ if (!BIO_should_retry(io))
+ {
+ if (!s_quiet)
+ ERR_print_errors(bio_err);
+ goto err;
+ }
+ else
+ {
+ BIO_printf(bio_s_out,"read R BLOCK\n");
+#if defined(OPENSSL_SYS_NETWARE)
+ delay(1000);
+#elif !defined(OPENSSL_SYS_MSDOS) && !defined(__DJGPP__)
+ sleep(1);
+#endif
+ continue;
+ }
+ }
+ else if (i == 0) /* end of input */
+ {
+ ret=1;
+ BIO_printf(bio_err, "CONNECTION CLOSED\n");
+ goto end;
+ }
+ else
+ {
+ char *p = buf + i - 1;
+ while(i && (*p == '\n' || *p == '\r'))
+ {
+ p--;
+ i--;
+ }
+ if (!s_ign_eof && i == 5 && !strncmp(buf, "CLOSE", 5))
+ {
+ ret = 1;
+ BIO_printf(bio_err, "CONNECTION CLOSED\n");
+ goto end;
+ }
+ BUF_reverse((unsigned char *)buf, NULL, i);
+ buf[i] = '\n';
+ BIO_write(io, buf, i + 1);
+ for (;;)
+ {
+ i = BIO_flush(io);
+ if (i > 0)
+ break;
+ if (!BIO_should_retry(io))
+ goto end;
+ }
+ }
+ }
+end:
+ /* make sure we re-use sessions */
+ SSL_set_shutdown(con,SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN);
+
+err:
+
+ if (buf != NULL) OPENSSL_free(buf);
+ if (io != NULL) BIO_free_all(io);
+ return(ret);
+ }
+
#ifndef OPENSSL_NO_RSA
static RSA MS_CALLBACK *tmp_rsa_cb(SSL *s, int is_export, int keylength)
{
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;
+ unsigned 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));
+
+ SSL_SESSION_get_id(session, &sess->idlen);
+ sess->derlen = i2d_SSL_SESSION(session, NULL);
+
+ sess->id = BUF_memdup(SSL_SESSION_get_id(session, NULL), 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 == (int)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;
+ const unsigned char *id;
+ unsigned int idlen;
+ id = SSL_SESSION_get_id(session, &idlen);
+ 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;
+ }
+