BIO pairs.
authorBodo Möller <bodo@openssl.org>
Sat, 12 Jun 1999 01:03:40 +0000 (01:03 +0000)
committerBodo Möller <bodo@openssl.org>
Sat, 12 Jun 1999 01:03:40 +0000 (01:03 +0000)
CHANGES
crypto/bio/bio.h
crypto/bio/bio_err.c
crypto/bio/bio_lib.c
crypto/bio/bss_bio.c
ms/test.bat
ssl/s3_clnt.c
ssl/ssl_stat.c
ssl/ssltest.c
test/testssl

diff --git a/CHANGES b/CHANGES
index 7e9e420..8e71a6a 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -4,6 +4,16 @@
 
  Changes between 0.9.3a and 0.9.4
 
+  *) Add missing case to s3_clnt.c state machine -- one of the new SSL tests
+     through a BIO pair triggered the default case, i.e.
+     SSLerr(...,SSL_R_UNKNOWN_STATE).
+     [Bodo Moeller]
+
+  *) New "BIO pair" concept (crypto/bio/bss_bio.c) so that applications
+     can use the SSL library even if none of the specific BIOs is
+     appropriate.
+     [Bodo Moeller]
+
   *) Fix a bug in i2d_DSAPublicKey() which meant it returned the wrong value
      for the encoded length.
      [Jeon KyoungHo <khjeon@sds.samsung.co.kr>]
index f75e77b..5d1654a 100644 (file)
@@ -64,6 +64,7 @@ extern "C" {
 #endif
 
 #include <stdio.h>
+#include <stdlib.h>
 #include <openssl/crypto.h>
 
 /* These are the 'types' of BIOs */
@@ -317,6 +318,15 @@ typedef struct bio_f_buffer_ctx_struct
 #define BIO_C_GET_SOCKS                                134
 #define BIO_C_SET_SOCKS                                135
 
+#define BIO_C_SET_WRITE_BUF_SIZE               136/* for BIO_s_bio */
+#define BIO_C_GET_WRITE_BUF_SIZE               137
+#define BIO_C_MAKE_BIO_PAIR                    138
+#define BIO_C_DESTROY_BIO_PAIR                 139
+#define BIO_C_GET_WRITE_GUARANTEE              140
+#define BIO_C_GET_READ_REQUEST                 141
+#define BIO_C_SHUTDOWN_WR                      142
+
+
 #define BIO_set_app_data(s,arg)                BIO_set_ex_data(s,0,(char *)arg)
 #define BIO_get_app_data(s)            BIO_get_ex_data(s,0)
 
@@ -431,6 +441,9 @@ int BIO_read_filename(BIO *b,const char *name);
 #define BIO_get_close(b)       (int)BIO_ctrl(b,BIO_CTRL_GET_CLOSE,0,NULL)
 #define BIO_pending(b)         (int)BIO_ctrl(b,BIO_CTRL_PENDING,0,NULL)
 #define BIO_wpending(b)                (int)BIO_ctrl(b,BIO_CTRL_WPENDING,0,NULL)
+/* ...pending macros have inappropriate return type */
+size_t BIO_ctrl_pending(BIO *b);
+size_t BIO_ctrl_wpending(BIO *b);
 #define BIO_flush(b)           (int)BIO_ctrl(b,BIO_CTRL_FLUSH,0,NULL)
 #define BIO_get_info_callback(b,cbp) (int)BIO_ctrl(b,BIO_CTRL_GET_CALLBACK,0,(char *)cbp)
 #define BIO_set_info_callback(b,cb) (int)BIO_ctrl(b,BIO_CTRL_SET_CALLBACK,0,(char *)cb)
@@ -438,6 +451,19 @@ int BIO_read_filename(BIO *b,const char *name);
 /* For the BIO_f_buffer() type */
 #define BIO_buffer_get_num_lines(b) BIO_ctrl(b,BIO_CTRL_GET,0,NULL)
 
+/* For BIO_s_bio() */
+#define BIO_set_write_buf_size(b,size) (int)BIO_ctrl(b,BIO_C_SET_WRITE_BUF_SIZE,size,NULL)
+#define BIO_get_write_buf_size(b,size) (size_t)BIO_ctrl(b,BIO_C_GET_WRITE_BUF_SIZE,size,NULL)
+#define BIO_make_bio_pair(b1,b2)   (int)BIO_ctrl(b1,BIO_C_MAKE_BIO_PAIR,0,b2)
+#define BIO_destroy_bio_pair(b)    (int)BIO_ctrl(b,BIO_C_DESTROY_BIO_PAIR,0,NULL)
+/* macros with inappropriate type -- but ...pending macros use int too: */
+#define BIO_get_write_guarantee(b) (int)BIO_ctrl(b,BIO_C_GET_WRITE_GUARANTEE,0,NULL)
+#define BIO_get_read_request(b)    (int)BIO_ctrl(b,BIO_C_GET_READ_REQUEST,0,NULL)
+size_t BIO_ctrl_get_write_guarantee(BIO *b);
+size_t BIO_ctrl_get_read_request(BIO *b);
+
+
+
 #ifdef NO_STDIO
 #define NO_FP_API
 #endif
@@ -473,7 +499,7 @@ int BIO_read(BIO *b, void *data, int len);
 int    BIO_gets(BIO *bp,char *buf, int size);
 int    BIO_write(BIO *b, const char *data, int len);
 int    BIO_puts(BIO *bp,const char *buf);
-long   BIO_ctrl(BIO *bp,int cmd,long larg,char *parg);
+long   BIO_ctrl(BIO *bp,int cmd,long larg,void *parg);
 char * BIO_ptr_ctrl(BIO *bp,int cmd,long larg);
 long   BIO_int_ctrl(BIO *bp,int cmd,long larg,int iarg);
 BIO *  BIO_push(BIO *b,BIO *append);
@@ -538,6 +564,13 @@ BIO *BIO_new_fd(int fd, int close_flag);
 BIO *BIO_new_connect(char *host_port);
 BIO *BIO_new_accept(char *host_port);
 
+int BIO_new_bio_pair(BIO **bio1, size_t writebuf1,
+       BIO **bio2, size_t writebuf2);
+/* If successful, returns 1 and in *bio1, *bio2 two BIO pair endpoints.
+ * Otherwise returns 0 and sets *bio1 and *bio2 to NULL.
+ * Size 0 uses default value.
+ */
+
 void BIO_copy_next_retry(BIO *b);
 
 long BIO_ghbn_ctrl(int cmd,int iarg,char *parg);
@@ -579,6 +612,7 @@ int BIO_printf(BIO *bio, ...);
 #define BIO_R_ACCEPT_ERROR                              100
 #define BIO_R_BAD_FOPEN_MODE                            101
 #define BIO_R_BAD_HOSTNAME_LOOKUP                       102
+#define BIO_R_BROKEN_PIPE                               124
 #define BIO_R_CONNECT_ERROR                             103
 #define BIO_R_ERROR_SETTING_NBIO                        104
 #define BIO_R_ERROR_SETTING_NBIO_ON_ACCEPTED_SOCKET     105
index badbfc1..e31bf32 100644 (file)
@@ -95,6 +95,7 @@ static ERR_STRING_DATA BIO_str_reasons[]=
 {BIO_R_ACCEPT_ERROR                      ,"accept error"},
 {BIO_R_BAD_FOPEN_MODE                    ,"bad fopen mode"},
 {BIO_R_BAD_HOSTNAME_LOOKUP               ,"bad hostname lookup"},
+{BIO_R_BROKEN_PIPE                       ,"broken pipe"},
 {BIO_R_CONNECT_ERROR                     ,"connect error"},
 {BIO_R_ERROR_SETTING_NBIO                ,"error setting nbio"},
 {BIO_R_ERROR_SETTING_NBIO_ON_ACCEPTED_SOCKET,"error setting nbio on accepted socket"},
index 62d2699..b72688e 100644 (file)
@@ -290,7 +290,7 @@ char *BIO_ptr_ctrl(BIO *b, int cmd, long larg)
                return(p);
        }
 
