From 1f26946f7e5cf85b20c3b97a8c0e6a869f9a04fa Mon Sep 17 00:00:00 2001
From: Stefan Eissing <stefan.eissing@greenbytes.de>
Date: Thu, 26 Mar 2015 17:46:18 -0400
Subject: [PATCH 15/26] RT3870 Async TLSEXT servername support.

Adds support for processing the servername TLSEXT asynchronously,
using the SSL_WANT_EVENT mechansism.

(cherry picked from commit 58ef21b3d08a17db260b8c8abf058e8935c09218)

Conflicts:
	include/openssl/ssl3.h
	ssl/s3_lib.c
	ssl/s3_srvr.c
	ssl/ssl_locl.h
	ssl/t1_lib.c
---
 doc/ssl/SSL_CTX_set_schedule_task_cb.pod |   4 +
 include/openssl/ssl.h                    |   2 +
 include/openssl/ssl3.h                   |   9 ++
 include/openssl/tls1.h                   |   1 +
 ssl/d1_srvr.c                            |  25 ++++-
 ssl/s3_lib.c                             |   1 +
 ssl/s3_srvr.c                            |  71 ++++++++++++--
 ssl/ssl_locl.h                           |  14 +++
 ssl/t1_lib.c                             |  37 +++++++
 test/Makefile                            |  15 ++-
 test/testtlsext                          |  17 ++++
 test/tlsexttest.c                        | 160 +++++++++++++++++++++++++++++++
 12 files changed, 341 insertions(+), 15 deletions(-)
 create mode 100755 test/testtlsext
 create mode 100644 test/tlsexttest.c

diff --git a/doc/ssl/SSL_CTX_set_schedule_task_cb.pod b/doc/ssl/SSL_CTX_set_schedule_task_cb.pod
index 2fb4a8b..70c5b32 100644
--- a/doc/ssl/SSL_CTX_set_schedule_task_cb.pod
+++ b/doc/ssl/SSL_CTX_set_schedule_task_cb.pod
@@ -113,6 +113,10 @@ The task signs the certificate verification message on the client side.
 
 The task signs the key verification message before it is sent to the client.
 
+=item B<SSL_EVENT_TLSEXT_SERVERNAME_READY>
+
+The task has procesed the servername extension from the client.
+
 =head1 SEE ALSO
 
 L<ssl(3)|ssl(3)>, L<SSL_signal_event(3)|SSL_signal_event(3)>
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 51bb816..6b6bf47 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -917,6 +917,8 @@ __owur int SSL_extension_supported(unsigned int ext_type);
 # define SSL_EVENT_SETUP_CERT_VRFY_DONE   1002
 /* server is siging the message for key exchange */
 # define SSL_EVENT_KEY_EXCH_MSG_SIGNED    1003
