Memory saving patch.
diff --git a/CHANGES b/CHANGES
index c5c5078..78272fa 100644
--- a/CHANGES
+++ b/CHANGES
@@ -4,6 +4,10 @@
 
  Changes between 0.9.8g and 0.9.9  [xx XXX xxxx]
 
+  *) Add a new SSL_MODE_RELEASE_BUFFERS mode flag to release unused buffer
+     RAM on SSL connections.  This option can save about 34k per idle SSL.
+     [Nick Mathewson]
+
   *) Expand ENGINE to support engine supplied SSL client certificate functions.
      [Steve Henson]
 
diff --git a/doc/ssl/SSL_CTX_set_mode.pod b/doc/ssl/SSL_CTX_set_mode.pod
index 9822544..8cb669d 100644
--- a/doc/ssl/SSL_CTX_set_mode.pod
+++ b/doc/ssl/SSL_CTX_set_mode.pod
@@ -61,6 +61,16 @@
 flag SSL_MODE_AUTO_RETRY will cause read/write operations to only
 return after the handshake and successful completion.
 
+=item SSL_MODE_RELEASE_BUFFERS
+
+When we no longer need a read buffer or a write buffer for a given SSL,
+then release the memory we were using to hold it.  Released memory is
+either appended to a list of unused RAM chunks on the SSL_CTX, or simply
+freed if the list of unused chunks would become longer than 
+SSL_CTX->freelist_max_len, which defaults to 32.  Using this flag can
+save around 34k per idle SSL connection.
+This flag has no effect on SSL v2 connections, or on DTLS connections.
+
 =back
 
 =head1 RETURN VALUES
diff --git a/ssl/s23_clnt.c b/ssl/s23_clnt.c
index 78c39d9..f90e107 100644
--- a/ssl/s23_clnt.c
+++ b/ssl/s23_clnt.c
@@ -640,6 +640,9 @@
 		 * for SSLv3 */
 		s->rstate=SSL_ST_READ_HEADER;
 		s->packet_length=n;
+		if (s->s3->rbuf.buf == NULL)
+			if (!ssl3_setup_read_buffer(s))
+				goto err;
 		s->packet= &(s->s3->rbuf.buf[0]);
 		memcpy(s->packet,buf,n);
 		s->s3->rbuf.left=n;
diff --git a/ssl/s23_srvr.c b/ssl/s23_srvr.c
index ddf5d4c..9d5481c 100644
--- a/ssl/s23_srvr.c
+++ b/ssl/s23_srvr.c
@@ -545,6 +545,10 @@
 			 * for SSLv3 */
 			s->rstate=SSL_ST_READ_HEADER;
 			s->packet_length=n;
+			if (s->s3->rbuf.buf == NULL)
+				if (!ssl3_setup_read_buffer(s))
+					goto err;
+
 			s->packet= &(s->s3->rbuf.buf[0]);
 			memcpy(s->packet,buf,n);
 			s->s3->rbuf.left=n;
diff --git a/ssl/s3_both.c b/ssl/s3_both.c
index 1a45e67..88bed0f 100644
--- a/ssl/s3_both.c
+++ b/ssl/s3_both.c
@@ -591,7 +591,81 @@
 	return(al);
 	}
 