-long BIO_ctrl(BIO *b, int cmd, long larg, char *parg)
+long BIO_ctrl(BIO *b, int cmd, long larg, void *parg)
        {
        long ret;
        long (*cb)();
@@ -317,6 +317,20 @@ long BIO_ctrl(BIO *b, int cmd, long larg, char *parg)
        return(ret);
        }
 
+/* It is unfortunate to duplicate in functions what the BIO_(w)pending macros
+ * do; but those macros have inappropriate return type, and for interfacing
+ * from other programming languages, C macros aren't much of a help anyway. */
+size_t BIO_ctrl_pending(BIO *bio)
+    {
+       return BIO_ctrl(bio, BIO_CTRL_PENDING, 0, NULL);
+       }
+
+size_t BIO_ctrl_wpending(BIO *bio)
+    {
+       return BIO_ctrl(bio, BIO_CTRL_WPENDING, 0, NULL);
+       }
+
+
 /* put the 'bio' on the end of b's list of operators */
 BIO *BIO_push(BIO *b, BIO *bio)
        {
index f026d51..ea9d5c7 100644 (file)
@@ -1,11 +1,11 @@
 /* crypto/bio/bss_bio.c  -*- Mode: C; c-file-style: "eay" -*- */
 
-/*  *** Not yet finished (or even tested). *** */
-
 /* Special method for a BIO where the other endpoint is also a BIO
- * of this kind, handled by the same thread.
+ * of this kind, handled by the same thread (i.e. the "peer" is actually
+ * ourselves, wearing a different hat).
  * Such "BIO pairs" are mainly for using the SSL library with I/O interfaces
- * for which no specific BIO method is available. */
+ * for which no specific BIO method is available.
+ * See ssl/ssltest.c for some hints on how this can be used. */
 
 #include <assert.h>
 #include <stdlib.h>
@@ -19,7 +19,7 @@ static int bio_new(BIO *bio);
 static int bio_free(BIO *bio);
 static int bio_read(BIO *bio, char *buf, int size);
 static int bio_write(BIO *bio, char *buf, int num);
-static long bio_ctrl(BIO *bio, int cmd, long num, char *ptr);
+static long bio_ctrl(BIO *bio, int cmd, long num, void *ptr);
 static int bio_puts(BIO *bio, char *str);
 
 static int bio_make_pair(BIO *bio1, BIO *bio2);
@@ -74,6 +74,7 @@ static int bio_new(BIO *bio)
        b->size = 17*1024; /* enough for one TLS record (just a default) */
        b->buf = NULL;
 
+       bio->ptr = b;
        return 1;
        }
 
@@ -103,19 +104,167 @@ static int bio_free(BIO *bio)
 
 
 