+/* tlsext servername has been processed */
+# define SSL_EVENT_TLSEXT_SERVERNAME_READY 1004
 
 /*
  * These will only be used when doing non-blocking IO or asynchronous
diff --git a/include/openssl/ssl3.h b/include/openssl/ssl3.h
index ef7d92b..db5d04f 100644
--- a/include/openssl/ssl3.h
+++ b/include/openssl/ssl3.h
@@ -512,6 +512,15 @@ extern "C" {
 # define SSL3_CHANGE_CIPHER_CLIENT_READ  (SSL3_CC_CLIENT|SSL3_CC_READ)
 # define SSL3_CHANGE_CIPHER_SERVER_WRITE (SSL3_CC_SERVER|SSL3_CC_WRITE)
 
+# define SSL3_ST_SUB_0		(0x00)
+# define SSL3_ST_SUB_1		(0x01)
+# define SSL3_ST_SUB_2		(0x02)
+# define SSL3_ST_SUB_3		(0x03)
+# define SSL3_ST_SUB_4		(0x04)
+# define SSL3_ST_SUB_5		(0x05)
+# define SSL3_ST_SUB_6		(0x06)
+# define SSL3_ST_SUB_7		(0x07)
+
 #ifdef  __cplusplus
 }
 #endif
diff --git a/include/openssl/tls1.h b/include/openssl/tls1.h
index 40205e1..beb9991 100644
--- a/include/openssl/tls1.h
+++ b/include/openssl/tls1.h
@@ -374,6 +374,7 @@ SSL_CTX_callback_ctrl(ctx,SSL_CTRL_SET_TLSEXT_SERVERNAME_CB,(void (*)(void))cb)
 # define SSL_TLSEXT_ERR_ALERT_WARNING 1
 # define SSL_TLSEXT_ERR_ALERT_FATAL 2
 # define SSL_TLSEXT_ERR_NOACK 3
+# define SSL_TLSEXT_ERR_WAIT_FOR_EVENT 4
 
 # define SSL_CTX_set_tlsext_servername_arg(ctx, arg) \
 SSL_CTX_ctrl(ctx,SSL_CTRL_SET_TLSEXT_SERVERNAME_ARG,0, (void *)arg)
diff --git a/ssl/d1_srvr.c b/ssl/d1_srvr.c
index 0f2cf45..7d24c6b 100644
--- a/ssl/d1_srvr.c
+++ b/ssl/d1_srvr.c
@@ -313,13 +313,30 @@ int dtls1_accept(SSL *s)
             break;
 
         case SSL3_ST_SR_CLNT_HELLO_A:
+            s->s3->tmp.sub_state = SSL3_ST_SUB_0;
         case SSL3_ST_SR_CLNT_HELLO_B:
         case SSL3_ST_SR_CLNT_HELLO_C:
 
-            s->shutdown = 0;
-            ret = ssl3_get_client_hello(s);
-            if (ret <= 0)
-                goto end;
+            if (s->s3->tmp.sub_state == SSL3_ST_SUB_0) {
+                s->shutdown = 0;
+                ret = ssl3_get_client_hello(s);
+                if (ret <= 0)
+                    goto end;
+                s->s3->tmp.sub_state = SSL3_ST_SUB_1;
+            }
+            if (s->s3->tmp.sub_state == SSL3_ST_SUB_1) {
+                s->s3->tmp.sub_state = SSL3_ST_SUB_2;
+                ret = ssl_check_clienthello_tlsext_async(s);
+                if (ret <= 0)
+                    goto end;
+            }
+            if (s->s3->tmp.sub_state == SSL3_ST_SUB_2) {
+                if (!ssl_event_did_succeed(s, SSL_EVENT_TLSEXT_SERVERNAME_READY, &ret))
+                    goto end;
+                ret = ssl3_get_client_hello_post_app(s, 0);
+                if (ret <= 0)
+                    goto end;
+            }
             dtls1_stop_timer(s);
 
             if (ret == 1 && (SSL_get_options(s) & SSL_OP_COOKIE_EXCHANGE))
diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c
index 4f2b9a8..97fc229 100644
--- a/ssl/s3_lib.c
+++ b/ssl/s3_lib.c
@@ -2913,6 +2913,7 @@ void ssl3_free(SSL *s)
 #ifndef OPENSSL_NO_SRP
     SSL_SRP_CTX_free(s);
 #endif
+    sk_SSL_CIPHER_free(s->s3->tmp.ciphers);
     OPENSSL_clear_free(s->s3, sizeof(*s->s3));
     s->s3 = NULL;
 }
diff --git a/ssl/s3_srvr.c b/ssl/s3_srvr.c
index e731ca7..ce6fb1a 100644
--- a/ssl/s3_srvr.c
+++ b/ssl/s3_srvr.c
@@ -343,14 +343,35 @@ int ssl3_accept(SSL *s)
             break;
 
         case SSL3_ST_SR_CLNT_HELLO_A:
+            s->s3->tmp.sub_state = SSL3_ST_SUB_0;
         case SSL3_ST_SR_CLNT_HELLO_B:
         case SSL3_ST_SR_CLNT_HELLO_C:
 
-            ret = ssl3_get_client_hello(s);
-            if (ret <= 0)
-                goto end;
+            if (s->s3->tmp.sub_state == SSL3_ST_SUB_0) {
+                s->shutdown = 0;
+                ret = ssl3_get_client_hello(s);
+                if (ret <= 0)
+                    goto end;
+                s->s3->tmp.sub_state = SSL3_ST_SUB_1;
+            }
+            if (s->s3->tmp.sub_state == SSL3_ST_SUB_1) {
+                s->s3->tmp.sub_state = SSL3_ST_SUB_2;
+                ret = ssl_check_clienthello_tlsext_async(s);
+                if (ret <= 0)
+                    goto end;
+            }
+            if (s->s3->tmp.sub_state == SSL3_ST_SUB_2) {
+                if (!ssl_event_did_succeed(s, SSL_EVENT_TLSEXT_SERVERNAME_READY, &ret)) {
+                    goto end;
+                }
+                ret = ssl3_get_client_hello_post_app(s, 0);
+                if (ret <= 0)
+                    goto end;
+                s->s3->tmp.sub_state = SSL3_ST_SUB_3;
+            }
 #ifndef OPENSSL_NO_SRP
-            s->state = SSL3_ST_SR_CLNT_HELLO_D;
+            if (s->s3->tmp.sub_state == SSL3_ST_SUB_3)
+                s->state = SSL3_ST_SR_CLNT_HELLO_D;
         case SSL3_ST_SR_CLNT_HELLO_D:
             {
                 int al;
@@ -886,13 +907,14 @@ int ssl3_get_client_hello(SSL *s)
     SSL_CIPHER *c;
 #ifndef OPENSSL_NO_COMP
     unsigned char *q = NULL;
-    SSL_COMP *comp = NULL;
 #endif
     STACK_OF(SSL_CIPHER) *ciphers = NULL;
     int protverr = 1;
 
-    if (s->state == SSL3_ST_SR_CLNT_HELLO_C && !s->first_packet)
-        goto retry_cert;
+    if (s->state == SSL3_ST_SR_CLNT_HELLO_C && !s->first_packet) {
+        /* head over to post_app and goto retry_cert there */
+        return ssl3_get_client_hello_post_app(s, 1);
+    }
 
     /*
      * We do this so that we will respond with our native type. If we are
@@ -1356,6 +1378,13 @@ int ssl3_get_client_hello(SSL *s)
         }
     }
 
+    /* ssl3_get_client_hello_post_app will need these when called. */
+    if (ciphers != NULL)
+        s->s3->tmp.ciphers = sk_SSL_CIPHER_dup(ciphers);
+    s->s3->tmp.q = q;
+    s->s3->tmp.i = i;
+    s->s3->tmp.complen = complen;
+
     /* TLS extensions */
     if (s->version >= SSL3_VERSION) {
         if (!ssl_parse_clienthello_tlsext(s, &p, d, n)) {
@@ -1363,7 +1392,35 @@ int ssl3_get_client_hello(SSL *s)
             goto err;
         }
     }