-int ssl3_setup_buffers(SSL *s)
+#if !defined(OPENSSL_NO_BUF_FREELISTS) && !defined(OPENSSL_NO_RELEASE_BUFFERS)
+/* On some platforms, malloc() performance is bad enough that you can't just
+ * free() and malloc() buffers all the time, so we need to use freelists from
+ * unused buffers.  Currently, each freelist holds memory chunks of only a
+ * given size (list->chunklen); other sized chunks are freed and malloced.
+ * This doesn't help much if you're using many different SSL option settings
+ * with a given context.  (The options affecting buffer size are
+ * max_send_fragment, read buffer vs write buffer,
+ * SSL_OP_MICROSOFT_BIG_WRITE_BUFFER, SSL_OP_NO_COMPRESSION, and
+ * SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS.)  Using a separate freelist for every
+ * possible size is not an option, since max_send_fragment can take on many
+ * different values.
+ *
+ * If you are on a platform with a slow malloc(), and you're using SSL
+ * connections with many different settings for these options, and you need to
+ * use the SSL_MOD_RELEASE_BUFFERS feature, you have a few options:
+ *    - Link against a faster malloc implementation.
+ *    - Use a separate SSL_CTX for each option set.
+ *    - Improve this code.
+ */
+static void *
+freelist_extract(SSL_CTX *ctx, int for_read, int sz)
+	{
+	SSL3_BUF_FREELIST *list;
+	SSL3_BUF_FREELIST_ENTRY *ent = NULL;
+	void *result = NULL;
+
+	CRYPTO_w_lock(CRYPTO_LOCK_SSL_CTX);
+	list = for_read ? ctx->rbuf_freelist : ctx->wbuf_freelist;
+	if (list != NULL && sz == list->chunklen)
+		ent = list->head;
+	if (ent != NULL)
+		{
+		list->head = ent->next;
+		result = ent;
+		if (--list->len == 0)
+			list->chunklen = 0;
+		}
+	CRYPTO_w_unlock(CRYPTO_LOCK_SSL_CTX);
+	if (!result)
+		result = OPENSSL_malloc(sz);
+	return result;
+}
+
+static void
+freelist_insert(SSL_CTX *ctx, int for_read, size_t sz, void *mem)
+	{
+	SSL3_BUF_FREELIST *list;
+	SSL3_BUF_FREELIST_ENTRY *ent;
+
+	CRYPTO_w_lock(CRYPTO_LOCK_SSL_CTX);
+	list = for_read ? ctx->rbuf_freelist : ctx->wbuf_freelist;
+	if (list != NULL &&
+	    (sz == list->chunklen || list->chunklen == 0) &&
+	    list->len < ctx->freelist_max_len &&
+	    sz >= sizeof(*ent))
+		{
+		list->chunklen = sz;
+		ent = mem;
+		ent->next = list->head;
+		list->head = ent;
+		++list->len;
+		mem = NULL;
+		}
+
+	CRYPTO_w_unlock(CRYPTO_LOCK_SSL_CTX);
+	if (mem)
+		OPENSSL_free(mem);
+	}
+#else
+#define freelist_extract(c,fr,sz) OPENSSL_malloc(sz)
+#define freelist_insert(c,fr,sz,m) OPENSSL_free(m)
+#endif
+
+int ssl3_setup_read_buffer(SSL *s)
 	{
 	unsigned char *p;
 	size_t len,align=0;
@@ -614,12 +688,29 @@
 		if (!(s->options & SSL_OP_NO_COMPRESSION))
 			len += SSL3_RT_MAX_COMPRESSED_OVERHEAD;
 #endif
-		if ((p=OPENSSL_malloc(len)) == NULL)
+		if ((p=freelist_extract(s->ctx, 1, len)) == NULL)
 			goto err;
 		s->s3->rbuf.buf = p;
 		s->s3->rbuf.len = len;
 		}
 
+	s->packet= &(s->s3->rbuf.buf[0]);
+	return 1;
+
+err:
+	SSLerr(SSL_F_SSL3_SETUP_BUFFERS,ERR_R_MALLOC_FAILURE);
+	return 0;
+	}
+
+int ssl3_setup_write_buffer(SSL *s)
+	{
+	unsigned char *p;
+	size_t len,align=0;
+
+#if defined(SSL3_ALIGN_PAYLOAD) && SSL3_ALIGN_PAYLOAD!=0
+	align = (-SSL3_RT_HEADER_LENGTH)&(SSL3_ALIGN_PAYLOAD-1);
+#endif
+
 	if (s->s3->wbuf.buf == NULL)
 		{
 		len = s->max_send_fragment
@@ -632,14 +723,47 @@
 		if (!(s->options & SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS))
 			len += SSL3_RT_HEADER_LENGTH + align
 				+ SSL3_RT_SEND_MAX_ENCRYPTED_OVERHEAD;
-		if ((p=OPENSSL_malloc(len)) == NULL)
+
+		if ((p=freelist_extract(s->ctx, 0, len)) == NULL)
 			goto err;
 		s->s3->wbuf.buf = p;
 		s->s3->wbuf.len = len;
 		}
-	s->packet= &(s->s3->rbuf.buf[0]);
-	return(1);
+
+	return 1;
+
 err:
 	SSLerr(SSL_F_SSL3_SETUP_BUFFERS,ERR_R_MALLOC_FAILURE);
-	return(0);
+	return 0;
 	}