-static int bio_read(BIO *bio, char *buf, int size)
+static int bio_read(BIO *bio, char *buf, int size_)
        {
-       /* XXX */
-       return -1;
+       size_t size = size_;
+       size_t rest;
+       struct bio_bio_st *b, *peer_b;
+
+       BIO_clear_retry_flags(bio);
+
+       if (!bio->init)
+               return 0;
+
+       b = bio->ptr;
+       assert(b != NULL);
+       assert(b->peer != NULL);
+       peer_b = b->peer->ptr;
+       assert(peer_b != NULL);
+       assert(peer_b->buf != NULL);
+
+       peer_b->request = 0; /* will be set in "retry_read" situation */
+
+       if (buf == NULL || size == 0)
+               return 0;
+
+       if (peer_b->len == 0)
+               {
+               if (peer_b->closed)
+                       return 0; /* writer has closed, and no data is left */
+               else
+                       {
+                       BIO_set_retry_read(bio); /* buffer is empty */
+                       if (size <= peer_b->size)
+                               peer_b->request = size;
+                       else
+                               peer_b->request = peer_b->size; /* don't ask for more than
+                                                                * the peer can deliver
+                                                                * in one write */
+                       return -1;
+                       }
+               }
+
+       /* we can read */
+       if (peer_b->len < size)
+               size = peer_b->len;
+
+       /* now read "size" bytes */
+       
+       rest = size;
+       
+       assert(rest > 0);
+       do /* one or two iterations */
+               {
+               size_t chunk;
+               
+               assert(rest <= peer_b->len);
+               if (peer_b->offset + rest <= peer_b->size)
+                       chunk = rest;
+               else
+                       /* wrap around ring buffer */
+                       chunk = peer_b->size - peer_b->offset;
+               assert(peer_b->offset + chunk <= peer_b->size);
+               
+               memcpy(buf, peer_b->buf + peer_b->offset, chunk);
+               
+               peer_b->len -= chunk;
+               if (peer_b->len)
+                       {
+                       peer_b->offset += chunk;
+                       assert(peer_b->offset <= peer_b->size);
+                       if (peer_b->offset == peer_b->size)
+                               peer_b->offset = 0;
+                       buf += chunk;
+                       }
+               else
+                       {
+                       /* buffer now empty, no need to advance "buf" */
+                       assert(chunk == rest);
+                       peer_b->offset = 0;
+                       }
+               rest -= chunk;
+               }
+       while (rest);
+       
+       peer_b->request -= size;
+       return size;
        }
 
-static int bio_write(BIO *bio, char *buf, int num)
+static int bio_write(BIO *bio, char *buf, int num_)
        {
-       /* XXX */
-       return -1;
-       }
+       size_t num = num_;
+       size_t rest;
+       struct bio_bio_st *b;
+
+       BIO_clear_retry_flags(bio);
+
+       if (!bio->init || buf == NULL || num == 0)
+               return 0;
+
+       b = bio->ptr;           
+       assert(b != NULL);
+       assert(b->peer != NULL);
+       assert(b->buf != NULL);
+
+       b->request = 0;
+       if (b->closed)
+               {
+               /* we already closed */
+               BIOerr(BIO_F_BIO_WRITE, BIO_R_BROKEN_PIPE);
+               return -1;
+               }
+
+       assert(b->len <= b->size);
+
+       if (b->len == b->size)
+               {
+               BIO_set_retry_write(bio); /* buffer is full */
+               return -1;
+               }
+
+       /* we can write */
+       if (num > b->size - b->len)
+               num = b->size - b->len;
+       
+       /* now write "num" bytes */
+
+       rest = num;
        
