Tolerate fragmentation and interleaving in the SSL 3/TLS record layer.
diff --git a/CHANGES b/CHANGES
index 6130650..d6ec6e0 100644
--- a/CHANGES
+++ b/CHANGES
@@ -4,6 +4,10 @@
 
  Changes between 0.9.4 and 0.9.5  [xx XXX 2000]
 
+  *) Bugfix: Tolerate fragmentation and interleaving in the SSL 3/TLS
+     record layer.
+     [Bodo Moeller]
+
   *) Change the 'other' type in certificate aux info to a STACK_OF
      X509_ALGOR. Although not an AlgorithmIdentifier as such it has
      the required ASN1 format: arbitrary types determined by an OID.
diff --git a/ssl/s23_srvr.c b/ssl/s23_srvr.c
index 65b46ed..4932064 100644
--- a/ssl/s23_srvr.c
+++ b/ssl/s23_srvr.c
@@ -200,6 +200,7 @@
 	                     *  6-8   length           > Client Hello message
 	                     *  9/10  client_version  /
 	                     */
+/* XXX */
 	char *buf= &(buf_space[0]);
 	unsigned char *p,*d,*dd;
 	unsigned int i;
@@ -277,6 +278,7 @@
 					 * throw this away and implement it in a way
 					 * that makes sense */
 					{
+#if 0
 					STACK_OF(SSL_CIPHER) *sk;
 					SSL_CIPHER *c;
 					int ne2,ne3;
@@ -326,6 +328,10 @@
 							goto next_bit;
 							}
 						}
+#else
+					SSLerr(SSL_F_SSL23_GET_CLIENT_HELLO,SSL_R_UNSUPPORTED_OPTION);
+					goto err;
+#endif
 					}
 				}
 			}