+
+
+int ssl3_setup_buffers(SSL *s)
+	{
+	if (!ssl3_setup_read_buffer(s))
+		return 0;
+	if (!ssl3_setup_write_buffer(s))
+		return 0;
+	return 1;
+	}
+
+int ssl3_release_write_buffer(SSL *s)
+	{
+	if (s->s3->wbuf.buf != NULL)
+		{
+		freelist_insert(s->ctx, 0, s->s3->wbuf.len, s->s3->wbuf.buf);
+		s->s3->wbuf.buf = NULL;
+		}
+	return 1;
+	}
+
+int ssl3_release_read_buffer(SSL *s)
+	{
+	if (s->s3->rbuf.buf != NULL)
+		{
+		freelist_insert(s->ctx, 1, s->s3->rbuf.len, s->s3->rbuf.buf);
+		s->s3->rbuf.buf = NULL;
+		}
+	return 1;
+	}
+
diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c
index 6844a43..2c0dc7a 100644
--- a/ssl/s3_lib.c
+++ b/ssl/s3_lib.c
@@ -2148,9 +2148,9 @@
 
 	ssl3_cleanup_key_block(s);
 	if (s->s3->rbuf.buf != NULL)
-		OPENSSL_free(s->s3->rbuf.buf);
+		ssl3_release_read_buffer(s);
 	if (s->s3->wbuf.buf != NULL)
-		OPENSSL_free(s->s3->wbuf.buf);
+		ssl3_release_write_buffer(s);
 	if (s->s3->rrec.comp != NULL)
 		OPENSSL_free(s->s3->rrec.comp);
 #ifndef OPENSSL_NO_DH
diff --git a/ssl/s3_pkt.c b/ssl/s3_pkt.c
index 3e5ce0c..b4a1629 100644
--- a/ssl/s3_pkt.c
+++ b/ssl/s3_pkt.c
@@ -137,6 +137,10 @@
 	if (n <= 0) return n;
 
 	rb    = &(s->s3->rbuf);
+	if (rb->buf == NULL)
+		if (!ssl3_setup_read_buffer(s))
+			return -1;
+
 	left  = rb->left;
 #if defined(SSL3_ALIGN_PAYLOAD) && SSL3_ALIGN_PAYLOAD!=0
 	align = (long)rb->buf + SSL3_RT_HEADER_LENGTH;
@@ -234,6 +238,11 @@
 		if (i <= 0)
 			{
 			rb->left = left;
+#ifndef OPENSSL_NO_RELEASE_BUFFERS
+			if (len+left == 0 &&
+			    (s->mode & SSL_MODE_RELEASE_BUFFERS))
+				ssl3_release_read_buffer(s);
+#endif
 			return(i);
 			}
 		left+=i;
@@ -609,6 +618,10 @@
 	SSL3_BUFFER *wb=&(s->s3->wbuf);
 	SSL_SESSION *sess;
 
+ 	if (wb->buf == NULL)
+		if (!ssl3_setup_write_buffer(s))
+			return -1;
+
 	/* first check if there is a SSL3_BUFFER still being written
 	 * out.  This will happen with non blocking IO */
 	if (wb->left != 0)