-static long bio_ctrl(BIO *bio, int cmd, long num, char *ptr)
+       assert(rest > 0);
+       do /* one or two iterations */
+               {
+               size_t write_offset;
+               size_t chunk;
+
+               assert(b->len + rest <= b->size);
+
+               write_offset = b->offset + b->len;
+               if (write_offset >= b->size)
+                       write_offset -= b->size;
+               /* b->buf[write_offset] is the first byte we can write to. */
+
+               if (write_offset + rest <= b->size)
+                       chunk = rest;
+               else
+                       /* wrap around ring buffer */
+                       chunk = b->size - write_offset;
+               
+               memcpy(b->buf + write_offset, buf, chunk);
+               
+               b->len += chunk;
+
+               assert(b->len <= b->size);
+               
+               rest -= chunk;
+               buf += chunk;
+               }
+       while (rest);
+
+       return num;
+       }
+
+
+static long bio_ctrl(BIO *bio, int cmd, long num, void *ptr)
        {
        long ret;
        struct bio_bio_st *b = bio->ptr;
@@ -124,13 +273,76 @@ static long bio_ctrl(BIO *bio, int cmd, long num, char *ptr)
 
        switch (cmd)
                {
-               /* XXX Additional commands: */
-               /* - Set buffer size */
-               /* - make pair */
-               /* - destroy pair */
-               /* - get number of bytes that the next write will accept */
-               /* - get number of bytes requested by peer */
-               /* - send "close" */
+       /* specific CTRL codes */
+
+       case BIO_C_SET_WRITE_BUF_SIZE:
+               if (b->peer)
+                       {
+                       BIOerr(BIO_F_BIO_CTRL, BIO_R_IN_USE);
+                       ret = 0;
+                       }
+               else
+                       {
+                       size_t new_size = num;
+
+                       if (b->size != new_size)
+                               {
+                               if (b->buf) 
+                                       {
+                                       Free(b->buf);
+                                       b->buf = NULL;
+                                       }
+                               b->size = new_size;
+                               }
+                       ret = 1;
+                       }
+               break;
+
+       case BIO_C_GET_WRITE_BUF_SIZE:
+               num = (long) b->size;
+
+       case BIO_C_MAKE_BIO_PAIR:
+               {
+               BIO *other_bio = ptr;
+               
+               if (bio_make_pair(bio, other_bio))
+                       ret = 1;
+               else
+                       ret = 0;
+               }
+               break;
+               
+       case BIO_C_DESTROY_BIO_PAIR:
+               /* Effects both BIOs in the pair -- call just once!
+                * Or let BIO_free(bio1); BIO_free(bio2); do the job. */
+               bio_destroy_pair(bio);
+               ret = 1;
+               break;
+
+       case BIO_C_GET_WRITE_GUARANTEE:
+               /* How many bytes can the caller feed to the next write
+                * withouth having to keep any? */
+               if (b->peer == NULL || b->closed)
+                       ret = 0;
+               else
+                       ret = (long) b->size - b->len;
+               break;
+
+       case BIO_C_GET_READ_REQUEST:
+               /* If the peer unsuccesfully tried to read, how many bytes
+                * were requested?  (As with BIO_CTRL_PENDING, that number
+                * can usually be treated as boolean.) */
+               ret = (long) b->request;
+               break;
+
+       case BIO_C_SHUTDOWN_WR:
+               /* similar to shutdown(..., SHUT_WR) */
+               b->closed = 1;
+               ret = 1;
+               break;
+
+
+       /* standard CTRL codes follow */
 
        case BIO_CTRL_RESET:
                if (b->buf != NULL)
@@ -169,7 +381,20 @@ static long bio_ctrl(BIO *bio, int cmd, long num, char *ptr)
                break;
 
        case BIO_CTRL_DUP:
-               /* XXX */
+               /* See BIO_dup_chain for circumstances we have to expect. */
+               {
+               BIO *other_bio = ptr;
+               struct bio_bio_st *other_b;
+               
+               assert(other_bio != NULL);
+               other_b = other_bio->ptr;
+               assert(other_b != NULL);
+               
+               assert(other_b->buf == NULL); /* other_bio is always fresh */
+
+               other_b->size = b->size;
+               }
+
                ret = 1;
                break;
 
@@ -177,6 +402,22 @@ static long bio_ctrl(BIO *bio, int cmd, long num, char *ptr)
                ret = 1;
                break;
 
+       case BIO_CTRL_EOF:
+               {
+               BIO *other_bio = ptr;
+               
+               if (other_bio)
+                       {
+                       struct bio_bio_st *other_b = other_bio->ptr;
+                       
+                       assert(other_b != NULL);
+                       ret = other_b->len == 0 && other_b->closed;
+                       }
+               else
+                       ret = 1;
+               }
+               break;
+
        default:
                ret = 0;
                }
@@ -188,8 +429,6 @@ static int bio_puts(BIO *bio, char *str)
        return bio_write(bio, str, strlen(str));
        }
 
-/* Until bio_make_pair is used, make a dummy function use it for -pedantic */
-void dummy() { bio_make_pair(NULL,NULL); }
 
 static int bio_make_pair(BIO *bio1, BIO *bio2)
        {
@@ -273,3 +512,67 @@ static void bio_destroy_pair(BIO *bio)
                        }
                }
        }
+
+/* Exported convenience functions */
+int BIO_new_bio_pair(BIO **bio1_p, size_t writebuf1,
+       BIO **bio2_p, size_t writebuf2)
+        {
+        BIO *bio1 = NULL, *bio2 = NULL;
+        long r;
+        int ret = 0;
+
+        bio1 = BIO_new(BIO_s_bio());
+        if (bio1 == NULL)
+                goto err;
+        bio2 = BIO_new(BIO_s_bio());
+        if (bio2 == NULL)
+                goto err;
+
+        if (writebuf1)
+                {
+                r = BIO_set_write_buf_size(bio1, writebuf1);
+                if (!r)
+                        goto err;
+                }
+        if (writebuf2)
+                {
+                r = BIO_set_write_buf_size(bio2, writebuf2);
+                if (!r)
+                        goto err;
+                }
+
+        r = BIO_make_bio_pair(bio1, bio2);
+        if (!r)
+                goto err;
+        ret = 1;
+
+ err:
+        if (ret == 0)
+                {
+                if (bio1)
+                        {
+                        BIO_free(bio1);
+                        bio1 = NULL;
+                        }
+                if (bio2)
+                        {
+                        BIO_free(bio2);
+                        bio2 = NULL;
+                        }
+                }
+
+        *bio1_p = bio1;
+        *bio2_p = bio2;
+        return ret;
+        }
+
+size_t BIO_ctrl_get_write_guarantee(BIO *bio)
+    {
+       return BIO_ctrl(bio, BIO_C_GET_WRITE_GUARANTEE, 0, NULL);
+       }
+
+size_t BIO_ctrl_read_request(BIO *bio)
+    {
+       return BIO_ctrl(bio, BIO_C_GET_READ_REQUEST, 0, NULL);
+       }
index e2779de..a40f747 100755 (executable)
@@ -91,7 +91,7 @@ echo test sslv2 with server authentication
 ssltest -ssl2 -server_auth -CAfile cert.tmp\r
 if errorlevel 1 goto done\r
 \r