+    if (ret < 0)
+        ret = 1;
+    if (0) {
+ f_err:
+        ssl3_send_alert(s, SSL3_AL_FATAL, al);
+    }
+ err:
+    sk_SSL_CIPHER_free(ciphers);
+    return ret < 0 ? -1 : ret;
+}
 
+int ssl3_get_client_hello_post_app(SSL *s, int retry_cert)
+{
+    int al = SSL_AD_INTERNAL_ERROR, ret= -1;
+    int complen = s->s3->tmp.complen;
+    unsigned char *q = s->s3->tmp.q;
+    SSL_CIPHER *c;
+    STACK_OF(SSL_CIPHER) *ciphers = s->s3->tmp.ciphers;
+
+#ifndef OPENSSL_NO_COMP
+    SSL_COMP *comp = NULL;
+#endif
+
+    /* we have already copied tmp_ciphers into ciphers. */
+    s->s3->tmp.ciphers = NULL;
+    if (retry_cert) {
+        /* we likely came through the regular function in this state */
+        goto retry_cert;
+    }
     /*
      * Check if we want to use external pre-shared secret for this handshake
      * for not reused session only. We need to generate server_random before
diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h
index 2c2c25d..7077a3b 100644
--- a/ssl/ssl_locl.h
+++ b/ssl/ssl_locl.h
@@ -1351,6 +1351,18 @@ typedef struct ssl3_state_st {
         unsigned long mask_ssl;
 
         int skip_client_verify;
+        /* managing micro state inside SSL3_ST_xxx states */
+        int sub_state;
+        /*
+         * Next three variables are temporary variables that store various information
+         * between call to ssl3_get_client_hello and ssl3_get_client_hello_post_app.
+         * I couldn't figure out a better way to break ssl3_get_client_hello into
+         * two parts without having to store this information.
+         */
+        STACK_OF(SSL_CIPHER) *ciphers;
+        unsigned char *q;
+        int i;
+        int complen;
     } tmp;
 
     /* Connection binding to prevent renegotiation attacks */
