- Added DEFLATE compression support as per RFC3749 (requires zlib)

diff --git a/library/ssl_cli.c b/library/ssl_cli.c
index 99120d5..4912379 100644
--- a/library/ssl_cli.c
+++ b/library/ssl_cli.c
@@ -135,11 +135,21 @@
         *p++ = (unsigned char)( ssl->ciphersuites[i]      );
     }
 
+#if defined(POLARSSL_ZLIB_SUPPORT)
+    SSL_DEBUG_MSG( 3, ( "client hello, compress len.: %d", 2 ) );
+    SSL_DEBUG_MSG( 3, ( "client hello, compress alg.: %d %d",
+                        SSL_COMPRESS_NULL, SSL_COMPRESS_DEFLATE ) );
+
+    *p++ = 2;
+    *p++ = SSL_COMPRESS_NULL;
+    *p++ = SSL_COMPRESS_DEFLATE;
+#else
     SSL_DEBUG_MSG( 3, ( "client hello, compress len.: %d", 1 ) );
-    SSL_DEBUG_MSG( 3, ( "client hello, compress alg.: %d", 0 ) );
+    SSL_DEBUG_MSG( 3, ( "client hello, compress alg.: %d", SSL_COMPRESS_NULL ) );
 
     *p++ = 1;
     *p++ = SSL_COMPRESS_NULL;
+#endif
 
     if ( ssl->hostname != NULL )
     {
@@ -281,7 +291,7 @@
 #if defined(POLARSSL_DEBUG_C)
     time_t t;
 #endif
-    int ret, i;
+    int ret, i, comp;
     size_t n;
     int ext_len;
     unsigned char *buf;
@@ -367,6 +377,7 @@
     }
 
     i = ( buf[39 + n] << 8 ) | buf[40 + n];
+    comp = buf[41 + n];
 
     /*
      * Initialize update checksum functions
@@ -381,6 +392,7 @@
      */
     if( ssl->resume == 0 || n == 0 ||
         ssl->session->ciphersuite != i ||
+        ssl->session->compression != comp ||
         ssl->session->length != n ||
         memcmp( ssl->session->id, buf + 39, n ) != 0 )
     {
@@ -388,6 +400,7 @@
         ssl->resume = 0;
         ssl->session->start = time( NULL );
         ssl->session->ciphersuite = i;
+        ssl->session->compression = comp;
         ssl->session->length = n;
         memcpy( ssl->session->id, buf + 39, n );
     }
@@ -421,11 +434,16 @@
             break;
     }
 
-    if( buf[41 + n] != SSL_COMPRESS_NULL )
+    if( comp != SSL_COMPRESS_NULL
+#if defined(POLARSSL_ZLIB_SUPPORT)
+        && comp != SSL_COMPRESS_DEFLATE
+#endif
+      )
     {
         SSL_DEBUG_MSG( 1, ( "bad server hello message" ) );
         return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO );
     }
+    ssl->session->compression = comp;
 
     /* TODO: Process extensions */
 
diff --git a/library/ssl_srv.c b/library/ssl_srv.c
index ecf1536..e4de68e 100644
--- a/library/ssl_srv.c
+++ b/library/ssl_srv.c
@@ -316,6 +316,18 @@
             return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO );
         }
 