-echo test sslv2 with client authentication \r
+echo test sslv2 with client authentication\r
 ssltest -ssl2 -client_auth -CAfile cert.tmp\r
 if errorlevel 1 goto done\r
 \r
@@ -107,7 +107,7 @@ echo test sslv3 with server authentication
 ssltest -ssl3 -server_auth -CAfile cert.tmp\r
 if errorlevel 1 goto done\r
 \r
-echo test sslv3 with client authentication \r
+echo test sslv3 with client authentication\r
 ssltest -ssl3 -client_auth -CAfile cert.tmp\r
 if errorlevel 1 goto done\r
 \r
@@ -123,7 +123,7 @@ echo test sslv2/sslv3 with server authentication
 ssltest -server_auth -CAfile cert.tmp\r
 if errorlevel 1 goto done\r
 \r
-echo test sslv2/sslv3 with client authentication \r
+echo test sslv2/sslv3 with client authentication\r
 ssltest -client_auth -CAfile cert.tmp\r
 if errorlevel 1 goto done\r
 \r
@@ -131,6 +131,53 @@ echo test sslv2/sslv3 with both client and server authentication
 ssltest -server_auth -client_auth -CAfile cert.tmp\r
 if errorlevel 1 goto done\r
 \r
+echo test sslv2 via BIO pair\r
+ssltest -bio_pair -ssl2\r
+if errorlevel 1 goto done\r
+\r
+echo test sslv2 with server authentication via BIO pair\r
+ssltest -bio_pair -ssl2 -server_auth -CAfile cert.tmp\r
+if errorlevel 1 goto done\r
+\r
+echo test sslv2 with client authentication via BIO pair\r
+ssltest -bio_pair -ssl2 -client_auth -CAfile cert.tmp\r
+if errorlevel 1 goto done\r
+\r
+echo test sslv2 with both client and server authentication via BIO pair\r
+ssltest -bio_pair -ssl2 -server_auth -client_auth -CAfile cert.tmp\r
+if errorlevel 1 goto done\r
+\r
+echo test sslv3 via BIO pair\r
+ssltest -bio_pair -ssl3\r
+if errorlevel 1 goto done\r
+\r
+echo test sslv3 with server authentication via BIO pair\r
+ssltest -bio_pair -ssl3 -server_auth -CAfile cert.tmp\r
+if errorlevel 1 goto done\r
+\r
+echo test sslv3 with client authentication  via BIO pair\r
+ssltest -bio_pair -ssl3 -client_auth -CAfile cert.tmp\r
+if errorlevel 1 goto done\r
+\r
+echo test sslv3 with both client and server authentication via BIO pair\r
+ssltest -bio_pair -ssl3 -server_auth -client_auth -CAfile cert.tmp\r
+if errorlevel 1 goto done\r
+\r
+echo test sslv2/sslv3 via BIO pair\r
+ssltest\r
+if errorlevel 1 goto done\r
+\r
+echo test sslv2/sslv3 with server authentication\r
+ssltest -bio_pair -server_auth -CAfile cert.tmp\r
+if errorlevel 1 goto done\r
+\r
+echo test sslv2/sslv3 with client authentication via BIO pair\r
+ssltest -bio_pair -client_auth -CAfile cert.tmp\r
+if errorlevel 1 goto done\r
+\r
+echo test sslv2/sslv3 with both client and server authentication via BIO pair\r
+ssltest -bio_pair -server_auth -client_auth -CAfile cert.tmp\r
+if errorlevel 1 goto done\r
 \r
 del cert.tmp\r
 \r
index 38d42c4..ae850c0 100644 (file)
@@ -254,6 +254,7 @@ int ssl3_connect(SSL *s)
                case SSL3_ST_CW_CERT_A:
                case SSL3_ST_CW_CERT_B:
                case SSL3_ST_CW_CERT_C:
+               case SSL3_ST_CW_CERT_D:
                        ret=ssl3_send_client_certificate(s);
                        if (ret <= 0) goto end;
                        s->state=SSL3_ST_CW_KEY_EXCH_A;
index 14f58db..0919488 100644 (file)
@@ -131,6 +131,8 @@ case SSL3_ST_CR_SRVR_DONE_A:        str="SSLv3 read server done A"; break;
 case SSL3_ST_CR_SRVR_DONE_B:   str="SSLv3 read server done B"; break;
 case SSL3_ST_CW_CERT_A:                str="SSLv3 write client certificate A"; break;
 case SSL3_ST_CW_CERT_B:                str="SSLv3 write client certificate B"; break;
+case SSL3_ST_CW_CERT_C:                str="SSLv3 write client certificate C"; break;
+case SSL3_ST_CW_CERT_D:                str="SSLv3 write client certificate D"; break;
 case SSL3_ST_CW_KEY_EXCH_A:    str="SSLv3 write client key exchange A"; break;
 case SSL3_ST_CW_KEY_EXCH_B:    str="SSLv3 write client key exchange B"; break;
 case SSL3_ST_CW_CERT_VRFY_A:   str="SSLv3 write certificate verify A"; break;
index c02c26c..91813dc 100644 (file)
@@ -60,6 +60,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
+#include <limits.h>
 
 #include "openssl/e_os.h"
 
@@ -105,6 +106,7 @@ static int s_nbio=0;
 #endif
 
 