@@ -812,6 +825,10 @@
 			{
 			wb->left=0;
 			wb->offset+=i;
+#ifndef OPENSSL_NO_RELEASE_BUFFERS
+			if (s->mode & SSL_MODE_RELEASE_BUFFERS)
+				ssl3_release_write_buffer(s);
+#endif
 			s->rwstate=SSL_NOTHING;
 			return(s->s3->wpend_ret);
 			}
@@ -857,7 +874,7 @@
 	void (*cb)(const SSL *ssl,int type2,int val)=NULL;
 
 	if (s->s3->rbuf.buf == NULL) /* Not initialized yet */
-		if (!ssl3_setup_buffers(s))
+		if (!ssl3_setup_read_buffer(s))
 			return(-1);
 
 	if ((type && (type != SSL3_RT_APPLICATION_DATA) && (type != SSL3_RT_HANDSHAKE) && type) ||
@@ -966,6 +983,10 @@
 				{
 				s->rstate=SSL_ST_READ_HEADER;
 				rr->off=0;
+#ifndef OPENSSL_NO_RELEASE_BUFFERS
+				if ((s->mode & SSL_MODE_RELEASE_BUFFERS))
+					ssl3_release_read_buffer(s);
+#endif
 				}
 			}
 		return(n);
diff --git a/ssl/ssl.h b/ssl/ssl.h
index b43fc0d..a9de499 100644
--- a/ssl/ssl.h
+++ b/ssl/ssl.h
@@ -587,7 +587,10 @@
 #define SSL_MODE_AUTO_RETRY 0x00000004L
 /* Don't attempt to automatically build certificate chain */
 #define SSL_MODE_NO_AUTO_CHAIN 0x00000008L
-
+/* Save RAM by releasing read and write buffers when they're empty. (SSL3 and
+ * TLS only.)  "Released" buffers are put onto a free-list in the context
+ * or just freed (depending on the context's setting for freelist_max_len). */
+#define SSL_MODE_RELEASE_BUFFERS 0x00000010L
 
 /* Note: SSL[_CTX]_set_{options,mode} use |= op on the previous value,
  * they cannot be used to clear bits. */
@@ -836,6 +839,13 @@
 	unsigned int (*psk_server_callback)(SSL *ssl, const char *identity,
 		unsigned char *psk, unsigned int max_psk_len);
 #endif