+        ssl->session->compression = SSL_COMPRESS_NULL;
+#if defined(POLARSSL_ZLIB_SUPPORT)
+        for( i = 0; i < comp_len; ++i )
+        {
+            if( buf[41 + sess_len + ciph_len + i] == SSL_COMPRESS_DEFLATE )
+            {
+                ssl->session->compression = SSL_COMPRESS_DEFLATE;
+                break;
+            }
+        }
+#endif
+
         SSL_DEBUG_BUF( 3, "client hello, random bytes",
                        buf +  6,  32 );
         SSL_DEBUG_BUF( 3, "client hello, session id",
@@ -449,11 +461,12 @@
 
     *p++ = (unsigned char)( ssl->session->ciphersuite >> 8 );
     *p++ = (unsigned char)( ssl->session->ciphersuite      );
-    *p++ = SSL_COMPRESS_NULL;
+    *p++ = (unsigned char)( ssl->session->compression      );
 
     SSL_DEBUG_MSG( 3, ( "server hello, chosen ciphersuite: %d",
                    ssl->session->ciphersuite ) );
-    SSL_DEBUG_MSG( 3, ( "server hello, compress alg.: %d", 0 ) );
+    SSL_DEBUG_MSG( 3, ( "server hello, compress alg.: %d",
+                   ssl->session->compression ) );
 
     ssl->out_msglen  = p - buf;
     ssl->out_msgtype = SSL_MSG_HANDSHAKE;
diff --git a/library/ssl_tls.c b/library/ssl_tls.c
index fab2004..a458e62 100644
--- a/library/ssl_tls.c
+++ b/library/ssl_tls.c
@@ -639,6 +639,25 @@
 
     memset( keyblk, 0, sizeof( keyblk ) );
 
+#if defined(POLARSSL_ZLIB_SUPPORT)
+    // Initialize compression
+    //
+    if( ssl->session->compression == SSL_COMPRESS_DEFLATE )
+    {
+        SSL_DEBUG_MSG( 3, ( "Initializing zlib states" ) );
+
+        memset( &ssl->ctx_deflate, 0, sizeof( ssl->ctx_deflate ) );
+        memset( &ssl->ctx_inflate, 0, sizeof( ssl->ctx_inflate ) );
+
+        if( deflateInit( &ssl->ctx_deflate, Z_DEFAULT_COMPRESSION ) != Z_OK ||
+            inflateInit( &ssl->ctx_inflate ) != Z_OK )
+        {
+            SSL_DEBUG_MSG( 1, ( "Failed to initialize compression" ) );
+            return( POLARSSL_ERR_SSL_COMPRESSION_FAILED );
+        }
+    }
+#endif /* POLARSSL_ZLIB_SUPPORT */
+
     SSL_DEBUG_MSG( 2, ( "<= derive keys" ) );
 
     return( 0 );
@@ -1397,6 +1416,113 @@
     return( 0 );
 }
 