diff --git a/ssl/s2_lib.c b/ssl/s2_lib.c
index 9d5f898..47713ec 100644
--- a/ssl/s2_lib.c
+++ b/ssl/s2_lib.c
@@ -262,14 +262,14 @@
 
 int ssl2_new(SSL *s)
 	{
-	SSL2_CTX *s2;
+	SSL2_STATE *s2;
 
-	if ((s2=(SSL2_CTX *)Malloc(sizeof(SSL2_CTX))) == NULL) goto err;
-	memset(s2,0,sizeof(SSL2_CTX));
+	if ((s2=Malloc(sizeof *s2)) == NULL) goto err;
+	memset(s2,0,sizeof *s2);
 
-	if ((s2->rbuf=(unsigned char *)Malloc(
+	if ((s2->rbuf=Malloc(
 		SSL2_MAX_RECORD_LENGTH_2_BYTE_HEADER+2)) == NULL) goto err;
-	if ((s2->wbuf=(unsigned char *)Malloc(
+	if ((s2->wbuf=Malloc(
 		SSL2_MAX_RECORD_LENGTH_2_BYTE_HEADER+2)) == NULL) goto err;
 	s->s2=s2;
 
@@ -287,7 +287,7 @@
 
 void ssl2_free(SSL *s)
 	{
-	SSL2_CTX *s2;
+	SSL2_STATE *s2;
 
 	if(s == NULL)
 	    return;
@@ -295,14 +295,14 @@
 	s2=s->s2;
 	if (s2->rbuf != NULL) Free(s2->rbuf);
 	if (s2->wbuf != NULL) Free(s2->wbuf);
-	memset(s2,0,sizeof(SSL2_CTX));
+	memset(s2,0,sizeof *s2);
 	Free(s2);
 	s->s2=NULL;
 	}
 
 void ssl2_clear(SSL *s)
 	{
-	SSL2_CTX *s2;
+	SSL2_STATE *s2;
 	unsigned char *rbuf,*wbuf;
 
 	s2=s->s2;
@@ -310,7 +310,7 @@
 	rbuf=s2->rbuf;
 	wbuf=s2->wbuf;
 
-	memset(s2,0,sizeof(SSL2_CTX));
+	memset(s2,0,sizeof *s2);
 
 	s2->rbuf=rbuf;
 	s2->wbuf=wbuf;
diff --git a/ssl/s2_srvr.c b/ssl/s2_srvr.c
index 4eb453b..332e284 100644
--- a/ssl/s2_srvr.c
+++ b/ssl/s2_srvr.c
@@ -898,7 +898,7 @@
 		EVP_VerifyUpdate(&ctx,ccd,SSL2_MIN_CERT_CHALLENGE_LENGTH);
 
 		i=i2d_X509(s->cert->pkeys[SSL_PKEY_RSA_ENC].x509,NULL);
-		buf2=(unsigned char *)Malloc((unsigned int)i);
+		buf2=Malloc((unsigned int)i);
 		if (buf2 == NULL)
 			{
 			SSLerr(SSL_F_REQUEST_CERTIFICATE,ERR_R_MALLOC_FAILURE);
diff --git a/ssl/s3_both.c b/ssl/s3_both.c
index 9b67663..6236b74 100644
--- a/ssl/s3_both.c
+++ b/ssl/s3_both.c
@@ -123,7 +123,7 @@
 
 	if (!ok) return((int)n);
 
-	/* If this occurs if we has missed a message */
+	/* If this occurs, we have missed a message */
 	if (!s->s3->change_cipher_spec)
 		{
 		al=SSL_AD_UNEXPECTED_MESSAGE;
@@ -283,16 +283,22 @@
 
 	p=(unsigned char *)s->init_buf->data;
 
-	if (s->state == st1)
+	if (s->state == st1) /* s->init_num < 4 */
 		{
-		i=ssl3_read_bytes(s,SSL3_RT_HANDSHAKE,&p[s->init_num],
-				  4-s->init_num);
-		if (i < (4-s->init_num))
+		while (s->init_num < 4)
 			{
-			*ok=0;
-			return(ssl3_part_read(s,i));
+			i=ssl3_read_bytes(s,SSL3_RT_HANDSHAKE,&p[s->init_num],
+				4-s->init_num);
+			if (i <= 0)
+				{
+				s->rwstate=SSL_READING;
+				*ok = 0;
+				return i;
+				}
+			s->init_num+=i;
 			}
 
+/* XXX server may always send Hello Request */
 		if ((mt >= 0) && (*p != mt))
 			{
 			al=SSL_AD_UNEXPECTED_MESSAGE;
@@ -334,17 +340,20 @@
 	/* next state (stn) */
 	p=(unsigned char *)s->init_buf->data;
 	n=s->s3->tmp.message_size;
-	if (n > 0)
+	while (n > 0)
 		{
 		i=ssl3_read_bytes(s,SSL3_RT_HANDSHAKE,&p[s->init_num],n);
-		if (i != (int)n)
+		if (i <= 0)
 			{
-			*ok=0;
-			return(ssl3_part_read(s,i));
+			s->rwstate=SSL_READING;
+			*ok = 0;
+			return i;
 			}
+		s->init_num += i;
+		n -= i;
 		}
 	*ok=1;
-	return(n);
+	return s->init_num;
 f_err:
 	ssl3_send_alert(s,SSL3_AL_FATAL,al);
 err:
@@ -465,7 +474,7 @@
 			extra=SSL3_RT_MAX_EXTRA;
 		else
 			extra=0;
-		if ((p=(unsigned char *)Malloc(SSL3_RT_MAX_PACKET_SIZE+extra))
+		if ((p=Malloc(SSL3_RT_MAX_PACKET_SIZE+extra))
 			== NULL)
 			goto err;
 		s->s3->rbuf.buf=p;
@@ -473,7 +482,7 @@
 
 	if (s->s3->wbuf.buf == NULL)
 		{
-		if ((p=(unsigned char *)Malloc(SSL3_RT_MAX_PACKET_SIZE))
+		if ((p=Malloc(SSL3_RT_MAX_PACKET_SIZE))
 			== NULL)
 			goto err;
 		s->s3->wbuf.buf=p;
diff --git a/ssl/s3_enc.c b/ssl/s3_enc.c
index fd0edf2..f340fc5 100644
--- a/ssl/s3_enc.c
+++ b/ssl/s3_enc.c
@@ -300,7 +300,7 @@
 
 	ssl3_cleanup_key_block(s);
 
-	if ((p=(unsigned char *)Malloc(num)) == NULL)
+	if ((p=Malloc(num)) == NULL)
 		goto err;
 
 	s->s3->tmp.key_block_length=num;
diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c
index 8bcbe50..7c71f5e 100644
--- a/ssl/s3_lib.c
+++ b/ssl/s3_lib.c
@@ -655,19 +655,12 @@
 
 int ssl3_new(SSL *s)
 	{
-	SSL3_CTX *s3;
+	SSL3_STATE *s3;
 
-	if ((s3=(SSL3_CTX *)Malloc(sizeof(SSL3_CTX))) == NULL) goto err;
-	memset(s3,0,sizeof(SSL3_CTX));
+	if ((s3=Malloc(sizeof *s3)) == NULL) goto err;
+	memset(s3,0,sizeof *s3);
 
 	s->s3=s3;
-	/*
-	s->s3->tmp.ca_names=NULL;
-	s->s3->tmp.key_block=NULL;
-	s->s3->tmp.key_block_length=0;
-	s->s3->rbuf.buf=NULL;
-	s->s3->wbuf.buf=NULL;
-	*/
 
 	s->method->ssl_clear(s);
 	return(1);
@@ -693,7 +686,7 @@
 #endif
 	if (s->s3->tmp.ca_names != NULL)
 		sk_X509_NAME_pop_free(s->s3->tmp.ca_names,X509_NAME_free);
-	memset(s->s3,0,sizeof(SSL3_CTX));
+	memset(s->s3,0,sizeof *s->s3);
 	Free(s->s3);
 	s->s3=NULL;
 	}
@@ -715,7 +708,7 @@
 	rp=s->s3->rbuf.buf;
 	wp=s->s3->wbuf.buf;
 
-	memset(s->s3,0,sizeof(SSL3_CTX));
+	memset(s->s3,0,sizeof *s->s3);
 	if (rp != NULL) s->s3->rbuf.buf=rp;
 	if (wp != NULL) s->s3->wbuf.buf=wp;
 
@@ -999,21 +992,6 @@
 	return(2);
 	}
 
-int ssl3_part_read(SSL *s, int i)
-	{
-	s->rwstate=SSL_READING;
-
-	if (i < 0)
-		{
-		return(i);
-		}
-	else
-		{
-		s->init_num+=i;
-		return(0);
-		}
-	}
-
 SSL_CIPHER *ssl3_choose_cipher(SSL *s, STACK_OF(SSL_CIPHER) *have,
 	     STACK_OF(SSL_CIPHER) *pref)
 	{
@@ -1214,8 +1192,12 @@
 	ret=ssl3_read_bytes(s,SSL3_RT_APPLICATION_DATA,buf,len);
 	if ((ret == -1) && (s->s3->in_read_app_data == 0))
 		{
-		ERR_get_error(); /* clear the error */
-		s->s3->in_read_app_data=0;
+		/* ssl3_read_bytes decided to call s->handshake_func, which
+		 * called ssl3_read_bytes to read handshake data.
+		 * However, ssl3_read_bytes actually found application data
+		 * and thinks that application data makes sense here (signalled
+		 * by resetting 'in_read_app_data', strangely); so disable
+		 * handshake processing and try to read application data again. */
 		s->in_handshake++;
 		ret=ssl3_read_bytes(s,SSL3_RT_APPLICATION_DATA,buf,len);
 		s->in_handshake--;
diff --git a/ssl/s3_pkt.c b/ssl/s3_pkt.c
index 2f56df3..fcb3d17 100644
--- a/ssl/s3_pkt.c
+++ b/ssl/s3_pkt.c
@@ -125,6 +125,7 @@
 static int do_uncompress(SSL *ssl);
 static int do_change_cipher_spec(SSL *ssl);
 
+/* used only by ssl3_get_record */
 static int ssl3_read_n(SSL *s, int n, int max, int extend)
 	{
 	/* If extend == 0, obtain new n-byte packet; if extend == 1, increase
@@ -184,7 +185,7 @@
 		memmove(s->s3->rbuf.buf, s->packet, off+newb);
 		s->packet = s->s3->rbuf.buf;
 		}
-	
+
 	while (newb < n)
 		{
 		/* Now we have off+newb bytes at the front of s->s3->rbuf.buf and need
@@ -226,11 +227,11 @@
  * ssl->s3->rrec.data, 	 - data
  * ssl->s3->rrec.length, - number of bytes
  */
+/* used only by ssl3_read_bytes */
 static int ssl3_get_record(SSL *s)
 	{
 	int ssl_major,ssl_minor,al;
 	int n,i,ret= -1;
-	SSL3_BUFFER *rb;
 	SSL3_RECORD *rr;
 	SSL_SESSION *sess;
 	unsigned char *p;
@@ -240,7 +241,6 @@
 	int clear=0,extra;
 
 	rr= &(s->s3->rrec);
-	rb= &(s->s3->rbuf);
 	sess=s->session;
 
 	if (s->options & SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER)
@@ -499,7 +499,7 @@
 			nw=SSL3_RT_MAX_PLAIN_LENGTH;
 		else
 			nw=n;
-			
+
 		i=do_ssl3_write(s,type,&(buf[tot]),nw);
 		if (i <= 0)
 			{
@@ -546,7 +546,7 @@
 		}
 
 	if (len == 0) return(len);
-	
+
 	wr= &(s->s3->wrec);
 	wb= &(s->s3->wbuf);
 	sess=s->session;
@@ -569,11 +569,11 @@
 
 	*(p++)=(s->version>>8);
 	*(p++)=s->version&0xff;
-	
+
 	/* record where we are to write out packet length */
 	plen=p; 
 	p+=2;
-	
+
 	/* lets setup the record stuff. */
 	wr->data=p;
 	wr->length=(int)len;
@@ -680,6 +680,33 @@
 		}
 	}
 
+/* Return up to 'len' payload bytes received in 'type' records.
+ * 'type' is one of the following:
+ *
+ *   -  SSL3_RT_HANDSHAKE (when ssl3_get_message calls us)
+ *   -  SSL3_RT_APPLICATION_DATA (when ssl3_read calls us)
+ *   -  0 (during a shutdown, no data has to be returned)
+ *
+ * If we don't have stored data to work from, read a SSL/TLS record first
+ * (possibly multiple records if we still don't have anything to return).
+ *
+ * This function must handle any surprises the peer may have for us, such as
+ * Alert records (e.g. close_notify), ChangeCipherSpec records (not really
+ * a surprise, but handled as if it were), or renegotiation requests.
+ * Also if record payloads contain fragments too small to process, we store
+ * them until there is enough for the respective protocol (the record protocol
+ * may use arbitrary fragmentation and even interleaving):
+ *     Change cipher spec protocol
+ *             just 1 byte needed, no need for keeping anything stored
+ *     Alert protocol
+ *             2 bytes needed (AlertLevel, AlertDescription)
+ *     Handshake protocol
+ *             4 bytes needed (HandshakeType, uint24 length) -- we just have
+ *             to detect unexpected Client Hello and Hello Request messages
+ *             here, anything else is handled by higher layers
+ *     Application data protocol
+ *             none of our business
+ */
 int ssl3_read_bytes(SSL *s, int type, unsigned char *buf, int len)
 	{
 	int al,i,j,n,ret;
@@ -691,8 +718,37 @@
 		if (!ssl3_setup_buffers(s))
 			return(-1);
 
+	if ((type != SSL3_RT_APPLICATION_DATA) && (type != SSL3_RT_HANDSHAKE) && type)
+		{
+		SSLerr(SSL_F_SSL3_READ_BYTES, SSL_R_INTERNAL_ERROR);
+		return -1;
+		}
+
+	if ((type == SSL3_RT_HANDSHAKE) && (s->s3->handshake_fragment_len > 0))
+		/* (partially) satisfy request from storage */
+		{
+		unsigned char *src = s->s3->handshake_fragment;
+		unsigned char *dst = buf;
+
+		n = 0;
+		while ((len > 0) && (s->s3->handshake_fragment_len > 0))
+			{
+			*dst++ = *src++;
+			len--; s->s3->handshake_fragment_len--;
+			n++;
+			}
+		/* move any remaining fragment bytes: */
+		for (i = 0; i < s->s3->handshake_fragment_len; i++)
+			s->s3->handshake_fragment[i] = *src++;
+		ssl3_finish_mac(s, buf, n);
+		return n;
+	}
+
+	/* Now s->s3->handshake_fragment_len == 0 if type == SSL3_RT_HANDSHAKE. */
+
 	if (!s->in_handshake && SSL_in_init(s))
 		{
+		/* type == SSL3_RT_APPLICATION_DATA */
 		i=s->handshake_func(s);
 		if (i < 0) return(i);
 		if (i == 0)
@@ -708,7 +764,7 @@
 	 * s->s3->rrec.data,    - data
 	 * s->s3->rrec.off,     - offset into 'data' for next read
 	 * s->s3->rrec.length,  - number of bytes. */
-	rr= &(s->s3->rrec);
+	rr = &(s->s3->rrec);
 
 	/* get new packet */
 	if ((rr->length == 0) || (s->rstate == SSL_ST_READ_BODY))
@@ -719,7 +775,9 @@
 
 	/* we now have a packet which can be read and processed */
 
-	if (s->s3->change_cipher_spec && (rr->type != SSL3_RT_HANDSHAKE))
+	if (s->s3->change_cipher_spec /* set when we receive ChangeCipherSpec,
+	                               * reset by ssl3_get_finished */
+		&& (rr->type != SSL3_RT_HANDSHAKE))
 		{
 		al=SSL_AD_UNEXPECTED_MESSAGE;
 		SSLerr(SSL_F_SSL3_READ_BYTES,SSL_R_DATA_BETWEEN_CCS_AND_FINISHED);
@@ -734,13 +792,98 @@
 		return(0);
 		}
 
-	/* Check for an incoming 'Hello Request' message from client */
-	if ((rr->type == SSL3_RT_HANDSHAKE) && (rr->length == 4) &&
-		(rr->data[0] == SSL3_MT_HELLO_REQUEST) &&
+
+	if (type == rr->type) /* SSL3_RT_APPLICATION_DATA or SSL3_RT_HANDSHAKE */
+		{
+		/* make sure that we are not getting application data when we
+		 * are doing a handshake for the first time */
+		if (SSL_in_init(s) && (type == SSL3_RT_APPLICATION_DATA) &&
+			(s->enc_read_ctx == NULL))
+			{
+			al=SSL_AD_UNEXPECTED_MESSAGE;
+			SSLerr(SSL_F_SSL3_READ_BYTES,SSL_R_APP_DATA_IN_HANDSHAKE);
+			goto f_err;
+			}
+
+		if (len <= 0) return(len);
+
+		if ((unsigned int)len > rr->length)
+			n=rr->length;
+		else
+			n=len;
+
+		memcpy(buf,&(rr->data[rr->off]),(unsigned int)n);
+		rr->length-=n;
+		rr->off+=n;
+		if (rr->length == 0)
+			{
+			s->rstate=SSL_ST_READ_HEADER;
+			rr->off=0;
+			}
+
+		if (type == SSL3_RT_HANDSHAKE)
+			ssl3_finish_mac(s,buf,n);
+		return(n);
+		}
+
+
+	/* If we get here, then type != rr->type; if we have a handshake
+	 * message, then it was unexpected (Hello Request or Client Hello). */
+
+	/* In case of record types for which we have 'fragment' storage,
+	 * fill that so that we can process the data at a fixed place.
+	 */
+		{
+		int dest_maxlen = 0;
+		unsigned char *dest;
+		int *dest_len;
+
+		if (rr->type == SSL3_RT_HANDSHAKE)
+			{
+			dest_maxlen = sizeof s->s3->handshake_fragment;
+			dest = s->s3->handshake_fragment;
+			dest_len = &s->s3->handshake_fragment_len;
+			}
+		else if (rr->type == SSL3_RT_ALERT)
+			{
+			dest_maxlen = sizeof s->s3->alert_fragment;
+			dest = s->s3->alert_fragment;
+			dest_len = &s->s3->alert_fragment_len;
+			}
+
+		if (dest_maxlen > 0)
+			{
+			n = dest_maxlen - *dest_len; /* available space in 'dest' */
+			if (rr->length < n)
+				n = rr->length; /* available bytes */
+
+			/* now move 'n' bytes: */
+			while (n-- > 0)
+				{
+				dest[(*dest_len)++] = rr->data[rr->off++];
+				rr->length--;
+				}
+
+			if (*dest_len < dest_maxlen)
+				goto start; /* fragment was too small */
+			}
+		}
+
+	/* s->s3->handshake_fragment_len == 4  iff  rr->type == SSL3_RT_HANDSHAKE;
+	 * s->s3->alert_fragment_len == 2      iff  rr->type == SSL3_RT_ALERT.
+	 * (Possibly rr is 'empty' now, i.e. rr->length may be 0.) */
+
+	/* If we are a client, check for an incoming 'Hello Request': */
+	if ((!s->server) &&
+		(s->s3->handshake_fragment_len >= 4) &&
+		(s->s3->handshake_fragment[0] == SSL3_MT_HELLO_REQUEST) &&
 		(s->session != NULL) && (s->session->cipher != NULL))
 		{
-		if ((rr->data[1] != 0) || (rr->data[2] != 0) ||
-			(rr->data[3] != 0))
+		s->s3->handshake_fragment_len = 0;
+
+		if ((s->s3->handshake_fragment[1] != 0) ||
+			(s->s3->handshake_fragment[2] != 0) ||
+			(s->s3->handshake_fragment[3] != 0))
 			{
 			al=SSL_AD_DECODE_ERROR;
 			SSLerr(SSL_F_SSL3_READ_BYTES,SSL_R_BAD_HELLO_REQUEST);
@@ -763,214 +906,176 @@
 					}
 				}
 			}
-		rr->length=0;
-/* ZZZ */	goto start;
+		/* we either finished a handshake or ignored the request,
+		 * now try again to obtain the (application) data we were asked for */
+		goto start;
 		}
 
-	/* if it is not the type we want, or we have shutdown and want
-	 * the peer shutdown */
-	if ((rr->type != type) || (s->shutdown & SSL_SENT_SHUTDOWN))
+	if (s->s3->alert_fragment_len >= 2)
 		{
-		if (rr->type == SSL3_RT_ALERT)
+		i = s->s3->alert_fragment[0];
+		n = s->s3->alert_fragment[1];
+
+		s->s3->alert_fragment_len = 0;
+
+		if (s->info_callback != NULL)
+			cb=s->info_callback;
+		else if (s->ctx->info_callback != NULL)
+			cb=s->ctx->info_callback;
+
+		if (cb != NULL)
 			{
-			if ((rr->length != 2) || (rr->off != 0))
+			j=(i<<8)|n;
+			cb(s,SSL_CB_READ_ALERT,j);
+			}
+
+		if (i == 1) /* warning */
+			{
+			s->s3->warn_alert=n;
+			if (n == SSL_AD_CLOSE_NOTIFY)
 				{
-				al=SSL_AD_DECODE_ERROR;
-				SSLerr(SSL_F_SSL3_READ_BYTES,SSL_R_BAD_ALERT_RECORD);
-				goto f_err;
-				}
-
-			i=rr->data[0];
-			n=rr->data[1];
-
-			/* clear from buffer */
-			rr->length=0;
-
-			if (s->info_callback != NULL)
-				cb=s->info_callback;
-			else if (s->ctx->info_callback != NULL)
-				cb=s->ctx->info_callback;
-
-			if (cb != NULL)
-				{
-				j=(i<<8)|n;
-				cb(s,SSL_CB_READ_ALERT,j);
-				}
-
-			if (i == 1) /* warning */
-				{
-				s->s3->warn_alert=n;
-				if (n == SSL_AD_CLOSE_NOTIFY)
-					{
-					s->shutdown|=SSL_RECEIVED_SHUTDOWN;
-					return(0);
-					}
-				}
-			else if (i == 2) /* fatal */
-				{
-				char tmp[16];
-
-				s->rwstate=SSL_NOTHING;
-				s->s3->fatal_alert=n;
-				SSLerr(SSL_F_SSL3_READ_BYTES,
-					SSL_AD_REASON_OFFSET+n);
-				sprintf(tmp,"%d",n);
-				ERR_add_error_data(2,"SSL alert number ",tmp);
 				s->shutdown|=SSL_RECEIVED_SHUTDOWN;
-				SSL_CTX_remove_session(s->ctx,s->session);
 				return(0);
 				}
-			else
-				{
-				al=SSL_AD_ILLEGAL_PARAMETER;
-				SSLerr(SSL_F_SSL3_READ_BYTES,SSL_R_UNKNOWN_ALERT_TYPE);
-				goto f_err;
-				}
-
-			rr->length=0;
-			goto start;
 			}
-
-		if (s->shutdown & SSL_SENT_SHUTDOWN)
+		else if (i == 2) /* fatal */
 			{
+			char tmp[16];
+
 			s->rwstate=SSL_NOTHING;
-			rr->length=0;
+			s->s3->fatal_alert=n;
+			SSLerr(SSL_F_SSL3_READ_BYTES,
+				SSL_AD_REASON_OFFSET+n);
+			sprintf(tmp,"%d",n);
+			ERR_add_error_data(2,"SSL alert number ",tmp);
+			s->shutdown|=SSL_RECEIVED_SHUTDOWN;
+			SSL_CTX_remove_session(s->ctx,s->session);
 			return(0);
 			}
-
-		if (rr->type == SSL3_RT_CHANGE_CIPHER_SPEC)
+		else
 			{
-			if (	(rr->length != 1) || (rr->off != 0) ||
-				(rr->data[0] != SSL3_MT_CCS))
-				{
-				i=SSL_AD_ILLEGAL_PARAMETER;
-				SSLerr(SSL_F_SSL3_READ_BYTES,SSL_R_BAD_CHANGE_CIPHER_SPEC);
-				goto err;
-				}
-
-			rr->length=0;
-			s->s3->change_cipher_spec=1;
-			if (!do_change_cipher_spec(s))
-				goto err;
-			else
-				goto start;
+			al=SSL_AD_ILLEGAL_PARAMETER;
+			SSLerr(SSL_F_SSL3_READ_BYTES,SSL_R_UNKNOWN_ALERT_TYPE);
+			goto f_err;
 			}
 
-		/* else we have a handshake */
-		if ((rr->type == SSL3_RT_HANDSHAKE) &&
-			!s->in_handshake)
+		goto start;
+		}
+
+	if (s->shutdown & SSL_SENT_SHUTDOWN) /* but we have not received a shutdown */
+		{
+		s->rwstate=SSL_NOTHING;
+		rr->length=0;
+		return(0);
+		}
+
+	if (rr->type == SSL3_RT_CHANGE_CIPHER_SPEC)
+		{
+		/* 'Change Cipher Spec' is just a single byte, so we know
+		 * exactly what the record payload has to look like */
+		if (	(rr->length != 1) || (rr->off != 0) ||
+			(rr->data[0] != SSL3_MT_CCS))
 			{
-			if (((s->state&SSL_ST_MASK) == SSL_ST_OK) &&
-				!(s->s3->flags & SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS))
-				{
-				s->state=SSL_ST_BEFORE|(s->server)
-						?SSL_ST_ACCEPT
-						:SSL_ST_CONNECT;
-				s->new_session=1;
-				}
-			n=s->handshake_func(s);
-			if (n < 0) return(n);
-			if (n == 0)
-				{
-				SSLerr(SSL_F_SSL3_READ_BYTES,SSL_R_SSL_HANDSHAKE_FAILURE);
-				return(-1);
-				}
+			i=SSL_AD_ILLEGAL_PARAMETER;
+			SSLerr(SSL_F_SSL3_READ_BYTES,SSL_R_BAD_CHANGE_CIPHER_SPEC);
+			goto err;
+			}
 
-			/* In the case where we try to read application data
-			 * the first time, but we trigger an SSL handshake, we
-			 * return -1 with the retry option set.  I do this
-			 * otherwise renegotiation can cause nasty problems 
-			 * in the non-blocking world */
+		rr->length=0;
+		s->s3->change_cipher_spec=1;
+		if (!do_change_cipher_spec(s))
+			goto err;
+		else
+			goto start;
+		}
 
-			s->rwstate=SSL_READING;
-			bio=SSL_get_rbio(s);
-			BIO_clear_retry_flags(bio);
-			BIO_set_retry_read(bio);
+	/* Unexpected handshake message (Client Hello, or protocol violation) */
+	if ((s->s3->handshake_fragment_len >= 4) &&	!s->in_handshake)
+		{
+		if (((s->state&SSL_ST_MASK) == SSL_ST_OK) &&
+			!(s->s3->flags & SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS))
+			{
+			s->state=SSL_ST_BEFORE|(s->server)
+				?SSL_ST_ACCEPT
+				:SSL_ST_CONNECT;
+			s->new_session=1;
+			}
+		n=s->handshake_func(s);
+		if (n < 0) return(n);
+		if (n == 0)
+			{
+			SSLerr(SSL_F_SSL3_READ_BYTES,SSL_R_SSL_HANDSHAKE_FAILURE);
 			return(-1);
 			}
 
-		switch (rr->type)
-			{
-		default:
+		/* In the case where we try to read application data
+		 * the first time, but we trigger an SSL handshake, we
+		 * return -1 with the retry option set.  I do this
+		 * otherwise renegotiation can cause nasty problems 
+		 * in the non-blocking world */
+
+		s->rwstate=SSL_READING;
+		bio=SSL_get_rbio(s);
+		BIO_clear_retry_flags(bio);
+		BIO_set_retry_read(bio);
+		return(-1);
+		}
+
+	switch (rr->type)
+		{
+	default:
 #ifndef NO_TLS
-			/* TLS just ignores unknown message types */
-			if (s->version == TLS1_VERSION)
-				{
-				goto start;
-				}
+		/* TLS just ignores unknown message types */
+		if (s->version == TLS1_VERSION)
+			{
+			goto start;
+			}
 #endif
-		case SSL3_RT_CHANGE_CIPHER_SPEC:
-		case SSL3_RT_ALERT:
-		case SSL3_RT_HANDSHAKE:
+		al=SSL_AD_UNEXPECTED_MESSAGE;
+		SSLerr(SSL_F_SSL3_READ_BYTES,SSL_R_UNEXPECTED_RECORD);
+		goto f_err;
+	case SSL3_RT_CHANGE_CIPHER_SPEC:
+	case SSL3_RT_ALERT:
+	case SSL3_RT_HANDSHAKE:
+		/* we already handled all of these, with the possible exception
+		 * of SSL3_RT_HANDSHAKE when s->in_handshake is set, but that
+		 * should not happen when type != rr->type */
+		al=SSL_AD_UNEXPECTED_MESSAGE;
+		SSLerr(SSL_F_SSL3_READ_BYTES,SSL_R_INTERNAL_ERROR);
+		goto f_err;
+	case SSL3_RT_APPLICATION_DATA:
+		/* At this point, we were expecting handshake data,
+		 * but have application data.  If the library was
+		 * running inside ssl3_read() (i.e. in_read_app_data
+		 * is set) and it makes sense to read application data
+		 * at this point (session renegotation not yet started),
+		 * we will indulge it.
+		 */
+		if (s->s3->in_read_app_data &&
+			(s->s3->total_renegotiations != 0) &&
+			((
+				(s->state & SSL_ST_CONNECT) &&
+				(s->state >= SSL3_ST_CW_CLNT_HELLO_A) &&
+				(s->state <= SSL3_ST_CR_SRVR_HELLO_A)
+				) || (
+					(s->state & SSL_ST_ACCEPT) &&
+					(s->state <= SSL3_ST_SW_HELLO_REQ_A) &&
+					(s->state >= SSL3_ST_SR_CLNT_HELLO_A)
+					)
+				))
+			{
+			s->s3->in_read_app_data=0;
+			return(-1);
+			}
+		else
+			{
 			al=SSL_AD_UNEXPECTED_MESSAGE;
 			SSLerr(SSL_F_SSL3_READ_BYTES,SSL_R_UNEXPECTED_RECORD);
 			goto f_err;
-		case SSL3_RT_APPLICATION_DATA:
-			/* At this point, we were expecting something else,
-			 * but have application data.  What we do is set the
-			 * error, and return -1.  On the way out, if the
-			 * library was running inside ssl3_read() and it makes
-			 * sense to read application data at this point, we
-			 * will indulge it.  This will mostly happen during
-			 * session renegotiation.
-			 */
-			if (s->s3->in_read_app_data &&
-				(s->s3->total_renegotiations != 0) &&
-				((
-				  (s->state & SSL_ST_CONNECT) &&
-				  (s->state >= SSL3_ST_CW_CLNT_HELLO_A) &&
-				  (s->state <= SSL3_ST_CR_SRVR_HELLO_A)
-				 ) || (
-				  (s->state & SSL_ST_ACCEPT) &&
-				  (s->state <= SSL3_ST_SW_HELLO_REQ_A) &&
-				  (s->state >= SSL3_ST_SR_CLNT_HELLO_A)
-				 )
-				))
-				{
-				s->s3->in_read_app_data=0;
-				return(-1);
-				}
-			else
-				{
-				al=SSL_AD_UNEXPECTED_MESSAGE;
-				SSLerr(SSL_F_SSL3_READ_BYTES,SSL_R_UNEXPECTED_RECORD);
-				goto f_err;
-				}
 			}
-		/* not reached */
 		}
+	/* not reached */
 
-	/* rr->type == type */
-
-	/* make sure that we are not getting application data when we
-	 * are doing a handshake for the first time */
-	if (SSL_in_init(s) && (type == SSL3_RT_APPLICATION_DATA) &&
-		(s->enc_read_ctx == NULL))
-		{
-		al=SSL_AD_UNEXPECTED_MESSAGE;
-		SSLerr(SSL_F_SSL3_READ_BYTES,SSL_R_APP_DATA_IN_HANDSHAKE);
-		goto f_err;
-		}
-
-	if (len <= 0) return(len);
-
-	if ((unsigned int)len > rr->length)
-		n=rr->length;
-	else
-		n=len;
-
-	memcpy(buf,&(rr->data[rr->off]),(unsigned int)n);
-	rr->length-=n;
-	rr->off+=n;
-	if (rr->length == 0)
-		{
-		s->rstate=SSL_ST_READ_HEADER;
-		rr->off=0;
-		}
-
-	if (type == SSL3_RT_HANDSHAKE)
-		ssl3_finish_mac(s,buf,n);
-	return(n);
 f_err:
 	ssl3_send_alert(s,SSL3_AL_FATAL,al);
 err:
@@ -1075,7 +1180,7 @@
 			cb=s->info_callback;
 		else if (s->ctx->info_callback != NULL)
 			cb=s->ctx->info_callback;
-		
+
 		if (cb != NULL)
 			{
 			j=(s->s3->send_alert[0]<<8)|s->s3->send_alert[1];
diff --git a/ssl/ssl.h b/ssl/ssl.h
index 261dcf8..6f31992 100644
--- a/ssl/ssl.h
+++ b/ssl/ssl.h
@@ -581,8 +581,8 @@
 	unsigned char *packet;
 	unsigned int packet_length;
 
-	struct ssl2_ctx_st *s2;	/* SSLv2 variables */
-	struct ssl3_ctx_st *s3;	/* SSLv3 variables */
+	struct ssl2_state_st *s2; /* SSLv2 variables */
+	struct ssl3_state_st *s3; /* SSLv3 variables */
 
 	int read_ahead;		/* Read as many input bytes as possible
 	               	 	 * (for non-blocking reads) */
@@ -1510,6 +1510,7 @@
 #define SSL_R_UNKNOWN_STATE				 255
 #define SSL_R_UNSUPPORTED_CIPHER			 256
 #define SSL_R_UNSUPPORTED_COMPRESSION_ALGORITHM		 257
+#define SSL_R_UNSUPPORTED_OPTION			 1091
 #define SSL_R_UNSUPPORTED_PROTOCOL			 258
 #define SSL_R_UNSUPPORTED_SSL_VERSION			 259
 #define SSL_R_WRITE_BIO_NOT_SET				 260
diff --git a/ssl/ssl2.h b/ssl/ssl2.h
index d7f24ac..01d41c8 100644
--- a/ssl/ssl2.h
+++ b/ssl/ssl2.h
@@ -151,7 +151,7 @@
 #define  CERT		char
 #endif
 
-typedef struct ssl2_ctx_st
+typedef struct ssl2_state_st
 	{
 	int three_byte_header;
 	int clear_text;		/* clear text */
@@ -214,7 +214,7 @@
 		unsigned int clen;
 		unsigned int rlen;
 		} tmp;
-	} SSL2_CTX;
+	} SSL2_STATE;
 
 /* SSLv2 */
 /* client */
diff --git a/ssl/ssl3.h b/ssl/ssl3.h
index 88a04b4..654ad1e 100644
--- a/ssl/ssl3.h
+++ b/ssl/ssl3.h
@@ -188,12 +188,12 @@
 
 typedef struct ssl3_record_st
 	{
-/*r */	int type;		/* type of record */
-/*rw*/	unsigned int length;	/* How many bytes available */
-/*r */	unsigned int off;	/* read/write offset into 'buf' */
-/*rw*/	unsigned char *data;	/* pointer to the record data */
-/*rw*/	unsigned char *input;	/* where the decode bytes are */
-/*r */	unsigned char *comp;	/* only used with decompression - malloc()ed */
+/*r */	int type;               /* type of record */
+/*rw*/	unsigned int length;    /* How many bytes available */
+/*r */	unsigned int off;       /* read/write offset into 'buf' */
+/*rw*/	unsigned char *data;    /* pointer to the record data */
+/*rw*/	unsigned char *input;   /* where the decode bytes are */
+/*r */	unsigned char *comp;    /* only used with decompression - malloc()ed */
 	} SSL3_RECORD;
 
 typedef struct ssl3_buffer_st
@@ -218,34 +218,7 @@
 #define SSL3_FLAGS_POP_BUFFER			0x0004
 #define TLS1_FLAGS_TLS_PADDING_BUG		0x0008
 
-#if 0
-#define AD_CLOSE_NOTIFY			0
-#define AD_UNEXPECTED_MESSAGE		1
-#define AD_BAD_RECORD_MAC		2
-#define AD_DECRYPTION_FAILED		3
-#define AD_RECORD_OVERFLOW		4
-#define AD_DECOMPRESSION_FAILURE	5	/* fatal */
-#define AD_HANDSHAKE_FAILURE		6	/* fatal */
-#define AD_NO_CERTIFICATE		7	/* Not under TLS */
-#define AD_BAD_CERTIFICATE		8
-#define AD_UNSUPPORTED_CERTIFICATE	9
-#define AD_CERTIFICATE_REVOKED		10	
-#define AD_CERTIFICATE_EXPIRED		11
-#define AD_CERTIFICATE_UNKNOWN		12
-#define AD_ILLEGAL_PARAMETER		13	/* fatal */
-#define AD_UNKNOWN_CA			14	/* fatal */
-#define AD_ACCESS_DENIED		15	/* fatal */
-#define AD_DECODE_ERROR			16	/* fatal */
-#define AD_DECRYPT_ERROR		17
-#define AD_EXPORT_RESTRICION		18	/* fatal */
-#define AD_PROTOCOL_VERSION		19	/* fatal */
-#define AD_INSUFFICIENT_SECURITY	20	/* fatal */
-#define AD_INTERNAL_ERROR		21	/* fatal */
-#define AD_USER_CANCLED			22
-#define AD_NO_RENEGOTIATION		23
-#endif
-
-typedef struct ssl3_ctx_st
+typedef struct ssl3_state_st
 	{
 	long flags;
 	int delay_buf_pop_ret;
@@ -260,10 +233,16 @@
 
 	SSL3_BUFFER rbuf;	/* read IO goes into here */
 	SSL3_BUFFER wbuf;	/* write IO goes into here */
+
 	SSL3_RECORD rrec;	/* each decoded record goes in here */
 	SSL3_RECORD wrec;	/* goes out from here */
-				/* Used by ssl3_read_n to point
-				 * to input data packet */
+
+	/* storage for Alert/Handshake protocol data received but not
+	 * yet processed by ssl3_read_bytes: */
+	unsigned char alert_fragment[2];
+	int alert_fragment_len;
+	unsigned char handshake_fragment[4];
+	int handshake_fragment_len;
 
 	/* partial write - check the numbers match */
 	unsigned int wnum;	/* number of bytes sent so far */
@@ -339,7 +318,7 @@
 		int cert_request;
 		} tmp;
 
-	} SSL3_CTX;
+	} SSL3_STATE;
 
 /* SSLv3 */
 /*client */
diff --git a/ssl/ssl_err.c b/ssl/ssl_err.c
index 5630d4c..b8bfa03 100644
--- a/ssl/ssl_err.c
+++ b/ssl/ssl_err.c
@@ -392,6 +392,7 @@
 {SSL_R_UNKNOWN_STATE                     ,"unknown state"},
 {SSL_R_UNSUPPORTED_CIPHER                ,"unsupported cipher"},
 {SSL_R_UNSUPPORTED_COMPRESSION_ALGORITHM ,"unsupported compression algorithm"},
+{SSL_R_UNSUPPORTED_OPTION                ,"unsupported option"},
 {SSL_R_UNSUPPORTED_PROTOCOL              ,"unsupported protocol"},
 {SSL_R_UNSUPPORTED_SSL_VERSION           ,"unsupported ssl version"},
 {SSL_R_WRITE_BIO_NOT_SET                 ,"write bio not set"},
diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h
index 19ab4c6..eb28917 100644
--- a/ssl/ssl_locl.h
+++ b/ssl/ssl_locl.h
@@ -492,7 +492,6 @@
 int ssl3_renegotiate_check(SSL *ssl); 
 int ssl3_dispatch_alert(SSL *s);
 int ssl3_read_bytes(SSL *s, int type, unsigned char *buf, int len);
-int ssl3_part_read(SSL *s, int i);
 int ssl3_write_bytes(SSL *s, int type, const void *buf, int len);
 int ssl3_final_finish_mac(SSL *s, EVP_MD_CTX *ctx1, EVP_MD_CTX *ctx2,
 	const char *sender, int slen,unsigned char *p);