+
+#if !defined(OPENSSL_NO_BUF_FREELISTS) || !defined(OPENSSL_NO_RELEASE_BUFFERS)
+#define SSL_MAX_BUF_FREELIST_LEN_DEFAULT 32
+	unsigned int freelist_max_len;
+	struct ssl3_buf_freelist_st *wbuf_freelist;
+	struct ssl3_buf_freelist_st *rbuf_freelist;
+#endif
 	};
 
 #define SSL_SESS_CACHE_OFF			0x0000
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index fac080c..ebff8e2 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -1449,7 +1449,7 @@
 SSL_CTX *SSL_CTX_new(const SSL_METHOD *meth)
 	{
 	SSL_CTX *ret=NULL;
-	
+
 	if (meth == NULL)
 		{
 		SSLerr(SSL_F_SSL_CTX_NEW,SSL_R_NULL_SSL_METHOD_PASSED);
@@ -1581,6 +1581,24 @@
 	ret->psk_client_callback=NULL;
 	ret->psk_server_callback=NULL;
 #endif
+#if !defined(OPENSSL_NO_BUF_FREELISTS) && !defined(OPENSSL_NO_RELEASE_BUFFERS)
+	ret->freelist_max_len = SSL_MAX_BUF_FREELIST_LEN_DEFAULT;
+	ret->rbuf_freelist = OPENSSL_malloc(sizeof(SSL3_BUF_FREELIST));
+	if (!ret->rbuf_freelist)
+		goto err;
+	ret->rbuf_freelist->chunklen = 0;
+	ret->rbuf_freelist->len = 0;
+	ret->rbuf_freelist->head = NULL;
+	ret->wbuf_freelist = OPENSSL_malloc(sizeof(SSL3_BUF_FREELIST));
+	if (!ret->wbuf_freelist)
+		{
+		OPENSSL_free(ret->rbuf_freelist);
+		goto err;
+		}
+	ret->wbuf_freelist->chunklen = 0;
+	ret->wbuf_freelist->len = 0;
+	ret->wbuf_freelist->head = NULL;
+#endif
 	return(ret);
 err:
 	SSLerr(SSL_F_SSL_CTX_NEW,ERR_R_MALLOC_FAILURE);
@@ -1594,6 +1612,20 @@
     { OPENSSL_free(comp); }
 #endif
 
+#if !defined(OPENSSL_NO_BUF_FREELISTS) && !defined(OPENSSL_NO_RELEASE_BUFFERS)
+static void
+ssl_buf_freelist_free(SSL3_BUF_FREELIST *list)
+	{
+	SSL3_BUF_FREELIST_ENTRY *ent, *next;
+	for (ent = list->head; ent; ent = next)
+		{
+		next = ent->next;
+		OPENSSL_free(ent);
+		}
+	OPENSSL_free(list);
+	}
+#endif
+
 void SSL_CTX_free(SSL_CTX *a)
 	{
 	int i;
@@ -1660,6 +1692,14 @@
 	if (a->client_cert_engine)
 		ENGINE_finish(a->client_cert_engine);
 #endif
+
+#if !defined(OPENSSL_NO_BUF_FREELISTS) && !defined(OPENSSL_NO_RELEASE_BUFFERS)
+	if (a->wbuf_freelist)
+		ssl_buf_freelist_free(a->wbuf_freelist);
+	if (a->rbuf_freelist)
+		ssl_buf_freelist_free(a->rbuf_freelist);
+#endif
+
 	OPENSSL_free(a);
 	}
 
@@ -2901,7 +2941,7 @@
  * vairable, freeing  EVP_MD_CTX previously stored in that variable, if
  * any. If EVP_MD pointer is passed, initializes ctx with this md
  * Returns newly allocated ctx;
- */ 
+ */
 
 EVP_MD_CTX *ssl_replace_hash(EVP_MD_CTX **hash,const EVP_MD *md) 
 {
diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h
index 55b2330..0238676 100644
--- a/ssl/ssl_locl.h
+++ b/ssl/ssl_locl.h
@@ -561,6 +561,20 @@
 	COMP_METHOD *method; /* The method :-) */
 	} SSL3_COMP;
 
+#if !defined(OPENSSL_NO_BUF_FREELISTS) && !defined(OPENSSL_NO_RELEASE_BUFFERS)
+typedef struct ssl3_buf_freelist_st
+	{
+	size_t chunklen;
+	int len;
+	struct ssl3_buf_freelist_entry_st *head;
+	} SSL3_BUF_FREELIST;
+
+typedef struct ssl3_buf_freelist_entry_st
+	{
+	struct ssl3_buf_freelist_entry_st *next;
+	} SSL3_BUF_FREELIST_ENTRY;
+#endif
+
 extern SSL3_ENC_METHOD ssl3_undef_enc_method;
 OPENSSL_EXTERN SSL_CIPHER ssl2_ciphers[];
 OPENSSL_EXTERN SSL_CIPHER ssl3_ciphers[];
@@ -859,6 +873,10 @@
 SSL_CIPHER *ssl3_choose_cipher(SSL *ssl,STACK_OF(SSL_CIPHER) *clnt,
 			       STACK_OF(SSL_CIPHER) *srvr);
 int	ssl3_setup_buffers(SSL *s);
+int	ssl3_setup_read_buffer(SSL *s);
+int	ssl3_setup_write_buffer(SSL *s);
+int	ssl3_release_read_buffer(SSL *s);
+int	ssl3_release_write_buffer(SSL *s);
 void ssl3_digest_cached_records(SSL *s);
 int	ssl3_new(SSL *s);
 void	ssl3_free(SSL *s);