+#if defined(POLARSSL_ZLIB_SUPPORT)
+/*
+ * Compression/decompression functions
+ */
+static int ssl_compress_buf( ssl_context *ssl )
+{
+    int ret;
+    unsigned char *msg_post = ssl->out_msg;
+    size_t len_pre = ssl->out_msglen;
+    unsigned char *msg_pre;
+
+    SSL_DEBUG_MSG( 2, ( "=> compress buf" ) );
+
+    msg_pre = (unsigned char*) malloc( len_pre );
+    if( msg_pre == NULL )
+    {
+        SSL_DEBUG_MSG( 1, ( "malloc(%d bytes) failed", len_pre ) );
+        return( POLARSSL_ERR_SSL_MALLOC_FAILED );
+    }
+
+    memcpy( msg_pre, ssl->out_msg, len_pre );
+
+    SSL_DEBUG_MSG( 3, ( "before compression: msglen = %d, ",
+                   ssl->out_msglen ) );
+
+    SSL_DEBUG_BUF( 4, "before compression: output payload",
+                   ssl->out_msg, ssl->out_msglen );
+
+    ssl->ctx_deflate.next_in = msg_pre;
+    ssl->ctx_deflate.avail_in = len_pre;
+    ssl->ctx_deflate.next_out = msg_post;
+    ssl->ctx_deflate.avail_out = SSL_BUFFER_LEN;
+
+    ret = deflate( &ssl->ctx_deflate, Z_SYNC_FLUSH );
+    if( ret != Z_OK )
+    {
+        SSL_DEBUG_MSG( 1, ( "failed to perform compression (%d)", ret ) );
+        return( POLARSSL_ERR_SSL_COMPRESSION_FAILED );
+    }
+
+    ssl->out_msglen = SSL_BUFFER_LEN - ssl->ctx_deflate.avail_out;
+
+    free( msg_pre );
+
+    SSL_DEBUG_MSG( 3, ( "after compression: msglen = %d, ",
+                   ssl->out_msglen ) );
+
+    SSL_DEBUG_BUF( 4, "after compression: output payload",
+                   ssl->out_msg, ssl->out_msglen );
+
+    SSL_DEBUG_MSG( 2, ( "<= compress buf" ) );
+
+    return( 0 );
+}
+
+static int ssl_decompress_buf( ssl_context *ssl )
+{
+    int ret;
+    unsigned char *msg_post = ssl->in_msg;
+    size_t len_pre = ssl->in_msglen;
+    unsigned char *msg_pre;
+
+    SSL_DEBUG_MSG( 2, ( "=> decompress buf" ) );
+
+    msg_pre = (unsigned char*) malloc( len_pre );
+    if( msg_pre == NULL )
+    {
+        SSL_DEBUG_MSG( 1, ( "malloc(%d bytes) failed", len_pre ) );
+        return( POLARSSL_ERR_SSL_MALLOC_FAILED );
+    }
+
+    memcpy( msg_pre, ssl->in_msg, len_pre );
+
+    SSL_DEBUG_MSG( 3, ( "before decompression: msglen = %d, ",
+                   ssl->in_msglen ) );
+
+    SSL_DEBUG_BUF( 4, "before decompression: input payload",
+                   ssl->in_msg, ssl->in_msglen );
+
+    ssl->ctx_inflate.next_in = msg_pre;
+    ssl->ctx_inflate.avail_in = len_pre;
+    ssl->ctx_inflate.next_out = msg_post;
+    ssl->ctx_inflate.avail_out = SSL_MAX_CONTENT_LEN;
+
+    ret = inflate( &ssl->ctx_inflate, Z_SYNC_FLUSH );
+    if( ret != Z_OK )
+    {
+        SSL_DEBUG_MSG( 1, ( "failed to perform decompression (%d)", ret ) );
+        return( POLARSSL_ERR_SSL_COMPRESSION_FAILED );
+    }
+
+    ssl->in_msglen = SSL_MAX_CONTENT_LEN - ssl->ctx_inflate.avail_out;
+
+    free( msg_pre );
+
+    SSL_DEBUG_MSG( 3, ( "after decompression: msglen = %d, ",
+                   ssl->in_msglen ) );
+
+    SSL_DEBUG_BUF( 4, "after decompression: input payload",
+                   ssl->in_msg, ssl->in_msglen );
+
+    SSL_DEBUG_MSG( 2, ( "<= decompress buf" ) );
+
+    return( 0 );
+}
+#endif /* POLARSSL_ZLIB_SUPPORT */
+
 /*
  * Fill the input message buffer
  */
@@ -1495,6 +1621,20 @@
         ssl->update_checksum( ssl, ssl->out_msg, len );
     }
 
+#if defined(POLARSSL_ZLIB_SUPPORT)
+    if( ssl->do_crypt != 0 &&
+        ssl->session->compression == SSL_COMPRESS_DEFLATE )
+    {
+        if( ( ret = ssl_compress_buf( ssl ) ) != 0 )
+        {
+            SSL_DEBUG_RET( 1, "ssl_compress_buf", ret );
+            return( ret );
+        }
+
+        len = ssl->out_msglen;
+    }
+#endif /*POLARSSL_ZLIB_SUPPORT */
+
 #if defined(POLARSSL_SSL_HW_RECORD_ACCEL)
     if( ssl_hw_record_write != NULL)
     {
@@ -1708,6 +1848,21 @@
         }
     }
 