+int doit_biopair(SSL *s_ssl,SSL *c_ssl,long bytes);
 int doit(SSL *s_ssl,SSL *c_ssl,long bytes);
 static void sv_usage(void)
        {
@@ -132,12 +134,16 @@ static void sv_usage(void)
        fprintf(stderr," -s_cert arg   - Just the server certificate file\n");
        fprintf(stderr," -c_cert arg   - Just the client certificate file\n");
        fprintf(stderr," -cipher arg   - The cipher list\n");
+       fprintf(stderr," -bio_pair     - Use BIO pairs\n");
+       fprintf(stderr," -f            - Test even cases that can't work\n");
        }
 
 int main(int argc, char *argv[])
        {
        char *CApath=NULL,*CAfile=NULL;
        int badop=0;
+       int bio_pair=0;
+       int force=0;
        int tls1=0,ssl2=0,ssl3=0,ret=1;
        int client_auth=0;
        int server_auth=0,i;
@@ -225,6 +231,14 @@ int main(int argc, char *argv[])
                        if (--argc < 1) goto bad;
                        CAfile= *(++argv);
                        }
+               else if (strcmp(*argv,"-bio_pair") == 0)
+                       {
+                       bio_pair = 1;
+                       }
+               else if (strcmp(*argv,"-f") == 0)
+                       {
+                       force = 1;
+                       }
                else
                        {
                        fprintf(stderr,"unknown option %s\n",*argv);
@@ -241,6 +255,17 @@ bad:
                goto end;
                }
 
+       if (!ssl2 && !ssl3 && !tls1 && number > 1 && !reuse && !force)
+               {
+               fprintf(stderr, "This case cannot work.  Use -f switch to perform "
+                       "the test anyway\n"
+                       "(and -d to see what happens, "
+                       "and -bio_pair to really make it happen :-)\n"
+                       "or add one of -ssl2, -ssl3, -tls1, -reuse to "
+                       "avoid protocol mismatch.\n");
+               exit(1);
+               }
+
 /*     if (cipher == NULL) cipher=getenv("SSL_CIPHER"); */
 
        SSL_library_init();
@@ -338,7 +363,10 @@ bad:
        for (i=0; i<number; i++)
                {
                if (!reuse) SSL_set_session(c_ssl,NULL);
-               ret=doit(s_ssl,c_ssl,bytes);
+               if (bio_pair)
+                       ret=doit_biopair(s_ssl,c_ssl,bytes);
+               else
+                       ret=doit(s_ssl,c_ssl,bytes);
                }
 
        if (!verbose)
@@ -368,6 +396,353 @@ end:
        EXIT(ret);
        }
 