@@ -2104,6 +2116,7 @@ int dtls1_client_hello(SSL *s);
 
 /* some server-only functions */
 __owur int ssl3_get_client_hello(SSL *s);
+__owur int ssl3_get_client_hello_post_app(SSL *s, int retry_cert);
 __owur int ssl3_send_server_hello(SSL *s);
 __owur int ssl3_send_hello_request(SSL *s);
 __owur int ssl3_send_server_key_exchange(SSL *s);
@@ -2179,6 +2192,7 @@ __owur unsigned char *ssl_add_serverhello_tlsext(SSL *s, unsigned char *buf,
 __owur int ssl_parse_clienthello_tlsext(SSL *s, unsigned char **data,
                                  unsigned char *d, int n);
 __owur int tls1_set_server_sigalgs(SSL *s);
+__owur int ssl_check_clienthello_tlsext_async(SSL *s);
 __owur int ssl_check_clienthello_tlsext_late(SSL *s);
 __owur int ssl_parse_serverhello_tlsext(SSL *s, unsigned char **data,
                                  unsigned char *d, int n);
diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c
index b059c12..b3550ba 100644
--- a/ssl/t1_lib.c
+++ b/ssl/t1_lib.c
@@ -2693,6 +2693,43 @@ static void ssl_set_default_md(SSL *s)
 #endif
 }
 
