Merge branch 'maximum-fragment-length' into development
diff --git a/include/polarssl/ssl.h b/include/polarssl/ssl.h
index eca6879..4e1d25a 100644
--- a/include/polarssl/ssl.h
+++ b/include/polarssl/ssl.h
@@ -117,6 +117,15 @@
 #define SSL_MINOR_VERSION_2             2   /*!< TLS v1.1 */
 #define SSL_MINOR_VERSION_3             3   /*!< TLS v1.2 */
 
+/* RFC 6066 section 4, see also mfl_code_to_length in ssl_tls.c
+ * NONE must be zero so that memset()ing structure to zero works */
+#define SSL_MAX_FRAG_LEN_NONE           0   /*!< don't use this extension   */
+#define SSL_MAX_FRAG_LEN_512            1   /*!< MaxFragmentLength 2^9      */
+#define SSL_MAX_FRAG_LEN_1024           2   /*!< MaxFragmentLength 2^10     */
+#define SSL_MAX_FRAG_LEN_2048           3   /*!< MaxFragmentLength 2^11     */
+#define SSL_MAX_FRAG_LEN_4096           4   /*!< MaxFragmentLength 2^12     */
+#define SSL_MAX_FRAG_LEN_INVALID        5   /*!< first invalid value        */
+
 #define SSL_IS_CLIENT                   0
 #define SSL_IS_SERVER                   1
 #define SSL_COMPRESS_NULL               0
@@ -240,6 +249,8 @@
 #define TLS_EXT_SERVERNAME                   0
 #define TLS_EXT_SERVERNAME_HOSTNAME          0
 
+#define TLS_EXT_MAX_FRAGMENT_LENGTH          1
+
 #define TLS_EXT_SUPPORTED_ELLIPTIC_CURVES   10
 #define TLS_EXT_SUPPORTED_POINT_FORMATS     11
 
@@ -320,6 +331,8 @@
 #if defined(POLARSSL_X509_PARSE_C)
     x509_cert *peer_cert;       /*!< peer X.509 cert chain */
 #endif /* POLARSSL_X509_PARSE_C */
+
+    unsigned char mfl_code;     /*!< MaxFragmentLength negotiated by peer */
 };
 
 /*
@@ -498,6 +511,8 @@
     size_t out_msglen;          /*!< record header: message length    */
     size_t out_left;            /*!< amount of data not yet written   */
 
+    unsigned char mfl_code;     /*!< MaxFragmentLength chosen by us   */
+
     /*
      * PKI layer
      */