+int doit_biopair(SSL *s_ssl, SSL *c_ssl, long count)
+       {
+       long cw_num = count, cr_num = count, sw_num = count, sr_num = count;
+       BIO *s_ssl_bio = NULL, *c_ssl_bio = NULL;
+       BIO *server = NULL, *server_io = NULL, *client = NULL, *client_io = NULL;
+       SSL_CIPHER *ciph;
+       int ret = 1;
+       
+       size_t bufsiz = 256; /* small buffer for testing */
+
+       if (!BIO_new_bio_pair(&server, bufsiz, &server_io, bufsiz))
+               goto err;
+       if (!BIO_new_bio_pair(&client, bufsiz, &client_io, bufsiz))
+               goto err;
+       
+       s_ssl_bio = BIO_new(BIO_f_ssl());
+       if (!s_ssl_bio)
+               goto err;
+
+       c_ssl_bio = BIO_new(BIO_f_ssl());
+       if (!c_ssl_bio)
+               goto err;
+
+       SSL_set_connect_state(c_ssl);
+       SSL_set_bio(c_ssl, client, client);
+       (void)BIO_set_ssl(c_ssl_bio, c_ssl, BIO_NOCLOSE);
+
+       SSL_set_accept_state(s_ssl);
+       SSL_set_bio(s_ssl, server, server);
+       (void)BIO_set_ssl(s_ssl_bio, s_ssl, BIO_NOCLOSE);
+
+       do
+               {
+               /* c_ssl_bio:          SSL filter BIO
+                *
+                * client:             pseudo-I/O for SSL library
+                *
+                * client_io:          client's SSL communication; usually to be
+                *                     relayed over some I/O facility, but in this
+                *                     test program, we're the server, too:
+                *
+                * server_io:          server's SSL communication
+                *
+                * server:             pseudo-I/O for SSL library
+                *
+                * s_ssl_bio:          SSL filter BIO
+                *
+                * The client and the server each employ a "BIO pair":
+                * client + client_io, server + server_io.
+                * BIO pairs are symmetric.  A BIO pair behaves similar
+                * to a non-blocking socketpair (but both endpoints must
+                * be handled by the same thread).
+                *
+                * Useful functions for querying the state of BIO pair endpoints:
+                *
+                * BIO_ctrl_pending(bio)              number of bytes we can read now
+                * BIO_ctrl_get_read_request(bio)     number of bytes needed to fulfil
+                *                                      other side's read attempt
+                * BIO_ctrl_get_write_gurantee(bio)   number of bytes we can write now
+                *
+                * ..._read_request is never more than ..._write_guarantee;
+                * it depends on the application which one you should use.
+                */
+
+               /* We have non-blocking behaviour throughout this test program, but
+                * can be sure that there is *some* progress in each iteration; so
+                * we don't have to worry about ..._SHOULD_READ or ..._SHOULD_WRITE
+                * -- we just try everything in each iteration
+                */
+
+                       {
+                       /* CLIENT */
+               
+                       MS_STATIC char cbuf[1024*8];
+                       int i, r;
+
+                       if (debug)
+                               if (SSL_in_init(c_ssl))
+                                       printf("client waiting in SSL_connect - %s\n",
+                                               SSL_state_string_long(c_ssl));
+
+                       if (cw_num > 0)
+                               {
+                               /* Write to server. */
+                               
+                               if (cw_num > (long)sizeof cbuf)
+                                       i = sizeof cbuf;
+                               else
+                                       i = (int)cw_num;
+                               r = BIO_write(c_ssl_bio, cbuf, i);
+                               if (r == -1)
+                                       {
+                                       if (!BIO_should_retry(c_ssl_bio))
+                                               {
+                                               fprintf(stderr,"ERROR in CLIENT\n");
+                                               goto err;
+                                               }
+                                       /* BIO_should_retry(...) can just be ignored here.
+                                        * The library expects us to call BIO_write with
+                                        * the same arguments again, and that's what we will
+                                        * do in the next iteration. */
+                                       }
+                               else if (r == 0)
+                                       {
+                                       fprintf(stderr,"SSL CLIENT STARTUP FAILED\n");
+                                       goto err;
+                                       }
+                               else
+                                       {
+                                       if (debug)
+                                               printf("client wrote %d\n", r);
+                                       cw_num -= r;                            
+                                       }
+                               }
+
+                       if (cr_num > 0)
+                               {
+                               /* Read from server. */
+
+                               r = BIO_read(c_ssl_bio, cbuf, sizeof(cbuf));
+                               if (r < 0)
+                                       {
+                                       if (!BIO_should_retry(c_ssl_bio))
+                                               {
+                                               fprintf(stderr,"ERROR in CLIENT\n");
+                                               goto err;
+                                               }
+                                       /* Again, "BIO_should_retry" can be ignored. */
+                                       }
+                               else if (r == 0)
+                                       {
+                                       fprintf(stderr,"SSL CLIENT STARTUP FAILED\n");
+                                       goto err;
+                                       }
+                               else
+                                       {
+                                       if (debug)
+                                               printf("client read %d\n", r);
+                                       cr_num -= r;
+                                       }
+                               }
+                       }
+
+                       {
+                       /* SERVER */
+               
+                       MS_STATIC char sbuf[1024*8];
+                       int i, r;
+
+                       if (debug)
+                               if (SSL_in_init(s_ssl))
+                                       printf("server waiting in SSL_accept - %s\n",
+                                               SSL_state_string_long(s_ssl));
+
+                       if (sw_num > 0)
+                               {
+                               /* Write to client. */
+                               
+                               if (sw_num > (long)sizeof sbuf)
+                                       i = sizeof sbuf;
+                               else
+                                       i = (int)sw_num;
+                               r = BIO_write(s_ssl_bio, sbuf, i);
+                               if (r == -1)
+                                       {
+                                       if (!BIO_should_retry(s_ssl_bio))
+                                               {
+                                               fprintf(stderr,"ERROR in SERVER\n");
+                                               goto err;
+                                               }
+                                       /* Ignore "BIO_should_retry". */
+                                       }
+                               else if (r == 0)
+                                       {
+                                       fprintf(stderr,"SSL SERVER STARTUP FAILED\n");
+                                       goto err;
+                                       }
+                               else
+                                       {
+                                       if (debug)
+                                               printf("server wrote %d\n", r);
+                                       sw_num -= r;                            
+                                       }
+                               }
+
+                       if (sr_num > 0)
+                               {
+                               /* Read from client. */
+
+                               r = BIO_read(s_ssl_bio, sbuf, sizeof(sbuf));
+                               if (r < 0)
+                                       {
+                                       if (!BIO_should_retry(s_ssl_bio))
+                                               {
+                                               fprintf(stderr,"ERROR in SERVER\n");
+                                               goto err;
+                                               }
+                                       /* blah, blah */
+                                       }
+                               else if (r == 0)
+                                       {
+                                       fprintf(stderr,"SSL SERVER STARTUP FAILED\n");
+                                       goto err;
+                                       }
+                               else
+                                       {
+                                       if (debug)
+                                               printf("server read %d\n", r);
+                                       sr_num -= r;
+                                       }
+                               }
+                       }
+                       
+                       {
+                       /* "I/O" BETWEEN CLIENT AND SERVER. */
+
+#define RELAYBUFSIZ 200
+                       static char buf[RELAYBUFSIZ];
+
+                       /* RELAYBUF is arbitrary.  When writing data over some real
+                        * network, use a buffer of the same size as in the BIO_pipe
+                        * and make that size large (for reading from the network
+                        * small buffers usually won't hurt).
+                        * Here sizes differ for testing. */
+
+                       size_t r1, r2;
+                       size_t num;
+                       int r;
+                       static int prev_progress = 1;
+                       int progress = 0;
+                       
+                       /* client to server */
+                       do
+                               {
+                               r1 = BIO_ctrl_pending(client_io);
+                               r2 = BIO_ctrl_get_write_guarantee(server_io);
+
+                               num = r1;
+                               if (r2 < num)
+                                       num = r2;
+                               if (num)
+                                       {
+                                       if (sizeof buf < num)
+                                               num = sizeof buf;
+                                       if (INT_MAX < num) /* yeah, right */
+                                               num = INT_MAX;
+                                       
+                                       r = BIO_read(client_io, buf, (int)num);
+                                       if (r != (int)num) /* can't happen */
+                                               {
+                                               fprintf(stderr, "ERROR: BIO_read could not read "
+                                                       "BIO_ctrl_pending() bytes");
+                                               goto err;
+                                               }
+                                       r = BIO_write(server_io, buf, (int)num);
+                                       if (r != (int)num) /* can't happen */
+                                               {
+                                               fprintf(stderr, "ERROR: BIO_write could not write "
+                                                       "BIO_ctrl_get_write_guarantee() bytes");
+                                               goto err;
+                                               }
+                                       progress = 1;
+
+                                       if (debug)
+                                               printf("C->S relaying: %d bytes\n", (int)num);
+                                       }
+                               }
+                       while (r1 && r2);
+
+                       /* server to client */
+                       do
+                               {
+                               r1 = BIO_ctrl_pending(server_io);
+                               r2 = BIO_ctrl_get_write_guarantee(client_io);
+
+                               num = r1;
+                               if (r2 < num)
+                                       num = r2;
+                               if (num)
+                                       {
+                                       if (sizeof buf < num)
+                                               num = sizeof buf;
+                                       if (INT_MAX < num)
+                                               num = INT_MAX;
+                                       
+                                       r = BIO_read(server_io, buf, (int)num);
+                                       if (r != (int)num) /* can't happen */
+                                               {
+                                               fprintf(stderr, "ERROR: BIO_read could not read "
+                                                       "BIO_ctrl_pending() bytes");
+                                               goto err;
+                                               }
+                                       r = BIO_write(client_io, buf, (int)num);
+                                       if (r != (int)num) /* can't happen */
+                                               {
+                                               fprintf(stderr, "ERROR: BIO_write could not write "
+                                                       "BIO_ctrl_get_write_guarantee() bytes");
+                                               goto err;
+                                               }
+                                       progress = 1;
+
+                                       if (debug)
+                                               printf("S->C relaying: %d bytes\n", (int)num);
+                                       }
+                               }
+                       while (r1 && r2);
+
+                       if (!progress && !prev_progress)
+                               if (cw_num > 0 || cr_num > 0 || sw_num > 0 || sr_num > 0)
+                                        /* can't happen */
+                                       {
+                                       fprintf(stderr, "ERROR: got stuck\n");
+                                       goto err;
+                                       }
+                       prev_progress = progress;
+                       }
+               }
+       while (cw_num > 0 || cr_num > 0 || sw_num > 0 || sr_num > 0);
+
+       ciph = SSL_get_current_cipher(c_ssl);
+       if (verbose)
+               fprintf(stdout,"DONE via BIO pair, protocol %s, cipher %s, %s\n",
+                       SSL_get_version(c_ssl),
+                       SSL_CIPHER_get_version(ciph),
+                       SSL_CIPHER_get_name(ciph));
+       ret = 0;
+
+ err:
+       ERR_print_errors(bio_err);
+       
+       if (server)
+               BIO_free(server);
+       if (server_io)
+               BIO_free(server_io);
+       if (client)
+               BIO_free(client);
+       if (client_io)
+               BIO_free(client_io);
+       if (s_ssl_bio)
+               BIO_free(s_ssl_bio);
+       if (c_ssl_bio)
+               BIO_free(c_ssl_bio);
+
+       return ret;
+       }
+
+
 #define W_READ 1
 #define W_WRITE        2
 #define C_DONE 1