+int ssl_check_clienthello_tlsext_async(SSL *s)
+{
+    int ret = SSL_TLSEXT_ERR_NOACK;
+    int al = SSL_AD_UNRECOGNIZED_NAME;
+
+    s->rwstate = SSL_EVENT_TLSEXT_SERVERNAME_READY;
+    if (s->ctx != NULL && s->ctx->tlsext_servername_callback != 0)
+        ret =
+            s->ctx->tlsext_servername_callback(s, &al,
+                                               s->ctx->tlsext_servername_arg);
+    else if (s->initial_ctx != NULL
+             && s->initial_ctx->tlsext_servername_callback != 0)
+        ret =
+            s->initial_ctx->tlsext_servername_callback(s, &al,
+                                                       s->
+                                                       initial_ctx->tlsext_servername_arg);
+    if (ret == SSL_TLSEXT_ERR_WAIT_FOR_EVENT)
+        /* we shall wait for event SSL_EVENT_TLSEXT_SERVERNAME_READY */
+        return -1;
+    SSL_signal_event(s, SSL_EVENT_TLSEXT_SERVERNAME_READY, 1);
+
+    switch (ret) {
+    case SSL_TLSEXT_ERR_ALERT_FATAL:
+        ssl3_send_alert(s, SSL3_AL_FATAL, al);
+        return 0;
+
+    case SSL_TLSEXT_ERR_ALERT_WARNING:
+        ssl3_send_alert(s, SSL3_AL_WARNING, al);
+        return 1;
+
+    case SSL_TLSEXT_ERR_NOACK:
+        s->servername_done = 0;
+    default:
+        return 1;
+    }
+}
+
 int tls1_set_server_sigalgs(SSL *s)
 {
     int al;
diff --git a/test/Makefile b/test/Makefile
index 6880d20..bef9f0a 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -68,6 +68,7 @@ V3NAMETEST=	v3nametest
 HEARTBEATTEST=  heartbeat_test
 CONSTTIMETEST=  constant_time_test
 TASKTEST=       tasktest
+TLSEXTTEST=     tlsexttest
 
 TESTS=		alltests
 
@@ -84,7 +85,7 @@ EXE=	$(BNTEST)$(EXE_EXT) $(ECTEST)$(EXE_EXT)  $(ECDSATEST)$(EXE_EXT) $(ECDHTEST)
 	$(JPAKETEST)$(EXE_EXT) $(SRPTEST)$(EXE_EXT) $(V3NAMETEST)$(EXE_EXT) \
 	$(HEARTBEATTEST)$(EXE_EXT) $(P5_CRPT2_TEST)$(EXE_EXT) \
 	$(CONSTTIMETEST)$(EXE_EXT) \
-	$(TASKTEST)$(EXE_EXT)
+	$(TASKTEST)$(EXE_EXT) $(TLSEXTTEST)$(EXE_EXT)
 
 # $(METHTEST)$(EXE_EXT)
 
@@ -99,7 +100,7 @@ OBJ=	$(BNTEST).o $(ECTEST).o  $(ECDSATEST).o $(ECDHTEST).o $(IDEATEST).o \
 	$(EVPTEST).o $(EVPEXTRATEST).o $(IGETEST).o $(JPAKETEST).o $(V3NAMETEST).o \
 	$(GOST2814789TEST).o $(HEARTBEATTEST).o $(P5_CRPT2_TEST).o \
 	$(CONSTTIMETEST).o testutil.o \
-	$(TASKTEST).o
+	$(TASKTEST).o $(TLSEXTTEST).o
 
 SRC=	$(BNTEST).c $(ECTEST).c  $(ECDSATEST).c $(ECDHTEST).c $(IDEATEST).c \
 	$(MD2TEST).c  $(MD4TEST).c $(MD5TEST).c \
@@ -111,7 +112,7 @@ SRC=	$(BNTEST).c $(ECTEST).c  $(ECDSATEST).c $(ECDHTEST).c $(IDEATEST).c \
 	$(EVPTEST).c $(EVPEXTRATEST).c $(IGETEST).c $(JPAKETEST).c $(V3NAMETEST).c \
 	$(GOST2814789TEST).c $(HEARTBEATTEST).c $(P5_CRPT2_TEST).c \
 	$(CONSTTIMETEST).c testutil.c \
-	$(TASKTEST).c
+	$(TASKTEST).c $(TLSEXTTEST).c
 
 HEADER=	testutil.h
 
@@ -152,7 +153,7 @@ alltests: \
 	test_ige test_jpake test_srp test_cms test_v3name test_ocsp \
 	test_gost2814789 test_heartbeat test_p5_crpt2 \
 	test_constant_time \
-	test_task
+	test_task test_tlsext
 
 test_evp: $(EVPTEST)$(EXE_EXT) evptests.txt
 	@echo $(START) $@
@@ -400,6 +401,9 @@ test_constant_time: $(CONSTTIMETEST)$(EXE_EXT)
 test_task: $(TASKTEST)$(EXE_EXT) testtask
 	@sh ./testtask
 
+test_tlsext: $(TLSEXTTEST)$(EXE_EXT) testtlsext
+	@sh ./testtlsext
+
 update: local_depend
 	@if [ -z "$(THIS)" ]; then $(MAKE) -f $(TOP)/Makefile reflect THIS=$@; fi
 
@@ -588,6 +592,9 @@ $(CONSTTIMETEST)$(EXE_EXT): $(CONSTTIMETEST).o
 $(TASKTEST)$(EXE_EXT): $(TASKTEST).o
 	@target=$(TASKTEST) $(BUILD_CMD)
 
+$(TLSEXTTEST)$(EXE_EXT): $(TLSEXTTEST).o
+	@target=$(TLSEXTTEST) $(BUILD_CMD)
+
 #$(AESTEST).o: $(AESTEST).c
 #	$(CC) -c $(CFLAGS) -DINTERMEDIATE_VALUE_KAT -DTRACE_KAT_MCT $(AESTEST).c
 
diff --git a/test/testtlsext b/test/testtlsext
new file mode 100755
index 0000000..65fdf7a
--- /dev/null
+++ b/test/testtlsext
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+tlsexttest="../util/shlib_wrap.sh ./tlsexttest"
+
+#############################################################################
+
+echo Test TLSEXT Callbacks
+if [ ! -d gen ]; then
+    ./gen_cert.sh server
+fi
+
+# test openssl tlsext callback extension
+$tlsexttest --tlsv1_2 || echo 1
+$tlsexttest --tlsv1_1 || echo 1
+$tlsexttest --sslv3   || echo 1
+
+exit 0
diff --git a/test/tlsexttest.c b/test/tlsexttest.c
new file mode 100644
index 0000000..0ce859c
--- /dev/null
+++ b/test/tlsexttest.c
@@ -0,0 +1,160 @@
+/* test/tlsexttest.c */
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+/*
+ * Copyright (C) 2015 Akamai Technologies. ALL RIGHTS RESERVED.
+ * This code was originally developed by Akamai Technologies and
+ * contributed to the OpenSSL project under the terms of the Corporate
+ * Contributor License Agreement v1.0
+ */
+#include "testharness.c"
+
+static const int SLEN = 1024;
+
+static int invocations;
+
+static int dummy_cb(SSL *s, int *al, void *arg)
+{
+    ++invocations;
+    return (SSL_TLSEXT_ERR_OK);
+}
+
+static int fail_cb(SSL *s, int *al, void *arg)
+{
+    ++invocations;
+    *al = 123;
+    return (SSL_TLSEXT_ERR_ALERT_FATAL);
+}
+
+static int warn_cb(SSL *s, int *al, void *arg)
+{
+    ++invocations;
+    *al = 123;
+    return (SSL_TLSEXT_ERR_ALERT_WARNING);
+}
+
+static void *delay_task2(void *c)
+{
+    SSL *s = (SSL *)c;
+	
+    usleep(250000);
+    ++invocations;
+    SSL_signal_event(s, SSL_EVENT_TLSEXT_SERVERNAME_READY, 17);
+    return (NULL);
+}
+
+static pthread_t worker;
+/* Executes task asynchrounously in separate thread */
+static int async_cb(SSL *s, int *al, void *arg)
+{
+    int rc = pthread_create(&worker, NULL, delay_task2, (void *)s);
+    if (rc) {
+        fprintf(stderr, "ERROR; return code from pthread_create() is %d\n", rc);
+        return (-1);
+    }
+    return (SSL_TLSEXT_ERR_WAIT_FOR_EVENT);
+}
+
+
+static int test_server(TestContext *tctx, const char *variant, int (*cb)(SSL*, int*, void*))
+{
+    char buffer[SLEN];
+    snprintf(buffer, SLEN, "test_task_cb(server, %s)", variant);
+    TESTCASE(tctx, buffer);
+	
+    invocations = 0;
+    SSL_CTX_set_tlsext_servername_callback(tctx->s_ctx, cb);
+	
+    SSL *s_ssl = SSL_new(tctx->s_ctx);
+    SSL *c_ssl = SSL_new(tctx->c_ctx);
+	
+    if (!strcmp("fail", variant))
+        (void)TESTASSERT(tctx, chatter(tctx, s_ssl, c_ssl, 1024) != 0, "data transfer failed");
+    else
+        (void)TESTASSERT(tctx, chatter(tctx, s_ssl, c_ssl, 1024) == 0, "data transfered");
+	
+    SSL_free(s_ssl);
+    SSL_free(c_ssl);
+    (void)TESTEQINT(tctx, 1, invocations, "unexpected invocations");
+	
+    SSL_CTX_set_tlsext_servername_callback(tctx->s_ctx, NULL);
+    return (tctx->failed);
+}
+
+int test_setup(void)
+{
+    return (1);
+}
+
+int test_run(TestContext *tctx)
+{
+    int ret = 0;
+	
+    if (strcmp("sslv2", tctx->protocol) == 0)
+        ;
+    else if (strcmp("sslv3", tctx->protocol) == 0)
+        ;
+    else {
+        ret |= TESTRUN(tctx, test_server(tctx, "dummy", dummy_cb));
+        ret |= TESTRUN(tctx, test_server(tctx, "warn", warn_cb));
+        BIO_printf(tctx->bio_err, "...error will be generated\n");
+        ret |= TESTRUN(tctx, test_server(tctx, "fail", fail_cb));
+        ret |= TESTRUN(tctx, test_server(tctx, "async", async_cb));
+    }
+	
+    return (ret);
+}
-- 
2.3.2 (Apple Git-55)