@@ -945,6 +960,23 @@
 void ssl_set_min_version( ssl_context *ssl, int major, int minor );
 
 /**
+ * \brief          Set the maximum fragment length to emit and/or negotiate
+ *                 (Default: SSL_MAX_CONTENT_LEN, usually 2^14 bytes)
+ *                 (Server: set maximum fragment length to emit,
+ *                 usually negotiated by the client during handshake
+ *                 (Client: set maximum fragment length to emit *and*
+ *                 negotiate with the server during handshake)
+ *
+ * \param ssl      SSL context
+ * \param mfl      Code for maximum fragment length (allowed values:
+ *                 SSL_MAX_FRAG_LEN_512,  SSL_MAX_FRAG_LEN_1024,
+ *                 SSL_MAX_FRAG_LEN_2048, SSL_MAX_FRAG_LEN_4096)
+ *
+ * \return         O if successful or POLARSSL_ERR_SSL_BAD_INPUT_DATA
+ */
+int ssl_set_max_frag_len( ssl_context *ssl, unsigned char mfl_code );
+
+/**
  * \brief          Enable / Disable renegotiation support for connection when
  *                 initiated by peer
  *                 (Default: SSL_RENEGOTIATION_DISABLED)
diff --git a/library/ssl_cli.c b/library/ssl_cli.c
index aeba799..c89bf0c 100644
--- a/library/ssl_cli.c
+++ b/library/ssl_cli.c
@@ -269,6 +269,30 @@
 }
 #endif
 
+static void ssl_write_max_fragment_length_ext( ssl_context *ssl,
+                                               unsigned char *buf,
+                                               size_t *olen )
+{
+    unsigned char *p = buf;
+
+    if( ssl->mfl_code == SSL_MAX_FRAG_LEN_NONE ) {
+        *olen = 0;
+        return;
+    }
+
+    SSL_DEBUG_MSG( 3, ( "client hello, adding max_fragment_length extension" ) );
+
+    *p++ = (unsigned char)( ( TLS_EXT_MAX_FRAGMENT_LENGTH >> 8 ) & 0xFF );
+    *p++ = (unsigned char)( ( TLS_EXT_MAX_FRAGMENT_LENGTH      ) & 0xFF );
+
+    *p++ = 0x00;
+    *p++ = 1;
+
+    *p++ = ssl->mfl_code;
+
+    *olen = 5;
+}
+
 static int ssl_write_client_hello( ssl_context *ssl )
 {
     int ret;
@@ -436,6 +460,9 @@
     ext_len += olen;
 #endif
 
+    ssl_write_max_fragment_length_ext( ssl, p + 2 + ext_len, &olen );
+    ext_len += olen;
+
     SSL_DEBUG_MSG( 3, ( "client hello, total extension length: %d",
                    ext_len ) );
 
@@ -461,7 +488,7 @@
 }
 
 static int ssl_parse_renegotiation_info( ssl_context *ssl,
-                                         unsigned char *buf,
+                                         const unsigned char *buf,
                                          size_t len )
 {
     int ret;
@@ -499,6 +526,23 @@
 
     return( 0 );
 }
+static int ssl_parse_max_fragment_length_ext( ssl_context *ssl,
+                                              const unsigned char *buf,
+                                              size_t len )
+{
+    /*
+     * server should use the extension only if we did,
+     * and if so the server's value should match ours (and len is always 1)
+     */
+    if( ssl->mfl_code == SSL_MAX_FRAG_LEN_NONE ||
+        len != 1 ||
+        buf[0] != ssl->mfl_code )
+    {
+        return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO );
+    }
+
+    return( 0 );
+}
 
 static int ssl_parse_server_hello( ssl_context *ssl )
 {
@@ -690,6 +734,8 @@
 
     ext = buf + 44 + n;
 
+    SSL_DEBUG_MSG( 2, ( "server hello, total extension length: %d", ext_len ) );
+
     while( ext_len )
     {
         unsigned int ext_id   = ( ( ext[0] <<  8 )
@@ -714,6 +760,17 @@
 
             break;
 
+        case TLS_EXT_MAX_FRAGMENT_LENGTH:
+            SSL_DEBUG_MSG( 3, ( "found max_fragment_length extension" ) );
+
+            if( ( ret = ssl_parse_max_fragment_length_ext( ssl,
+                            ext + 4, ext_size ) ) != 0 )
+            {
+                return( ret );
+            }
+
+            break;
+
         default:
             SSL_DEBUG_MSG( 3, ( "unknown extension found: %d (ignoring)",
                            ext_id ) );
diff --git a/library/ssl_srv.c b/library/ssl_srv.c
index 2aef9c4..3e18386 100644
--- a/library/ssl_srv.c
+++ b/library/ssl_srv.c
@@ -291,6 +291,21 @@
 }
 #endif /* POLARSSL_ECP_C */
 
+static int ssl_parse_max_fragment_length_ext( ssl_context *ssl,
+                                              const unsigned char *buf,
+                                              size_t len )
+{
+    if( len != 1 || buf[0] >= SSL_MAX_FRAG_LEN_INVALID )
+    {
+        SSL_DEBUG_MSG( 1, ( "bad client hello message" ) );
+        return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO );
+    }
+
+    ssl->session_negotiate->mfl_code = buf[0];
+
+    return( 0 );
+}
+
 #if defined(POLARSSL_SSL_SRV_SUPPORT_SSLV2_CLIENT_HELLO)
 static int ssl_parse_client_hello_v2( ssl_context *ssl )
 {
@@ -825,6 +840,14 @@
             break;
 #endif /* POLARSSL_ECP_C */
 
+        case TLS_EXT_MAX_FRAGMENT_LENGTH:
+            SSL_DEBUG_MSG( 3, ( "found max fragment length extension" ) );
+
+            ret = ssl_parse_max_fragment_length_ext( ssl, ext + 4, ext_size );
+            if( ret != 0 )
+                return( ret );
+            break;
+
         default:
             SSL_DEBUG_MSG( 3, ( "unknown extension found: %d (ignoring)",
                            ext_id ) );
@@ -934,13 +957,67 @@
     return( 0 );
 }
 
+static void ssl_write_renegotiation_ext( ssl_context *ssl,
+                                         unsigned char *buf,
+                                         size_t *olen )
+{
+    unsigned char *p = buf;
+
+    if( ssl->secure_renegotiation != SSL_SECURE_RENEGOTIATION )
+    {
+        *olen = 0;
+        return;
+    }
+
+    SSL_DEBUG_MSG( 3, ( "server hello, secure renegotiation extension" ) );
+
+    *p++ = (unsigned char)( ( TLS_EXT_RENEGOTIATION_INFO >> 8 ) & 0xFF );
+    *p++ = (unsigned char)( ( TLS_EXT_RENEGOTIATION_INFO      ) & 0xFF );
+
+    *p++ = 0x00;
+    *p++ = ( ssl->verify_data_len * 2 + 1 ) & 0xFF;
+    *p++ = ssl->verify_data_len * 2 & 0xFF;
+
+    memcpy( p, ssl->peer_verify_data, ssl->verify_data_len );
+    p += ssl->verify_data_len;
+    memcpy( p, ssl->own_verify_data, ssl->verify_data_len );
+    p += ssl->verify_data_len;
+
+    *olen = 5 + ssl->verify_data_len * 2;
+}
+
+static void ssl_write_max_fragment_length_ext( ssl_context *ssl,
+                                               unsigned char *buf,
+                                               size_t *olen )
+{
+    unsigned char *p = buf;
+
+    if( ssl->session_negotiate->mfl_code == SSL_MAX_FRAG_LEN_NONE )
+    {
+        *olen = 0;
+        return;
+    }
+
+    SSL_DEBUG_MSG( 3, ( "server hello, max_fragment_length extension" ) );
+
+    *p++ = (unsigned char)( ( TLS_EXT_MAX_FRAGMENT_LENGTH >> 8 ) & 0xFF );
+    *p++ = (unsigned char)( ( TLS_EXT_MAX_FRAGMENT_LENGTH      ) & 0xFF );
+
+    *p++ = 0x00;
+    *p++ = 1;
+
+    *p++ = ssl->session_negotiate->mfl_code;
+
+    *olen = 5;
+}
+
 static int ssl_write_server_hello( ssl_context *ssl )
 {
 #if defined(POLARSSL_HAVE_TIME)
     time_t t;
 #endif
     int ret, n;
-    size_t ext_len = 0;
+    size_t olen, ext_len = 0;
     unsigned char *buf, *p;
 
     SSL_DEBUG_MSG( 2, ( "=> write server hello" ) );
@@ -986,10 +1063,12 @@
     SSL_DEBUG_BUF( 3, "server hello, random bytes", buf + 6, 32 );
 
     /*
-     *    38  .  38   session id length
-     *    39  . 38+n  session id
-     *   39+n . 40+n  chosen ciphersuite
-     *   41+n . 41+n  chosen compression alg.
+     *    38  .  38     session id length
+     *    39  . 38+n    session id
+     *   39+n . 40+n    chosen ciphersuite
+     *   41+n . 41+n    chosen compression alg.
+     *   42+n . 43+n    extensions length
+     *   44+n . 43+n+m  extensions
      */
     ssl->session_negotiate->length = n = 32;
     *p++ = (unsigned char) ssl->session_negotiate->length;
@@ -1040,34 +1119,20 @@
     SSL_DEBUG_MSG( 3, ( "server hello, compress alg.: %d",
                    ssl->session_negotiate->compression ) );
 
-    if( ssl->secure_renegotiation == SSL_SECURE_RENEGOTIATION )
-    {
-        SSL_DEBUG_MSG( 3, ( "server hello, prepping for secure renegotiation extension" ) );
-        ext_len += 5 + ssl->verify_data_len * 2;
+    /*
+     *  First write extensions, then the total length
+     */
+    ssl_write_renegotiation_ext( ssl, p + 2 + ext_len, &olen );
+    ext_len += olen;
 
-        SSL_DEBUG_MSG( 3, ( "server hello, total extension length: %d",
-                       ext_len ) );
+    ssl_write_max_fragment_length_ext( ssl, p + 2 + ext_len, &olen );
+    ext_len += olen;
 
-        *p++ = (unsigned char)( ( ext_len >> 8 ) & 0xFF );
-        *p++ = (unsigned char)( ( ext_len      ) & 0xFF );
+    SSL_DEBUG_MSG( 3, ( "server hello, total extension length: %d", ext_len ) );
 
-        /*
-         * Secure renegotiation
-         */
-        SSL_DEBUG_MSG( 3, ( "client hello, secure renegotiation extension" ) );
-
-        *p++ = (unsigned char)( ( TLS_EXT_RENEGOTIATION_INFO >> 8 ) & 0xFF );
-        *p++ = (unsigned char)( ( TLS_EXT_RENEGOTIATION_INFO      ) & 0xFF );
-
-        *p++ = 0x00;
-        *p++ = ( ssl->verify_data_len * 2 + 1 ) & 0xFF;
-        *p++ = ssl->verify_data_len * 2 & 0xFF;
-
-        memcpy( p, ssl->peer_verify_data, ssl->verify_data_len );
-        p += ssl->verify_data_len;
-        memcpy( p, ssl->own_verify_data, ssl->verify_data_len );
-        p += ssl->verify_data_len;
-    }
+    *p++ = (unsigned char)( ( ext_len >> 8 ) & 0xFF );
+    *p++ = (unsigned char)( ( ext_len      ) & 0xFF );
+    p += ext_len;
 
     ssl->out_msglen  = p - buf;
     ssl->out_msgtype = SSL_MSG_HANDSHAKE;
diff --git a/library/ssl_tls.c b/library/ssl_tls.c
index dfeed33..f701a8a 100644
--- a/library/ssl_tls.c
+++ b/library/ssl_tls.c
@@ -59,6 +59,23 @@
 #define strcasecmp _stricmp
 #endif
 
+/*
+ * Convert max_fragment_length codes to length.
+ * RFC 6066 says:
+ *    enum{
+ *        2^9(1), 2^10(2), 2^11(3), 2^12(4), (255)
+ *    } MaxFragmentLength;
+ * and we add 0 -> extension unused
+ */
+static unsigned int mfl_code_to_length[SSL_MAX_FRAG_LEN_INVALID] =
+{
+    SSL_MAX_CONTENT_LEN,    /* SSL_MAX_FRAG_LEN_NONE */
+    512,                    /* SSL_MAX_FRAG_LEN_512  */
+    1024,                   /* SSL_MAX_FRAG_LEN_1024 */
+    2048,                   /* SSL_MAX_FRAG_LEN_2048 */
+    4096,                   /* SSL_MAX_FRAG_LEN_4096 */
+};
+
 #if defined(POLARSSL_SSL_HW_RECORD_ACCEL)
 int (*ssl_hw_record_init)(ssl_context *ssl,
                        const unsigned char *key_enc, const unsigned char *key_dec,
@@ -3111,6 +3128,19 @@
     ssl->min_minor_ver = minor;
 }
 
+int ssl_set_max_frag_len( ssl_context *ssl, unsigned char mfl_code )
+{
+    if( mfl_code >= sizeof( mfl_code_to_length ) ||
+        mfl_code_to_length[mfl_code] > SSL_MAX_CONTENT_LEN )
+    {
+        return( POLARSSL_ERR_SSL_BAD_INPUT_DATA );
+    }
+
+    ssl->mfl_code = mfl_code;
+
+    return( 0 );
+}
+
 void ssl_set_renegotiation( ssl_context *ssl, int renegotiation )
 {
     ssl->disable_renegotiation = renegotiation;
@@ -3372,6 +3402,7 @@
 {
     int ret;
     size_t n;
+    unsigned int max_len;
 
     SSL_DEBUG_MSG( 2, ( "=> write" ) );
 
@@ -3384,8 +3415,21 @@
         }
     }
 
-    n = ( len < SSL_MAX_CONTENT_LEN )
-        ? len : SSL_MAX_CONTENT_LEN;
+    /*
+     * Assume mfl_code is correct since it was checked when set
+     */
+    max_len = mfl_code_to_length[ssl->mfl_code];
+
+    /*
+     * Check if a smaller max length was negociated
+     */
+    if( ssl->session_out != NULL &&
+        mfl_code_to_length[ssl->session_out->mfl_code] < max_len )
+    {
+        max_len = mfl_code_to_length[ssl->session_out->mfl_code];
+    }
+
+    n = ( len < max_len) ? len : max_len;
 
     if( ssl->out_left != 0 )
     {
diff --git a/programs/ssl/ssl_client2.c b/programs/ssl/ssl_client2.c
index 036cc67..56d0e91 100644
--- a/programs/ssl/ssl_client2.c
+++ b/programs/ssl/ssl_client2.c
@@ -57,8 +57,20 @@
 #define DFL_MIN_VERSION         -1
 #define DFL_MAX_VERSION         -1
 #define DFL_AUTH_MODE           SSL_VERIFY_OPTIONAL
+#define DFL_MFL_CODE            SSL_MAX_FRAG_LEN_NONE
 
-#define GET_REQUEST "GET %s HTTP/1.0\r\n\r\n"
+#define LONG_HEADER "User-agent: blah-blah-blah-blah-blah-blah-blah-blah-"   \
+    "-01--blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-" \
+    "-02--blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-" \
+    "-03--blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-" \
+    "-04--blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-" \
+    "-05--blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-" \
+    "-06--blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-" \
+    "-07--blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-END\r\n"
+
+/* Uncomment LONG_HEADER in the definition of GET_REQUEST to test sending
+ * longer paquets (for fragmentation purposes) */
+#define GET_REQUEST "GET %s HTTP/1.0\r\n" /* LONG_HEADER */ "\r\n"
 
 /*
  * global options
@@ -81,6 +93,7 @@
     int min_version;            /* minimum protocol version accepted        */
     int max_version;            /* maximum protocol version accepted        */
     int auth_mode;              /* verify mode for connection               */
+    unsigned char mfl_code;     /* code for maximum fragment length         */
 } opt;
 
 static void my_debug( void *ctx, int level, const char *str )
@@ -176,6 +189,8 @@
     "                        options: ssl3, tls1, tls1_1, tls1_2\n" \
     "    auth_mode=%%s        default: \"optional\"\n"          \
     "                        options: none, optional, required\n" \
+    "    max_frag_len=%%d     default: 16384 (tls default)" \
+    "                        options: 512, 1024, 2048, 4096" \
     USAGE_PSK                                               \
     "\n"                                                    \
     "    force_ciphersuite=<name>    default: all enabled\n"\
@@ -197,7 +212,7 @@
 #else
 int main( int argc, char *argv[] )
 {
-    int ret = 0, len, server_fd, i;
+    int ret = 0, len, server_fd, i, written, frags;
     unsigned char buf[1024];
 #if defined(POLARSSL_KEY_EXCHANGE_PSK_ENABLED)
     unsigned char psk[256];
@@ -265,6 +280,7 @@
     opt.min_version         = DFL_MIN_VERSION;
     opt.max_version         = DFL_MAX_VERSION;
     opt.auth_mode           = DFL_AUTH_MODE;
+    opt.mfl_code            = DFL_MFL_CODE;
 
     for( i = 1; i < argc; i++ )
     {
@@ -387,6 +403,19 @@
             else
                 goto usage;
         }
+        else if( strcmp( p, "max_frag_len" ) == 0 )
+        {
+            if( strcmp( q, "512" ) == 0 )
+                opt.mfl_code = SSL_MAX_FRAG_LEN_512;
+            else if( strcmp( q, "1024" ) == 0 )
+                opt.mfl_code = SSL_MAX_FRAG_LEN_1024;
+            else if( strcmp( q, "2048" ) == 0 )
+                opt.mfl_code = SSL_MAX_FRAG_LEN_2048;
+            else if( strcmp( q, "4096" ) == 0 )
+                opt.mfl_code = SSL_MAX_FRAG_LEN_4096;
+            else
+                goto usage;
+        }
         else
             goto usage;
     }
@@ -592,6 +621,8 @@
     ssl_set_endpoint( &ssl, SSL_IS_CLIENT );
     ssl_set_authmode( &ssl, opt.auth_mode );
 
+    ssl_set_max_frag_len( &ssl, opt.mfl_code );
+
     ssl_set_rng( &ssl, ctr_drbg_random, &ctr_drbg );
     ssl_set_dbg( &ssl, my_debug, stdout );
     ssl_set_bio( &ssl, net_recv, &server_fd,
@@ -682,17 +713,20 @@
 
     len = sprintf( (char *) buf, GET_REQUEST, opt.request_page );
 
-    while( ( ret = ssl_write( &ssl, buf, len ) ) <= 0 )
+    for( written = 0, frags = 0; written < len; written += ret, frags++ )
     {
-        if( ret != POLARSSL_ERR_NET_WANT_READ && ret != POLARSSL_ERR_NET_WANT_WRITE )
+        while( ( ret = ssl_write( &ssl, buf + written, len - written ) ) <= 0 )
         {
-            printf( " failed\n  ! ssl_write returned -0x%x\n\n", -ret );
-            goto exit;
+            if( ret != POLARSSL_ERR_NET_WANT_READ && ret != POLARSSL_ERR_NET_WANT_WRITE )
+            {
+                printf( " failed\n  ! ssl_write returned -0x%x\n\n", -ret );
+                goto exit;
+            }
         }
     }
 
-    len = ret;
-    printf( " %d bytes written\n\n%s", len, (char *) buf );
+    buf[written] = '\0';
+    printf( " %d bytes written in %d fragments\n\n%s\n", written, frags, (char *) buf );
 
     /*
      * 7. Read the HTTP response
diff --git a/programs/ssl/ssl_server2.c b/programs/ssl/ssl_server2.c
index 5b7639a..d5ebb81 100644
--- a/programs/ssl/ssl_server2.c
+++ b/programs/ssl/ssl_server2.c
@@ -68,11 +68,22 @@
 #define DFL_MIN_VERSION         -1
 #define DFL_MAX_VERSION         -1
 #define DFL_AUTH_MODE           SSL_VERIFY_OPTIONAL
+#define DFL_MFL_CODE            SSL_MAX_FRAG_LEN_NONE
 
+#define LONG_RESPONSE "<p>01-blah-blah-blah-blah-blah-blah-blah-blah-blah\r\n" \
+    "02-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah\r\n"  \
+    "03-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah\r\n"  \
+    "04-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah\r\n"  \
+    "05-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah\r\n"  \
+    "06-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah\r\n"  \
+    "07-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah</p>\r\n"
+
+/* Uncomment LONG_RESPONSE at the end of HTTP_RESPONSE to test sending longer
+ * packets (for fragmentation purposes) */
 #define HTTP_RESPONSE \
     "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n" \
     "<h2>PolarSSL Test Server</h2>\r\n" \
-    "<p>Successful connection using: %s</p>\r\n"
+    "<p>Successful connection using: %s</p>\r\n" // LONG_RESPONSE
 
 /*
  * global options
@@ -93,6 +104,7 @@
     int min_version;            /* minimum protocol version accepted        */
     int max_version;            /* maximum protocol version accepted        */
     int auth_mode;              /* verify mode for connection               */
+    unsigned char mfl_code;     /* code for maximum fragment length         */
 } opt;
 
 static void my_debug( void *ctx, int level, const char *str )
@@ -147,6 +159,8 @@
     "                        options: ssl3, tls1, tls1_1, tls1_2\n" \
     "    auth_mode=%%s        default: \"optional\"\n"      \
     "                        options: none, optional, required\n" \
+    "    max_frag_len=%%d     default: 16384 (tls default)" \
+    "                        options: 512, 1024, 2048, 4096" \
     USAGE_PSK                                               \
     "\n"                                                    \
     "    force_ciphersuite=<name>    default: all enabled\n"\
@@ -168,7 +182,7 @@
 #else
 int main( int argc, char *argv[] )
 {
-    int ret = 0, len;
+    int ret = 0, len, written, frags;
     int listen_fd;
     int client_fd = -1;
     unsigned char buf[1024];
@@ -250,6 +264,7 @@
     opt.min_version         = DFL_MIN_VERSION;
     opt.max_version         = DFL_MAX_VERSION;
     opt.auth_mode           = DFL_AUTH_MODE;
+    opt.mfl_code            = DFL_MFL_CODE;
 
     for( i = 1; i < argc; i++ )
     {
@@ -368,6 +383,19 @@
             else
                 goto usage;
         }
+        else if( strcmp( p, "max_frag_len" ) == 0 )
+        {
+            if( strcmp( q, "512" ) == 0 )
+                opt.mfl_code = SSL_MAX_FRAG_LEN_512;
+            else if( strcmp( q, "1024" ) == 0 )
+                opt.mfl_code = SSL_MAX_FRAG_LEN_1024;
+            else if( strcmp( q, "2048" ) == 0 )
+                opt.mfl_code = SSL_MAX_FRAG_LEN_2048;
+            else if( strcmp( q, "4096" ) == 0 )
+                opt.mfl_code = SSL_MAX_FRAG_LEN_4096;
+            else
+                goto usage;
+        }
         else
             goto usage;
     }
@@ -562,6 +590,8 @@
     ssl_set_endpoint( &ssl, SSL_IS_SERVER );
     ssl_set_authmode( &ssl, opt.auth_mode );
 
+    ssl_set_max_frag_len( &ssl, opt.mfl_code );
+
     ssl_set_rng( &ssl, ctr_drbg_random, &ctr_drbg );
     ssl_set_dbg( &ssl, my_debug, stdout );
 
@@ -748,7 +778,7 @@
         }
 
         len = ret;
-        printf( " %d bytes read\n\n%s", len, (char *) buf );
+        printf( " %d bytes read\n\n%s\n", len, (char *) buf );
 
         if( memcmp( buf, "SERVERQUIT", 10 ) == 0 )
             goto exit;
@@ -767,23 +797,26 @@
     len = sprintf( (char *) buf, HTTP_RESPONSE,
                    ssl_get_ciphersuite( &ssl ) );
 
-    while( ( ret = ssl_write( &ssl, buf, len ) ) <= 0 )
+    for( written = 0, frags = 0; written < len; written += ret, frags++ )
     {
-        if( ret == POLARSSL_ERR_NET_CONN_RESET )
+        while( ( ret = ssl_write( &ssl, buf + written, len - written ) ) <= 0 )
         {
-            printf( " failed\n  ! peer closed the connection\n\n" );
-            goto reset;
-        }
+            if( ret == POLARSSL_ERR_NET_CONN_RESET )
+            {
+                printf( " failed\n  ! peer closed the connection\n\n" );
+                goto reset;
+            }
 
-        if( ret != POLARSSL_ERR_NET_WANT_READ && ret != POLARSSL_ERR_NET_WANT_WRITE )
-        {
-            printf( " failed\n  ! ssl_write returned %d\n\n", ret );
-            goto exit;
+            if( ret != POLARSSL_ERR_NET_WANT_READ && ret != POLARSSL_ERR_NET_WANT_WRITE )
+            {
+                printf( " failed\n  ! ssl_write returned %d\n\n", ret );
+                goto exit;
+            }
         }
     }
 
-    len = ret;
-    printf( " %d bytes written\n\n%s\n", len, (char *) buf );
+    buf[written] = '\0';
+    printf( " %d bytes written in %d fragments\n\n%s\n", written, frags, (char *) buf );
 
     ret = 0;
     goto reset;