index f115adb..255ae5e 100644 (file)
@@ -36,5 +36,40 @@ echo test sslv2/sslv3 with client authentication
 echo test sslv2/sslv3 with both client and server authentication
 ./ssltest -server_auth -client_auth -CApath ../certs || exit 1
 
-exit 0
+echo test sslv2 via BIO pair
+./ssltest -bio_pair -ssl2 || exit 1
+
+echo test sslv2 with server authentication via BIO pair
+./ssltest -bio_pair -ssl2 -server_auth -CApath ../certs || exit 1
+
+echo test sslv2 with client authentication via BIO pair
+./ssltest -bio_pair -ssl2 -client_auth -CApath ../certs || exit 1
+
+echo test sslv2 with both client and server authentication via BIO pair
+./ssltest -bio_pair -ssl2 -server_auth -client_auth -CApath ../certs || exit 1
+
+echo test sslv3 via BIO pair
+./ssltest -bio_pair -ssl3 || exit 1
+
+echo test sslv3 with server authentication via BIO pair
+./ssltest -bio_pair -ssl3 -server_auth -CApath ../certs || exit 1
+
+echo test sslv3 with client authentication via BIO pair
+./ssltest -bio_pair -ssl3 -client_auth -CApath ../certs || exit 1
+
+echo test sslv3 with both client and server authentication via BIO pair
+./ssltest -bio_pair -ssl3 -server_auth -client_auth -CApath ../certs || exit 1
 
+echo test sslv2/sslv3 via BIO pair
+./ssltest || exit 1
+
+echo test sslv2/sslv3 with server authentication
+./ssltest -bio_pair -server_auth -CApath ../certs || exit 1
+
+echo test sslv2/sslv3 with client authentication via BIO pair
+./ssltest -bio_pair -client_auth -CApath ../certs || exit 1
+
+echo test sslv2/sslv3 with both client and server authentication via BIO pair
+./ssltest -bio_pair -server_auth -client_auth -CApath ../certs || exit 1
+
+exit 0