+#if defined(POLARSSL_ZLIB_SUPPORT)
+    if( ssl->do_crypt != 0 &&
+        ssl->session->compression == SSL_COMPRESS_DEFLATE )
+    {
+        if( ( ret = ssl_decompress_buf( ssl ) ) != 0 )
+        {
+            SSL_DEBUG_RET( 1, "ssl_decompress_buf", ret );
+            return( ret );
+        }
+
+        ssl->in_hdr[3] = (unsigned char)( ssl->in_msglen >> 8 );
+        ssl->in_hdr[4] = (unsigned char)( ssl->in_msglen      );
+    }
+#endif /* POLARSSL_ZLIB_SUPPORT */
+
     if( ssl->in_msgtype != SSL_MSG_HANDSHAKE &&
         ssl->in_msgtype != SSL_MSG_ALERT &&
         ssl->in_msgtype != SSL_MSG_CHANGE_CIPHER_SPEC &&
@@ -1761,12 +1916,13 @@
          */
         if( ssl->in_msg[0] == SSL_ALERT_LEVEL_FATAL )
         {
-            SSL_DEBUG_MSG( 1, ( "is a fatal alert message" ) );
+            SSL_DEBUG_MSG( 1, ( "is a fatal alert message (msg %d)",
+                           ssl->in_msg[1] ) );
             /**
              * Subtract from error code as ssl->in_msg[1] is 7-bit positive
              * error identifier.
              */
-            return( POLARSSL_ERR_SSL_FATAL_ALERT_MESSAGE - ssl->in_msg[1] );
+            return( POLARSSL_ERR_SSL_FATAL_ALERT_MESSAGE );
         }
 
         if( ssl->in_msg[0] == SSL_ALERT_LEVEL_WARNING &&
@@ -2514,7 +2670,7 @@
  * Reset an initialized and used SSL context for re-use while retaining
  * all application-set variables, function pointers and data.
  */
-void ssl_session_reset( ssl_context *ssl )
+int ssl_session_reset( ssl_context *ssl )
 {
     ssl->state = SSL_HELLO_REQUEST;
     
@@ -2555,9 +2711,39 @@
     if( ssl_hw_record_reset != NULL)
     {
         SSL_DEBUG_MSG( 2, ( "going for ssl_hw_record_reset()" ) );
-        ssl_hw_record_reset( ssl );
+        if( ssl_hw_record_reset( ssl ) != 0 )
+        {
+            SSL_DEBUG_RET( 1, "ssl_hw_record_reset", ret );
+            return( POLARSSL_ERR_SSL_HW_ACCEL_FAILED );
+        }
     }
 #endif
+
+#if defined(POLARSSL_ZLIB_SUPPORT)
+    // Reset compression state
+    //
+    if( ssl->session->compression == SSL_COMPRESS_DEFLATE )
+    {
+        ssl->ctx_deflate.next_in = Z_NULL;
+        ssl->ctx_deflate.next_out = Z_NULL;
+        ssl->ctx_deflate.avail_in = 0;
+        ssl->ctx_deflate.avail_out = 0;
+
+        ssl->ctx_inflate.next_in = Z_NULL;
+        ssl->ctx_inflate.next_out = Z_NULL;
+        ssl->ctx_inflate.avail_in = 0;
+        ssl->ctx_inflate.avail_out = 0;
+
+        if( deflateReset( &ssl->ctx_deflate ) != Z_OK ||
+            inflateReset( &ssl->ctx_inflate ) != Z_OK )
+        {
+            SSL_DEBUG_MSG( 1, ( "Failed to reset compression" ) );
+            return( POLARSSL_ERR_SSL_COMPRESSION_FAILED );
+        }
+    }
+#endif /* POLARSSL_ZLIB_SUPPORT */
+
+    return( 0 );
 }
 
 /*
@@ -3265,6 +3451,11 @@
     }
 #endif
 
+#if defined(POLARSSL_ZLIB_SUPPORT)
+    deflateEnd( &ssl->ctx_deflate );
+    inflateEnd( &ssl->ctx_inflate );
+#endif
+
     SSL_DEBUG_MSG( 2, ( "<= free" ) );
 
     /* Actually free after last debug message */