Merge
authoramurillo
Tue, 02 Jun 2015 09:15:47 -0700
changeset 30909 aa5928dda820
parent 30908 900ee6d02a1e (current diff)
parent 30905 bba6fefdd660 (diff)
child 30910 1b6a91197762
Merge
jdk/src/java.base/share/classes/sun/security/ssl/EngineArgs.java
jdk/src/java.base/share/classes/sun/security/ssl/EngineInputRecord.java
jdk/src/java.base/share/classes/sun/security/ssl/EngineOutputRecord.java
jdk/src/java.base/share/classes/sun/security/ssl/EngineWriter.java
jdk/src/java.base/share/classes/sun/security/ssl/KerberosClientKeyExchange.java
jdk/src/java.base/share/classes/sun/security/ssl/Krb5Helper.java
jdk/src/java.base/share/classes/sun/security/ssl/Krb5Proxy.java
jdk/src/java.security.jgss/share/classes/sun/security/ssl/krb5/KerberosClientKeyExchangeImpl.java
jdk/src/java.security.jgss/share/classes/sun/security/ssl/krb5/KerberosPreMasterSecret.java
jdk/src/java.security.jgss/share/classes/sun/security/ssl/krb5/Krb5ProxyImpl.java
--- a/jdk/src/java.base/share/classes/javax/net/ssl/ExtendedSSLSession.java	Mon Jun 01 10:15:21 2015 -0700
+++ b/jdk/src/java.base/share/classes/javax/net/ssl/ExtendedSSLSession.java	Tue Jun 02 09:15:47 2015 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -28,7 +28,7 @@
 import java.util.List;
 
 /**
- * Extends the <code>SSLSession</code> interface to support additional
+ * Extends the {@code SSLSession} interface to support additional
  * session attributes.
  *
  * @since 1.7
@@ -39,8 +39,8 @@
      * is willing to use.
      * <p>
      * Note: this method is used to indicate to the peer which signature
-     * algorithms may be used for digital signatures in TLS 1.2. It is
-     * not meaningful for TLS versions prior to 1.2.
+     * algorithms may be used for digital signatures in TLS/DTLS 1.2. It is
+     * not meaningful for TLS/DTLS versions prior to 1.2.
      * <p>
      * The signature algorithm name must be a standard Java Security
      * name (such as "SHA1withRSA", "SHA256withECDSA", and so on).
@@ -52,7 +52,7 @@
      * Note: the local supported signature algorithms should conform to
      * the algorithm constraints specified by
      * {@link SSLParameters#getAlgorithmConstraints getAlgorithmConstraints()}
-     * method in <code>SSLParameters</code>.
+     * method in {@code SSLParameters}.
      *
      * @return An array of supported signature algorithms, in descending
      *     order of preference.  The return value is an empty array if
@@ -67,8 +67,8 @@
      * able to use.
      * <p>
      * Note: this method is used to indicate to the local side which signature
-     * algorithms may be used for digital signatures in TLS 1.2. It is
-     * not meaningful for TLS versions prior to 1.2.
+     * algorithms may be used for digital signatures in TLS/DTLS 1.2. It is
+     * not meaningful for TLS/DTLS versions prior to 1.2.
      * <p>
      * The signature algorithm name must be a standard Java Security
      * name (such as "SHA1withRSA", "SHA256withECDSA", and so on).
--- a/jdk/src/java.base/share/classes/javax/net/ssl/SNIServerName.java	Mon Jun 01 10:15:21 2015 -0700
+++ b/jdk/src/java.base/share/classes/javax/net/ssl/SNIServerName.java	Tue Jun 02 09:15:47 2015 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -31,7 +31,7 @@
  * Instances of this class represent a server name in a Server Name
  * Indication (SNI) extension.
  * <P>
- * The SNI extension is a feature that extends the SSL/TLS protocols to
+ * The SNI extension is a feature that extends the SSL/TLS/DTLS protocols to
  * indicate what server name the client is attempting to connect to during
  * handshaking.  See section 3, "Server Name Indication", of <A
  * HREF="http://www.ietf.org/rfc/rfc6066.txt">TLS Extensions (RFC 6066)</A>.
--- a/jdk/src/java.base/share/classes/javax/net/ssl/SSLContext.java	Mon Jun 01 10:15:21 2015 -0700
+++ b/jdk/src/java.base/share/classes/javax/net/ssl/SSLContext.java	Tue Jun 02 09:15:47 2015 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -32,12 +32,12 @@
 /**
  * Instances of this class represent a secure socket protocol
  * implementation which acts as a factory for secure socket
- * factories or <code>SSLEngine</code>s. This class is initialized
+ * factories or {@code SSLEngine}s. This class is initialized
  * with an optional set of key and trust managers and source of
  * secure random bytes.
  *
  * <p> Every implementation of the Java platform is required to support the
- * following standard <code>SSLContext</code> protocol:
+ * following standard {@code SSLContext} protocol:
  * <ul>
  * <li><tt>TLSv1</tt></li>
  * </ul>
@@ -79,7 +79,7 @@
      * <p>If a default context was set using the {@link #setDefault
      * SSLContext.setDefault()} method, it is returned. Otherwise, the first
      * call of this method triggers the call
-     * <code>SSLContext.getInstance("Default")</code>.
+     * {@code SSLContext.getInstance("Default")}.
      * If successful, that object is made the default SSL context and returned.
      *
      * <p>The default context is immediately
@@ -106,8 +106,8 @@
      * @param context the SSLContext
      * @throws  NullPointerException if context is null
      * @throws  SecurityException if a security manager exists and its
-     *          <code>checkPermission</code> method does not allow
-     *          <code>SSLPermission("setDefaultSSLContext")</code>
+     *          {@code checkPermission} method does not allow
+     *          {@code SSLPermission("setDefaultSSLContext")}
      * @since 1.6
      */
     public static synchronized void setDefault(SSLContext context) {
@@ -122,7 +122,7 @@
     }
 
     /**
-     * Returns a <code>SSLContext</code> object that implements the
+     * Returns a {@code SSLContext} object that implements the
      * specified secure socket protocol.
      *
      * <p> This method traverses the list of registered security Providers,
@@ -141,7 +141,7 @@
      *          Documentation</a>
      *          for information about standard protocol names.
      *
-     * @return the new <code>SSLContext</code> object.
+     * @return the new {@code SSLContext} object.
      *
      * @exception NoSuchAlgorithmException if no Provider supports a
      *          SSLContextSpi implementation for the
@@ -159,7 +159,7 @@
     }
 
     /**
-     * Returns a <code>SSLContext</code> object that implements the
+     * Returns a {@code SSLContext} object that implements the
      * specified secure socket protocol.
      *
      * <p> A new SSLContext object encapsulating the
@@ -179,7 +179,7 @@
      *
      * @param provider the name of the provider.
      *
-     * @return the new <code>SSLContext</code> object.
+     * @return the new {@code SSLContext} object.
      *
      * @throws NoSuchAlgorithmException if a SSLContextSpi
      *          implementation for the specified protocol is not
@@ -202,7 +202,7 @@
     }
 
     /**
-     * Returns a <code>SSLContext</code> object that implements the
+     * Returns a {@code SSLContext} object that implements the
      * specified secure socket protocol.
      *
      * <p> A new SSLContext object encapsulating the
@@ -219,7 +219,7 @@
      *
      * @param provider an instance of the provider.
      *
-     * @return the new <code>SSLContext</code> object.
+     * @return the new {@code SSLContext} object.
      *
      * @throws NoSuchAlgorithmException if a SSLContextSpi
      *          implementation for the specified protocol is not available
@@ -239,22 +239,22 @@
     }
 
     /**
-     * Returns the protocol name of this <code>SSLContext</code> object.
+     * Returns the protocol name of this {@code SSLContext} object.
      *
      * <p>This is the same name that was specified in one of the
-     * <code>getInstance</code> calls that created this
-     * <code>SSLContext</code> object.
+     * {@code getInstance} calls that created this
+     * {@code SSLContext} object.
      *
-     * @return the protocol name of this <code>SSLContext</code> object.
+     * @return the protocol name of this {@code SSLContext} object.
      */
     public final String getProtocol() {
         return this.protocol;
     }
 
     /**
-     * Returns the provider of this <code>SSLContext</code> object.
+     * Returns the provider of this {@code SSLContext} object.
      *
-     * @return the provider of this <code>SSLContext</code> object
+     * @return the provider of this {@code SSLContext} object
      */
     public final Provider getProvider() {
         return this.provider;
@@ -283,31 +283,35 @@
     }
 
     /**
-     * Returns a <code>SocketFactory</code> object for this
+     * Returns a {@code SocketFactory} object for this
      * context.
      *
-     * @return the <code>SocketFactory</code> object
+     * @return the {@code SocketFactory} object
+     * @throws UnsupportedOperationException if the underlying provider
+     *         does not implement the operation.
      * @throws IllegalStateException if the SSLContextImpl requires
-     *          initialization and the <code>init()</code> has not been called
+     *         initialization and the {@code init()} has not been called
      */
     public final SSLSocketFactory getSocketFactory() {
         return contextSpi.engineGetSocketFactory();
     }
 
     /**
-     * Returns a <code>ServerSocketFactory</code> object for
+     * Returns a {@code ServerSocketFactory} object for
      * this context.
      *
-     * @return the <code>ServerSocketFactory</code> object
+     * @return the {@code ServerSocketFactory} object
+     * @throws UnsupportedOperationException if the underlying provider
+     *         does not implement the operation.
      * @throws IllegalStateException if the SSLContextImpl requires
-     *          initialization and the <code>init()</code> has not been called
+     *         initialization and the {@code init()} has not been called
      */
     public final SSLServerSocketFactory getServerSocketFactory() {
         return contextSpi.engineGetServerSocketFactory();
     }
 
     /**
-     * Creates a new <code>SSLEngine</code> using this context.
+     * Creates a new {@code SSLEngine} using this context.
      * <P>
      * Applications using this factory method are providing no hints
      * for an internal session reuse strategy. If hints are desired,
@@ -317,11 +321,11 @@
      * Some cipher suites (such as Kerberos) require remote hostname
      * information, in which case this factory method should not be used.
      *
-     * @return  the <code>SSLEngine</code> object
+     * @return  the {@code SSLEngine} object
      * @throws  UnsupportedOperationException if the underlying provider
      *          does not implement the operation.
      * @throws  IllegalStateException if the SSLContextImpl requires
-     *          initialization and the <code>init()</code> has not been called
+     *          initialization and the {@code init()} has not been called
      * @since   1.5
      */
     public final SSLEngine createSSLEngine() {
@@ -338,7 +342,7 @@
     }
 
     /**
-     * Creates a new <code>SSLEngine</code> using this context using
+     * Creates a new {@code SSLEngine} using this context using
      * advisory peer information.
      * <P>
      * Applications using this factory method are providing hints
@@ -349,11 +353,11 @@
      *
      * @param   peerHost the non-authoritative name of the host
      * @param   peerPort the non-authoritative port
-     * @return  the new <code>SSLEngine</code> object
+     * @return  the new {@code SSLEngine} object
      * @throws  UnsupportedOperationException if the underlying provider
      *          does not implement the operation.
      * @throws  IllegalStateException if the SSLContextImpl requires
-     *          initialization and the <code>init()</code> has not been called
+     *          initialization and the {@code init()} has not been called
      * @since   1.5
      */
     public final SSLEngine createSSLEngine(String peerHost, int peerPort) {
--- a/jdk/src/java.base/share/classes/javax/net/ssl/SSLContextSpi.java	Mon Jun 01 10:15:21 2015 -0700
+++ b/jdk/src/java.base/share/classes/javax/net/ssl/SSLContextSpi.java	Tue Jun 02 09:15:47 2015 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -29,7 +29,7 @@
 
 /**
  * This class defines the <i>Service Provider Interface</i> (<b>SPI</b>)
- * for the <code>SSLContext</code> class.
+ * for the {@code SSLContext} class.
  *
  * <p> All the abstract methods in this class must be implemented by each
  * cryptographic service provider who wishes to supply the implementation
@@ -52,31 +52,35 @@
         SecureRandom sr) throws KeyManagementException;
 
     /**
-     * Returns a <code>SocketFactory</code> object for this
+     * Returns a {@code SocketFactory} object for this
      * context.
      *
-     * @return the <code>SocketFactory</code> object
+     * @return the {@code SocketFactory} object
+     * @throws UnsupportedOperationException if the underlying provider
+     *         does not implement the operation.
      * @throws IllegalStateException if the SSLContextImpl requires
-     *         initialization and the <code>engineInit()</code>
+     *         initialization and the {@code engineInit()}
      *         has not been called
      * @see javax.net.ssl.SSLContext#getSocketFactory()
      */
     protected abstract SSLSocketFactory engineGetSocketFactory();
 
     /**
-     * Returns a <code>ServerSocketFactory</code> object for
+     * Returns a {@code ServerSocketFactory} object for
      * this context.
      *
-     * @return the <code>ServerSocketFactory</code> object
+     * @return the {@code ServerSocketFactory} object
+     * @throws UnsupportedOperationException if the underlying provider
+     *         does not implement the operation.
      * @throws IllegalStateException if the SSLContextImpl requires
-     *         initialization and the <code>engineInit()</code>
+     *         initialization and the {@code engineInit()}
      *         has not been called
      * @see javax.net.ssl.SSLContext#getServerSocketFactory()
      */
     protected abstract SSLServerSocketFactory engineGetServerSocketFactory();
 
     /**
-     * Creates a new <code>SSLEngine</code> using this context.
+     * Creates a new {@code SSLEngine} using this context.
      * <P>
      * Applications using this factory method are providing no hints
      * for an internal session reuse strategy. If hints are desired,
@@ -86,9 +90,9 @@
      * Some cipher suites (such as Kerberos) require remote hostname
      * information, in which case this factory method should not be used.
      *
-     * @return  the <code>SSLEngine</code> Object
+     * @return  the {@code SSLEngine} Object
      * @throws IllegalStateException if the SSLContextImpl requires
-     *         initialization and the <code>engineInit()</code>
+     *         initialization and the {@code engineInit()}
      *         has not been called
      *
      * @see     SSLContext#createSSLEngine()
@@ -98,7 +102,7 @@
     protected abstract SSLEngine engineCreateSSLEngine();
 
     /**
-     * Creates a <code>SSLEngine</code> using this context.
+     * Creates a {@code SSLEngine} using this context.
      * <P>
      * Applications using this factory method are providing hints
      * for an internal session reuse strategy.
@@ -108,9 +112,9 @@
      *
      * @param host the non-authoritative name of the host
      * @param port the non-authoritative port
-     * @return  the <code>SSLEngine</code> Object
+     * @return  the {@code SSLEngine} Object
      * @throws IllegalStateException if the SSLContextImpl requires
-     *         initialization and the <code>engineInit()</code>
+     *         initialization and the {@code engineInit()}
      *         has not been called
      *
      * @see     SSLContext#createSSLEngine(String, int)
@@ -120,19 +124,19 @@
     protected abstract SSLEngine engineCreateSSLEngine(String host, int port);
 
     /**
-     * Returns a server <code>SSLSessionContext</code> object for
+     * Returns a server {@code SSLSessionContext} object for
      * this context.
      *
-     * @return the <code>SSLSessionContext</code> object
+     * @return the {@code SSLSessionContext} object
      * @see javax.net.ssl.SSLContext#getServerSessionContext()
      */
     protected abstract SSLSessionContext engineGetServerSessionContext();
 
     /**
-     * Returns a client <code>SSLSessionContext</code> object for
+     * Returns a client {@code SSLSessionContext} object for
      * this context.
      *
-     * @return the <code>SSLSessionContext</code> object
+     * @return the {@code SSLSessionContext} object
      * @see javax.net.ssl.SSLContext#getClientSessionContext()
      */
     protected abstract SSLSessionContext engineGetClientSessionContext();
--- a/jdk/src/java.base/share/classes/javax/net/ssl/SSLEngine.java	Mon Jun 01 10:15:21 2015 -0700
+++ b/jdk/src/java.base/share/classes/javax/net/ssl/SSLEngine.java	Tue Jun 02 09:15:47 2015 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -37,15 +37,15 @@
  * <P>
  * The secure communications modes include: <UL>
  *
- *      <LI> <em>Integrity Protection</em>.  SSL/TLS protects against
+ *      <LI> <em>Integrity Protection</em>.  SSL/TLS/DTLS protects against
  *      modification of messages by an active wiretapper.
  *
- *      <LI> <em>Authentication</em>.  In most modes, SSL/TLS provides
+ *      <LI> <em>Authentication</em>.  In most modes, SSL/TLS/DTLS provides
  *      peer authentication.  Servers are usually authenticated, and
  *      clients may be authenticated as requested by servers.
  *
  *      <LI> <em>Confidentiality (Privacy Protection)</em>.  In most
- *      modes, SSL/TLS encrypts data being sent between client and
+ *      modes, SSL/TLS/DTLS encrypts data being sent between client and
  *      server.  This protects the confidentiality of data, so that
  *      passive wiretappers won't see sensitive data such as financial
  *      information or personal information of many kinds.
@@ -65,19 +65,19 @@
  * handshaking has completed, you can access session attributes by
  * using the {@link #getSession()} method.
  * <P>
- * The <code>SSLSocket</code> class provides much of the same security
+ * The {@code SSLSocket} class provides much of the same security
  * functionality, but all of the inbound and outbound data is
  * automatically transported using the underlying {@link
  * java.net.Socket Socket}, which by design uses a blocking model.
  * While this is appropriate for many applications, this model does not
  * provide the scalability required by large servers.
  * <P>
- * The primary distinction of an <code>SSLEngine</code> is that it
+ * The primary distinction of an {@code SSLEngine} is that it
  * operates on inbound and outbound byte streams, independent of the
  * transport mechanism.  It is the responsibility of the
- * <code>SSLEngine</code> user to arrange for reliable I/O transport to
- * the peer.  By separating the SSL/TLS abstraction from the I/O
- * transport mechanism, the <code>SSLEngine</code> can be used for a
+ * {@code SSLEngine} user to arrange for reliable I/O transport to
+ * the peer.  By separating the SSL/TLS/DTLS abstraction from the I/O
+ * transport mechanism, the {@code SSLEngine} can be used for a
  * wide variety of I/O types, such as {@link
  * java.nio.channels.spi.AbstractSelectableChannel#configureBlocking(boolean)
  * non-blocking I/O (polling)}, {@link java.nio.channels.Selector
@@ -87,7 +87,7 @@
  * HREF="http://www.jcp.org/en/jsr/detail?id=203"> future asynchronous
  * I/O models </A>, and so on.
  * <P>
- * At a high level, the <code>SSLEngine</code> appears thus:
+ * At a high level, the {@code SSLEngine} appears thus:
  *
  * <pre>
  *                   app data
@@ -115,18 +115,18 @@
  * mechanism.  Inbound data is data which has been received from the
  * peer, and outbound data is destined for the peer.
  * <P>
- * (In the context of an <code>SSLEngine</code>, the term "handshake
+ * (In the context of an {@code SSLEngine}, the term "handshake
  * data" is taken to mean any data exchanged to establish and control a
- * secure connection.  Handshake data includes the SSL/TLS messages
+ * secure connection.  Handshake data includes the SSL/TLS/DTLS messages
  * "alert", "change_cipher_spec," and "handshake.")
  * <P>
- * There are five distinct phases to an <code>SSLEngine</code>.
+ * There are five distinct phases to an {@code SSLEngine}.
  *
  * <OL>
- *     <li> Creation - The <code>SSLEngine</code> has been created and
+ *     <li> Creation - The {@code SSLEngine} has been created and
  *     initialized, but has not yet been used.  During this phase, an
- *     application may set any <code>SSLEngine</code>-specific settings
- *     (enabled cipher suites, whether the <code>SSLEngine</code> should
+ *     application may set any {@code SSLEngine}-specific settings
+ *     (enabled cipher suites, whether the {@code SSLEngine} should
  *     handshake in client or server mode, and so on).  Once
  *     handshaking has begun, though, any new settings (except
  *     client/server mode, see below) will be used for
@@ -139,7 +139,7 @@
  *
  *     <li> Application Data - Once the communication parameters have
  *     been established and the handshake is complete, application data
- *     may flow through the <code>SSLEngine</code>.  Outbound
+ *     may flow through the {@code SSLEngine}.  Outbound
  *     application messages are encrypted and integrity protected,
  *     and inbound messages reverse the process.
  *
@@ -147,50 +147,50 @@
  *     the session at any time during the Application Data phase.  New
  *     handshaking data can be intermixed among the application data.
  *     Before starting the rehandshake phase, the application may
- *     reset the SSL/TLS communication parameters such as the list of
+ *     reset the SSL/TLS/DTLS communication parameters such as the list of
  *     enabled ciphersuites and whether to use client authentication,
  *     but can not change between client/server modes.  As before, once
- *     handshaking has begun, any new <code>SSLEngine</code>
+ *     handshaking has begun, any new {@code SSLEngine}
  *     configuration settings will not be used until the next
  *     handshake.
  *
  *     <li>  Closure - When the connection is no longer needed, the
- *     application should close the <code>SSLEngine</code> and should
+ *     application should close the {@code SSLEngine} and should
  *     send/receive any remaining messages to the peer before
  *     closing the underlying transport mechanism.  Once an engine is
- *     closed, it is not reusable:  a new <code>SSLEngine</code> must
+ *     closed, it is not reusable:  a new {@code SSLEngine} must
  *     be created.
  * </OL>
- * An <code>SSLEngine</code> is created by calling {@link
+ * An {@code SSLEngine} is created by calling {@link
  * SSLContext#createSSLEngine()} from an initialized
- * <code>SSLContext</code>.  Any configuration
+ * {@code SSLContext}.  Any configuration
  * parameters should be set before making the first call to
- * <code>wrap()</code>, <code>unwrap()</code>, or
- * <code>beginHandshake()</code>.  These methods all trigger the
+ * {@code wrap()}, {@code unwrap()}, or
+ * {@code beginHandshake()}.  These methods all trigger the
  * initial handshake.
  * <P>
  * Data moves through the engine by calling {@link #wrap(ByteBuffer,
  * ByteBuffer) wrap()} or {@link #unwrap(ByteBuffer, ByteBuffer)
  * unwrap()} on outbound or inbound data, respectively.  Depending on
- * the state of the <code>SSLEngine</code>, a <code>wrap()</code> call
+ * the state of the {@code SSLEngine}, a {@code wrap()} call
  * may consume application data from the source buffer and may produce
  * network data in the destination buffer.  The outbound data
  * may contain application and/or handshake data.  A call to
- * <code>unwrap()</code> will examine the source buffer and may
+ * {@code unwrap()} will examine the source buffer and may
  * advance the handshake if the data is handshaking information, or
  * may place application data in the destination buffer if the data
- * is application.  The state of the underlying SSL/TLS algorithm
+ * is application.  The state of the underlying SSL/TLS/DTLS algorithm
  * will determine when data is consumed and produced.
  * <P>
- * Calls to <code>wrap()</code> and <code>unwrap()</code> return an
- * <code>SSLEngineResult</code> which indicates the status of the
+ * Calls to {@code wrap()} and {@code unwrap()} return an
+ * {@code SSLEngineResult} which indicates the status of the
  * operation, and (optionally) how to interact with the engine to make
  * progress.
  * <P>
- * The <code>SSLEngine</code> produces/consumes complete SSL/TLS
+ * The {@code SSLEngine} produces/consumes complete SSL/TLS/DTLS
  * packets only, and does not store application data internally between
- * calls to <code>wrap()/unwrap()</code>.  Thus input and output
- * <code>ByteBuffer</code>s must be sized appropriately to hold the
+ * calls to {@code wrap()/unwrap()}.  Thus input and output
+ * {@code ByteBuffer}s must be sized appropriately to hold the
  * maximum record that can be produced.  Calls to {@link
  * SSLSession#getPacketBufferSize()} and {@link
  * SSLSession#getApplicationBufferSize()} should be used to determine
@@ -200,12 +200,12 @@
  * must determine (via {@link SSLEngineResult}) and correct the
  * problem, and then try the call again.
  * <P>
- * For example, <code>unwrap()</code> will return a {@link
+ * For example, {@code unwrap()} will return a {@link
  * SSLEngineResult.Status#BUFFER_OVERFLOW} result if the engine
  * determines that there is not enough destination buffer space available.
  * Applications should call {@link SSLSession#getApplicationBufferSize()}
  * and compare that value with the space available in the destination buffer,
- * enlarging the buffer if necessary.  Similarly, if <code>unwrap()</code>
+ * enlarging the buffer if necessary.  Similarly, if {@code unwrap()}
  * were to return a {@link SSLEngineResult.Status#BUFFER_UNDERFLOW}, the
  * application should call {@link SSLSession#getPacketBufferSize()} to ensure
  * that the source buffer has enough room to hold a record (enlarging if
@@ -241,8 +241,8 @@
  * }</pre>
  *
  * <P>
- * Unlike <code>SSLSocket</code>, all methods of SSLEngine are
- * non-blocking.  <code>SSLEngine</code> implementations may
+ * Unlike {@code SSLSocket}, all methods of SSLEngine are
+ * non-blocking.  {@code SSLEngine} implementations may
  * require the results of tasks that may take an extended period of
  * time to complete, or may even block.  For example, a TrustManager
  * may need to connect to a remote certificate validation service,
@@ -252,8 +252,8 @@
  * seemingly blocking.
  * <P>
  * For any operation which may potentially block, the
- * <code>SSLEngine</code> will create a {@link java.lang.Runnable}
- * delegated task.  When <code>SSLEngineResult</code> indicates that a
+ * {@code SSLEngine} will create a {@link java.lang.Runnable}
+ * delegated task.  When {@code SSLEngineResult} indicates that a
  * delegated task result is needed, the application must call {@link
  * #getDelegatedTask()} to obtain an outstanding delegated task and
  * call its {@link java.lang.Runnable#run() run()} method (possibly using
@@ -262,16 +262,16 @@
  * exist, and try the original operation again.
  * <P>
  * At the end of a communication session, applications should properly
- * close the SSL/TLS link.  The SSL/TLS protocols have closure handshake
- * messages, and these messages should be communicated to the peer
- * before releasing the <code>SSLEngine</code> and closing the
+ * close the SSL/TLS/DTLS link.  The SSL/TLS/DTLS protocols have closure
+ * handshake messages, and these messages should be communicated to the
+ * peer before releasing the {@code SSLEngine} and closing the
  * underlying transport mechanism.  A close can be initiated by one of:
  * an SSLException, an inbound closure handshake message, or one of the
  * close methods.  In all cases, closure handshake messages are
- * generated by the engine, and <code>wrap()</code> should be repeatedly
- * called until the resulting <code>SSLEngineResult</code>'s status
+ * generated by the engine, and {@code wrap()} should be repeatedly
+ * called until the resulting {@code SSLEngineResult}'s status
  * returns "CLOSED", or {@link #isOutboundDone()} returns true.  All
- * data obtained from the <code>wrap()</code> method should be sent to the
+ * data obtained from the {@code wrap()} method should be sent to the
  * peer.
  * <P>
  * {@link #closeOutbound()} is used to signal the engine that the
@@ -279,12 +279,12 @@
  * <P>
  * A peer will signal its intent to close by sending its own closure
  * handshake message.  After this message has been received and
- * processed by the local <code>SSLEngine</code>'s <code>unwrap()</code>
+ * processed by the local {@code SSLEngine}'s {@code unwrap()}
  * call, the application can detect the close by calling
- * <code>unwrap()</code> and looking for a <code>SSLEngineResult</code>
+ * {@code unwrap()} and looking for a {@code SSLEngineResult}
  * with status "CLOSED", or if {@link #isInboundDone()} returns true.
  * If for some reason the peer closes the communication link without
- * sending the proper SSL/TLS closure message, the application can
+ * sending the proper SSL/TLS/DTLS closure message, the application can
  * detect the end-of-stream and can signal the engine via {@link
  * #closeInbound()} that there will no more inbound messages to
  * process.  Some applications might choose to require orderly shutdown
@@ -315,16 +315,16 @@
  * and/or non-private (unencrypted) communications will such a
  * cipher suite be selected.
  * <P>
- * Each SSL/TLS connection must have one client and one server, thus
+ * Each SSL/TLS/DTLS connection must have one client and one server, thus
  * each endpoint must decide which role to assume.  This choice determines
  * who begins the handshaking process as well as which type of messages
  * should be sent by each party.  The method {@link
  * #setUseClientMode(boolean)} configures the mode.  Once the initial
- * handshaking has started, an <code>SSLEngine</code> can not switch
+ * handshaking has started, an {@code SSLEngine} can not switch
  * between client and server modes, even when performing renegotiations.
  * <P>
  * Applications might choose to process delegated tasks in different
- * threads.  When an <code>SSLEngine</code>
+ * threads.  When an {@code SSLEngine}
  * is created, the current {@link java.security.AccessControlContext}
  * is saved.  All future delegated tasks will be processed using this
  * context:  that is, all access control decisions will be made using the
@@ -336,10 +336,10 @@
  * There are two concurrency issues to be aware of:
  *
  * <OL>
- *      <li>The <code>wrap()</code> and <code>unwrap()</code> methods
+ *      <li>The {@code wrap()} and {@code unwrap()} methods
  *      may execute concurrently of each other.
  *
- *      <li> The SSL/TLS protocols employ ordered packets.
+ *      <li> The SSL/TLS/DTLS protocols employ ordered packets.
  *      Applications must take care to ensure that generated packets
  *      are delivered in sequence.  If packets arrive
  *      out-of-order, unexpected or fatal results may occur.
@@ -354,7 +354,7 @@
  *      </pre>
  *
  *      As a corollary, two threads must not attempt to call the same method
- *      (either <code>wrap()</code> or <code>unwrap()</code>) concurrently,
+ *      (either {@code wrap()} or {@code unwrap()}) concurrently,
  *      because there is no way to guarantee the eventual packet ordering.
  * </OL>
  *
@@ -374,7 +374,7 @@
     private int peerPort = -1;
 
     /**
-     * Constructor for an <code>SSLEngine</code> providing no hints
+     * Constructor for an {@code SSLEngine} providing no hints
      * for an internal session reuse strategy.
      *
      * @see     SSLContext#createSSLEngine()
@@ -384,10 +384,10 @@
     }
 
     /**
-     * Constructor for an <code>SSLEngine</code>.
+     * Constructor for an {@code SSLEngine}.
      * <P>
-     * <code>SSLEngine</code> implementations may use the
-     * <code>peerHost</code> and <code>peerPort</code> parameters as hints
+     * {@code SSLEngine} implementations may use the
+     * {@code peerHost} and {@code peerPort} parameters as hints
      * for their internal session reuse strategy.
      * <P>
      * Some cipher suites (such as Kerberos) require remote hostname
@@ -395,7 +395,7 @@
      * constructor to use Kerberos.
      * <P>
      * The parameters are not authenticated by the
-     * <code>SSLEngine</code>.
+     * {@code SSLEngine}.
      *
      * @param   peerHost the name of the peer host
      * @param   peerPort the port number of the peer
@@ -435,7 +435,7 @@
 
     /**
      * Attempts to encode a buffer of plaintext application data into
-     * SSL/TLS network data.
+     * SSL/TLS/DTLS network data.
      * <P>
      * An invocation of this method behaves in exactly the same manner
      * as the invocation:
@@ -445,20 +445,20 @@
      * </pre></blockquote>
      *
      * @param   src
-     *          a <code>ByteBuffer</code> containing outbound application data
+     *          a {@code ByteBuffer} containing outbound application data
      * @param   dst
-     *          a <code>ByteBuffer</code> to hold outbound network data
-     * @return  an <code>SSLEngineResult</code> describing the result
+     *          a {@code ByteBuffer} to hold outbound network data
+     * @return  an {@code SSLEngineResult} describing the result
      *          of this operation.
      * @throws  SSLException
      *          A problem was encountered while processing the
-     *          data that caused the <code>SSLEngine</code> to abort.
+     *          data that caused the {@code SSLEngine} to abort.
      *          See the class description for more information on
      *          engine closure.
      * @throws  ReadOnlyBufferException
-     *          if the <code>dst</code> buffer is read-only.
+     *          if the {@code dst} buffer is read-only.
      * @throws  IllegalArgumentException
-     *          if either <code>src</code> or <code>dst</code>
+     *          if either {@code src} or {@code dst}
      *          is null.
      * @throws  IllegalStateException if the client/server mode
      *          has not yet been set.
@@ -471,7 +471,7 @@
 
     /**
      * Attempts to encode plaintext bytes from a sequence of data
-     * buffers into SSL/TLS network data.
+     * buffers into SSL/TLS/DTLS network data.
      * <P>
      * An invocation of this method behaves in exactly the same manner
      * as the invocation:
@@ -481,22 +481,22 @@
      * </pre></blockquote>
      *
      * @param   srcs
-     *          an array of <code>ByteBuffers</code> containing the
+     *          an array of {@code ByteBuffers} containing the
      *          outbound application data
      * @param   dst
-     *          a <code>ByteBuffer</code> to hold outbound network data
-     * @return  an <code>SSLEngineResult</code> describing the result
+     *          a {@code ByteBuffer} to hold outbound network data
+     * @return  an {@code SSLEngineResult} describing the result
      *          of this operation.
      * @throws  SSLException
      *          A problem was encountered while processing the
-     *          data that caused the <code>SSLEngine</code> to abort.
+     *          data that caused the {@code SSLEngine} to abort.
      *          See the class description for more information on
      *          engine closure.
      * @throws  ReadOnlyBufferException
-     *          if the <code>dst</code> buffer is read-only.
+     *          if the {@code dst} buffer is read-only.
      * @throws  IllegalArgumentException
-     *          if either <code>srcs</code> or <code>dst</code>
-     *          is null, or if any element in <code>srcs</code> is null.
+     *          if either {@code srcs} or {@code dst}
+     *          is null, or if any element in {@code srcs} is null.
      * @throws  IllegalStateException if the client/server mode
      *          has not yet been set.
      * @see     #wrap(ByteBuffer [], int, int, ByteBuffer)
@@ -512,7 +512,7 @@
 
     /**
      * Attempts to encode plaintext bytes from a subsequence of data
-     * buffers into SSL/TLS network data.  This <i>"gathering"</i>
+     * buffers into SSL/TLS/DTLS network data.  This <i>"gathering"</i>
      * operation encodes, in a single invocation, a sequence of bytes
      * from one or more of a given sequence of buffers.  Gathering
      * wraps are often useful when implementing network protocols or
@@ -535,49 +535,49 @@
      * it was generated.  The application must properly synchronize
      * multiple calls to this method.
      * <P>
-     * If this <code>SSLEngine</code> has not yet started its initial
+     * If this {@code SSLEngine} has not yet started its initial
      * handshake, this method will automatically start the handshake.
      * <P>
-     * This method will attempt to produce SSL/TLS records, and will
+     * This method will attempt to produce SSL/TLS/DTLS records, and will
      * consume as much source data as possible, but will never consume
      * more than the sum of the bytes remaining in each buffer.  Each
-     * <code>ByteBuffer</code>'s position is updated to reflect the
+     * {@code ByteBuffer}'s position is updated to reflect the
      * amount of data consumed or produced.  The limits remain the
      * same.
      * <P>
-     * The underlying memory used by the <code>srcs</code> and
-     * <code>dst ByteBuffer</code>s must not be the same.
+     * The underlying memory used by the {@code srcs} and
+     * {@code dst ByteBuffer}s must not be the same.
      * <P>
      * See the class description for more information on engine closure.
      *
      * @param   srcs
-     *          an array of <code>ByteBuffers</code> containing the
+     *          an array of {@code ByteBuffers} containing the
      *          outbound application data
      * @param   offset
      *          The offset within the buffer array of the first buffer from
      *          which bytes are to be retrieved; it must be non-negative
-     *          and no larger than <code>srcs.length</code>
+     *          and no larger than {@code srcs.length}
      * @param   length
      *          The maximum number of buffers to be accessed; it must be
      *          non-negative and no larger than
-     *          <code>srcs.length</code>&nbsp;-&nbsp;<code>offset</code>
+     *          {@code srcs.length}&nbsp;-&nbsp;{@code offset}
      * @param   dst
-     *          a <code>ByteBuffer</code> to hold outbound network data
-     * @return  an <code>SSLEngineResult</code> describing the result
+     *          a {@code ByteBuffer} to hold outbound network data
+     * @return  an {@code SSLEngineResult} describing the result
      *          of this operation.
      * @throws  SSLException
      *          A problem was encountered while processing the
-     *          data that caused the <code>SSLEngine</code> to abort.
+     *          data that caused the {@code SSLEngine} to abort.
      *          See the class description for more information on
      *          engine closure.
      * @throws  IndexOutOfBoundsException
-     *          if the preconditions on the <code>offset</code> and
-     *          <code>length</code> parameters do not hold.
+     *          if the preconditions on the {@code offset} and
+     *          {@code length} parameters do not hold.
      * @throws  ReadOnlyBufferException
-     *          if the <code>dst</code> buffer is read-only.
+     *          if the {@code dst} buffer is read-only.
      * @throws  IllegalArgumentException
-     *          if either <code>srcs</code> or <code>dst</code>
-     *          is null, or if any element in the <code>srcs</code>
+     *          if either {@code srcs} or {@code dst}
+     *          is null, or if any element in the {@code srcs}
      *          subsequence specified is null.
      * @throws  IllegalStateException if the client/server mode
      *          has not yet been set.
@@ -589,7 +589,7 @@
             int length, ByteBuffer dst) throws SSLException;
 
     /**
-     * Attempts to decode SSL/TLS network data into a plaintext
+     * Attempts to decode SSL/TLS/DTLS network data into a plaintext
      * application data buffer.
      * <P>
      * An invocation of this method behaves in exactly the same manner
@@ -600,20 +600,20 @@
      * </pre></blockquote>
      *
      * @param   src
-     *          a <code>ByteBuffer</code> containing inbound network data.
+     *          a {@code ByteBuffer} containing inbound network data.
      * @param   dst
-     *          a <code>ByteBuffer</code> to hold inbound application data.
-     * @return  an <code>SSLEngineResult</code> describing the result
+     *          a {@code ByteBuffer} to hold inbound application data.
+     * @return  an {@code SSLEngineResult} describing the result
      *          of this operation.
      * @throws  SSLException
      *          A problem was encountered while processing the
-     *          data that caused the <code>SSLEngine</code> to abort.
+     *          data that caused the {@code SSLEngine} to abort.
      *          See the class description for more information on
      *          engine closure.
      * @throws  ReadOnlyBufferException
-     *          if the <code>dst</code> buffer is read-only.
+     *          if the {@code dst} buffer is read-only.
      * @throws  IllegalArgumentException
-     *          if either <code>src</code> or <code>dst</code>
+     *          if either {@code src} or {@code dst}
      *          is null.
      * @throws  IllegalStateException if the client/server mode
      *          has not yet been set.
@@ -625,7 +625,7 @@
     }
 
     /**
-     * Attempts to decode SSL/TLS network data into a sequence of plaintext
+     * Attempts to decode SSL/TLS/DTLS network data into a sequence of plaintext
      * application data buffers.
      * <P>
      * An invocation of this method behaves in exactly the same manner
@@ -636,22 +636,22 @@
      * </pre></blockquote>
      *
      * @param   src
-     *          a <code>ByteBuffer</code> containing inbound network data.
+     *          a {@code ByteBuffer} containing inbound network data.
      * @param   dsts
-     *          an array of <code>ByteBuffer</code>s to hold inbound
+     *          an array of {@code ByteBuffer}s to hold inbound
      *          application data.
-     * @return  an <code>SSLEngineResult</code> describing the result
+     * @return  an {@code SSLEngineResult} describing the result
      *          of this operation.
      * @throws  SSLException
      *          A problem was encountered while processing the
-     *          data that caused the <code>SSLEngine</code> to abort.
+     *          data that caused the {@code SSLEngine} to abort.
      *          See the class description for more information on
      *          engine closure.
      * @throws  ReadOnlyBufferException
-     *          if any of the <code>dst</code> buffers are read-only.
+     *          if any of the {@code dst} buffers are read-only.
      * @throws  IllegalArgumentException
-     *          if either <code>src</code> or <code>dsts</code>
-     *          is null, or if any element in <code>dsts</code> is null.
+     *          if either {@code src} or {@code dsts}
+     *          is null, or if any element in {@code dsts} is null.
      * @throws  IllegalStateException if the client/server mode
      *          has not yet been set.
      * @see     #unwrap(ByteBuffer, ByteBuffer [], int, int)
@@ -665,7 +665,7 @@
     }
 
     /**
-     * Attempts to decode SSL/TLS network data into a subsequence of
+     * Attempts to decode SSL/TLS/DTLS network data into a subsequence of
      * plaintext application data buffers.  This <i>"scattering"</i>
      * operation decodes, in a single invocation, a sequence of bytes
      * into one or more of a given sequence of buffers.  Scattering
@@ -688,55 +688,55 @@
      * order it was received.  The application must properly synchronize
      * multiple calls to this method.
      * <P>
-     * If this <code>SSLEngine</code> has not yet started its initial
+     * If this {@code SSLEngine} has not yet started its initial
      * handshake, this method will automatically start the handshake.
      * <P>
-     * This method will attempt to consume one complete SSL/TLS network
+     * This method will attempt to consume one complete SSL/TLS/DTLS network
      * packet, but will never consume more than the sum of the bytes
-     * remaining in the buffers.  Each <code>ByteBuffer</code>'s
+     * remaining in the buffers.  Each {@code ByteBuffer}'s
      * position is updated to reflect the amount of data consumed or
      * produced.  The limits remain the same.
      * <P>
-     * The underlying memory used by the <code>src</code> and
-     * <code>dsts ByteBuffer</code>s must not be the same.
+     * The underlying memory used by the {@code src} and
+     * {@code dsts ByteBuffer}s must not be the same.
      * <P>
      * The inbound network buffer may be modified as a result of this
      * call:  therefore if the network data packet is required for some
      * secondary purpose, the data should be duplicated before calling this
      * method.  Note:  the network data will not be useful to a second
      * SSLEngine, as each SSLEngine contains unique random state which
-     * influences the SSL/TLS messages.
+     * influences the SSL/TLS/DTLS messages.
      * <P>
      * See the class description for more information on engine closure.
      *
      * @param   src
-     *          a <code>ByteBuffer</code> containing inbound network data.
+     *          a {@code ByteBuffer} containing inbound network data.
      * @param   dsts
-     *          an array of <code>ByteBuffer</code>s to hold inbound
+     *          an array of {@code ByteBuffer}s to hold inbound
      *          application data.
      * @param   offset
      *          The offset within the buffer array of the first buffer from
      *          which bytes are to be transferred; it must be non-negative
-     *          and no larger than <code>dsts.length</code>.
+     *          and no larger than {@code dsts.length}.
      * @param   length
      *          The maximum number of buffers to be accessed; it must be
      *          non-negative and no larger than
-     *          <code>dsts.length</code>&nbsp;-&nbsp;<code>offset</code>.
-     * @return  an <code>SSLEngineResult</code> describing the result
+     *          {@code dsts.length}&nbsp;-&nbsp;{@code offset}.
+     * @return  an {@code SSLEngineResult} describing the result
      *          of this operation.
      * @throws  SSLException
      *          A problem was encountered while processing the
-     *          data that caused the <code>SSLEngine</code> to abort.
+     *          data that caused the {@code SSLEngine} to abort.
      *          See the class description for more information on
      *          engine closure.
      * @throws  IndexOutOfBoundsException
-     *          If the preconditions on the <code>offset</code> and
-     *          <code>length</code> parameters do not hold.
+     *          If the preconditions on the {@code offset} and
+     *          {@code length} parameters do not hold.
      * @throws  ReadOnlyBufferException
-     *          if any of the <code>dst</code> buffers are read-only.
+     *          if any of the {@code dst} buffers are read-only.
      * @throws  IllegalArgumentException
-     *          if either <code>src</code> or <code>dsts</code>
-     *          is null, or if any element in the <code>dsts</code>
+     *          if either {@code src} or {@code dsts}
+     *          is null, or if any element in the {@code dsts}
      *          subsequence specified is null.
      * @throws  IllegalStateException if the client/server mode
      *          has not yet been set.
@@ -749,19 +749,19 @@
 
 
     /**
-     * Returns a delegated <code>Runnable</code> task for
-     * this <code>SSLEngine</code>.
+     * Returns a delegated {@code Runnable} task for
+     * this {@code SSLEngine}.
      * <P>
-     * <code>SSLEngine</code> operations may require the results of
+     * {@code SSLEngine} operations may require the results of
      * operations that block, or may take an extended period of time to
      * complete.  This method is used to obtain an outstanding {@link
      * java.lang.Runnable} operation (task).  Each task must be assigned
      * a thread (possibly the current) to perform the {@link
      * java.lang.Runnable#run() run} operation.  Once the
-     * <code>run</code> method returns, the <code>Runnable</code> object
+     * {@code run} method returns, the {@code Runnable} object
      * is no longer needed and may be discarded.
      * <P>
-     * Delegated tasks run in the <code>AccessControlContext</code>
+     * Delegated tasks run in the {@code AccessControlContext}
      * in place when this object was created.
      * <P>
      * A call to this method will return each outstanding task
@@ -769,7 +769,7 @@
      * <P>
      * Multiple delegated tasks can be run in parallel.
      *
-     * @return  a delegated <code>Runnable</code> task, or null
+     * @return  a delegated {@code Runnable} task, or null
      *          if none are available.
      */
     public abstract Runnable getDelegatedTask();
@@ -777,7 +777,7 @@
 
     /**
      * Signals that no more inbound network data will be sent
-     * to this <code>SSLEngine</code>.
+     * to this {@code SSLEngine}.
      * <P>
      * If the application initiated the closing process by calling
      * {@link #closeOutbound()}, under some circumstances it is not
@@ -789,9 +789,9 @@
      * <P>
      * But if the application did not initiate the closure process, or
      * if the circumstances above do not apply, this method should be
-     * called whenever the end of the SSL/TLS data stream is reached.
+     * called whenever the end of the SSL/TLS/DTLS data stream is reached.
      * This ensures closure of the inbound side, and checks that the
-     * peer followed the SSL/TLS close procedure properly, thus
+     * peer followed the SSL/TLS/DTLS close procedure properly, thus
      * detecting possible truncation attacks.
      * <P>
      * This method is idempotent:  if the inbound side has already
@@ -801,7 +801,7 @@
      * called to flush any remaining handshake data.
      *
      * @throws  SSLException
-     *          if this engine has not received the proper SSL/TLS close
+     *          if this engine has not received the proper SSL/TLS/DTLS close
      *          notification message from the peer.
      *
      * @see     #isInboundDone()
@@ -814,7 +814,7 @@
      * Returns whether {@link #unwrap(ByteBuffer, ByteBuffer)} will
      * accept any more inbound data messages.
      *
-     * @return  true if the <code>SSLEngine</code> will not
+     * @return  true if the {@code SSLEngine} will not
      *          consume anymore network data (and by implication,
      *          will not produce any more application data.)
      * @see     #closeInbound()
@@ -824,7 +824,7 @@
 
     /**
      * Signals that no more outbound application data will be sent
-     * on this <code>SSLEngine</code>.
+     * on this {@code SSLEngine}.
      * <P>
      * This method is idempotent:  if the outbound side has already
      * been closed, this method does not do anything.
@@ -841,12 +841,12 @@
      * Returns whether {@link #wrap(ByteBuffer, ByteBuffer)} will
      * produce any more outbound data messages.
      * <P>
-     * Note that during the closure phase, a <code>SSLEngine</code> may
+     * Note that during the closure phase, a {@code SSLEngine} may
      * generate handshake closure data that must be sent to the peer.
-     * <code>wrap()</code> must be called to generate this data.  When
+     * {@code wrap()} must be called to generate this data.  When
      * this method returns true, no more outbound data will be created.
      *
-     * @return  true if the <code>SSLEngine</code> will not produce
+     * @return  true if the {@code SSLEngine} will not produce
      *          any more network data
      *
      * @see     #closeOutbound()
@@ -890,10 +890,10 @@
     /**
      * Sets the cipher suites enabled for use on this engine.
      * <P>
-     * Each cipher suite in the <code>suites</code> parameter must have
+     * Each cipher suite in the {@code suites} parameter must have
      * been listed by getSupportedCipherSuites(), or the method will
      * fail.  Following a successful call to this method, only suites
-     * listed in the <code>suites</code> parameter are enabled for use.
+     * listed in the {@code suites} parameter are enabled for use.
      * <P>
      * See {@link #getEnabledCipherSuites()} for more information
      * on why a specific cipher suite may never be used on a engine.
@@ -910,7 +910,7 @@
 
     /**
      * Returns the names of the protocols which could be enabled for use
-     * with this <code>SSLEngine</code>.
+     * with this {@code SSLEngine}.
      *
      * @return  an array of protocols supported
      */
@@ -919,7 +919,7 @@
 
     /**
      * Returns the names of the protocol versions which are currently
-     * enabled for use with this <code>SSLEngine</code>.
+     * enabled for use with this {@code SSLEngine}.
      *
      * @return  an array of protocols
      * @see     #setEnabledProtocols(String [])
@@ -932,7 +932,7 @@
      * <P>
      * The protocols must have been listed by getSupportedProtocols()
      * as being supported.  Following a successful call to this method,
-     * only protocols listed in the <code>protocols</code> parameter
+     * only protocols listed in the {@code protocols} parameter
      * are enabled for use.
      *
      * @param   protocols Names of all the protocols to enable.
@@ -945,8 +945,8 @@
 
 
     /**
-     * Returns the <code>SSLSession</code> in use in this
-     * <code>SSLEngine</code>.
+     * Returns the {@code SSLSession} in use in this
+     * {@code SSLEngine}.
      * <P>
      * These can be long lived, and frequently correspond to an entire
      * login session for some user.  The session specifies a particular
@@ -961,22 +961,22 @@
      * a session object which reports an invalid cipher suite of
      * "SSL_NULL_WITH_NULL_NULL".
      *
-     * @return  the <code>SSLSession</code> for this <code>SSLEngine</code>
+     * @return  the {@code SSLSession} for this {@code SSLEngine}
      * @see     SSLSession
      */
     public abstract SSLSession getSession();
 
 
     /**
-     * Returns the {@code SSLSession} being constructed during a SSL/TLS
+     * Returns the {@code SSLSession} being constructed during a SSL/TLS/DTLS
      * handshake.
      * <p>
-     * TLS protocols may negotiate parameters that are needed when using
+     * TLS/DTLS protocols may negotiate parameters that are needed when using
      * an instance of this class, but before the {@code SSLSession} has
      * been completely initialized and made available via {@code getSession}.
      * For example, the list of valid signature algorithms may restrict
      * the type of certificates that can used during TrustManager
-     * decisions, or the maximum TLS fragment packet sizes can be
+     * decisions, or the maximum TLS/DTLS fragment packet sizes can be
      * resized to better support the network environment.
      * <p>
      * This method provides early access to the {@code SSLSession} being
@@ -1012,26 +1012,26 @@
      * Initiates handshaking (initial or renegotiation) on this SSLEngine.
      * <P>
      * This method is not needed for the initial handshake, as the
-     * <code>wrap()</code> and <code>unwrap()</code> methods will
+     * {@code wrap()} and {@code unwrap()} methods will
      * implicitly call this method if handshaking has not already begun.
      * <P>
      * Note that the peer may also request a session renegotiation with
-     * this <code>SSLEngine</code> by sending the appropriate
+     * this {@code SSLEngine} by sending the appropriate
      * session renegotiate handshake message.
      * <P>
      * Unlike the {@link SSLSocket#startHandshake()
      * SSLSocket#startHandshake()} method, this method does not block
      * until handshaking is completed.
      * <P>
-     * To force a complete SSL/TLS session renegotiation, the current
+     * To force a complete SSL/TLS/DTLS session renegotiation, the current
      * session should be invalidated prior to calling this method.
      * <P>
      * Some protocols may not support multiple handshakes on an existing
-     * engine and may throw an <code>SSLException</code>.
+     * engine and may throw an {@code SSLException}.
      *
      * @throws  SSLException
      *          if a problem was encountered while signaling the
-     *          <code>SSLEngine</code> to begin a new handshake.
+     *          {@code SSLEngine} to begin a new handshake.
      *          See the class description for more information on
      *          engine closure.
      * @throws  IllegalStateException if the client/server mode
@@ -1042,9 +1042,9 @@
 
 
     /**
-     * Returns the current handshake status for this <code>SSLEngine</code>.
+     * Returns the current handshake status for this {@code SSLEngine}.
      *
-     * @return  the current <code>SSLEngineResult.HandshakeStatus</code>.
+     * @return  the current {@code SSLEngineResult.HandshakeStatus}.
      */
     public abstract SSLEngineResult.HandshakeStatus getHandshakeStatus();
 
--- a/jdk/src/java.base/share/classes/javax/net/ssl/SSLEngineResult.java	Mon Jun 01 10:15:21 2015 -0700
+++ b/jdk/src/java.base/share/classes/javax/net/ssl/SSLEngineResult.java	Tue Jun 02 09:15:47 2015 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -27,13 +27,13 @@
 
 /**
  * An encapsulation of the result state produced by
- * <code>SSLEngine</code> I/O calls.
+ * {@code SSLEngine} I/O calls.
  *
- * <p> A <code>SSLEngine</code> provides a means for establishing
- * secure communication sessions between two peers.  <code>SSLEngine</code>
+ * <p> A {@code SSLEngine} provides a means for establishing
+ * secure communication sessions between two peers.  {@code SSLEngine}
  * operations typically consume bytes from an input buffer and produce
  * bytes in an output buffer.  This class provides operational result
- * values describing the state of the <code>SSLEngine</code>, including
+ * values describing the state of the {@code SSLEngine}, including
  * indications of what operations are needed to finish an
  * ongoing handshake.  Lastly, it reports the number of bytes consumed
  * and produced as a result of this operation.
@@ -49,12 +49,12 @@
 public class SSLEngineResult {
 
     /**
-     * An <code>SSLEngineResult</code> enum describing the overall result
-     * of the <code>SSLEngine</code> operation.
+     * An {@code SSLEngineResult} enum describing the overall result
+     * of the {@code SSLEngine} operation.
      *
-     * The <code>Status</code> value does not reflect the
-     * state of a <code>SSLEngine</code> handshake currently
-     * in progress.  The <code>SSLEngineResult's HandshakeStatus</code>
+     * The {@code Status} value does not reflect the
+     * state of a {@code SSLEngine} handshake currently
+     * in progress.  The {@code SSLEngineResult's HandshakeStatus}
      * should be consulted for that information.
      *
      * @author Brad R. Wetmore
@@ -63,7 +63,7 @@
     public static enum Status {
 
         /**
-         * The <code>SSLEngine</code> was not able to unwrap the
+         * The {@code SSLEngine} was not able to unwrap the
          * incoming data because there were not enough source bytes
          * available to make a complete packet.
          *
@@ -73,7 +73,7 @@
         BUFFER_UNDERFLOW,
 
         /**
-         * The <code>SSLEngine</code> was not able to process the
+         * The {@code SSLEngine} was not able to process the
          * operation because there are not enough bytes available in the
          * destination buffer to hold the result.
          * <P>
@@ -85,22 +85,22 @@
         BUFFER_OVERFLOW,
 
         /**
-         * The <code>SSLEngine</code> completed the operation, and
+         * The {@code SSLEngine} completed the operation, and
          * is available to process similar calls.
          */
         OK,
 
         /**
          * The operation just closed this side of the
-         * <code>SSLEngine</code>, or the operation
+         * {@code SSLEngine}, or the operation
          * could not be completed because it was already closed.
          */
         CLOSED;
     }
 
     /**
-     * An <code>SSLEngineResult</code> enum describing the current
-     * handshaking state of this <code>SSLEngine</code>.
+     * An {@code SSLEngineResult} enum describing the current
+     * handshaking state of this {@code SSLEngine}.
      *
      * @author Brad R. Wetmore
      * @since 1.5
@@ -108,17 +108,17 @@
     public static enum HandshakeStatus {
 
         /**
-         * The <code>SSLEngine</code> is not currently handshaking.
+         * The {@code SSLEngine} is not currently handshaking.
          */
         NOT_HANDSHAKING,
 
         /**
-         * The <code>SSLEngine</code> has just finished handshaking.
+         * The {@code SSLEngine} has just finished handshaking.
          * <P>
          * This value is only generated by a call to
-         * <code>SSLEngine.wrap()/unwrap()</code> when that call
+         * {@code SSLEngine.wrap()/unwrap()} when that call
          * finishes a handshake.  It is never generated by
-         * <code>SSLEngine.getHandshakeStatus()</code>.
+         * {@code SSLEngine.getHandshakeStatus()}.
          *
          * @see SSLEngine#wrap(ByteBuffer, ByteBuffer)
          * @see SSLEngine#unwrap(ByteBuffer, ByteBuffer)
@@ -127,7 +127,7 @@
         FINISHED,
 
         /**
-         * The <code>SSLEngine</code> needs the results of one (or more)
+         * The {@code SSLEngine} needs the results of one (or more)
          * delegated tasks before handshaking can continue.
          *
          * @see SSLEngine#getDelegatedTask()
@@ -135,8 +135,8 @@
         NEED_TASK,
 
         /**
-         * The <code>SSLEngine</code> must send data to the remote side
-         * before handshaking can continue, so <code>SSLEngine.wrap()</code>
+         * The {@code SSLEngine} must send data to the remote side
+         * before handshaking can continue, so {@code SSLEngine.wrap()}
          * should be called.
          *
          * @see SSLEngine#wrap(ByteBuffer, ByteBuffer)
@@ -144,10 +144,22 @@
         NEED_WRAP,
 
         /**
-         * The <code>SSLEngine</code> needs to receive data from the
+         * The {@code SSLEngine} needs to receive data from the
          * remote side before handshaking can continue.
          */
-        NEED_UNWRAP;
+        NEED_UNWRAP,
+
+        /**
+         * The {@code SSLEngine} needs to unwrap before handshaking can
+         * can continue.
+         * <P>
+         * This value is used to indicate that not-yet-interpreted data
+         * has been previously received from the remote side, and does
+         * not need to be received again.
+         *
+         * @since   1.9
+         */
+        NEED_UNWRAP_AGAIN;
     }
 
 
@@ -155,6 +167,7 @@
     private final HandshakeStatus handshakeStatus;
     private final int bytesConsumed;
     private final int bytesProduced;
+    private final long sequenceNumber;
 
     /**
      * Initializes a new instance of this class.
@@ -172,12 +185,44 @@
      *          the number of bytes placed into the destination ByteBuffer
      *
      * @throws  IllegalArgumentException
-     *          if the <code>status</code> or <code>handshakeStatus</code>
-     *          arguments are null, or if <code>bytesConsumed</code> or
-     *          <code>bytesProduced</code> is negative.
+     *          if the {@code status} or {@code handshakeStatus}
+     *          arguments are null, or if {@code bytesConsumed} or
+     *          {@code bytesProduced} is negative.
      */
     public SSLEngineResult(Status status, HandshakeStatus handshakeStatus,
             int bytesConsumed, int bytesProduced) {
+        this(status, handshakeStatus, bytesConsumed, bytesProduced, -1);
+    }
+
+    /**
+     * Initializes a new instance of this class.
+     *
+     * @param   status
+     *          the return value of the operation.
+     *
+     * @param   handshakeStatus
+     *          the current handshaking status.
+     *
+     * @param   bytesConsumed
+     *          the number of bytes consumed from the source ByteBuffer
+     *
+     * @param   bytesProduced
+     *          the number of bytes placed into the destination ByteBuffer
+     *
+     * @param   sequenceNumber
+     *          the sequence number (unsigned long) of the produced or
+     *          consumed SSL/TLS/DTLS record, or ${@code -1L} if no record
+     *          produced or consumed
+     *
+     * @throws  IllegalArgumentException
+     *          if the {@code status} or {@code handshakeStatus}
+     *          arguments are null, or if {@code bytesConsumed} or
+     *          {@code bytesProduced} is negative
+     *
+     * @since   1.9
+     */
+    public SSLEngineResult(Status status, HandshakeStatus handshakeStatus,
+            int bytesConsumed, int bytesProduced, long sequenceNumber) {
 
         if ((status == null) || (handshakeStatus == null) ||
                 (bytesConsumed < 0) || (bytesProduced < 0)) {
@@ -188,10 +233,11 @@
         this.handshakeStatus = handshakeStatus;
         this.bytesConsumed = bytesConsumed;
         this.bytesProduced = bytesProduced;
+        this.sequenceNumber = sequenceNumber;
     }
 
     /**
-     * Gets the return value of this <code>SSLEngine</code> operation.
+     * Gets the return value of this {@code SSLEngine} operation.
      *
      * @return  the return value
      */
@@ -200,7 +246,7 @@
     }
 
     /**
-     * Gets the handshake status of this <code>SSLEngine</code>
+     * Gets the handshake status of this {@code SSLEngine}
      * operation.
      *
      * @return  the handshake status
@@ -228,6 +274,41 @@
     }
 
     /**
+     * Returns the sequence number of the produced or consumed SSL/TLS/DTLS
+     * record (optional operation).
+     *
+     * @apiNote  Note that sequence number is an unsigned long and cannot
+     *           exceed {@code -1L}.  It is desired to use the unsigned
+     *           long comparing mode for comparison of unsigned long values
+     *           (see also {@link java.lang.Long#compareUnsigned()
+     *           Long.compareUnsigned()}).
+     *           <P>
+     *           For DTLS protocols, the first 16 bits of the sequence
+     *           number is a counter value (epoch) that is incremented on
+     *           every cipher state change.  The remaining 48 bits on the
+     *           right side of the sequence number represents the sequence
+     *           of the record, which is maintained separately for each epoch.
+     *
+     * @implNote It is recommended that providers should never allow the
+     *           sequence number incremented to {@code -1L}.  If the sequence
+     *           number is close to wrapping, renegotiate should be requested,
+     *           otherwise the connection should be closed immediately.
+     *           This should be carried on automatically by the underlying
+     *           implementation.
+     *
+     * @return  the sequence number of the produced or consumed SSL/TLS/DTLS
+     *          record; or ${@code -1L} if no record is produced or consumed,
+     *          or this operation is not supported by the underlying provider
+     *
+     * @see     java.lang.Long#compareUnsigned(boolean, boolean)
+     *
+     * @since   1.9
+     */
+    final public long sequenceNumber() {
+        return sequenceNumber;
+    }
+
+    /**
      * Returns a String representation of this object.
      */
     @Override
@@ -235,6 +316,8 @@
         return ("Status = " + status +
             " HandshakeStatus = " + handshakeStatus +
             "\nbytesConsumed = " + bytesConsumed +
-            " bytesProduced = " + bytesProduced);
+            " bytesProduced = " + bytesProduced +
+            (sequenceNumber == -1 ? "" :
+                " sequenceNumber = " + Long.toUnsignedString(sequenceNumber)));
     }
 }
--- a/jdk/src/java.base/share/classes/javax/net/ssl/SSLParameters.java	Mon Jun 01 10:15:21 2015 -0700
+++ b/jdk/src/java.base/share/classes/javax/net/ssl/SSLParameters.java	Tue Jun 02 09:15:47 2015 -0700
@@ -35,22 +35,22 @@
 import java.util.LinkedHashMap;
 
 /**
- * Encapsulates parameters for an SSL/TLS connection. The parameters
- * are the list of ciphersuites to be accepted in an SSL/TLS handshake,
+ * Encapsulates parameters for an SSL/TLS/DTLS connection. The parameters
+ * are the list of ciphersuites to be accepted in an SSL/TLS/DTLS handshake,
  * the list of protocols to be allowed, the endpoint identification
- * algorithm during SSL/TLS handshaking, the Server Name Indication (SNI),
- * the algorithm constraints and whether SSL/TLS servers should request
- * or require client authentication, etc.
+ * algorithm during SSL/TLS/DTLS handshaking, the Server Name Indication (SNI),
+ * the maximum network packet size, the algorithm constraints and whether
+ * SSL/TLS/DTLS servers should request or require client authentication, etc.
  * <p>
  * SSLParameters can be created via the constructors in this class.
- * Objects can also be obtained using the <code>getSSLParameters()</code>
+ * Objects can also be obtained using the {@code getSSLParameters()}
  * methods in
  * {@link SSLSocket#getSSLParameters SSLSocket} and
  * {@link SSLServerSocket#getSSLParameters SSLServerSocket} and
  * {@link SSLEngine#getSSLParameters SSLEngine} or the
  * {@link SSLContext#getDefaultSSLParameters getDefaultSSLParameters()} and
  * {@link SSLContext#getSupportedSSLParameters getSupportedSSLParameters()}
- * methods in <code>SSLContext</code>.
+ * methods in {@code SSLContext}.
  * <p>
  * SSLParameters can be applied to a connection via the methods
  * {@link SSLSocket#setSSLParameters SSLSocket.setSSLParameters()} and
@@ -74,14 +74,18 @@
     private Map<Integer, SNIServerName> sniNames = null;
     private Map<Integer, SNIMatcher> sniMatchers = null;
     private boolean preferLocalCipherSuites;
+    private boolean enableRetransmissions = true;
+    private int maximumPacketSize = 0;
 
     /**
      * Constructs SSLParameters.
      * <p>
      * The values of cipherSuites, protocols, cryptographic algorithm
      * constraints, endpoint identification algorithm, server names and
-     * server name matchers are set to <code>null</code>, useCipherSuitesOrder,
-     * wantClientAuth and needClientAuth are set to <code>false</code>.
+     * server name matchers are set to {@code null}; useCipherSuitesOrder,
+     * wantClientAuth and needClientAuth are set to {@code false};
+     * enableRetransmissions is set to {@code true}; maximum network packet
+     * size is set to {@code 0}.
      */
     public SSLParameters() {
         // empty
@@ -92,7 +96,7 @@
      * <p>
      * Calling this constructor is equivalent to calling the no-args
      * constructor followed by
-     * <code>setCipherSuites(cipherSuites);</code>.
+     * {@code setCipherSuites(cipherSuites);}.
      *
      * @param cipherSuites the array of ciphersuites (or null)
      */
@@ -106,7 +110,7 @@
      * <p>
      * Calling this constructor is equivalent to calling the no-args
      * constructor followed by
-     * <code>setCipherSuites(cipherSuites); setProtocols(protocols);</code>.
+     * {@code setCipherSuites(cipherSuites); setProtocols(protocols);}.
      *
      * @param cipherSuites the array of ciphersuites (or null)
      * @param protocols the array of protocols (or null)
@@ -171,7 +175,7 @@
 
     /**
      * Sets whether client authentication should be requested. Calling
-     * this method clears the <code>needClientAuth</code> flag.
+     * this method clears the {@code needClientAuth} flag.
      *
      * @param wantClientAuth whether client authentication should be requested
      */
@@ -191,7 +195,7 @@
 
     /**
      * Sets whether client authentication should be required. Calling
-     * this method clears the <code>wantClientAuth</code> flag.
+     * this method clears the {@code wantClientAuth} flag.
      *
      * @param needClientAuth whether client authentication should be required
      */
@@ -218,9 +222,9 @@
      * Sets the cryptographic algorithm constraints, which will be used
      * in addition to any configured by the runtime environment.
      * <p>
-     * If the <code>constraints</code> parameter is non-null, every
+     * If the {@code constraints} parameter is non-null, every
      * cryptographic algorithm, key and algorithm parameters used in the
-     * SSL/TLS handshake must be permitted by the constraints.
+     * SSL/TLS/DTLS handshake must be permitted by the constraints.
      *
      * @param constraints the algorithm constraints (or null)
      *
@@ -249,9 +253,9 @@
     /**
      * Sets the endpoint identification algorithm.
      * <p>
-     * If the <code>algorithm</code> parameter is non-null or non-empty, the
+     * If the {@code algorithm} parameter is non-null or non-empty, the
      * endpoint identification/verification procedures must be handled during
-     * SSL/TLS handshaking.  This is to prevent man-in-the-middle attacks.
+     * SSL/TLS/DTLS handshaking.  This is to prevent man-in-the-middle attacks.
      *
      * @param algorithm The standard string name of the endpoint
      *     identification algorithm (or null).  See Appendix A in the <a href=
@@ -317,7 +321,7 @@
      * This method is only useful to {@link SSLSocket}s or {@link SSLEngine}s
      * operating in client mode.
      * <P>
-     * For SSL/TLS connections, the underlying SSL/TLS provider
+     * For SSL/TLS/DTLS connections, the underlying SSL/TLS/DTLS provider
      * may specify a default value for a certain server name type.  In
      * client mode, it is recommended that, by default, providers should
      * include the server name indication whenever the server can be located
@@ -440,7 +444,7 @@
      *
      * @param honorOrder whether local cipher suites order in
      *        {@code #getCipherSuites} should be honored during
-     *        SSL/TLS handshaking.
+     *        SSL/TLS/DTLS handshaking.
      *
      * @see #getUseCipherSuitesOrder()
      *
@@ -454,7 +458,7 @@
      * Returns whether the local cipher suites preference should be honored.
      *
      * @return whether local cipher suites order in {@code #getCipherSuites}
-     *         should be honored during SSL/TLS handshaking.
+     *         should be honored during SSL/TLS/DTLS handshaking.
      *
      * @see #setUseCipherSuitesOrder(boolean)
      *
@@ -463,5 +467,107 @@
     public final boolean getUseCipherSuitesOrder() {
         return preferLocalCipherSuites;
     }
+
+    /**
+     * Sets whether DTLS handshake retransmissions should be enabled.
+     *
+     * This method only applies to DTLS.
+     *
+     * @param   enableRetransmissions
+     *          {@code true} indicates that DTLS handshake retransmissions
+     *          should be enabled; {@code false} indicates that DTLS handshake
+     *          retransmissions should be disabled
+     *
+     * @see     #getEnableRetransmissions()
+     *
+     * @since 1.9
+     */
+    public void setEnableRetransmissions(boolean enableRetransmissions) {
+        this.enableRetransmissions = enableRetransmissions;
+    }
+
+    /**
+     * Returns whether DTLS handshake retransmissions should be enabled.
+     *
+     * This method only applies to DTLS.
+     *
+     * @return  true, if DTLS handshake retransmissions should be enabled
+     *
+     * @see     #setEnableRetransmissions(boolean)
+     *
+     * @since 1.9
+     */
+    public boolean getEnableRetransmissions() {
+        return enableRetransmissions;
+    }
+
+    /**
+     * Sets the maximum expected network packet size in bytes for
+     * SSL/TLS/DTLS records.
+     *
+     * @apiNote  It is recommended that if possible, the maximum packet size
+     *           should not be less than 256 bytes so that small handshake
+     *           messages, such as HelloVerifyRequests, are not fragmented.
+     *
+     * @implNote If the maximum packet size is too small to hold a minimal
+     *           record, an implementation may attempt to generate as minimal
+     *           records as possible.  However, this may cause a generated
+     *           packet to be larger than the maximum packet size.
+     *
+     * @param   maximumPacketSize
+     *          the maximum expected network packet size in bytes, or
+     *          {@code 0} to use the implicit size that is automatically
+     *          specified by the underlying implementation.
+     * @throws  IllegalArgumentException
+     *          if {@code maximumPacketSize} is negative.
+     *
+     * @see     #getMaximumPacketSize()
+     *
+     * @since 1.9
+     */
+    public void setMaximumPacketSize(int maximumPacketSize) {
+        if (maximumPacketSize < 0) {
+            throw new IllegalArgumentException(
+                "The maximum packet size cannot be negative");
+        }
+
+        this.maximumPacketSize = maximumPacketSize;
+    }
+
+    /**
+     * Returns the maximum expected network packet size in bytes for
+     * SSL/TLS/DTLS records.
+     *
+     * @apiNote  The implicit size may not be a fixed value, especially
+     *           for a DTLS protocols implementation.
+     *
+     * @implNote For SSL/TLS/DTLS connections, the underlying provider
+     *           should calculate and specify the implicit value of the
+     *           maximum expected network packet size if it is not
+     *           configured explicitly.  For any connection populated
+     *           object, this method should never return {@code 0} so
+     *           that applications can retrieve the actual implicit size
+     *           of the underlying implementation.
+     *           <P>
+     *           An implementation should attempt to comply with the maximum
+     *           packet size configuration.  However, if the maximum packet
+     *           size is too small to hold a minimal record, an implementation
+     *           may try to generate as minimal records as possible.  This
+     *           may cause a generated packet to be larger than the maximum
+     *           packet size.
+     *
+     * @return   the maximum expected network packet size, or {@code 0} if
+     *           use the implicit size that is automatically specified by
+     *           the underlying implementation and this object has not been
+     *           populated by any connection.
+     *
+     * @see      #setMaximumPacketSize(int)
+     *
+     * @since 1.9
+     */
+    public int getMaximumPacketSize() {
+        return maximumPacketSize;
+    }
+
 }
 
--- a/jdk/src/java.base/share/classes/javax/net/ssl/SSLSession.java	Mon Jun 01 10:15:21 2015 -0700
+++ b/jdk/src/java.base/share/classes/javax/net/ssl/SSLSession.java	Tue Jun 02 09:15:47 2015 -0700
@@ -35,7 +35,7 @@
  * also be replaced by a different session.  Sessions are created, or
  * rejoined, as part of the SSL handshaking protocol. Sessions may be
  * invalidated due to policies affecting security or resource usage,
- * or by an application explicitly calling <code>invalidate</code>.
+ * or by an application explicitly calling {@code invalidate}.
  * Session management policies are typically used to tune performance.
  *
  * <P> In addition to the standard session attributes, SSL sessions expose
@@ -82,8 +82,8 @@
      * security manager installed, the caller may require
      * permission to access it or a security exception may be thrown.
      * In a Java environment, the security manager's
-     * <code>checkPermission</code> method is called with a
-     * <code>SSLPermission("getSSLSessionContext")</code> permission.
+     * {@code checkPermission} method is called with a
+     * {@code SSLPermission("getSSLSessionContext")} permission.
      *
      * @throws SecurityException if the calling thread does not have
      *         permission to get SSL session context.
@@ -148,14 +148,14 @@
 
     /**
      *
-     * Binds the specified <code>value</code> object into the
+     * Binds the specified {@code value} object into the
      * session's application layer data
-     * with the given <code>name</code>.
+     * with the given {@code name}.
      * <P>
-     * Any existing binding using the same <code>name</code> is
-     * replaced.  If the new (or existing) <code>value</code> implements the
-     * <code>SSLSessionBindingListener</code> interface, the object
-     * represented by <code>value</code> is notified appropriately.
+     * Any existing binding using the same {@code name} is
+     * replaced.  If the new (or existing) {@code value} implements the
+     * {@code SSLSessionBindingListener} interface, the object
+     * represented by {@code value} is notified appropriately.
      * <p>
      * For security reasons, the same named values may not be
      * visible across different access control contexts.
@@ -187,7 +187,7 @@
      * Removes the object bound to the given name in the session's
      * application layer data.  Does nothing if there is no object
      * bound to the given name.  If the bound existing object
-     * implements the <code>SessionBindingListener</code> interface,
+     * implements the {@code SessionBindingListener} interface,
      * it is notified appropriately.
      * <p>
      * For security reasons, the same named values may not be
@@ -349,7 +349,7 @@
      * by this method.
      * <P>
      * This value is not authenticated and should not be relied upon.
-     * It is mainly used as a hint for <code>SSLSession</code> caching
+     * It is mainly used as a hint for {@code SSLSession} caching
      * strategies.
      *
      * @return  the host name of the peer host, or null if no information
@@ -364,7 +364,7 @@
      * the client, it is the server's port number.
      * <P>
      * This value is not authenticated and should not be relied upon.
-     * It is mainly used as a hint for <code>SSLSession</code> caching
+     * It is mainly used as a hint for {@code SSLSession} caching
      * strategies.
      *
      * @return  the port number of the peer host, or -1 if no information
@@ -375,14 +375,14 @@
     public int getPeerPort();
 
     /**
-     * Gets the current size of the largest SSL/TLS packet that is expected
-     * when using this session.
+     * Gets the current size of the largest SSL/TLS/DTLS packet that is
+     * expected when using this session.
      * <P>
-     * A <code>SSLEngine</code> using this session may generate SSL/TLS
+     * An {@code SSLEngine} using this session may generate SSL/TLS/DTLS
      * packets of any size up to and including the value returned by this
-     * method. All <code>SSLEngine</code> network buffers should be sized
+     * method. All {@code SSLEngine} network buffers should be sized
      * at least this large to avoid insufficient space problems when
-     * performing <code>wrap</code> and <code>unwrap</code> calls.
+     * performing {@code wrap} and {@code unwrap} calls.
      *
      * @return  the current maximum expected network packet size
      *
@@ -398,7 +398,7 @@
      * Gets the current size of the largest application data that is
      * expected when using this session.
      * <P>
-     * <code>SSLEngine</code> application data buffers must be large
+     * {@code SSLEngine} application data buffers must be large
      * enough to hold the application data from any inbound network
      * application data packet received.  Typically, outbound
      * application data buffers can be of any size.
--- a/jdk/src/java.base/share/classes/javax/net/ssl/X509ExtendedTrustManager.java	Mon Jun 01 10:15:21 2015 -0700
+++ b/jdk/src/java.base/share/classes/javax/net/ssl/X509ExtendedTrustManager.java	Tue Jun 02 09:15:47 2015 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -32,16 +32,17 @@
 import java.security.cert.CertificateException;
 
 /**
- * Extensions to the <code>X509TrustManager</code> interface to support
- * SSL/TLS connection sensitive trust management.
+ * Extensions to the {@code X509TrustManager} interface to support
+ * SSL/TLS/DTLS connection sensitive trust management.
  * <p>
  * To prevent man-in-the-middle attacks, hostname checks can be done
  * to verify that the hostname in an end-entity certificate matches the
- * targeted hostname.  TLS does not require such checks, but some protocols
- * over TLS (such as HTTPS) do.  In earlier versions of the JDK, the
- * certificate chain checks were done at the SSL/TLS layer, and the hostname
- * verification checks were done at the layer over TLS.  This class allows
- * for the checking to be done during a single call to this class.
+ * targeted hostname.  TLS/DTLS does not require such checks, but some
+ * protocols over TLS/DTLS (such as HTTPS) do.  In earlier versions of the
+ * JDK, the certificate chain checks were done at the SSL/TLS/DTLS layer,
+ * and the hostname verification checks were done at the layer over TLS/DTLS.
+ * This class allows for the checking to be done during a single call to
+ * this class.
  * <p>
  * RFC 2830 defines the server identification specification for the "LDAPS"
  * algorithm. RFC 2818 defines both the server identification and the
@@ -62,17 +63,17 @@
      * used. For instance, if RSAPublicKey is used, the authType
      * should be "RSA". Checking is case-sensitive.
      * <p>
-     * If the <code>socket</code> parameter is an instance of
+     * If the {@code socket} parameter is an instance of
      * {@link javax.net.ssl.SSLSocket}, and the endpoint identification
-     * algorithm of the <code>SSLParameters</code> is non-empty, to prevent
-     * man-in-the-middle attacks, the address that the <code>socket</code>
+     * algorithm of the {@code SSLParameters} is non-empty, to prevent
+     * man-in-the-middle attacks, the address that the {@code socket}
      * connected to should be checked against the peer's identity presented
      * in the end-entity X509 certificate, as specified in the endpoint
      * identification algorithm.
      * <p>
-     * If the <code>socket</code> parameter is an instance of
+     * If the {@code socket} parameter is an instance of
      * {@link javax.net.ssl.SSLSocket}, and the algorithm constraints of the
-     * <code>SSLParameters</code> is non-null, for every certificate in the
+     * {@code SSLParameters} is non-null, for every certificate in the
      * certification path, fields such as subject public key, the signature
      * algorithm, key usage, extended key usage, etc. need to conform to the
      * algorithm constraints in place on this socket.
@@ -83,8 +84,8 @@
      *        can be null, which indicates that implementations need not check
      *        the ssl parameters
      * @throws IllegalArgumentException if null or zero-length array is passed
-     *        in for the <code>chain</code> parameter or if null or zero-length
-     *        string is passed in for the <code>authType</code> parameter
+     *        in for the {@code chain} parameter or if null or zero-length
+     *        string is passed in for the {@code authType} parameter
      * @throws CertificateException if the certificate chain is not trusted
      *        by this TrustManager
      *
@@ -110,17 +111,17 @@
      * used for the key exchange, and RSA when the key from the server
      * certificate is used. Checking is case-sensitive.
      * <p>
-     * If the <code>socket</code> parameter is an instance of
+     * If the {@code socket} parameter is an instance of
      * {@link javax.net.ssl.SSLSocket}, and the endpoint identification
-     * algorithm of the <code>SSLParameters</code> is non-empty, to prevent
-     * man-in-the-middle attacks, the address that the <code>socket</code>
+     * algorithm of the {@code SSLParameters} is non-empty, to prevent
+     * man-in-the-middle attacks, the address that the {@code socket}
      * connected to should be checked against the peer's identity presented
      * in the end-entity X509 certificate, as specified in the endpoint
      * identification algorithm.
      * <p>
-     * If the <code>socket</code> parameter is an instance of
+     * If the {@code socket} parameter is an instance of
      * {@link javax.net.ssl.SSLSocket}, and the algorithm constraints of the
-     *  <code>SSLParameters</code> is non-null, for every certificate in the
+     *  {@code SSLParameters} is non-null, for every certificate in the
      * certification path, fields such as subject public key, the signature
      * algorithm, key usage, extended key usage, etc. need to conform to the
      * algorithm constraints in place on this socket.
@@ -131,8 +132,8 @@
      *        can be null, which indicates that implementations need not check
      *        the ssl parameters
      * @throws IllegalArgumentException if null or zero-length array is passed
-     *        in for the <code>chain</code> parameter or if null or zero-length
-     *        string is passed in for the <code>authType</code> parameter
+     *        in for the {@code chain} parameter or if null or zero-length
+     *        string is passed in for the {@code authType} parameter
      * @throws CertificateException if the certificate chain is not trusted
      *        by this TrustManager
      *
@@ -153,15 +154,15 @@
      * used. For instance, if RSAPublicKey is used, the authType
      * should be "RSA". Checking is case-sensitive.
      * <p>
-     * If the <code>engine</code> parameter is available, and the endpoint
-     * identification algorithm of the <code>SSLParameters</code> is
+     * If the {@code engine} parameter is available, and the endpoint
+     * identification algorithm of the {@code SSLParameters} is
      * non-empty, to prevent man-in-the-middle attacks, the address that
-     * the <code>engine</code> connected to should be checked against
+     * the {@code engine} connected to should be checked against
      * the peer's identity presented in the end-entity X509 certificate,
      * as specified in the endpoint identification algorithm.
      * <p>
-     * If the <code>engine</code> parameter is available, and the algorithm
-     * constraints of the <code>SSLParameters</code> is non-null, for every
+     * If the {@code engine} parameter is available, and the algorithm
+     * constraints of the {@code SSLParameters} is non-null, for every
      * certificate in the certification path, fields such as subject public
      * key, the signature algorithm, key usage, extended key usage, etc.
      * need to conform to the algorithm constraints in place on this engine.
@@ -172,8 +173,8 @@
      *        can be null, which indicates that implementations need not check
      *        the ssl parameters
      * @throws IllegalArgumentException if null or zero-length array is passed
-     *        in for the <code>chain</code> parameter or if null or zero-length
-     *        string is passed in for the <code>authType</code> parameter
+     *        in for the {@code chain} parameter or if null or zero-length
+     *        string is passed in for the {@code authType} parameter
      * @throws CertificateException if the certificate chain is not trusted
      *        by this TrustManager
      *
@@ -199,15 +200,15 @@
      * used for the key exchange, and RSA when the key from the server
      * certificate is used. Checking is case-sensitive.
      * <p>
-     * If the <code>engine</code> parameter is available, and the endpoint
-     * identification algorithm of the <code>SSLParameters</code> is
+     * If the {@code engine} parameter is available, and the endpoint
+     * identification algorithm of the {@code SSLParameters} is
      * non-empty, to prevent man-in-the-middle attacks, the address that
-     * the <code>engine</code> connected to should be checked against
+     * the {@code engine} connected to should be checked against
      * the peer's identity presented in the end-entity X509 certificate,
      * as specified in the endpoint identification algorithm.
      * <p>
-     * If the <code>engine</code> parameter is available, and the algorithm
-     * constraints of the <code>SSLParameters</code> is non-null, for every
+     * If the {@code engine} parameter is available, and the algorithm
+     * constraints of the {@code SSLParameters} is non-null, for every
      * certificate in the certification path, fields such as subject public
      * key, the signature algorithm, key usage, extended key usage, etc.
      * need to conform to the algorithm constraints in place on this engine.
@@ -218,8 +219,8 @@
      *        can be null, which indicates that implementations need not check
      *        the ssl parameters
      * @throws IllegalArgumentException if null or zero-length array is passed
-     *        in for the <code>chain</code> parameter or if null or zero-length
-     *        string is passed in for the <code>authType</code> parameter
+     *        in for the {@code chain} parameter or if null or zero-length
+     *        string is passed in for the {@code authType} parameter
      * @throws CertificateException if the certificate chain is not trusted
      *        by this TrustManager
      *
--- a/jdk/src/java.base/share/classes/sun/security/ssl/AppInputStream.java	Mon Jun 01 10:15:21 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/AppInputStream.java	Tue Jun 02 09:15:47 2015 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,41 +26,54 @@
 
 package sun.security.ssl;
 
-import java.io.*;
+import java.io.InputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+import javax.net.ssl.SSLProtocolException;
 
 /**
  * InputStream for application data as returned by SSLSocket.getInputStream().
- * It uses an InputRecord as internal buffer that is refilled on demand
- * whenever it runs out of data.
  *
  * @author David Brownell
  */
-class AppInputStream extends InputStream {
+final class AppInputStream extends InputStream {
+    // the buffer size for each read of network data
+    private static final int READ_BUFFER_SIZE = 4096;
 
     // static dummy array we use to implement skip()
-    private final static byte[] SKIP_ARRAY = new byte[1024];
+    private static final byte[] SKIP_ARRAY = new byte[256];
+
+    // the related socket of the input stream
+    private final SSLSocketImpl socket;
 
-    private SSLSocketImpl c;
-    InputRecord r;
+    // the temporary buffer used to read network
+    private ByteBuffer buffer;
+
+    // Is application data available in the stream?
+    private boolean appDataIsAvailable;
 
     // One element array used to implement the single byte read() method
     private final byte[] oneByte = new byte[1];
 
     AppInputStream(SSLSocketImpl conn) {
-        r = new InputRecord();
-        c = conn;
+        this.buffer = ByteBuffer.allocate(READ_BUFFER_SIZE);
+        this.socket = conn;
+        this.appDataIsAvailable = false;
     }
 
     /**
      * Return the minimum number of bytes that can be read without blocking.
+     *
      * Currently not synchronized.
      */
     @Override
     public int available() throws IOException {
-        if (c.checkEOF() || (r.isAppDataValid() == false)) {
+        if ((!appDataIsAvailable) || socket.checkEOF()) {
             return 0;
         }
-        return r.available();
+
+        return buffer.remaining();
     }
 
     /**
@@ -72,17 +85,21 @@
         if (n <= 0) { // EOF
             return -1;
         }
-        return oneByte[0] & 0xff;
+        return oneByte[0] & 0xFF;
     }
 
     /**
-     * Read up to "len" bytes into this buffer, starting at "off".
+     * Reads up to {@code len} bytes of data from the input stream into an
+     * array of bytes. An attempt is made to read as many as {@code len} bytes,
+     * but a smaller number may be read. The number of bytes actually read
+     * is returned as an integer.
+     *
      * If the layer above needs more data, it asks for more, so we
      * are responsible only for blocking to fill at most one buffer,
      * and returning "-1" on non-fault EOF status.
      */
     @Override
-    public synchronized int read(byte b[], int off, int len)
+    public synchronized int read(byte[] b, int off, int len)
             throws IOException {
         if (b == null) {
             throw new NullPointerException();
@@ -92,28 +109,69 @@
             return 0;
         }
 
-        if (c.checkEOF()) {
+        if (socket.checkEOF()) {
             return -1;
         }
+
+        // Read the available bytes at first.
+        int remains = available();
+        if (remains > 0) {
+            int howmany = Math.min(remains, len);
+            buffer.get(b, off, howmany);
+
+            return howmany;
+        }
+
+        appDataIsAvailable = false;
+        int volume = 0;
+
         try {
             /*
              * Read data if needed ... notice that the connection guarantees
              * that handshake, alert, and change cipher spec data streams are
              * handled as they arrive, so we never see them here.
              */
-            while (r.available() == 0) {
-                c.readDataRecord(r);
-                if (c.checkEOF()) {
+            while(volume == 0) {
+                // Clear the buffer for a new record reading.
+                buffer.clear();
+
+                //
+                // grow the buffer if needed
+                //
+
+                // Read the header of a record into the buffer, and return
+                // the packet size.
+                int packetLen = socket.bytesInCompletePacket();
+                if (packetLen < 0) {    // EOF
                     return -1;
                 }
+
+                // Is this packet bigger than SSL/TLS normally allows?
+                if (packetLen > SSLRecord.maxLargeRecordSize) {
+                    throw new SSLProtocolException(
+                        "Illegal packet size: " + packetLen);
+                }
+
+                if (packetLen > buffer.remaining()) {
+                    buffer = ByteBuffer.allocate(packetLen);
+                }
+
+                volume = socket.readRecord(buffer);
+                if (volume < 0) {    // EOF
+                    return -1;
+                } else if (volume > 0) {
+                    appDataIsAvailable = true;
+                    break;
+                }
             }
 
-            int howmany = Math.min(len, r.available());
-            howmany = r.read(b, off, howmany);
+            int howmany = Math.min(len, volume);
+            buffer.get(b, off, howmany);
             return howmany;
         } catch (Exception e) {
             // shutdown and rethrow (wrapped) exception as appropriate
-            c.handleException(e);
+            socket.handleException(e);
+
             // dummy for compiler
             return -1;
         }
@@ -147,9 +205,8 @@
      */
     @Override
     public void close() throws IOException {
-        c.close();
+        socket.close();
     }
 
     // inherit default mark/reset behavior (throw Exceptions) from InputStream
-
 }
--- a/jdk/src/java.base/share/classes/sun/security/ssl/AppOutputStream.java	Mon Jun 01 10:15:21 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/AppOutputStream.java	Tue Jun 02 09:15:47 2015 -0700
@@ -23,41 +23,33 @@
  * questions.
  */
 
-
 package sun.security.ssl;
 
 import java.io.OutputStream;
 import java.io.IOException;
+import java.nio.ByteBuffer;
 
 /*
- * Output stream for application data. This is the kind of stream
- * that's handed out via SSLSocket.getOutputStream(). It's all the application
- * ever sees.
- *
- * Once the initial handshake has completed, application data may be
- * interleaved with handshake data. That is handled internally and remains
- * transparent to the application.
+ * OutputStream for application data as returned by SSLSocket.getOutputStream().
  *
  * @author  David Brownell
  */
 class AppOutputStream extends OutputStream {
 
-    private SSLSocketImpl c;
-    OutputRecord r;
+    private SSLSocketImpl socket;
 
     // One element array used to implement the write(byte) method
     private final byte[] oneByte = new byte[1];
 
     AppOutputStream(SSLSocketImpl conn) {
-        r = new OutputRecord(Record.ct_application_data);
-        c = conn;
+        this.socket = conn;
     }
 
     /**
      * Write the data out, NOW.
      */
     @Override
-    synchronized public void write(byte b[], int off, int len)
+    synchronized public void write(byte[] b, int off, int len)
             throws IOException {
         if (b == null) {
             throw new NullPointerException();
@@ -68,64 +60,15 @@
         }
 
         // check if the Socket is invalid (error or closed)
-        c.checkWrite();
+        socket.checkWrite();
 
-        /*
-         * By default, we counter chosen plaintext issues on CBC mode
-         * ciphersuites in SSLv3/TLS1.0 by sending one byte of application
-         * data in the first record of every payload, and the rest in
-         * subsequent record(s). Note that the issues have been solved in
-         * TLS 1.1 or later.
-         *
-         * It is not necessary to split the very first application record of
-         * a freshly negotiated TLS session, as there is no previous
-         * application data to guess.  To improve compatibility, we will not
-         * split such records.
-         *
-         * This avoids issues in the outbound direction.  For a full fix,
-         * the peer must have similar protections.
-         */
-        boolean isFirstRecordOfThePayload = true;
-
-        // Always flush at the end of each application level record.
-        // This lets application synchronize read and write streams
-        // however they like; if we buffered here, they couldn't.
+        // Delegate the writing to the underlying socket.
         try {
-            do {
-                boolean holdRecord = false;
-                int howmuch;
-                if (isFirstRecordOfThePayload && c.needToSplitPayload()) {
-                    howmuch = Math.min(0x01, r.availableDataBytes());
-                     /*
-                      * Nagle's algorithm (TCP_NODELAY) was coming into
-                      * play here when writing short (split) packets.
-                      * Signal to the OutputRecord code to internally
-                      * buffer this small packet until the next outbound
-                      * packet (of any type) is written.
-                      */
-                     if ((len != 1) && (howmuch == 1)) {
-                         holdRecord = true;
-                     }
-                } else {
-                    howmuch = Math.min(len, r.availableDataBytes());
-                }
-
-                if (isFirstRecordOfThePayload && howmuch != 0) {
-                    isFirstRecordOfThePayload = false;
-                }
-
-                // NOTE: *must* call c.writeRecord() even for howmuch == 0
-                if (howmuch > 0) {
-                    r.write(b, off, howmuch);
-                    off += howmuch;
-                    len -= howmuch;
-                }
-                c.writeRecord(r, holdRecord);
-                c.checkWrite();
-            } while (len > 0);
+            socket.writeRecord(b, off, len);
+            socket.checkWrite();
         } catch (Exception e) {
             // shutdown and rethrow (wrapped) exception as appropriate
-            c.handleException(e);
+            socket.handleException(e);
         }
     }
 
@@ -143,7 +86,7 @@
      */
     @Override
     public void close() throws IOException {
-        c.close();
+        socket.close();
     }
 
     // inherit no-op flush()
--- a/jdk/src/java.base/share/classes/sun/security/ssl/Authenticator.java	Mon Jun 01 10:15:21 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/Authenticator.java	Tue Jun 02 09:15:47 2015 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -28,19 +28,26 @@
 import java.util.Arrays;
 
 /**
- * This class represents an SSL/TLS message authentication token,
+ * This class represents an SSL/TLS/DTLS message authentication token,
  * which encapsulates a sequence number and ensures that attempts to
  * delete or reorder messages can be detected.
  *
- * Each SSL/TLS connection state contains a sequence number, which
- * is maintained separately for read and write states.  The sequence
- * number MUST be set to zero whenever a connection state is made the
- * active state.  Sequence numbers are of type uint64 and may not
- * exceed 2^64-1.  Sequence numbers do not wrap.  If a SSL/TLS
- * implementation would need to wrap a sequence number, it must
- * renegotiate instead.  A sequence number is incremented after each
- * record: specifically, the first record transmitted under a
- * particular connection state MUST use sequence number 0.
+ * Each connection state contains a sequence number, which is maintained
+ * separately for read and write states.
+ *
+ * For SSL/TLS protocols, the sequence number MUST be set to zero
+ * whenever a connection state is made the active state.
+ *
+ * DTLS uses an explicit sequence number, rather than an implicit one.
+ * Sequence numbers are maintained separately for each epoch, with
+ * each sequence number initially being 0 for each epoch.  The sequence
+ * number used to compute the DTLS MAC is the 64-bit value formed by
+ * concatenating the epoch and the sequence number.
+ *
+ * Sequence numbers do not wrap.  If an implementation would need to wrap
+ * a sequence number, it must renegotiate instead.  A sequence number is
+ * incremented after each record: specifically, the first record transmitted
+ * under a particular connection state MUST use sequence number 0.
  */
 class Authenticator {
 
@@ -56,13 +63,30 @@
     // sequence number + record type + protocol version + record length
     private static final int BLOCK_SIZE_TLS = 8 + 1 + 2 + 2;
 
+    // the block size of DTLS v1.0 and later:
+    // epoch + sequence number + record type + protocol version + record length
+    private static final int BLOCK_SIZE_DTLS = 2 + 6 + 1 + 2 + 2;
+
+    private final boolean isDTLS;
+
     /**
      * Default construct, no message authentication token is initialized.
      *
      * Note that this construct can only be called for null MAC
      */
-    Authenticator() {
-        block = new byte[0];
+    protected Authenticator(boolean isDTLS) {
+        if (isDTLS) {
+            // For DTLS protocols, plaintexts use explicit epoch and
+            // sequence number in each record.  The first 8 byte of
+            // the block is initialized for null MAC so that the
+            // epoch and sequence number can be acquired to generate
+            // plaintext records.
+            block = new byte[8];
+        } else {
+            block = new byte[0];
+        }
+
+        this.isDTLS = isDTLS;
     }
 
     /**
@@ -70,12 +94,22 @@
      * SSL/TLS protocol.
      */
     Authenticator(ProtocolVersion protocolVersion) {
-        if (protocolVersion.v >= ProtocolVersion.TLS10.v) {
+        if (protocolVersion.isDTLSProtocol()) {
+            block = new byte[BLOCK_SIZE_DTLS];
+            block[9] = protocolVersion.major;
+            block[10] = protocolVersion.minor;
+
+            this.isDTLS = true;
+        } else if (protocolVersion.v >= ProtocolVersion.TLS10.v) {
             block = new byte[BLOCK_SIZE_TLS];
             block[9] = protocolVersion.major;
             block[10] = protocolVersion.minor;
+
+            this.isDTLS = false;
         } else {
             block = new byte[BLOCK_SIZE_SSL];
+
+            this.isDTLS = false;
         }
     }
 
@@ -93,11 +127,19 @@
          * Conservatively, we don't allow more records to be generated
          * when there are only 2^8 sequence numbers left.
          */
-        return (block.length != 0 &&
+        if (isDTLS) {
+            return (block.length != 0 &&
+                // no epoch bytes, block[0] and block[1]
+                block[2] == (byte)0xFF && block[3] == (byte)0xFF &&
+                block[4] == (byte)0xFF && block[5] == (byte)0xFF &&
+                block[6] == (byte)0xFF);
+        } else {
+            return (block.length != 0 &&
                 block[0] == (byte)0xFF && block[1] == (byte)0xFF &&
                 block[2] == (byte)0xFF && block[3] == (byte)0xFF &&
                 block[4] == (byte)0xFF && block[5] == (byte)0xFF &&
                 block[6] == (byte)0xFF);
+        }
     }
 
     /**
@@ -113,14 +155,22 @@
     final boolean seqNumIsHuge() {
         /*
          * Conservatively, we should ask for renegotiation when there are
-         * only 2^48 sequence numbers left.
+         * only 2^32 sequence numbers left.
          */
-        return (block.length != 0 &&
-                block[0] == (byte)0xFF && block[1] == (byte)0xFF);
+        if (isDTLS) {
+            return (block.length != 0 &&
+                // no epoch bytes, block[0] and block[1]
+                block[2] == (byte)0xFF && block[3] == (byte)0xFF);
+        } else {
+            return (block.length != 0 &&
+                block[0] == (byte)0xFF && block[1] == (byte)0xFF &&
+                block[2] == (byte)0xFF && block[3] == (byte)0xFF);
+        }
     }
 
     /**
-     * Gets the current sequence number.
+     * Gets the current sequence number, including the epoch number for
+     * DTLS protocols.
      *
      * @return the byte array of the current sequence number
      */
@@ -129,33 +179,83 @@
     }
 
     /**
+     * Sets the epoch number (only apply to DTLS protocols).
+     */
+    final void setEpochNumber(int epoch) {
+        if (!isDTLS) {
+            throw new RuntimeException(
+                "Epoch numbers apply to DTLS protocols only");
+        }
+
+        block[0] = (byte)((epoch >> 8) & 0xFF);
+        block[1] = (byte)(epoch & 0xFF);
+    }
+
+    /**
+     * Increase the sequence number.
+     */
+    final void increaseSequenceNumber() {
+        /*
+         * The sequence number in the block array is a 64-bit
+         * number stored in big-endian format.
+         */
+        int k = 7;
+        while ((k >= 0) && (++block[k] == 0)) {
+            k--;
+        }
+    }
+
+    /**
      * Acquires the current message authentication information with the
      * specified record type and fragment length, and then increases the
      * sequence number.
      *
      * @param  type the record type
      * @param  length the fragment of the record
+     * @param  sequence the explicit sequence number of the record
+     *
      * @return the byte array of the current message authentication information
      */
-    final byte[] acquireAuthenticationBytes(byte type, int length) {
+    final byte[] acquireAuthenticationBytes(
+            byte type, int length, byte[] sequence) {
+
         byte[] copy = block.clone();
+        if (sequence != null) {
+            if (sequence.length != 8) {
+                throw new RuntimeException(
+                        "Insufficient explicit sequence number bytes");
+            }
+
+            System.arraycopy(sequence, 0, copy, 0, sequence.length);
+        }   // Otherwise, use the implicit sequence number.
 
         if (block.length != 0) {
             copy[8] = type;
+
             copy[copy.length - 2] = (byte)(length >> 8);
             copy[copy.length - 1] = (byte)(length);
 
-            /*
-             * Increase the sequence number in the block array
-             * it is a 64-bit number stored in big-endian format
-             */
-            int k = 7;
-            while ((k >= 0) && (++block[k] == 0)) {
-                k--;
+            if (sequence == null || sequence.length != 0) {
+                // Increase the implicit sequence number in the block array.
+                increaseSequenceNumber();
             }
         }
 
         return copy;
     }
 
+    final static long toLong(byte[] recordEnS) {
+        if (recordEnS != null && recordEnS.length == 8) {
+            return ((recordEnS[0] & 0xFFL) << 56) |
+                   ((recordEnS[1] & 0xFFL) << 48) |
+                   ((recordEnS[2] & 0xFFL) << 40) |
+                   ((recordEnS[3] & 0xFFL) << 32) |
+                   ((recordEnS[4] & 0xFFL) << 24) |
+                   ((recordEnS[5] & 0xFFL) << 16) |
+                   ((recordEnS[6] & 0xFFL) <<  8) |
+                    (recordEnS[7] & 0xFFL);
+        }
+
+        return -1L;
+    }
 }
--- a/jdk/src/java.base/share/classes/sun/security/ssl/CipherBox.java	Mon Jun 01 10:15:21 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/CipherBox.java	Tue Jun 02 09:15:47 2015 -0700
@@ -154,9 +154,9 @@
      * NULL cipherbox. Identity operation, no encryption.
      */
     private CipherBox() {
-        this.protocolVersion = ProtocolVersion.DEFAULT;
+        this.protocolVersion = ProtocolVersion.DEFAULT_TLS;
         this.cipher = null;
-        this.cipherType = STREAM_CIPHER;
+        this.cipherType = NULL_CIPHER;
         this.fixedIv = new byte[0];
         this.key = null;
         this.mode = Cipher.ENCRYPT_MODE;    // choose at random
@@ -197,7 +197,7 @@
              */
             if (iv == null && bulkCipher.ivSize != 0 &&
                     mode == Cipher.DECRYPT_MODE &&
-                    protocolVersion.v >= ProtocolVersion.TLS11.v) {
+                    protocolVersion.useTLS11PlusSpec()) {
                 iv = getFixedMask(bulkCipher.ivSize);
             }
 
@@ -491,7 +491,7 @@
                 newLen = removePadding(
                     buf, offset, newLen, tagLen, blockSize, protocolVersion);
 
-                if (protocolVersion.v >= ProtocolVersion.TLS11.v) {
+                if (protocolVersion.useTLS11PlusSpec()) {
                     if (newLen < blockSize) {
                         throw new BadPaddingException("invalid explicit IV");
                     }
@@ -573,7 +573,7 @@
                 newLen = removePadding(bb, tagLen, blockSize, protocolVersion);
 
                 // check the explicit IV of TLS v1.1 or later
-                if (protocolVersion.v >= ProtocolVersion.TLS11.v) {
+                if (protocolVersion.useTLS11PlusSpec()) {
                     if (newLen < blockSize) {
                         throw new BadPaddingException("invalid explicit IV");
                     }
@@ -746,7 +746,7 @@
         // The padding data should be filled with the padding length value.
         int[] results = checkPadding(buf, offset + newLen,
                         padLen + 1, (byte)(padLen & 0xFF));
-        if (protocolVersion.v >= ProtocolVersion.TLS10.v) {
+        if (protocolVersion.useTLS10PlusSpec()) {
             if (results[0] != 0) {          // padding data has invalid bytes
                 throw new BadPaddingException("Invalid TLS padding data");
             }
@@ -792,7 +792,7 @@
         int[] results = checkPadding(
                 bb.duplicate().position(offset + newLen),
                 (byte)(padLen & 0xFF));
-        if (protocolVersion.v >= ProtocolVersion.TLS10.v) {
+        if (protocolVersion.useTLS10PlusSpec()) {
             if (results[0] != 0) {          // padding data has invalid bytes
                 throw new BadPaddingException("Invalid TLS padding data");
             }
@@ -873,7 +873,7 @@
                 // For block ciphers, the explicit IV length is of length
                 // SecurityParameters.record_iv_length, which is equal to
                 // the SecurityParameters.block_size.
-                if (protocolVersion.v >= ProtocolVersion.TLS11.v) {
+                if (protocolVersion.useTLS11PlusSpec()) {
                     return cipher.getBlockSize();
                 }
                 break;
@@ -902,7 +902,7 @@
      * @return the explicit nonce size of the cipher.
      */
     int applyExplicitNonce(Authenticator authenticator, byte contentType,
-            ByteBuffer bb) throws BadPaddingException {
+            ByteBuffer bb, byte[] sequence) throws BadPaddingException {
         switch (cipherType) {
             case BLOCK_CIPHER:
                 // sanity check length of the ciphertext
@@ -918,7 +918,7 @@
                 // For block ciphers, the explicit IV length is of length
                 // SecurityParameters.record_iv_length, which is equal to
                 // the SecurityParameters.block_size.
-                if (protocolVersion.v >= ProtocolVersion.TLS11.v) {
+                if (protocolVersion.useTLS11PlusSpec()) {
                     return cipher.getBlockSize();
                 }
                 break;
@@ -945,7 +945,8 @@
 
                 // update the additional authentication data
                 byte[] aad = authenticator.acquireAuthenticationBytes(
-                        contentType, bb.remaining() - recordIvSize - tagSize);
+                        contentType, bb.remaining() - recordIvSize - tagSize,
+                        sequence);
                 cipher.updateAAD(aad);
 
                 return recordIvSize;
@@ -957,33 +958,6 @@
     }
 
     /*
-     * Applies the explicit nonce/IV to this cipher. This method is used to
-     * decrypt an SSL/TLS input record.
-     *
-     * The returned value is the SecurityParameters.record_iv_length in
-     * RFC 4346/5246.  It is the size of explicit IV for CBC mode, and the
-     * size of explicit nonce for AEAD mode.
-     *
-     * @param  authenticator the authenticator to get the additional
-     *         authentication data
-     * @param  contentType the content type of the input record
-     * @param  buf the byte array to get the explicit nonce from
-     * @param  offset the offset of the byte buffer
-     * @param  cipheredLength the ciphered fragment length of the output
-     *         record, it is the TLSCiphertext.length in RFC 4346/5246.
-     *
-     * @return the explicit nonce size of the cipher.
-     */
-    int applyExplicitNonce(Authenticator authenticator,
-            byte contentType, byte[] buf, int offset,
-            int cipheredLength) throws BadPaddingException {
-
-        ByteBuffer bb = ByteBuffer.wrap(buf, offset, cipheredLength);
-
-        return applyExplicitNonce(authenticator, contentType, bb);
-    }
-
-    /*
      * Creates the explicit nonce/IV to this cipher. This method is used to
      * encrypt an SSL/TLS output record.
      *
@@ -1005,7 +979,7 @@
         byte[] nonce = new byte[0];
         switch (cipherType) {
             case BLOCK_CIPHER:
-                if (protocolVersion.v >= ProtocolVersion.TLS11.v) {
+                if (protocolVersion.useTLS11PlusSpec()) {
                     // For block ciphers, the explicit IV length is of length
                     // SecurityParameters.record_iv_length, which is equal to
                     // the SecurityParameters.block_size.
@@ -1034,9 +1008,10 @@
                                 "invalid key or spec in GCM mode", ikae);
                 }
 
-                // update the additional authentication data
+                // Update the additional authentication data, using the
+                // implicit sequence number of the authenticator.
                 byte[] aad = authenticator.acquireAuthenticationBytes(
-                                                contentType, fragmentLength);
+                                        contentType, fragmentLength, null);
                 cipher.updateAAD(aad);
                 break;
         }
@@ -1044,6 +1019,93 @@
         return nonce;
     }
 
+    // See also CipherSuite.calculatePacketSize().
+    int calculatePacketSize(int fragmentSize, int macLen, int headerSize) {
+        int packetSize = fragmentSize;
+        if (cipher != null) {
+            int blockSize = cipher.getBlockSize();
+            switch (cipherType) {
+                case BLOCK_CIPHER:
+                    packetSize += macLen;
+                    packetSize += 1;        // 1 byte padding length field
+                    packetSize +=           // use the minimal padding
+                            (blockSize - (packetSize % blockSize)) % blockSize;
+                    if (protocolVersion.useTLS11PlusSpec()) {
+                        packetSize += blockSize;        // explicit IV
+                    }
+
+                    break;
+                case AEAD_CIPHER:
+                    packetSize += recordIvSize;
+                    packetSize += tagSize;
+
+                    break;
+                default:    // NULL_CIPHER or STREAM_CIPHER
+                    packetSize += macLen;
+            }
+        }
+
+        return packetSize + headerSize;
+    }
+
+    // See also CipherSuite.calculateFragSize().
+    int calculateFragmentSize(int packetLimit, int macLen, int headerSize) {
+        int fragLen = packetLimit - headerSize;
+        if (cipher != null) {
+            int blockSize = cipher.getBlockSize();
+            switch (cipherType) {
+                case BLOCK_CIPHER:
+                    if (protocolVersion.useTLS11PlusSpec()) {
+                        fragLen -= blockSize;           // explicit IV
+                    }
+                    fragLen -= (fragLen % blockSize);   // cannot hold a block
+                    // No padding for a maximum fragment.
+                    fragLen -= 1;       // 1 byte padding length field: 0x00
+                    fragLen -= macLen;
+
+                    break;
+                case AEAD_CIPHER:
+                    fragLen -= recordIvSize;
+                    fragLen -= tagSize;
+
+                    break;
+                default:    // NULL_CIPHER or STREAM_CIPHER
+                    fragLen -= macLen;
+            }
+        }
+
+        return fragLen;
+    }
+
+    // Estimate the maximum fragment size of a received packet.
+    int estimateFragmentSize(int packetSize, int macLen, int headerSize) {
+        int fragLen = packetSize - headerSize;
+        if (cipher != null) {
+            int blockSize = cipher.getBlockSize();
+            switch (cipherType) {
+                case BLOCK_CIPHER:
+                    if (protocolVersion.useTLS11PlusSpec()) {
+                        fragLen -= blockSize;       // explicit IV
+                    }
+                    // No padding for a maximum fragment.
+                    fragLen -= 1;       // 1 byte padding length field: 0x00
+                    fragLen -= macLen;
+
+                    break;
+                case AEAD_CIPHER:
+                    fragLen -= recordIvSize;
+                    fragLen -= tagSize;
+
+                    break;
+                default:    // NULL_CIPHER or STREAM_CIPHER
+                    fragLen -= macLen;
+            }
+        }
+
+        return fragLen;
+    }
+
+
     /*
      * Is this cipher available?
      *
@@ -1100,7 +1162,7 @@
         if ((fragmentLen % blockSize) == 0) {
             int minimal = tagLen + 1;
             minimal = (minimal >= blockSize) ? minimal : blockSize;
-            if (protocolVersion.v >= ProtocolVersion.TLS11.v) {
+            if (protocolVersion.useTLS11PlusSpec()) {
                 minimal += blockSize;   // plus the size of the explicit IV
             }
 
--- a/jdk/src/java.base/share/classes/sun/security/ssl/CipherSuite.java	Mon Jun 01 10:15:21 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/CipherSuite.java	Tue Jun 02 09:15:47 2015 -0700
@@ -122,9 +122,15 @@
     final boolean allowed;
 
     // obsoleted since protocol version
+    //
+    // TLS version is used.  If checking DTLS versions, please map to
+    // TLS version firstly.  See ProtocolVersion.mapToTLSProtocol().
     final int obsoleted;
 
-    // supported since protocol version
+    // supported since protocol version (TLS version is used)
+    //
+    // TLS version is used.  If checking DTLS versions, please map to
+    // TLS version firstly.  See ProtocolVersion.mapToTLSProtocol().
     final int supported;
 
     /**
@@ -182,6 +188,70 @@
         return this != C_SCSV && isAvailable();
     }
 
+    // See also CipherBox.calculatePacketSize().
+    int calculatePacketSize(int fragmentSize,
+            ProtocolVersion protocolVersion, boolean isDTLS) {
+
+        int packetSize = fragmentSize;
+        if (cipher != B_NULL) {
+            int blockSize = cipher.ivSize;
+            switch (cipher.cipherType) {
+                case BLOCK_CIPHER:
+                    packetSize += macAlg.size;
+                    packetSize += 1;        // 1 byte padding length field
+                    packetSize +=           // use the minimal padding
+                            (blockSize - (packetSize % blockSize)) % blockSize;
+                    if (protocolVersion.useTLS11PlusSpec()) {
+                        packetSize += blockSize;        // explicit IV
+                    }
+
+                    break;
+            case AEAD_CIPHER:
+                packetSize += cipher.ivSize - cipher.fixedIvSize;   // record IV
+                packetSize += cipher.tagSize;
+
+                break;
+            default:    // NULL_CIPHER or STREAM_CIPHER
+                packetSize += macAlg.size;
+            }
+        }
+
+        return packetSize +
+            (isDTLS ? DTLSRecord.headerSize : SSLRecord.headerSize);
+    }
+
+    // See also CipherBox.calculateFragmentSize().
+    int calculateFragSize(int packetLimit,
+            ProtocolVersion protocolVersion, boolean isDTLS) {
+
+        int fragSize = packetLimit -
+                (isDTLS ? DTLSRecord.headerSize : SSLRecord.headerSize);
+        if (cipher != B_NULL) {
+            int blockSize = cipher.ivSize;
+            switch (cipher.cipherType) {
+            case BLOCK_CIPHER:
+                if (protocolVersion.useTLS11PlusSpec()) {
+                    fragSize -= blockSize;              // explicit IV
+                }
+                fragSize -= (fragSize % blockSize);     // cannot hold a block
+                // No padding for a maximum fragment.
+                fragSize -= 1;        // 1 byte padding length field: 0x00
+                fragSize -= macAlg.size;
+
+                break;
+            case AEAD_CIPHER:
+                fragSize -= cipher.tagSize;
+                fragSize -= cipher.ivSize - cipher.fixedIvSize;     // record IV
+
+                break;
+            default:    // NULL_CIPHER or STREAM_CIPHER
+                fragSize -= macAlg.size;
+            }
+        }
+
+        return fragSize;
+    }
+
     /**
      * Compares CipherSuites based on their priority. Has the effect of
      * sorting CipherSuites when put in a sorted collection, which is
@@ -242,7 +312,7 @@
         return c;
     }
 
-    // for use by CipherSuiteList only
+    // for use by SSLContextImpl only
     static Collection<CipherSuite> allowedCipherSuites() {
         return nameMap.values();
     }
@@ -372,7 +442,8 @@
     }
 
     static enum CipherType {
-        STREAM_CIPHER,         // null or stream cipher
+        NULL_CIPHER,           // null cipher
+        STREAM_CIPHER,         // stream cipher
         BLOCK_CIPHER,          // block cipher in CBC mode
         AEAD_CIPHER            // AEAD cipher
     }
@@ -387,7 +458,7 @@
     static enum BulkCipher {
 
         // export strength ciphers
-        B_NULL("NULL", STREAM_CIPHER, 0, 0, 0, 0, true),
+        B_NULL("NULL", NULL_CIPHER, 0, 0, 0, 0, true),
         B_RC4_40(CIPHER_RC4, STREAM_CIPHER, 5, 16, 0, 0, true),
         B_RC2_40("RC2", BLOCK_CIPHER, 5, 16, 8, 0, false),
         B_DES_40(CIPHER_DES,  BLOCK_CIPHER, 5, 8, 8, 0, true),
@@ -568,7 +639,7 @@
                             iv = new IvParameterSpec(new byte[cipher.ivSize]);
                         }
                         temporary = cipher.newCipher(
-                                            ProtocolVersion.DEFAULT,
+                                            ProtocolVersion.DEFAULT_TLS,
                                             key, iv, secureRandom, true);
                         b = temporary.isAvailable();
                     } catch (NoSuchAlgorithmException e) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/Ciphertext.java	Tue Jun 02 09:15:47 2015 -0700
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ssl;
+
+import javax.net.ssl.SSLEngineResult.HandshakeStatus;
+import static sun.security.ssl.HandshakeMessage.*;
+
+/*
+ * enumeration of record type
+ */
+final class Ciphertext {
+    final static Ciphertext CIPHERTEXT_NULL = new Ciphertext();
+
+    RecordType recordType;
+    long recordSN;
+
+    HandshakeStatus handshakeStatus;    // null if not used or not handshaking
+
+    Ciphertext() {
+        this.recordType = null;
+        this.recordSN = -1L;
+        this.handshakeStatus = null;
+    }
+
+    Ciphertext(RecordType recordType, long recordSN) {
+        this.recordType = recordType;
+        this.recordSN = recordSN;
+        this.handshakeStatus = null;
+    }
+
+    static enum RecordType {
+        RECORD_CHANGE_CIPHER_SPEC (
+                Record.ct_change_cipher_spec, ht_not_applicable),
+        RECORD_ALERT (
+                Record.ct_alert, ht_not_applicable),
+        RECORD_HELLO_REQUEST (
+                Record.ct_handshake, ht_hello_request),
+        RECORD_CLIENT_HELLO (
+                Record.ct_handshake, ht_client_hello),
+        RECORD_SERVER_HELLO (
+                Record.ct_handshake, ht_server_hello),
+        RECORD_HELLO_VERIFY_REQUEST (
+                Record.ct_handshake, ht_hello_verify_request),
+        RECORD_NEW_SESSION_TICKET (
+                Record.ct_handshake, ht_new_session_ticket),
+        RECORD_CERTIFICATE (
+                Record.ct_handshake, ht_certificate),
+        RECORD_SERVER_KEY_EXCHANGE (
+                Record.ct_handshake, ht_server_key_exchange),
+        RECORD_CERTIFICATE_REQUEST (
+                Record.ct_handshake, ht_certificate_request),
+        RECORD_SERVER_HELLO_DONE (
+                Record.ct_handshake, ht_server_hello_done),
+        RECORD_CERTIFICATE_VERIFY (
+                Record.ct_handshake, ht_certificate_verify),
+        RECORD_CLIENT_KEY_EXCHANGE (
+                Record.ct_handshake, ht_client_key_exchange),
+        RECORD_FINISHED (
+                Record.ct_handshake, ht_finished),
+        RECORD_CERTIFICATE_URL (
+                Record.ct_handshake, ht_certificate_url),
+        RECORD_CERTIFICATE_STATUS (
+                Record.ct_handshake, ht_certificate_status),
+        RECORD_SUPPLIEMENTAL_DATA (
+                Record.ct_handshake, ht_supplemental_data),
+        RECORD_APPLICATION_DATA (
+                Record.ct_application_data, ht_not_applicable);
+
+        byte contentType;
+        byte handshakeType;
+
+        private RecordType(byte contentType, byte handshakeType) {
+            this.contentType = contentType;
+            this.handshakeType = handshakeType;
+        }
+
+        static RecordType valueOf(byte contentType, byte handshakeType) {
+            if (contentType == Record.ct_change_cipher_spec) {
+                return RECORD_CHANGE_CIPHER_SPEC;
+            } else if (contentType == Record.ct_alert) {
+                return RECORD_ALERT;
+            } else if (contentType == Record.ct_application_data) {
+                return RECORD_APPLICATION_DATA;
+            } else if (handshakeType == ht_hello_request) {
+                return RECORD_HELLO_REQUEST;
+            } else if (handshakeType == ht_client_hello) {
+                return RECORD_CLIENT_HELLO;
+            } else if (handshakeType == ht_server_hello) {
+                return RECORD_SERVER_HELLO;
+            } else if (handshakeType == ht_hello_verify_request) {
+                return RECORD_HELLO_VERIFY_REQUEST;
+            } else if (handshakeType == ht_new_session_ticket) {
+                return RECORD_NEW_SESSION_TICKET;
+            } else if (handshakeType == ht_certificate) {
+                return RECORD_CERTIFICATE;
+            } else if (handshakeType == ht_server_key_exchange) {
+                return RECORD_SERVER_KEY_EXCHANGE;
+            } else if (handshakeType == ht_certificate_request) {
+                return RECORD_CERTIFICATE_REQUEST;
+            } else if (handshakeType == ht_server_hello_done) {
+                return RECORD_SERVER_HELLO_DONE;
+            } else if (handshakeType == ht_certificate_verify) {
+                return RECORD_CERTIFICATE_VERIFY;
+            } else if (handshakeType == ht_client_key_exchange) {
+                return RECORD_CLIENT_KEY_EXCHANGE;
+            } else if (handshakeType == ht_finished) {
+                return RECORD_FINISHED;
+            } else if (handshakeType == ht_certificate_url) {
+                return RECORD_CERTIFICATE_URL;
+            } else if (handshakeType == ht_certificate_status) {
+                return RECORD_CERTIFICATE_STATUS;
+            } else if (handshakeType == ht_supplemental_data) {
+                return RECORD_SUPPLIEMENTAL_DATA;
+            }
+
+            // otherwise, invalid record type
+            throw new IllegalArgumentException(
+                    "Invalid record type (ContentType:" + contentType +
+                    ", HandshakeType:" + handshakeType + ")");
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/ClientAuthType.java	Tue Jun 02 09:15:47 2015 -0700
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ssl;
+
+/**
+ * Client authentication type.
+ */
+enum ClientAuthType {
+    CLIENT_AUTH_NONE,           // turn off client authentication
+    CLIENT_AUTH_REQUESTED,      // need to request client authentication
+    CLIENT_AUTH_REQUIRED        // require client authentication
+}
+
--- a/jdk/src/java.base/share/classes/sun/security/ssl/ClientHandshaker.java	Mon Jun 01 10:15:21 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/ClientHandshaker.java	Tue Jun 02 09:15:47 2015 -0700
@@ -44,8 +44,6 @@
 
 import javax.net.ssl.*;
 
-import javax.security.auth.Subject;
-
 import sun.security.ssl.HandshakeMessage.*;
 import static sun.security.ssl.CipherSuite.KeyExchange.*;
 
@@ -141,11 +139,20 @@
     private final static boolean allowUnsafeServerCertChange =
         Debug.getBooleanProperty("jdk.tls.allowUnsafeServerCertChange", false);
 
+    // To switch off the max_fragment_length extension.
+    private final static boolean enableMFLExtension =
+            Debug.getBooleanProperty("jsse.enableMFLExtension", false);
+
     private List<SNIServerName> requestedServerNames =
             Collections.<SNIServerName>emptyList();
 
+    // maximum fragment length
+    private int requestedMFLength = -1;     // -1: no fragment length limit
+
     private boolean serverNamesAccepted = false;
 
+    private ClientHello initialClientHelloMsg = null;   // DTLS only
+
     /*
      * the reserved server certificate chain in previous handshaking
      *
@@ -172,11 +179,12 @@
             ProtocolList enabledProtocols,
             ProtocolVersion activeProtocolVersion,
             boolean isInitialHandshake, boolean secureRenegotiation,
-            byte[] clientVerifyData, byte[] serverVerifyData) {
+            byte[] clientVerifyData, byte[] serverVerifyData,
+            boolean isDTLS) {
 
         super(engine, context, enabledProtocols, true, true,
             activeProtocolVersion, isInitialHandshake, secureRenegotiation,
-            clientVerifyData, serverVerifyData);
+            clientVerifyData, serverVerifyData, isDTLS);
     }
 
     /*
@@ -191,29 +199,48 @@
      */
     @Override
     void processMessage(byte type, int messageLen) throws IOException {
-        if (state >= type
-                && (type != HandshakeMessage.ht_hello_request)) {
-            throw new SSLProtocolException(
-                    "Handshake message sequence violation, " + type);
-        }
+        // check the handshake state
+        handshakeState.check(type);
 
         switch (type) {
         case HandshakeMessage.ht_hello_request:
-            this.serverHelloRequest(new HelloRequest(input));
+            HelloRequest helloRequest = new HelloRequest(input);
+            handshakeState.update(helloRequest, resumingSession);
+            this.serverHelloRequest(helloRequest);
+            break;
+
+        case HandshakeMessage.ht_hello_verify_request:
+            if (!isDTLS) {
+                throw new SSLProtocolException(
+                    "hello_verify_request is not a SSL/TLS handshake message");
+            }
+
+            HelloVerifyRequest helloVerifyRequest =
+                        new HelloVerifyRequest(input, messageLen);
+            handshakeState.update(helloVerifyRequest, resumingSession);
+            this.helloVerifyRequest(helloVerifyRequest);
             break;
 
         case HandshakeMessage.ht_server_hello:
-            this.serverHello(new ServerHello(input, messageLen));
+            ServerHello serverHello = new ServerHello(input, messageLen);
+            this.serverHello(serverHello);
+
+            // This handshake state update needs the resumingSession value
+            // set by serverHello().
+            handshakeState.update(serverHello, resumingSession);
             break;
 
         case HandshakeMessage.ht_certificate:
             if (keyExchange == K_DH_ANON || keyExchange == K_ECDH_ANON
-                    || keyExchange == K_KRB5 || keyExchange == K_KRB5_EXPORT) {
+                    || ClientKeyExchangeService.find(keyExchange.name) != null) {
+                // No external key exchange provider needs a cert now.
                 fatalSE(Alerts.alert_unexpected_message,
                     "unexpected server cert chain");
                 // NOTREACHED
             }
-            this.serverCertificate(new CertificateMsg(input));
+            CertificateMsg certificateMsg = new CertificateMsg(input);
+            handshakeState.update(certificateMsg, resumingSession);
+            this.serverCertificate(certificateMsg);
             serverKey =
                 session.getPeerCertificates()[0].getPublicKey();
             break;
@@ -249,41 +276,52 @@
                 }
 
                 try {
-                    this.serverKeyExchange(new RSA_ServerKeyExchange(input));
+                    RSA_ServerKeyExchange rsaSrvKeyExchange =
+                                    new RSA_ServerKeyExchange(input);
+                    handshakeState.update(rsaSrvKeyExchange, resumingSession);
+                    this.serverKeyExchange(rsaSrvKeyExchange);
                 } catch (GeneralSecurityException e) {
-                    throwSSLException("Server key", e);
+                    throw new SSLException("Server key", e);
                 }
                 break;
             case K_DH_ANON:
                 try {
-                    this.serverKeyExchange(new DH_ServerKeyExchange(
-                                                input, protocolVersion));
+                    DH_ServerKeyExchange dhSrvKeyExchange =
+                            new DH_ServerKeyExchange(input, protocolVersion);
+                    handshakeState.update(dhSrvKeyExchange, resumingSession);
+                    this.serverKeyExchange(dhSrvKeyExchange);
                 } catch (GeneralSecurityException e) {
-                    throwSSLException("Server key", e);
+                    throw new SSLException("Server key", e);
                 }
                 break;
             case K_DHE_DSS:
             case K_DHE_RSA:
                 try {
-                    this.serverKeyExchange(new DH_ServerKeyExchange(
-                        input, serverKey,
-                        clnt_random.random_bytes, svr_random.random_bytes,
-                        messageLen,
-                        localSupportedSignAlgs, protocolVersion));
+                    DH_ServerKeyExchange dhSrvKeyExchange =
+                        new DH_ServerKeyExchange(
+                            input, serverKey,
+                            clnt_random.random_bytes, svr_random.random_bytes,
+                            messageLen,
+                            localSupportedSignAlgs, protocolVersion);
+                    handshakeState.update(dhSrvKeyExchange, resumingSession);
+                    this.serverKeyExchange(dhSrvKeyExchange);
                 } catch (GeneralSecurityException e) {
-                    throwSSLException("Server key", e);
+                    throw new SSLException("Server key", e);
                 }
                 break;
             case K_ECDHE_ECDSA:
             case K_ECDHE_RSA:
             case K_ECDH_ANON:
                 try {
-                    this.serverKeyExchange(new ECDH_ServerKeyExchange
-                        (input, serverKey, clnt_random.random_bytes,
-                        svr_random.random_bytes,
-                        localSupportedSignAlgs, protocolVersion));
+                    ECDH_ServerKeyExchange ecdhSrvKeyExchange =
+                        new ECDH_ServerKeyExchange
+                            (input, serverKey, clnt_random.random_bytes,
+                            svr_random.random_bytes,
+                            localSupportedSignAlgs, protocolVersion);
+                    handshakeState.update(ecdhSrvKeyExchange, resumingSession);
+                    this.serverKeyExchange(ecdhSrvKeyExchange);
                 } catch (GeneralSecurityException e) {
-                    throwSSLException("Server key", e);
+                    throw new SSLException("Server key", e);
                 }
                 break;
             case K_RSA:
@@ -294,13 +332,9 @@
                 throw new SSLProtocolException(
                     "Protocol violation: server sent a server key exchange"
                     + " message for key exchange " + keyExchange);
-            case K_KRB5:
-            case K_KRB5_EXPORT:
-                throw new SSLProtocolException(
-                    "unexpected receipt of server key exchange algorithm");
             default:
                 throw new SSLProtocolException(
-                    "unsupported key exchange algorithm = "
+                    "unsupported or unexpected key exchange algorithm = "
                     + keyExchange);
             }
             break;
@@ -311,17 +345,19 @@
                 throw new SSLHandshakeException(
                     "Client authentication requested for "+
                     "anonymous cipher suite.");
-            } else if (keyExchange == K_KRB5 || keyExchange == K_KRB5_EXPORT) {
+            } else if (ClientKeyExchangeService.find(keyExchange.name) != null) {
+                // No external key exchange provider needs a cert now.
                 throw new SSLHandshakeException(
                     "Client certificate requested for "+
-                    "kerberos cipher suite.");
+                    "external cipher suite: " + keyExchange);
             }
             certRequest = new CertificateRequest(input, protocolVersion);
             if (debug != null && Debug.isOn("handshake")) {
                 certRequest.print(System.out);
             }
+            handshakeState.update(certRequest, resumingSession);
 
-            if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+            if (protocolVersion.useTLS12PlusSpec()) {
                 Collection<SignatureAndHashAlgorithm> peerSignAlgs =
                                         certRequest.getSignAlgorithms();
                 if (peerSignAlgs == null || peerSignAlgs.isEmpty()) {
@@ -345,33 +381,24 @@
             break;
 
         case HandshakeMessage.ht_server_hello_done:
-            this.serverHelloDone(new ServerHelloDone(input));
+            ServerHelloDone serverHelloDone = new ServerHelloDone(input);
+            handshakeState.update(serverHelloDone, resumingSession);
+            this.serverHelloDone(serverHelloDone);
+
             break;
 
         case HandshakeMessage.ht_finished:
-            // A ChangeCipherSpec record must have been received prior to
-            // reception of the Finished message (RFC 5246, 7.4.9).
-            if (!receivedChangeCipherSpec()) {
-                fatalSE(Alerts.alert_handshake_failure,
-                    "Received Finished message before ChangeCipherSpec");
-            }
+            Finished serverFinished =
+                    new Finished(protocolVersion, input, cipherSuite);
+            handshakeState.update(serverFinished, resumingSession);
+            this.serverFinished(serverFinished);
 
-            this.serverFinished(
-                new Finished(protocolVersion, input, cipherSuite));
             break;
 
         default:
             throw new SSLProtocolException(
                 "Illegal client handshake msg, " + type);
         }
-
-        //
-        // Move state machine forward if the message handling
-        // code didn't already do so
-        //
-        if (state < type) {
-            state = type;
-        }
     }
 
     /*
@@ -389,10 +416,10 @@
         // Could be (e.g. at connection setup) that we already
         // sent the "client hello" but the server's not seen it.
         //
-        if (state < HandshakeMessage.ht_client_hello) {
+        if (!clientHelloDelivered) {
             if (!secureRenegotiation && !allowUnsafeRenegotiation) {
                 // renegotiation is not allowed.
-                if (activeProtocolVersion.v >= ProtocolVersion.TLS10.v) {
+                if (activeProtocolVersion.useTLS10PlusSpec()) {
                     // response with a no_renegotiation warning,
                     warningSE(Alerts.alert_no_renegotiation);
 
@@ -428,6 +455,29 @@
         }
     }
 
+    private void helloVerifyRequest(
+            HelloVerifyRequest mesg) throws IOException {
+
+        if (debug != null && Debug.isOn("handshake")) {
+            mesg.print(System.out);
+        }
+
+        //
+        // Note that HelloVerifyRequest.server_version is used solely to
+        // indicate packet formatting, and not as part of version negotiation.
+        // Need not to check version values match for HelloVerifyRequest
+        // message.
+        //
+        initialClientHelloMsg.cookie = mesg.cookie.clone();
+
+        if (debug != null && Debug.isOn("handshake")) {
+            initialClientHelloMsg.print(System.out);
+        }
+
+        // deliver the ClientHello message with cookie
+        initialClientHelloMsg.write(output);
+        handshakeState.update(initialClientHelloMsg, resumingSession);
+    }
 
     /*
      * Server chooses session parameters given options created by the
@@ -441,6 +491,9 @@
      * probably authentication getting done.
      */
     private void serverHello(ServerHello mesg) throws IOException {
+        // Dispose the reserved ClientHello message (if exists).
+        initialClientHelloMsg = null;
+
         serverKeyExchangeReceived = false;
         if (debug != null && Debug.isOn("handshake")) {
             mesg.print(System.out);
@@ -536,7 +589,7 @@
         }
 
         setCipherSuite(mesg.cipherSuite);
-        if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+        if (protocolVersion.useTLS12PlusSpec()) {
             handshakeHash.setFinishedAlg(cipherSuite.prfAlg.getPRFHashAlg());
         }
 
@@ -569,51 +622,22 @@
                 }
 
                 // validate subject identity
-                if (sessionSuite.keyExchange == K_KRB5 ||
-                    sessionSuite.keyExchange == K_KRB5_EXPORT) {
+                ClientKeyExchangeService p =
+                        ClientKeyExchangeService.find(sessionSuite.keyExchange.name);
+                if (p != null) {
                     Principal localPrincipal = session.getLocalPrincipal();
 
-                    Subject subject = null;
-                    try {
-                        subject = AccessController.doPrivileged(
-                            new PrivilegedExceptionAction<Subject>() {
-                            @Override
-                            public Subject run() throws Exception {
-                                return Krb5Helper.getClientSubject(getAccSE());
-                            }});
-                    } catch (PrivilegedActionException e) {
-                        subject = null;
-                        if (debug != null && Debug.isOn("session")) {
-                            System.out.println("Attempt to obtain" +
-                                        " subject failed!");
-                        }
-                    }
-
-                    if (subject != null) {
-                        // Eliminate dependency on KerberosPrincipal
-                        Set<Principal> principals =
-                            subject.getPrincipals(Principal.class);
-                        if (!principals.contains(localPrincipal)) {
-                            throw new SSLProtocolException("Server resumed" +
-                                " session with wrong subject identity");
-                        } else {
-                            if (debug != null && Debug.isOn("session"))
-                                System.out.println("Subject identity is same");
-                        }
+                    if (p.isRelated(true, getAccSE(), localPrincipal)) {
+                        if (debug != null && Debug.isOn("session"))
+                            System.out.println("Subject identity is same");
                     } else {
-                        if (debug != null && Debug.isOn("session"))
-                            System.out.println("Kerberos credentials are not" +
-                                " present in the current Subject; check if " +
-                                " javax.security.auth.useSubjectAsCreds" +
-                                " system property has been set to false");
-                        throw new SSLProtocolException
-                            ("Server resumed session with no subject");
+                        throw new SSLProtocolException("Server resumed" +
+                                " session with wrong subject identity or no subject");
                     }
                 }
 
-                // looks fine; resume it, and update the state machine.
+                // looks fine; resume it.
                 resumingSession = true;
-                state = HandshakeMessage.ht_finished - 1;
                 calculateConnectionKeys(session.getMasterSecret());
                 if (debug != null && Debug.isOn("session")) {
                     System.out.println("%% Server resumed " + session);
@@ -627,6 +651,24 @@
             }
         }
 
+        // check the "max_fragment_length" extension
+        MaxFragmentLengthExtension maxFragLenExt = (MaxFragmentLengthExtension)
+                mesg.extensions.get(ExtensionType.EXT_MAX_FRAGMENT_LENGTH);
+        if (maxFragLenExt != null) {
+            if ((requestedMFLength == -1) ||
+                    maxFragLenExt.getMaxFragLen() != requestedMFLength) {
+                // If the client did not request this extension, or the
+                // response value is different from the length it requested,
+                // abort the handshake with a fatal illegal_parameter alert.
+                fatalSE(Alerts.alert_illegal_parameter,
+                        "Failed to negotiate the max_fragment_length");
+            }
+        } else if (!resumingSession) {
+            // no "max_fragment_length" extension
+            requestedMFLength = -1;
+        }   // Otherwise, using the value negotiated during the original
+            // session initiation
+
         if (resumingSession && session != null) {
             setHandshakeSessionSE(session);
             // Reserve the handshake state if this is a session-resumption
@@ -657,6 +699,8 @@
                             getLocalSupportedSignAlgs(),
                             mesg.sessionId, getHostSE(), getPortSE());
         session.setRequestedServerNames(requestedServerNames);
+        session.setNegotiatedMaxFragSize(requestedMFLength);
+        session.setMaximumPacketSize(maximumPacketSize);
         setHandshakeSessionSE(session);
         if (debug != null && Debug.isOn("handshake")) {
             System.out.println("** " + cipherSuite);
@@ -681,7 +725,6 @@
         ephemeralServerKey = mesg.getPublicKey();
     }
 
-
     /*
      * Diffie-Hellman key exchange.  We save the server public key and
      * our own D-H algorithm object so we can defer key calculations
@@ -716,13 +759,6 @@
         if (debug != null && Debug.isOn("handshake")) {
             mesg.print(System.out);
         }
-        /*
-         * Always make sure the input has been digested before we
-         * start emitting data, to ensure the hashes are correctly
-         * computed for the Finished and CertificateVerify messages
-         * which we send (here).
-         */
-        input.digestNow();
 
         /*
          * FIRST ... if requested, send an appropriate Certificate chain
@@ -817,7 +853,7 @@
                 // server.  For SSLv3, send the no_certificate alert;
                 // TLS uses an empty cert chain instead.
                 //
-                if (protocolVersion.v >= ProtocolVersion.TLS10.v) {
+                if (protocolVersion.useTLS10PlusSpec()) {
                     m1 = new CertificateMsg(new X509Certificate [0]);
                 } else {
                     warningSE(Alerts.alert_no_certificate);
@@ -837,6 +873,7 @@
                     m1.print(System.out);
                 }
                 m1.write(output);
+                handshakeState.update(m1, resumingSession);
             }
         }
 
@@ -943,8 +980,14 @@
             ecdh = new ECDHCrypt(params, sslContext.getSecureRandom());
             m2 = new ECDHClientKeyExchange(ecdh.getPublicKey());
             break;
-        case K_KRB5:
-        case K_KRB5_EXPORT:
+        default:
+            ClientKeyExchangeService p =
+                    ClientKeyExchangeService.find(keyExchange.name);
+            if (p == null) {
+                // somethings very wrong
+                throw new RuntimeException
+                        ("Unsupported key exchange: " + keyExchange);
+            }
             String sniHostname = null;
             for (SNIServerName serverName : requestedServerNames) {
                 if (serverName instanceof SNIHostName) {
@@ -953,13 +996,13 @@
                 }
             }
 
-            KerberosClientKeyExchange kerberosMsg = null;
+            ClientKeyExchange exMsg = null;
             if (sniHostname != null) {
                 // use first requested SNI hostname
                 try {
-                    kerberosMsg = new KerberosClientKeyExchange(
-                        sniHostname, getAccSE(), protocolVersion,
-                        sslContext.getSecureRandom());
+                    exMsg = p.createClientExchange(
+                            sniHostname, getAccSE(), protocolVersion,
+                            sslContext.getSecureRandom());
                 } catch(IOException e) {
                     if (serverNamesAccepted) {
                         // server accepted requested SNI hostname,
@@ -975,32 +1018,28 @@
                 }
             }
 
-            if (kerberosMsg == null) {
+            if (exMsg == null) {
                 String hostname = getHostSE();
                 if (hostname == null) {
                     throw new IOException("Hostname is required" +
-                        " to use Kerberos cipher suites");
+                        " to use " + keyExchange + " key exchange");
                 }
-                kerberosMsg = new KerberosClientKeyExchange(
-                     hostname, getAccSE(), protocolVersion,
-                     sslContext.getSecureRandom());
+                exMsg = p.createClientExchange(
+                        hostname, getAccSE(), protocolVersion,
+                        sslContext.getSecureRandom());
             }
 
             // Record the principals involved in exchange
-            session.setPeerPrincipal(kerberosMsg.getPeerPrincipal());
-            session.setLocalPrincipal(kerberosMsg.getLocalPrincipal());
-            m2 = kerberosMsg;
+            session.setPeerPrincipal(exMsg.getPeerPrincipal());
+            session.setLocalPrincipal(exMsg.getLocalPrincipal());
+            m2 = exMsg;
             break;
-        default:
-            // somethings very wrong
-            throw new RuntimeException
-                                ("Unsupported key exchange: " + keyExchange);
         }
         if (debug != null && Debug.isOn("handshake")) {
             m2.print(System.out);
         }
         m2.write(output);
-
+        handshakeState.update(m2, resumingSession);
 
         /*
          * THIRD, send a "change_cipher_spec" record followed by the
@@ -1010,8 +1049,6 @@
          * to compute the "Finished" message, and to compute the keys used
          * to protect all records following the change_cipher_spec.
          */
-
-        output.doHashes();
         output.flush();
 
         /*
@@ -1027,13 +1064,6 @@
         case K_RSA_EXPORT:
             preMasterSecret = ((RSAClientKeyExchange)m2).preMaster;
             break;
-        case K_KRB5:
-        case K_KRB5_EXPORT:
-            byte[] secretBytes =
-                ((KerberosClientKeyExchange)m2).getUnencryptedPreMasterSecret();
-            preMasterSecret = new SecretKeySpec(secretBytes,
-                "TlsPremasterSecret");
-            break;
         case K_DHE_RSA:
         case K_DHE_DSS:
         case K_DH_ANON:
@@ -1049,8 +1079,13 @@
             preMasterSecret = ecdh.getAgreedSecret(serverKey);
             break;
         default:
-            throw new IOException("Internal error: unknown key exchange "
-                + keyExchange);
+            if (ClientKeyExchangeService.find(keyExchange.name) != null) {
+                preMasterSecret =
+                        ((ClientKeyExchange) m2).clientKeyExchange();
+            } else {
+                throw new IOException("Internal error: unknown key exchange "
+                        + keyExchange);
+            }
         }
 
         calculateKeys(preMasterSecret, null);
@@ -1069,7 +1104,7 @@
             CertificateVerify m3;
             try {
                 SignatureAndHashAlgorithm preferableSignatureAlgorithm = null;
-                if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+                if (protocolVersion.useTLS12PlusSpec()) {
                     preferableSignatureAlgorithm =
                         SignatureAndHashAlgorithm.getPreferableAlgorithm(
                             peerSupportedSignAlgs, signingKey.getAlgorithm(),
@@ -1103,13 +1138,17 @@
                 m3.print(System.out);
             }
             m3.write(output);
-            output.doHashes();
+            handshakeState.update(m3, resumingSession);
+            output.flush();
         }
 
         /*
          * OK, that's that!
          */
         sendChangeCipherAndFinish(false);
+
+        // expecting the final ChangeCipherSpec and Finished messages
+        expectingFinishFlightSE();
     }
 
 
@@ -1158,8 +1197,9 @@
          * completed handshakes.
          */
         if (resumingSession) {
-            input.digestNow();
             sendChangeCipherAndFinish(true);
+        } else {
+            handshakeFinished = true;
         }
         session.setLastAccessedTime(System.currentTimeMillis());
 
@@ -1188,6 +1228,10 @@
      */
     private void sendChangeCipherAndFinish(boolean finishedTag)
             throws IOException {
+
+        // Reload if this message has been reserved.
+        handshakeHash.reload();
+
         Finished mesg = new Finished(protocolVersion, handshakeHash,
             Finished.CLIENT, session.getMasterSecret(), cipherSuite);
 
@@ -1205,13 +1249,6 @@
         if (secureRenegotiation) {
             clientVerifyData = mesg.getVerifyData();
         }
-
-        /*
-         * Update state machine so server MUST send 'finished' next.
-         * (In "long" handshake case; in short case, we're responding
-         * to its message.)
-         */
-        state = HandshakeMessage.ht_finished - 1;
     }
 
 
@@ -1361,10 +1398,10 @@
         // create the ClientHello message
         ClientHello clientHelloMessage = new ClientHello(
                 sslContext.getSecureRandom(), maxProtocolVersion,
-                sessionId, cipherSuites);
+                sessionId, cipherSuites, isDTLS);
 
         // add signature_algorithm extension
-        if (maxProtocolVersion.v >= ProtocolVersion.TLS12.v) {
+        if (maxProtocolVersion.useTLS12PlusSpec()) {
             // we will always send the signature_algorithm extension
             Collection<SignatureAndHashAlgorithm> localSignAlgs =
                                                 getLocalSupportedSignAlgs();
@@ -1389,6 +1426,37 @@
             }
         }
 
+        // add max_fragment_length extension
+        if (enableMFLExtension) {
+            if (session != null) {
+                // The same extension should be sent for resumption.
+                requestedMFLength = session.getNegotiatedMaxFragSize();
+            } else if (maximumPacketSize != 0) {
+                // Maybe we can calculate the fragment size more accurate
+                // by condering the enabled cipher suites in the future.
+                requestedMFLength = maximumPacketSize;
+                if (isDTLS) {
+                    requestedMFLength -= DTLSRecord.maxPlaintextPlusSize;
+                } else {
+                    requestedMFLength -= SSLRecord.maxPlaintextPlusSize;
+                }
+            } else {
+                // Need no max_fragment_length extension.
+                requestedMFLength = -1;
+            }
+
+            if ((requestedMFLength > 0) &&
+                MaxFragmentLengthExtension.needFragLenNego(requestedMFLength)) {
+
+                requestedMFLength =
+                        MaxFragmentLengthExtension.getValidMaxFragLen(
+                                                        requestedMFLength);
+                clientHelloMessage.addMFLExtension(requestedMFLength);
+            } else {
+                requestedMFLength = -1;
+            }
+        }
+
         // reset the client random cookie
         clnt_random = clientHelloMessage.clnt_random;
 
@@ -1403,6 +1471,11 @@
             clientHelloMessage.addRenegotiationInfoExtension(clientVerifyData);
         }
 
+        if (isDTLS) {
+            // Cookie exchange need to reserve the initial ClientHello message.
+            initialClientHelloMsg = clientHelloMessage;
+        }
+
         return clientHelloMessage;
     }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/ClientKeyExchange.java	Tue Jun 02 09:15:47 2015 -0700
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ssl;
+
+import javax.crypto.SecretKey;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.security.Principal;
+
+/**
+ * Models a non-certificate based ClientKeyExchange
+ */
+public abstract class ClientKeyExchange extends HandshakeMessage {
+
+    public ClientKeyExchange() {
+    }
+
+    @Override
+    int messageType() {
+        return ht_client_key_exchange;
+    }
+
+    @Override
+    abstract public int messageLength();
+
+    @Override
+    abstract public void send(HandshakeOutStream s) throws IOException;
+
+    @Override
+    abstract public void print(PrintStream s) throws IOException;
+
+    abstract public SecretKey clientKeyExchange();
+
+    abstract public Principal getPeerPrincipal();
+
+    abstract public Principal getLocalPrincipal();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/ClientKeyExchangeService.java	Tue Jun 02 09:15:47 2015 -0700
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ssl;
+
+import sun.security.action.GetPropertyAction;
+
+import java.io.File;
+import java.io.FilePermission;
+import java.io.IOException;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.Principal;
+import java.security.PrivilegedAction;
+import java.security.SecureRandom;
+import java.util.*;
+
+/**
+ * Models a service that provides support for a particular client key exchange
+ * mode. Currently used to implement Kerberos-related cipher suites.
+ *
+ * @since 1.9
+ */
+public interface ClientKeyExchangeService {
+
+    static class Loader {
+        private static final Map<String,ClientKeyExchangeService>
+                providers = new HashMap<>();
+
+        static {
+            final String key = "java.home";
+            String path = AccessController.doPrivileged(
+                    new GetPropertyAction(key), null,
+                    new PropertyPermission(key, "read"));
+            ServiceLoader<ClientKeyExchangeService> sc =
+                    AccessController.doPrivileged(
+                            (PrivilegedAction<ServiceLoader<ClientKeyExchangeService>>)
+                                    () -> ServiceLoader.loadInstalled(ClientKeyExchangeService.class),
+                            null,
+                            new FilePermission(new File(path, "-").toString(), "read"));
+            Iterator<ClientKeyExchangeService> iter = sc.iterator();
+            while (iter.hasNext()) {
+                ClientKeyExchangeService cs = iter.next();
+                for (String ex: cs.supported()) {
+                    providers.put(ex, cs);
+                }
+            }
+        }
+
+    }
+
+    public static ClientKeyExchangeService find(String ex) {
+        return Loader.providers.get(ex);
+    }
+
+
+    /**
+     * Returns the supported key exchange modes by this provider.
+     * @return the supported key exchange modes
+     */
+    String[] supported();
+
+    /**
+     * Returns a generalized credential object on the server side. The server
+     * side can use the info to determine if a cipher suite can be enabled.
+     * @param acc the AccessControlContext of the SSL session
+     * @return the credential object
+     */
+    Object getServiceCreds(AccessControlContext acc);
+
+    /**
+     * Returns the host name for a service principal. The info can be used in
+     * SNI or host name verifier.
+     * @param principal the principal of a service
+     * @return the string formed host name
+     */
+    String getServiceHostName(Principal principal);
+
+    /**
+     * Returns whether the specified principal is related to the current
+     * SSLSession. The info can be used to verify a SSL resume.
+     * @param isClient if true called from client side, otherwise from server
+     * @param acc the AccessControlContext of the SSL session
+     * @param p the specified principal
+     * @return true if related
+     */
+    boolean isRelated(boolean isClient, AccessControlContext acc, Principal p);
+
+    /**
+     * Creates the ClientKeyExchange object on the client side.
+     * @param serverName the intented peer name
+     * @param acc the AccessControlContext of the SSL session
+     * @param protocolVersion the TLS protocol version
+     * @param rand the SecureRandom that will used to generate the premaster
+     * @return the new Exchanger object
+     * @throws IOException if there is an error
+     */
+    ClientKeyExchange createClientExchange(String serverName, AccessControlContext acc,
+            ProtocolVersion protocolVersion, SecureRandom rand) throws IOException;
+
+    /**
+     * Create the ClientKeyExchange on the server side.
+     * @param protocolVersion the protocol version
+     * @param clientVersion the input protocol version
+     * @param rand a SecureRandom object used to generate premaster
+     *             (if the server has to create one)
+     * @param encodedTicket the ticket from client
+     * @param encrypted the encrypted premaster secret from client
+     * @param acc the AccessControlContext of the SSL session
+     * @param ServiceCreds the service side credentials object as retrived from
+     *                     {@link #getServiceCreds}
+     * @return the new Exchanger object
+     * @throws IOException if there is an error
+     */
+    ClientKeyExchange createServerExchange(
+            ProtocolVersion protocolVersion, ProtocolVersion clientVersion,
+            SecureRandom rand, byte[] encodedTicket, byte[] encrypted,
+            AccessControlContext acc, Object ServiceCreds) throws IOException;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/DTLSInputRecord.java	Tue Jun 02 09:15:47 2015 -0700
@@ -0,0 +1,1265 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ssl;
+
+import java.io.*;
+import java.nio.*;
+import java.util.*;
+import javax.crypto.BadPaddingException;
+
+import javax.net.ssl.*;
+
+import sun.misc.HexDumpEncoder;
+import static sun.security.ssl.HandshakeMessage.*;
+
+/**
+ * DTLS {@code InputRecord} implementation for {@code SSLEngine}.
+ */
+final class DTLSInputRecord extends InputRecord implements DTLSRecord {
+
+    private DTLSReassembler reassembler = null;
+
+    // Cache the session identifier for the detection of session-resuming
+    // handshake.
+    byte[]              prevSessionID = new byte[0];
+
+    int                 readEpoch;
+
+    int                 prevReadEpoch;
+    Authenticator       prevReadAuthenticator;
+    CipherBox           prevReadCipher;
+
+    DTLSInputRecord() {
+        this.readEpoch = 0;
+        this.readAuthenticator = new MAC(true);
+
+        this.prevReadEpoch = 0;
+        this.prevReadCipher = CipherBox.NULL;
+        this.prevReadAuthenticator = new MAC(true);
+    }
+
+    @Override
+    void changeReadCiphers(Authenticator readAuthenticator,
+            CipherBox readCipher) {
+
+        prevReadCipher.dispose();
+
+        this.prevReadAuthenticator = this.readAuthenticator;
+        this.prevReadCipher = this.readCipher;
+        this.prevReadEpoch = this.readEpoch;
+
+        this.readAuthenticator = readAuthenticator;
+        this.readCipher = readCipher;
+        this.readEpoch++;
+    }
+
+    @Override
+    synchronized public void close() throws IOException {
+        if (!isClosed) {
+            prevReadCipher.dispose();
+            super.close();
+        }
+    }
+
+    @Override
+    boolean isEmpty() {
+        return ((reassembler == null) || reassembler.isEmpty());
+    }
+
+    @Override
+    int estimateFragmentSize(int packetSize) {
+        int macLen = 0;
+        if (readAuthenticator instanceof MAC) {
+            macLen = ((MAC)readAuthenticator).MAClen();
+        }
+
+        if (packetSize > 0) {
+            return readCipher.estimateFragmentSize(
+                    packetSize, macLen, headerSize);
+        } else {
+            return Record.maxDataSize;
+        }
+    }
+
+    @Override
+    void expectingFinishFlight() {
+        if (reassembler != null) {
+            reassembler.expectingFinishFlight();
+        }
+    }
+
+    @Override
+    Plaintext acquirePlaintext() {
+        if (reassembler != null) {
+            Plaintext plaintext = reassembler.acquirePlaintext();
+            if (reassembler.finished()) {
+                // discard all buffered unused message.
+                reassembler = null;
+            }
+
+            return plaintext;
+        }
+
+        return null;
+    }
+
+    @Override
+    Plaintext decode(ByteBuffer packet) {
+
+        if (isClosed) {
+            return null;
+        }
+
+        if (debug != null && Debug.isOn("packet")) {
+             Debug.printHex(
+                    "[Raw read]: length = " + packet.remaining(), packet);
+        }
+
+        // The caller should have validated the record.
+        int srcPos = packet.position();
+        int srcLim = packet.limit();
+
+        byte contentType = packet.get();                   // pos: 0
+        byte majorVersion = packet.get();                  // pos: 1
+        byte minorVersion = packet.get();                  // pos: 2
+        byte[] recordEnS = new byte[8];                    // epoch + seqence
+        packet.get(recordEnS);
+        int recordEpoch = ((recordEnS[0] & 0xFF) << 8) |
+                           (recordEnS[1] & 0xFF);          // pos: 3, 4
+        long recordSeq  = Authenticator.toLong(recordEnS);
+        int contentLen = ((packet.get() & 0xFF) << 8) |
+                          (packet.get() & 0xFF);            // pos: 11, 12
+
+        if (debug != null && Debug.isOn("record")) {
+             System.out.println(Thread.currentThread().getName() +
+                    ", READ: " +
+                    ProtocolVersion.valueOf(majorVersion, minorVersion) +
+                    " " + Record.contentName(contentType) + ", length = " +
+                    contentLen);
+        }
+
+        int recLim = srcPos + DTLSRecord.headerSize + contentLen;
+        if (this.readEpoch > recordEpoch) {
+            // Discard old records delivered before this epoch.
+
+            // Reset the position of the packet buffer.
+            packet.position(recLim);
+            return null;
+        }
+
+        if (this.readEpoch < recordEpoch) {
+            if (contentType != Record.ct_handshake) {
+                // just discard it if not a handshake message
+                packet.position(recLim);
+                return null;
+            }
+
+            // Not ready to decrypt this record, may be encrypted Finished
+            // message, need to buffer it.
+            if (reassembler == null) {
+               reassembler = new DTLSReassembler();
+            }
+
+            byte[] fragment = new byte[contentLen];
+            packet.get(fragment);              // copy the fragment
+            RecordFragment buffered = new RecordFragment(fragment, contentType,
+                    majorVersion, minorVersion,
+                    recordEnS, recordEpoch, recordSeq, true);
+
+            reassembler.queueUpFragment(buffered);
+
+            // consume the full record in the packet buffer.
+            packet.position(recLim);
+
+            Plaintext plaintext = reassembler.acquirePlaintext();
+            if (reassembler.finished()) {
+                // discard all buffered unused message.
+                reassembler = null;
+            }
+
+            return plaintext;
+        }
+
+        if (this.readEpoch == recordEpoch) {
+            // decrypt the fragment
+            packet.limit(recLim);
+            packet.position(srcPos + DTLSRecord.headerSize);
+
+            ByteBuffer plaintextFragment;
+            try {
+                plaintextFragment = decrypt(readAuthenticator,
+                        readCipher, contentType, packet, recordEnS);
+            } catch (BadPaddingException bpe) {
+                if (debug != null && Debug.isOn("ssl")) {
+                    System.out.println(Thread.currentThread().getName() +
+                            " discard invalid record: " + bpe);
+                }
+
+                // invalid, discard this record [section 4.1.2.7, RFC 6347]
+                return null;
+            } finally {
+                // comsume a complete record
+                packet.limit(srcLim);
+                packet.position(recLim);
+            }
+
+            if (contentType != Record.ct_change_cipher_spec &&
+                contentType != Record.ct_handshake) {   // app data or alert
+                                                        // no retransmission
+               return new Plaintext(contentType, majorVersion, minorVersion,
+                        recordEpoch, recordSeq, plaintextFragment);
+            }
+
+            if (contentType == Record.ct_change_cipher_spec) {
+                if (reassembler == null) {
+                    // handshake has not started, should be an
+                    // old handshake message, discard it.
+                    return null;
+                }
+
+                reassembler.queueUpFragment(
+                        new RecordFragment(plaintextFragment, contentType,
+                                majorVersion, minorVersion,
+                                recordEnS, recordEpoch, recordSeq, false));
+            } else {    // handshake record
+                // One record may contain 1+ more handshake messages.
+                while (plaintextFragment.remaining() > 0) {
+
+                    HandshakeFragment hsFrag = parseHandshakeMessage(
+                        contentType, majorVersion, minorVersion,
+                        recordEnS, recordEpoch, recordSeq, plaintextFragment);
+
+                    if (hsFrag == null) {
+                        // invalid, discard this record
+                        return null;
+                    }
+
+                    if ((reassembler == null) &&
+                            isKickstart(hsFrag.handshakeType)) {
+                       reassembler = new DTLSReassembler();
+                    }
+
+                    if (reassembler != null) {
+                        reassembler.queueUpHandshake(hsFrag);
+                    }   // else, just ignore the message.
+                }
+            }
+
+            // Completed the read of the full record. Acquire the reassembled
+            // messages.
+            if (reassembler != null) {
+                Plaintext plaintext = reassembler.acquirePlaintext();
+                if (reassembler.finished()) {
+                    // discard all buffered unused message.
+                    reassembler = null;
+                }
+
+                return plaintext;
+            }
+        }
+
+        return null;    // make the complier happy
+    }
+
+    @Override
+    int bytesInCompletePacket(ByteBuffer packet) throws SSLException {
+
+        // DTLS length field is in bytes 11/12
+        if (packet.remaining() < headerSize) {
+            return -1;
+        }
+
+        // Last sanity check that it's not a wild record
+        int pos = packet.position();
+
+        // Check the content type of the record.
+        byte contentType = packet.get(pos);
+        if (!Record.isValidContentType(contentType)) {
+            throw new SSLException(
+                    "Unrecognized SSL message, plaintext connection?");
+        }
+
+        // Check the protocol version of the record.
+        ProtocolVersion recordVersion =
+            ProtocolVersion.valueOf(packet.get(pos + 1), packet.get(pos + 2));
+        checkRecordVersion(recordVersion, false);
+
+        // Get the fragment length of the record.
+        int fragLen = ((packet.get(pos + 11) & 0xFF) << 8) +
+                       (packet.get(pos + 12) & 0xFF) + headerSize;
+        if (fragLen > Record.maxFragmentSize) {
+            throw new SSLException(
+                    "Record overflow, fragment length (" + fragLen +
+                    ") MUST not exceed " + Record.maxFragmentSize);
+        }
+
+        return fragLen;
+    }
+
+    @Override
+    void checkRecordVersion(ProtocolVersion recordVersion,
+            boolean allowSSL20Hello) throws SSLException {
+
+        if (!recordVersion.maybeDTLSProtocol()) {
+            throw new SSLException(
+                    "Unrecognized record version " + recordVersion +
+                    " , plaintext connection?");
+        }
+    }
+
+    private static boolean isKickstart(byte handshakeType) {
+        return (handshakeType == HandshakeMessage.ht_client_hello) ||
+               (handshakeType == HandshakeMessage.ht_hello_request) ||
+               (handshakeType == HandshakeMessage.ht_hello_verify_request);
+    }
+
+    private static HandshakeFragment parseHandshakeMessage(
+            byte contentType, byte majorVersion, byte minorVersion,
+            byte[] recordEnS, int recordEpoch, long recordSeq,
+            ByteBuffer plaintextFragment) {
+
+        int remaining = plaintextFragment.remaining();
+        if (remaining < handshakeHeaderSize) {
+            if (debug != null && Debug.isOn("ssl")) {
+                System.out.println(
+                        Thread.currentThread().getName() +
+                        " discard invalid record: " +
+                        "too small record to hold a handshake fragment");
+            }
+
+            // invalid, discard this record [section 4.1.2.7, RFC 6347]
+            return null;
+        }
+
+        byte handshakeType = plaintextFragment.get();       // pos: 0
+        int messageLength =
+                ((plaintextFragment.get() & 0xFF) << 16) |
+                ((plaintextFragment.get() & 0xFF) << 8) |
+                 (plaintextFragment.get() & 0xFF);          // pos: 1-3
+        int messageSeq =
+                ((plaintextFragment.get() & 0xFF) << 8) |
+                 (plaintextFragment.get() & 0xFF);          // pos: 4/5
+        int fragmentOffset =
+                ((plaintextFragment.get() & 0xFF) << 16) |
+                ((plaintextFragment.get() & 0xFF) << 8) |
+                 (plaintextFragment.get() & 0xFF);          // pos: 6-8
+        int fragmentLength =
+                ((plaintextFragment.get() & 0xFF) << 16) |
+                ((plaintextFragment.get() & 0xFF) << 8) |
+                 (plaintextFragment.get() & 0xFF);          // pos: 9-11
+        if ((remaining - handshakeHeaderSize) < fragmentLength) {
+            if (debug != null && Debug.isOn("ssl")) {
+                System.out.println(
+                        Thread.currentThread().getName() +
+                        " discard invalid record: " +
+                        "not a complete handshake fragment in the record");
+            }
+
+            // invalid, discard this record [section 4.1.2.7, RFC 6347]
+            return null;
+        }
+
+        byte[] fragment = new byte[fragmentLength];
+        plaintextFragment.get(fragment);
+
+        return new HandshakeFragment(fragment, contentType,
+                majorVersion, minorVersion,
+                recordEnS, recordEpoch, recordSeq,
+                handshakeType, messageLength,
+                messageSeq, fragmentOffset, fragmentLength);
+    }
+
+    // buffered record fragment
+    private static class RecordFragment implements Comparable<RecordFragment> {
+        boolean         isCiphertext;
+
+        byte            contentType;
+        byte            majorVersion;
+        byte            minorVersion;
+        int             recordEpoch;
+        long            recordSeq;
+        byte[]          recordEnS;
+        byte[]          fragment;
+
+        RecordFragment(ByteBuffer fragBuf, byte contentType,
+                byte majorVersion, byte minorVersion, byte[] recordEnS,
+                int recordEpoch, long recordSeq, boolean isCiphertext) {
+            this((byte[])null, contentType, majorVersion, minorVersion,
+                    recordEnS, recordEpoch, recordSeq, isCiphertext);
+
+            this.fragment = new byte[fragBuf.remaining()];
+            fragBuf.get(this.fragment);
+        }
+
+        RecordFragment(byte[] fragment, byte contentType,
+                byte majorVersion, byte minorVersion, byte[] recordEnS,
+                int recordEpoch, long recordSeq, boolean isCiphertext) {
+            this.isCiphertext = isCiphertext;
+
+            this.contentType = contentType;
+            this.majorVersion = majorVersion;
+            this.minorVersion = minorVersion;
+            this.recordEpoch = recordEpoch;
+            this.recordSeq = recordSeq;
+            this.recordEnS = recordEnS;
+            this.fragment = fragment;       // The caller should have cloned
+                                            // the buffer if necessary.
+        }
+
+        @Override
+        public int compareTo(RecordFragment o) {
+            return Long.compareUnsigned(this.recordSeq, o.recordSeq);
+        }
+    }
+
+    // buffered handshake message
+    private static final class HandshakeFragment extends RecordFragment {
+
+        byte            handshakeType;     // handshake msg_type
+        int             messageSeq;        // message_seq
+        int             messageLength;     // Handshake body length
+        int             fragmentOffset;    // fragment_offset
+        int             fragmentLength;    // fragment_length
+
+        HandshakeFragment(byte[] fragment, byte contentType,
+                byte majorVersion, byte minorVersion, byte[] recordEnS,
+                int recordEpoch, long recordSeq,
+                byte handshakeType, int messageLength,
+                int messageSeq, int fragmentOffset, int fragmentLength) {
+
+            super(fragment, contentType, majorVersion, minorVersion,
+                    recordEnS, recordEpoch , recordSeq, false);
+
+            this.handshakeType = handshakeType;
+            this.messageSeq = messageSeq;
+            this.messageLength = messageLength;
+            this.fragmentOffset = fragmentOffset;
+            this.fragmentLength = fragmentLength;
+        }
+
+        @Override
+        public int compareTo(RecordFragment o) {
+            if (o instanceof HandshakeFragment) {
+                HandshakeFragment other = (HandshakeFragment)o;
+                if (this.messageSeq != other.messageSeq) {
+                    // keep the insertion order for the same message
+                    return this.messageSeq - other.messageSeq;
+                }
+            }
+
+            return Long.compareUnsigned(this.recordSeq, o.recordSeq);
+        }
+    }
+
+    private static final class HoleDescriptor {
+        int offset;             // fragment_offset
+        int limit;              // fragment_offset + fragment_length
+
+        HoleDescriptor(int offset, int limit) {
+            this.offset = offset;
+            this.limit = limit;
+        }
+    }
+
+    final class DTLSReassembler {
+        TreeSet<RecordFragment> bufferedFragments = new TreeSet<>();
+
+        HashMap<Byte, List<HoleDescriptor>> holesMap = new HashMap<>(5);
+
+        // Epoch, sequence number and handshake message sequence of the
+        // beginning message of a flight.
+        byte        flightType = (byte)0xFF;
+
+        int         flightTopEpoch = 0;
+        long        flightTopRecordSeq = -1;
+        int         flightTopMessageSeq = 0;
+
+        // Epoch, sequence number and handshake message sequence of the
+        // next message acquisition of a flight.
+        int         nextRecordEpoch = 0;    // next record epoch
+        long        nextRecordSeq = 0;      // next record sequence number
+        int         nextMessageSeq = 0;     // next handshake message number
+
+        // Expect ChangeCipherSpec and Finished messages for the final flight.
+        boolean     expectCCSFlight = false;
+
+        // Ready to process this flight if received all messages of the flight.
+        boolean     flightIsReady = false;
+        boolean     needToCheckFlight = false;
+
+        // Is it a session-resuming abbreviated handshake.?
+        boolean     isAbbreviatedHandshake = false;
+
+        // The handshke fragment with the biggest record sequence number
+        // in a flight, not counting the Finished message.
+        HandshakeFragment lastHandshakeFragment = null;
+
+        // Is handshake (intput) finished?
+        boolean handshakeFinished = false;
+
+        DTLSReassembler() {
+            // blank
+        }
+
+        boolean finished() {
+            return handshakeFinished;
+        }
+
+        void expectingFinishFlight() {
+            expectCCSFlight = true;
+        }
+
+        void queueUpHandshake(HandshakeFragment hsf) {
+
+            if ((nextRecordEpoch > hsf.recordEpoch) ||
+                    (nextRecordSeq > hsf.recordSeq) ||
+                    (nextMessageSeq > hsf.messageSeq)) {
+                // too old, discard this record
+                return;
+            }
+
+            // Is it the first message of next flight?
+            if ((flightTopMessageSeq == hsf.messageSeq) &&
+                    (hsf.fragmentOffset == 0) && (flightTopRecordSeq == -1)) {
+
+                flightType = hsf.handshakeType;
+                flightTopEpoch = hsf.recordEpoch;
+                flightTopRecordSeq = hsf.recordSeq;
+
+                if (hsf.handshakeType == HandshakeMessage.ht_server_hello) {
+                    // Is it a session-resuming handshake?
+                    try {
+                        isAbbreviatedHandshake =
+                                isSessionResuming(hsf.fragment, prevSessionID);
+                    } catch (SSLException ssle) {
+                        if (debug != null && Debug.isOn("ssl")) {
+                            System.out.println(
+                                    Thread.currentThread().getName() +
+                                    " discard invalid record: " + ssle);
+                        }
+
+                        // invalid, discard it [section 4.1.2.7, RFC 6347]
+                        return;
+                    }
+
+                    if (!isAbbreviatedHandshake) {
+                        prevSessionID = getSessionID(hsf.fragment);
+                    }
+                }
+            }
+
+            boolean fragmented = false;
+            if ((hsf.fragmentOffset) != 0 ||
+                (hsf.fragmentLength != hsf.messageLength)) {
+
+                fragmented = true;
+            }
+
+            List<HoleDescriptor> holes = holesMap.get(hsf.handshakeType);
+            if (holes == null) {
+                if (!fragmented) {
+                    holes = Collections.emptyList();
+                } else {
+                    holes = new LinkedList<HoleDescriptor>();
+                    holes.add(new HoleDescriptor(0, hsf.messageLength));
+                }
+                holesMap.put(hsf.handshakeType, holes);
+            } else if (holes.isEmpty()) {
+                // Have got the full handshake message.  This record may be
+                // a handshake message retransmission.  Discard this record.
+                //
+                // It's OK to discard retransmission as the handshake hash
+                // is computed as if each handshake message had been sent
+                // as a single fragment.
+                //
+                // Note that ClientHello messages are delivered twice in
+                // DTLS handshaking.
+                if ((hsf.handshakeType != HandshakeMessage.ht_client_hello &&
+                     hsf.handshakeType != ht_hello_verify_request) ||
+                        (nextMessageSeq != hsf.messageSeq)) {
+                    return;
+                }
+
+                if (fragmented) {
+                    holes = new LinkedList<HoleDescriptor>();
+                    holes.add(new HoleDescriptor(0, hsf.messageLength));
+                }
+                holesMap.put(hsf.handshakeType, holes);
+            }
+
+            if (fragmented) {
+                int fragmentLimit = hsf.fragmentOffset + hsf.fragmentLength;
+                for (int i = 0; i < holes.size(); i++) {
+
+                    HoleDescriptor hole = holes.get(i);
+                    if ((hole.limit <= hsf.fragmentOffset) ||
+                        (hole.offset >= fragmentLimit)) {
+                        // Also discard overlapping handshake retransmissions.
+                        continue;
+                    }
+
+                    // The ranges SHOULD NOT overlap.
+                    if (((hole.offset > hsf.fragmentOffset) &&
+                         (hole.offset < fragmentLimit)) ||
+                        ((hole.limit > hsf.fragmentOffset) &&
+                         (hole.limit < fragmentLimit))) {
+
+                        if (debug != null && Debug.isOn("ssl")) {
+                            System.out.println(
+                                Thread.currentThread().getName() +
+                                " discard invalid record: " +
+                                "handshake fragment ranges are overlapping");
+                        }
+
+                        // invalid, discard it [section 4.1.2.7, RFC 6347]
+                        return;
+                    }
+
+                    // This record interacts with this hole, fill the hole.
+                    holes.remove(i);
+                    // i--;
+
+                    if (hsf.fragmentOffset > hole.offset) {
+                        holes.add(new HoleDescriptor(
+                                hole.offset, hsf.fragmentOffset));
+                        // i++;
+                    }
+
+                    if (fragmentLimit < hole.limit) {
+                        holes.add(new HoleDescriptor(
+                                fragmentLimit, hole.limit));
+                        // i++;
+                    }
+
+                    // As no ranges overlap, no interact with other holes.
+                    break;
+                }
+            }
+
+            // append this fragment
+            bufferedFragments.add(hsf);
+
+            if ((lastHandshakeFragment == null) ||
+                (lastHandshakeFragment.compareTo(hsf) < 0)) {
+
+                lastHandshakeFragment = hsf;
+            }
+
+            if (flightIsReady) {
+                flightIsReady = false;
+            }
+            needToCheckFlight = true;
+        }
+
+        // queue up change_cipher_spec or encrypted message
+        void queueUpFragment(RecordFragment rf) {
+            if ((nextRecordEpoch > rf.recordEpoch) ||
+                    (nextRecordSeq > rf.recordSeq)) {
+                // too old, discard this record
+                return;
+            }
+
+            // Is it the first message of next flight?
+            if (expectCCSFlight &&
+                    (rf.contentType == Record.ct_change_cipher_spec)) {
+
+                flightType = (byte)0xFE;
+                flightTopEpoch = rf.recordEpoch;
+                flightTopRecordSeq = rf.recordSeq;
+            }
+
+            // append this fragment
+            bufferedFragments.add(rf);
+
+            if (flightIsReady) {
+                flightIsReady = false;
+            }
+            needToCheckFlight = true;
+        }
+
+        boolean isEmpty() {
+            return (bufferedFragments.isEmpty() ||
+                    (!flightIsReady && !needToCheckFlight) ||
+                    (needToCheckFlight && !flightIsReady()));
+        }
+
+        Plaintext acquirePlaintext() {
+            if (bufferedFragments.isEmpty()) {
+                // reset the flight
+                if (flightIsReady) {
+                    flightIsReady = false;
+                    needToCheckFlight = false;
+                }
+
+                return null;
+            }
+
+            if (!flightIsReady && needToCheckFlight) {
+                // check the fligth status
+                flightIsReady = flightIsReady();
+
+                // set for next flight
+                if (flightIsReady) {
+                    flightTopMessageSeq = lastHandshakeFragment.messageSeq + 1;
+                    flightTopRecordSeq = -1;
+                }
+
+                needToCheckFlight = false;
+            }
+
+            if (!flightIsReady) {
+                return null;
+            }
+
+            RecordFragment rFrag = bufferedFragments.first();
+            if (!rFrag.isCiphertext) {
+                // handshake message, or ChangeCipherSpec message
+                return acquireHandshakeMessage();
+            } else {
+                // a Finished message or other ciphertexts
+                return acquireCachedMessage();
+            }
+        }
+
+        private Plaintext acquireCachedMessage() {
+
+            RecordFragment rFrag = bufferedFragments.first();
+            if (readEpoch != rFrag.recordEpoch) {
+                if (readEpoch > rFrag.recordEpoch) {
+                    // discard old records
+                    bufferedFragments.remove(rFrag);    // popup the fragment
+                }
+
+                // reset the flight
+                if (flightIsReady) {
+                    flightIsReady = false;
+                }
+                return null;
+            }
+
+            bufferedFragments.remove(rFrag);    // popup the fragment
+
+            ByteBuffer fragment = ByteBuffer.wrap(rFrag.fragment);
+            ByteBuffer plaintextFragment = null;
+            try {
+                plaintextFragment = decrypt(readAuthenticator, readCipher,
+                        rFrag.contentType, fragment, rFrag.recordEnS);
+            } catch (BadPaddingException bpe) {
+                if (debug != null && Debug.isOn("ssl")) {
+                    System.out.println(Thread.currentThread().getName() +
+                            " discard invalid record: " + bpe);
+                }
+
+                // invalid, discard this record [section 4.1.2.7, RFC 6347]
+                return null;
+            }
+
+            // The ciphtext handshake message can only be Finished (the
+            // end of this flight), ClinetHello or HelloRequest (the
+            // beginning of the next flight) message.  Need not to check
+            // any ChangeCipherSpec message.
+            if (rFrag.contentType == Record.ct_handshake) {
+                HandshakeFragment finFrag = null;
+                while (plaintextFragment.remaining() > 0) {
+                    HandshakeFragment hsFrag = parseHandshakeMessage(
+                            rFrag.contentType,
+                            rFrag.majorVersion, rFrag.minorVersion,
+                            rFrag.recordEnS, rFrag.recordEpoch, rFrag.recordSeq,
+                            plaintextFragment);
+
+                    if (hsFrag == null) {
+                        // invalid, discard this record
+                        return null;
+                    }
+
+                    if (hsFrag.handshakeType == HandshakeMessage.ht_finished) {
+                        finFrag = hsFrag;
+
+                        // reset for the next flight
+                        this.flightType = (byte)0xFF;
+                        this.flightTopEpoch = rFrag.recordEpoch;
+                        this.flightTopMessageSeq = hsFrag.messageSeq + 1;
+                        this.flightTopRecordSeq = -1;
+                    } else {
+                        // reset the flight
+                        if (flightIsReady) {
+                            flightIsReady = false;
+                        }
+                        queueUpHandshake(hsFrag);
+                    }
+                }
+
+                this.nextRecordSeq = rFrag.recordSeq + 1;
+                this.nextMessageSeq = 0;
+
+                if (finFrag != null) {
+                    this.nextRecordEpoch = finFrag.recordEpoch;
+                    this.nextRecordSeq = finFrag.recordSeq + 1;
+                    this.nextMessageSeq = finFrag.messageSeq + 1;
+
+                    // Finished message does not fragment.
+                    byte[] recordFrag = new byte[finFrag.messageLength + 4];
+                    Plaintext plaintext = new Plaintext(finFrag.contentType,
+                            finFrag.majorVersion, finFrag.minorVersion,
+                            finFrag.recordEpoch, finFrag.recordSeq,
+                            ByteBuffer.wrap(recordFrag));
+
+                    // fill the handshake fragment of the record
+                    recordFrag[0] = finFrag.handshakeType;
+                    recordFrag[1] =
+                            (byte)((finFrag.messageLength >>> 16) & 0xFF);
+                    recordFrag[2] =
+                            (byte)((finFrag.messageLength >>> 8) & 0xFF);
+                    recordFrag[3] = (byte)(finFrag.messageLength & 0xFF);
+
+                    System.arraycopy(finFrag.fragment, 0,
+                            recordFrag, 4, finFrag.fragmentLength);
+
+                    // handshake hashing
+                    handshakeHashing(finFrag, plaintext);
+
+                    // input handshake finished
+                    handshakeFinished = true;
+
+                    return plaintext;
+                } else {
+                    return acquirePlaintext();
+                }
+            } else {
+                return new Plaintext(rFrag.contentType,
+                        rFrag.majorVersion, rFrag.minorVersion,
+                        rFrag.recordEpoch, rFrag.recordSeq,
+                        plaintextFragment);
+            }
+        }
+
+        private Plaintext acquireHandshakeMessage() {
+
+            RecordFragment rFrag = bufferedFragments.first();
+            if (rFrag.contentType == Record.ct_change_cipher_spec) {
+                this.nextRecordEpoch = rFrag.recordEpoch + 1;
+                this.nextRecordSeq = 0;
+                // no change on next handshake message sequence number
+
+                bufferedFragments.remove(rFrag);        // popup the fragment
+
+                // Reload if this message has been reserved for handshake hash.
+                handshakeHash.reload();
+
+                return new Plaintext(rFrag.contentType,
+                        rFrag.majorVersion, rFrag.minorVersion,
+                        rFrag.recordEpoch, rFrag.recordSeq,
+                        ByteBuffer.wrap(rFrag.fragment));
+            } else {    // rFrag.contentType == Record.ct_handshake
+                HandshakeFragment hsFrag = (HandshakeFragment)rFrag;
+                if ((hsFrag.messageLength == hsFrag.fragmentLength) &&
+                    (hsFrag.fragmentOffset == 0)) {     // no fragmentation
+
+                    bufferedFragments.remove(rFrag);    // popup the fragment
+
+                    // this.nextRecordEpoch = hsFrag.recordEpoch;
+                    this.nextRecordSeq = hsFrag.recordSeq + 1;
+                    this.nextMessageSeq = hsFrag.messageSeq + 1;
+
+                    // Note: may try to avoid byte array copy in the future.
+                    byte[] recordFrag = new byte[hsFrag.messageLength + 4];
+                    Plaintext plaintext = new Plaintext(hsFrag.contentType,
+                            hsFrag.majorVersion, hsFrag.minorVersion,
+                            hsFrag.recordEpoch, hsFrag.recordSeq,
+                            ByteBuffer.wrap(recordFrag));
+
+                    // fill the handshake fragment of the record
+                    recordFrag[0] = hsFrag.handshakeType;
+                    recordFrag[1] =
+                            (byte)((hsFrag.messageLength >>> 16) & 0xFF);
+                    recordFrag[2] =
+                            (byte)((hsFrag.messageLength >>> 8) & 0xFF);
+                    recordFrag[3] = (byte)(hsFrag.messageLength & 0xFF);
+
+                    System.arraycopy(hsFrag.fragment, 0,
+                            recordFrag, 4, hsFrag.fragmentLength);
+
+                    // handshake hashing
+                    handshakeHashing(hsFrag, plaintext);
+
+                    return plaintext;
+                } else {                // fragmented handshake message
+                    // the first record
+                    //
+                    // Note: may try to avoid byte array copy in the future.
+                    byte[] recordFrag = new byte[hsFrag.messageLength + 4];
+                    Plaintext plaintext = new Plaintext(hsFrag.contentType,
+                            hsFrag.majorVersion, hsFrag.minorVersion,
+                            hsFrag.recordEpoch, hsFrag.recordSeq,
+                            ByteBuffer.wrap(recordFrag));
+
+                    // fill the handshake fragment of the record
+                    recordFrag[0] = hsFrag.handshakeType;
+                    recordFrag[1] =
+                            (byte)((hsFrag.messageLength >>> 16) & 0xFF);
+                    recordFrag[2] =
+                            (byte)((hsFrag.messageLength >>> 8) & 0xFF);
+                    recordFrag[3] = (byte)(hsFrag.messageLength & 0xFF);
+
+                    int msgSeq = hsFrag.messageSeq;
+                    long maxRecodeSN = hsFrag.recordSeq;
+                    HandshakeFragment hmFrag = hsFrag;
+                    do {
+                        System.arraycopy(hmFrag.fragment, 0,
+                                recordFrag, hmFrag.fragmentOffset + 4,
+                                hmFrag.fragmentLength);
+                        // popup the fragment
+                        bufferedFragments.remove(rFrag);
+
+                        if (maxRecodeSN < hmFrag.recordSeq) {
+                            maxRecodeSN = hmFrag.recordSeq;
+                        }
+
+                        // Note: may buffer retransmitted fragments in order to
+                        // speed up the reassembly in the future.
+
+                        // read the next buffered record
+                        if (!bufferedFragments.isEmpty()) {
+                            rFrag = bufferedFragments.first();
+                            if (rFrag.contentType != Record.ct_handshake) {
+                                break;
+                            } else {
+                                hmFrag = (HandshakeFragment)rFrag;
+                            }
+                        }
+                    } while (!bufferedFragments.isEmpty() &&
+                            (msgSeq == hmFrag.messageSeq));
+
+                    // handshake hashing
+                    handshakeHashing(hsFrag, plaintext);
+
+                    this.nextRecordSeq = maxRecodeSN + 1;
+                    this.nextMessageSeq = msgSeq + 1;
+
+                    return plaintext;
+                }
+            }
+        }
+
+        boolean flightIsReady() {
+
+            //
+            // the ChangeCipherSpec/Finished flight
+            //
+            if (expectCCSFlight) {
+                // Have the ChangeCipherSpec/Finished messages been received?
+                return hasFinisedMessage(bufferedFragments);
+            }
+
+            if (flightType == (byte)0xFF) {
+                return false;
+            }
+
+            if ((flightType == HandshakeMessage.ht_client_hello) ||
+                (flightType == HandshakeMessage.ht_hello_request) ||
+                (flightType == HandshakeMessage.ht_hello_verify_request)) {
+
+                // single handshake message flight
+                return hasCompleted(holesMap.get(flightType));
+            }
+
+            //
+            // the ServerHello flight
+            //
+            if (flightType == HandshakeMessage.ht_server_hello) {
+                // Firstly, check the first flight handshake message.
+                if (!hasCompleted(holesMap.get(flightType))) {
+                    return false;
+                }
+
+                //
+                // an abbreviated handshake
+                //
+                if (isAbbreviatedHandshake) {
+                    // Ready to use the flight if received the
+                    // ChangeCipherSpec and Finished messages.
+                    return hasFinisedMessage(bufferedFragments);
+                }
+
+                //
+                // a full handshake
+                //
+                if (lastHandshakeFragment.handshakeType !=
+                        HandshakeMessage.ht_server_hello_done) {
+                    // Not yet got the final message of the flight.
+                    return false;
+                }
+
+                // Have all handshake message been received?
+                return hasCompleted(bufferedFragments,
+                    flightTopMessageSeq, lastHandshakeFragment.messageSeq);
+            }
+
+            //
+            // the ClientKeyExchange flight
+            //
+            // Note: need to consider more messages in this flight if
+            //       ht_supplemental_data and ht_certificate_url are
+            //       suppported in the future.
+            //
+            if ((flightType == HandshakeMessage.ht_certificate) ||
+                (flightType == HandshakeMessage.ht_client_key_exchange)) {
+
+                // Firstly, check the first flight handshake message.
+                if (!hasCompleted(holesMap.get(flightType))) {
+                    return false;
+                }
+
+                if (!hasFinisedMessage(bufferedFragments)) {
+                    // not yet got the ChangeCipherSpec/Finished messages
+                    return false;
+                }
+
+                if (flightType == HandshakeMessage.ht_client_key_exchange) {
+                    // single handshake message flight
+                    return true;
+                }
+
+                //
+                // flightType == HandshakeMessage.ht_certificate
+                //
+                // We don't support certificates containing fixed
+                // Diffie-Hellman parameters.  Therefore, CertificateVerify
+                // message is required if client Certificate message presents.
+                //
+                if (lastHandshakeFragment.handshakeType !=
+                        HandshakeMessage.ht_certificate_verify) {
+                    // Not yet got the final message of the flight.
+                    return false;
+                }
+
+                // Have all handshake message been received?
+                return hasCompleted(bufferedFragments,
+                    flightTopMessageSeq, lastHandshakeFragment.messageSeq);
+            }
+
+            //
+            // Otherwise, need to receive more handshake messages.
+            //
+            return false;
+        }
+
+        private boolean isSessionResuming(
+                byte[] fragment, byte[] prevSid) throws SSLException {
+
+            // As the first fragment of ServerHello should be big enough
+            // to hold the session_id field, need not to worry about the
+            // fragmentation here.
+            if ((fragment == null) || (fragment.length < 38)) {
+                                    // 38: the minimal ServerHello body length
+                throw new SSLException(
+                        "Invalid ServerHello message: no sufficient data");
+            }
+
+            int sidLen = fragment[34];          // 34: the length field
+            if (sidLen > 32) {                  // opaque SessionID<0..32>
+                throw new SSLException(
+                        "Invalid ServerHello message: invalid session id");
+            }
+
+            if (fragment.length < 38 + sidLen) {
+                throw new SSLException(
+                        "Invalid ServerHello message: no sufficient data");
+            }
+
+            if (sidLen != 0 && (prevSid.length == sidLen)) {
+                // may be a session-resuming handshake
+                for (int i = 0; i < sidLen; i++) {
+                    if (prevSid[i] != fragment[35 + i]) {
+                                                // 35: the session identifier
+                        return false;
+                    }
+                }
+
+                return true;
+            }
+
+            return false;
+        }
+
+        private byte[] getSessionID(byte[] fragment) {
+            // The validity has been checked in the call to isSessionResuming().
+            int sidLen = fragment[34];      // 34: the sessionID length field
+
+            byte[] temporary = new byte[sidLen];
+            System.arraycopy(fragment, 35, temporary, 0, sidLen);
+
+            return temporary;
+        }
+
+        // Looking for the ChangeCipherSpec and Finished messages.
+        //
+        // As the cached Finished message should be a ciphertext, we don't
+        // exactly know a ciphertext is a Finished message or not.  According
+        // to the spec of TLS/DTLS handshaking, a Finished message is always
+        // sent immediately after a ChangeCipherSpec message.  The first
+        // ciphertext handshake message should be the expected Finished message.
+        private boolean hasFinisedMessage(
+                Set<RecordFragment> fragments) {
+
+            boolean hasCCS = false;
+            boolean hasFin = false;
+            for (RecordFragment fragment : fragments) {
+                if (fragment.contentType == Record.ct_change_cipher_spec) {
+                    if (hasFin) {
+                        return true;
+                    }
+                    hasCCS = true;
+                } else if (fragment.contentType == Record.ct_handshake) {
+                    // Finished is the first expected message of a new epoch.
+                    if (fragment.isCiphertext) {
+                        if (hasCCS) {
+                            return true;
+                        }
+                        hasFin = true;
+                    }
+                }
+            }
+
+            return hasFin && hasCCS;
+        }
+
+        private boolean hasCompleted(List<HoleDescriptor> holes) {
+            if (holes == null) {
+                // not yet received this kind of handshake message
+                return false;
+            }
+
+            return holes.isEmpty();  // no fragment hole for complete message
+        }
+
+        private boolean hasCompleted(
+                Set<RecordFragment> fragments,
+                int presentMsgSeq, int endMsgSeq) {
+
+            // The caller should have checked the completion of the first
+            // present handshake message.  Need not to check it again.
+            for (RecordFragment rFrag : fragments) {
+                if ((rFrag.contentType != Record.ct_handshake) ||
+                        rFrag.isCiphertext) {
+                    break;
+                }
+
+                HandshakeFragment hsFrag = (HandshakeFragment)rFrag;
+                if (hsFrag.messageSeq == presentMsgSeq) {
+                    continue;
+                } else if (hsFrag.messageSeq == (presentMsgSeq + 1)) {
+                    // check the completion of the handshake message
+                    if (!hasCompleted(holesMap.get(hsFrag.handshakeType))) {
+                        return false;
+                    }
+
+                    presentMsgSeq = hsFrag.messageSeq;
+                } else {
+                    // not yet got handshake message next to presentMsgSeq
+                    break;
+                }
+            }
+
+            return (presentMsgSeq >= endMsgSeq);
+                        // false: if not yet got all messages of the flight.
+        }
+
+        private void handshakeHashing(
+                HandshakeFragment hsFrag, Plaintext plaintext) {
+
+            byte hsType = hsFrag.handshakeType;
+            if ((hsType == HandshakeMessage.ht_hello_request) ||
+                (hsType == HandshakeMessage.ht_hello_verify_request)) {
+
+                // omitted from handshake hash computation
+                return;
+            }
+
+            if ((hsFrag.messageSeq == 0) &&
+                (hsType == HandshakeMessage.ht_client_hello)) {
+
+                // omit initial ClientHello message
+                //
+                //  4: handshake header
+                //  2: ClientHello.client_version
+                // 32: ClientHello.random
+                int sidLen = plaintext.fragment.get(38);
+
+                if (sidLen == 0) {      // empty session_id, initial handshake
+                    return;
+                }
+            }
+
+            // calculate the DTLS header
+            byte[] temporary = new byte[12];    // 12: handshake header size
+
+            // Handshake.msg_type
+            temporary[0] = hsFrag.handshakeType;
+
+            // Handshake.length
+            temporary[1] = (byte)((hsFrag.messageLength >> 16) & 0xFF);
+            temporary[2] = (byte)((hsFrag.messageLength >> 8) & 0xFF);
+            temporary[3] = (byte)(hsFrag.messageLength & 0xFF);
+
+            // Handshake.message_seq
+            temporary[4] = (byte)((hsFrag.messageSeq >> 8) & 0xFF);
+            temporary[5] = (byte)(hsFrag.messageSeq & 0xFF);
+
+            // Handshake.fragment_offset
+            temporary[6] = 0;
+            temporary[7] = 0;
+            temporary[8] = 0;
+
+            // Handshake.fragment_length
+            temporary[9] = temporary[1];
+            temporary[10] = temporary[2];
+            temporary[11] = temporary[3];
+
+            plaintext.fragment.position(4);     // ignore the TLS header
+            if ((hsType != HandshakeMessage.ht_finished) &&
+                (hsType != HandshakeMessage.ht_certificate_verify)) {
+
+                if (handshakeHash == null) {
+                    // used for cache only
+                    handshakeHash = new HandshakeHash(false);
+                }
+                handshakeHash.update(temporary, 0, 12);
+                handshakeHash.update(plaintext.fragment);
+            } else {
+                // Reserve until this handshake message has been processed.
+                if (handshakeHash == null) {
+                    // used for cache only
+                    handshakeHash = new HandshakeHash(false);
+                }
+                handshakeHash.reserve(temporary, 0, 12);
+                handshakeHash.reserve(plaintext.fragment);
+            }
+            plaintext.fragment.position(0);     // restore the position
+        }
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/DTLSOutputRecord.java	Tue Jun 02 09:15:47 2015 -0700
@@ -0,0 +1,597 @@
+/*
+ * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ssl;
+
+import java.io.*;
+import java.nio.*;
+import java.util.*;
+
+import javax.crypto.BadPaddingException;
+
+import javax.net.ssl.*;
+
+import sun.misc.HexDumpEncoder;
+import static sun.security.ssl.Ciphertext.RecordType;
+
+/**
+ * DTLS {@code OutputRecord} implementation for {@code SSLEngine}.
+ */
+final class DTLSOutputRecord extends OutputRecord implements DTLSRecord {
+
+    private DTLSFragmenter fragmenter = null;
+
+    int                 writeEpoch;
+
+    int                 prevWriteEpoch;
+    Authenticator       prevWriteAuthenticator;
+    CipherBox           prevWriteCipher;
+
+    private LinkedList<RecordMemo> alertMemos = new LinkedList<>();
+
+    DTLSOutputRecord() {
+        this.writeAuthenticator = new MAC(true);
+
+        this.writeEpoch = 0;
+        this.prevWriteEpoch = 0;
+        this.prevWriteCipher = CipherBox.NULL;
+        this.prevWriteAuthenticator = new MAC(true);
+
+        this.packetSize = DTLSRecord.maxRecordSize;
+        this.protocolVersion = ProtocolVersion.DEFAULT_DTLS;
+    }
+
+    @Override
+    void changeWriteCiphers(Authenticator writeAuthenticator,
+            CipherBox writeCipher) throws IOException {
+
+        encodeChangeCipherSpec();
+
+        prevWriteCipher.dispose();
+
+        this.prevWriteAuthenticator = this.writeAuthenticator;
+        this.prevWriteCipher = this.writeCipher;
+        this.prevWriteEpoch = this.writeEpoch;
+
+        this.writeAuthenticator = writeAuthenticator;
+        this.writeCipher = writeCipher;
+        this.writeEpoch++;
+
+        this.isFirstAppOutputRecord = true;
+
+        // set the epoch number
+        this.writeAuthenticator.setEpochNumber(this.writeEpoch);
+    }
+
+    @Override
+    void encodeAlert(byte level, byte description) throws IOException {
+        RecordMemo memo = new RecordMemo();
+
+        memo.contentType = Record.ct_alert;
+        memo.majorVersion = protocolVersion.major;
+        memo.minorVersion = protocolVersion.minor;
+        memo.encodeEpoch = writeEpoch;
+        memo.encodeCipher = writeCipher;
+        memo.encodeAuthenticator = writeAuthenticator;
+
+        memo.fragment = new byte[2];
+        memo.fragment[0] = level;
+        memo.fragment[1] = description;
+
+        alertMemos.add(memo);
+    }
+
+    @Override
+    void encodeChangeCipherSpec() throws IOException {
+        if (fragmenter == null) {
+           fragmenter = new DTLSFragmenter();
+        }
+        fragmenter.queueUpChangeCipherSpec();
+    }
+
+    @Override
+    void encodeHandshake(byte[] source,
+            int offset, int length) throws IOException {
+
+        if (firstMessage) {
+            firstMessage = false;
+        }
+
+        if (fragmenter == null) {
+           fragmenter = new DTLSFragmenter();
+        }
+
+        fragmenter.queueUpHandshake(source, offset, length);
+    }
+
+    @Override
+    Ciphertext encode(ByteBuffer[] sources, int offset, int length,
+            ByteBuffer destination) throws IOException {
+
+        if (writeAuthenticator.seqNumOverflow()) {
+            if (debug != null && Debug.isOn("ssl")) {
+                System.out.println(Thread.currentThread().getName() +
+                    ", sequence number extremely close to overflow " +
+                    "(2^64-1 packets). Closing connection.");
+            }
+
+            throw new SSLHandshakeException("sequence number overflow");
+        }
+
+        // not apply to handshake message
+        int macLen = 0;
+        if (writeAuthenticator instanceof MAC) {
+            macLen = ((MAC)writeAuthenticator).MAClen();
+        }
+
+        int fragLen;
+        if (packetSize > 0) {
+            fragLen = Math.min(maxRecordSize, packetSize);
+            fragLen = writeCipher.calculateFragmentSize(
+                    fragLen, macLen, headerSize);
+
+            fragLen = Math.min(fragLen, Record.maxDataSize);
+        } else {
+            fragLen = Record.maxDataSize;
+        }
+
+        if (fragmentSize > 0) {
+            fragLen = Math.min(fragLen, fragmentSize);
+        }
+
+        int dstPos = destination.position();
+        int dstLim = destination.limit();
+        int dstContent = dstPos + headerSize +
+                                writeCipher.getExplicitNonceSize();
+        destination.position(dstContent);
+
+        int remains = Math.min(fragLen, destination.remaining());
+        fragLen = 0;
+        int srcsLen = offset + length;
+        for (int i = offset; (i < srcsLen) && (remains > 0); i++) {
+            int amount = Math.min(sources[i].remaining(), remains);
+            int srcLimit = sources[i].limit();
+            sources[i].limit(sources[i].position() + amount);
+            destination.put(sources[i]);
+            sources[i].limit(srcLimit);         // restore the limit
+            remains -= amount;
+            fragLen += amount;
+        }
+
+        destination.limit(destination.position());
+        destination.position(dstContent);
+
+        if ((debug != null) && Debug.isOn("record")) {
+            System.out.println(Thread.currentThread().getName() +
+                    ", WRITE: " + protocolVersion + " " +
+                    Record.contentName(Record.ct_application_data) +
+                    ", length = " + destination.remaining());
+        }
+
+        // Encrypt the fragment and wrap up a record.
+        long recordSN = encrypt(writeAuthenticator, writeCipher,
+                Record.ct_application_data, destination,
+                dstPos, dstLim, headerSize,
+                protocolVersion, true);
+
+        if ((debug != null) && Debug.isOn("packet")) {
+            ByteBuffer temporary = destination.duplicate();
+            temporary.limit(temporary.position());
+            temporary.position(dstPos);
+            Debug.printHex(
+                    "[Raw write]: length = " + temporary.remaining(),
+                    temporary);
+        }
+
+        // remain the limit unchanged
+        destination.limit(dstLim);
+
+        return new Ciphertext(RecordType.RECORD_APPLICATION_DATA, recordSN);
+    }
+
+    @Override
+    Ciphertext acquireCiphertext(ByteBuffer destination) throws IOException {
+        if (alertMemos != null && !alertMemos.isEmpty()) {
+            RecordMemo memo = alertMemos.pop();
+
+            int macLen = 0;
+            if (memo.encodeAuthenticator instanceof MAC) {
+                macLen = ((MAC)memo.encodeAuthenticator).MAClen();
+            }
+
+            int dstPos = destination.position();
+            int dstLim = destination.limit();
+            int dstContent = dstPos + headerSize +
+                                writeCipher.getExplicitNonceSize();
+            destination.position(dstContent);
+
+            destination.put(memo.fragment);
+
+            destination.limit(destination.position());
+            destination.position(dstContent);
+
+            if ((debug != null) && Debug.isOn("record")) {
+                System.out.println(Thread.currentThread().getName() +
+                        ", WRITE: " + protocolVersion + " " +
+                        Record.contentName(Record.ct_alert) +
+                        ", length = " + destination.remaining());
+            }
+
+            // Encrypt the fragment and wrap up a record.
+            long recordSN = encrypt(memo.encodeAuthenticator, memo.encodeCipher,
+                    Record.ct_alert, destination, dstPos, dstLim, headerSize,
+                    ProtocolVersion.valueOf(memo.majorVersion,
+                            memo.minorVersion), true);
+
+            if ((debug != null) && Debug.isOn("packet")) {
+                ByteBuffer temporary = destination.duplicate();
+                temporary.limit(temporary.position());
+                temporary.position(dstPos);
+                Debug.printHex(
+                        "[Raw write]: length = " + temporary.remaining(),
+                        temporary);
+            }
+
+            // remain the limit unchanged
+            destination.limit(dstLim);
+
+            return new Ciphertext(RecordType.RECORD_ALERT, recordSN);
+        }
+
+        if (fragmenter != null) {
+            return fragmenter.acquireCiphertext(destination);
+        }
+
+        return null;
+    }
+
+    @Override
+    boolean isEmpty() {
+        return ((fragmenter == null) || fragmenter.isEmpty()) &&
+               ((alertMemos == null) || alertMemos.isEmpty());
+    }
+
+    @Override
+    void initHandshaker() {
+        // clean up
+        fragmenter = null;
+    }
+
+    // buffered record fragment
+    private static class RecordMemo {
+        byte            contentType;
+        byte            majorVersion;
+        byte            minorVersion;
+        int             encodeEpoch;
+        CipherBox       encodeCipher;
+        Authenticator   encodeAuthenticator;
+
+        byte[]          fragment;
+    }
+
+    private static class HandshakeMemo extends RecordMemo {
+        byte            handshakeType;
+        int             messageSequence;
+        int             acquireOffset;
+    }
+
+    private final class DTLSFragmenter {
+        private LinkedList<RecordMemo> handshakeMemos = new LinkedList<>();
+        private int acquireIndex = 0;
+        private int messageSequence = 0;
+        private boolean flightIsReady = false;
+
+        // Per section 4.1.1, RFC 6347:
+        //
+        // If repeated retransmissions do not result in a response, and the
+        // PMTU is unknown, subsequent retransmissions SHOULD back off to a
+        // smaller record size, fragmenting the handshake message as
+        // appropriate.
+        //
+        // In this implementation, two times of retransmits would be attempted
+        // before backing off.  The back off is supported only if the packet
+        // size is bigger than 256 bytes.
+        private int retransmits = 2;            // attemps of retransmits
+
+        void queueUpChangeCipherSpec() {
+
+            // Cleanup if a new flight starts.
+            if (flightIsReady) {
+                handshakeMemos.clear();
+                acquireIndex = 0;
+                flightIsReady = false;
+            }
+
+            RecordMemo memo = new RecordMemo();
+
+            memo.contentType = Record.ct_change_cipher_spec;
+            memo.majorVersion = protocolVersion.major;
+            memo.minorVersion = protocolVersion.minor;
+            memo.encodeEpoch = writeEpoch;
+            memo.encodeCipher = writeCipher;
+            memo.encodeAuthenticator = writeAuthenticator;
+
+            memo.fragment = new byte[1];
+            memo.fragment[0] = 1;
+
+            handshakeMemos.add(memo);
+        }
+
+        void queueUpHandshake(byte[] buf,
+                int offset, int length) throws IOException {
+
+            // Cleanup if a new flight starts.
+            if (flightIsReady) {
+                handshakeMemos.clear();
+                acquireIndex = 0;
+                flightIsReady = false;
+            }
+
+            HandshakeMemo memo = new HandshakeMemo();
+
+            memo.contentType = Record.ct_handshake;
+            memo.majorVersion = protocolVersion.major;
+            memo.minorVersion = protocolVersion.minor;
+            memo.encodeEpoch = writeEpoch;
+            memo.encodeCipher = writeCipher;
+            memo.encodeAuthenticator = writeAuthenticator;
+
+            memo.handshakeType = buf[offset];
+            memo.messageSequence = messageSequence++;
+            memo.acquireOffset = 0;
+            memo.fragment = new byte[length - 4];       // 4: header size
+                                                        //    1: HandshakeType
+                                                        //    3: message length
+            System.arraycopy(buf, offset + 4, memo.fragment, 0, length - 4);
+
+            handshakeHashing(memo, memo.fragment);
+            handshakeMemos.add(memo);
+
+            if ((memo.handshakeType == HandshakeMessage.ht_client_hello) ||
+                (memo.handshakeType == HandshakeMessage.ht_hello_request) ||
+                (memo.handshakeType ==
+                        HandshakeMessage.ht_hello_verify_request) ||
+                (memo.handshakeType == HandshakeMessage.ht_server_hello_done) ||
+                (memo.handshakeType == HandshakeMessage.ht_finished)) {
+
+                flightIsReady = true;
+            }
+        }
+
+        Ciphertext acquireCiphertext(ByteBuffer dstBuf) throws IOException {
+            if (isEmpty()) {
+                if (isRetransmittable()) {
+                    setRetransmission();    // configure for retransmission
+                } else {
+                    return null;
+                }
+            }
+
+            RecordMemo memo = handshakeMemos.get(acquireIndex);
+            HandshakeMemo hsMemo = null;
+            if (memo.contentType == Record.ct_handshake) {
+                hsMemo = (HandshakeMemo)memo;
+            }
+
+            int macLen = 0;
+            if (memo.encodeAuthenticator instanceof MAC) {
+                macLen = ((MAC)memo.encodeAuthenticator).MAClen();
+            }
+
+            // ChangeCipherSpec message is pretty small.  Don't worry about
+            // the fragmentation of ChangeCipherSpec record.
+            int fragLen;
+            if (packetSize > 0) {
+                fragLen = Math.min(maxRecordSize, packetSize);
+                fragLen = memo.encodeCipher.calculateFragmentSize(
+                        fragLen, macLen, 25);   // 25: header size
+                                                //   13: DTLS record
+                                                //   12: DTLS handshake message
+                fragLen = Math.min(fragLen, Record.maxDataSize);
+            } else {
+                fragLen = Record.maxDataSize;
+            }
+
+            if (fragmentSize > 0) {
+                fragLen = Math.min(fragLen, fragmentSize);
+            }
+
+            int dstPos = dstBuf.position();
+            int dstLim = dstBuf.limit();
+            int dstContent = dstPos + headerSize +
+                                    memo.encodeCipher.getExplicitNonceSize();
+            dstBuf.position(dstContent);
+
+            if (hsMemo != null) {
+                fragLen = Math.min(fragLen,
+                        (hsMemo.fragment.length - hsMemo.acquireOffset));
+
+                dstBuf.put(hsMemo.handshakeType);
+                dstBuf.put((byte)((hsMemo.fragment.length >> 16) & 0xFF));
+                dstBuf.put((byte)((hsMemo.fragment.length >> 8) & 0xFF));
+                dstBuf.put((byte)(hsMemo.fragment.length & 0xFF));
+                dstBuf.put((byte)((hsMemo.messageSequence >> 8) & 0xFF));
+                dstBuf.put((byte)(hsMemo.messageSequence & 0xFF));
+                dstBuf.put((byte)((hsMemo.acquireOffset >> 16) & 0xFF));
+                dstBuf.put((byte)((hsMemo.acquireOffset >> 8) & 0xFF));
+                dstBuf.put((byte)(hsMemo.acquireOffset & 0xFF));
+                dstBuf.put((byte)((fragLen >> 16) & 0xFF));
+                dstBuf.put((byte)((fragLen >> 8) & 0xFF));
+                dstBuf.put((byte)(fragLen & 0xFF));
+                dstBuf.put(hsMemo.fragment, hsMemo.acquireOffset, fragLen);
+            } else {
+                fragLen = Math.min(fragLen, memo.fragment.length);
+                dstBuf.put(memo.fragment, 0, fragLen);
+            }
+
+            dstBuf.limit(dstBuf.position());
+            dstBuf.position(dstContent);
+
+            if ((debug != null) && Debug.isOn("record")) {
+                System.out.println(Thread.currentThread().getName() +
+                        ", WRITE: " + protocolVersion + " " +
+                        Record.contentName(memo.contentType) +
+                        ", length = " + dstBuf.remaining());
+            }
+
+            // Encrypt the fragment and wrap up a record.
+            long recordSN = encrypt(memo.encodeAuthenticator, memo.encodeCipher,
+                    memo.contentType, dstBuf,
+                    dstPos, dstLim, headerSize,
+                    ProtocolVersion.valueOf(memo.majorVersion,
+                            memo.minorVersion), true);
+
+            if ((debug != null) && Debug.isOn("packet")) {
+                ByteBuffer temporary = dstBuf.duplicate();
+                temporary.limit(temporary.position());
+                temporary.position(dstPos);
+                Debug.printHex(
+                        "[Raw write]: length = " + temporary.remaining(),
+                        temporary);
+            }
+
+            // remain the limit unchanged
+            dstBuf.limit(dstLim);
+
+            // Reset the fragmentation offset.
+            if (hsMemo != null) {
+                hsMemo.acquireOffset += fragLen;
+                if (hsMemo.acquireOffset == hsMemo.fragment.length) {
+                    acquireIndex++;
+                }
+
+                return new Ciphertext(RecordType.valueOf(
+                        hsMemo.contentType, hsMemo.handshakeType), recordSN);
+            } else {
+                acquireIndex++;
+                return new Ciphertext(
+                        RecordType.RECORD_CHANGE_CIPHER_SPEC, recordSN);
+            }
+        }
+
+        private void handshakeHashing(HandshakeMemo hsFrag, byte[] hsBody) {
+
+            byte hsType = hsFrag.handshakeType;
+            if ((hsType == HandshakeMessage.ht_hello_request) ||
+                (hsType == HandshakeMessage.ht_hello_verify_request)) {
+
+                // omitted from handshake hash computation
+                return;
+            }
+
+            if ((hsFrag.messageSequence == 0) &&
+                (hsType == HandshakeMessage.ht_client_hello)) {
+
+                // omit initial ClientHello message
+                //
+                //  2: ClientHello.client_version
+                // 32: ClientHello.random
+                int sidLen = hsBody[34];
+
+                if (sidLen == 0) {      // empty session_id, initial handshake
+                    return;
+                }
+            }
+
+            // calculate the DTLS header
+            byte[] temporary = new byte[12];    // 12: handshake header size
+
+            // Handshake.msg_type
+            temporary[0] = hsFrag.handshakeType;
+
+            // Handshake.length
+            temporary[1] = (byte)((hsBody.length >> 16) & 0xFF);
+            temporary[2] = (byte)((hsBody.length >> 8) & 0xFF);
+            temporary[3] = (byte)(hsBody.length & 0xFF);
+
+            // Handshake.message_seq
+            temporary[4] = (byte)((hsFrag.messageSequence >> 8) & 0xFF);
+            temporary[5] = (byte)(hsFrag.messageSequence & 0xFF);
+
+            // Handshake.fragment_offset
+            temporary[6] = 0;
+            temporary[7] = 0;
+            temporary[8] = 0;
+
+            // Handshake.fragment_length
+            temporary[9] = temporary[1];
+            temporary[10] = temporary[2];
+            temporary[11] = temporary[3];
+
+            if ((hsType != HandshakeMessage.ht_finished) &&
+                (hsType != HandshakeMessage.ht_certificate_verify)) {
+
+                handshakeHash.update(temporary, 0, 12);
+                handshakeHash.update(hsBody, 0, hsBody.length);
+            } else {
+                // Reserve until this handshake message has been processed.
+                handshakeHash.reserve(temporary, 0, 12);
+                handshakeHash.reserve(hsBody, 0, hsBody.length);
+            }
+
+        }
+
+        boolean isEmpty() {
+            if (!flightIsReady || handshakeMemos.isEmpty() ||
+                    acquireIndex >= handshakeMemos.size()) {
+                return true;
+            }
+
+            return false;
+        }
+
+        boolean isRetransmittable() {
+            return (flightIsReady && !handshakeMemos.isEmpty() &&
+                                (acquireIndex >= handshakeMemos.size()));
+        }
+
+        private void setRetransmission() {
+            acquireIndex = 0;
+            for (RecordMemo memo : handshakeMemos) {
+                if (memo instanceof HandshakeMemo) {
+                    HandshakeMemo hmemo = (HandshakeMemo)memo;
+                    hmemo.acquireOffset = 0;
+                }
+            }
+
+            // Shrink packet size if:
+            // 1. maximum fragment size is allowed, in which case the packet
+            //    size is configured bigger than maxRecordSize;
+            // 2. maximum packet is bigger than 256 bytes;
+            // 3. two times of retransmits have been attempted.
+            if ((packetSize <= maxRecordSize) &&
+                    (packetSize > 256) && ((retransmits--) <= 0)) {
+
+                // shrink packet size
+                shrinkPacketSize();
+                retransmits = 2;        // attemps of retransmits
+            }
+        }
+
+        private void shrinkPacketSize() {
+            packetSize = Math.max(256, packetSize / 2);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/DTLSRecord.java	Tue Jun 02 09:15:47 2015 -0700
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ssl;
+
+/**
+ * DTLS record
+ */
+interface DTLSRecord extends Record {
+
+    static final int    headerSize = 13;            // DTLS record header
+
+    static final int    handshakeHeaderSize = 12;   // DTLS handshake header
+
+    /*
+     * The size of the header plus the max IV length
+     */
+    static final int    headerPlusMaxIVSize =
+                                      headerSize        // header
+                                    + maxIVLength;      // iv
+
+    /*
+     * The maximum size that may be increased when translating plaintext to
+     * ciphertext fragment.
+     */
+    static final int    maxPlaintextPlusSize =
+                                      headerSize        // header
+                                    + maxIVLength       // iv
+                                    + maxMacSize        // MAC or AEAD tag
+                                    + maxPadding;       // block cipher padding
+
+    /*
+     * the maximum record size
+     */
+    static final int    maxRecordSize =
+                                      headerPlusMaxIVSize   // header + iv
+                                    + maxDataSize           // data
+                                    + maxPadding            // padding
+                                    + maxMacSize;           // MAC or AEAD tag
+
+    /*
+     * For CBC protection in SSL3/TLS1, we break some plaintext into two
+     * packets.  Max application data size for the second packet.
+     */
+    static final int    maxDataSizeMinusOneByteRecord =
+                                  maxDataSize       // max data size
+                                - (                 // max one byte record size
+                                      headerPlusMaxIVSize   // header + iv
+                                    + 1             // one byte data
+                                    + maxPadding    // padding
+                                    + maxMacSize    // MAC
+                                  );
+
+    /*
+     * Maximum record size for alert and change cipher spec records.
+     * They only contain 2 and 1 bytes of data, respectively.
+     * Allocate a smaller array.
+     */
+    static final int    maxAlertRecordSize =
+                                      headerPlusMaxIVSize   // header + iv
+                                    + 2                     // alert
+                                    + maxPadding            // padding
+                                    + maxMacSize;           // MAC
+
+}
--- a/jdk/src/java.base/share/classes/sun/security/ssl/Debug.java	Mon Jun 01 10:15:21 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/Debug.java	Tue Jun 02 09:15:47 2015 -0700
@@ -29,6 +29,9 @@
 import java.security.AccessController;
 import java.util.Locale;
 
+import sun.misc.HexDumpEncoder;
+import java.nio.ByteBuffer;
+
 import sun.security.action.GetPropertyAction;
 
 /**
@@ -198,4 +201,47 @@
     static String toString(byte[] b) {
         return sun.security.util.Debug.toString(b);
     }
+
+    static void printHex(String prefix, byte[] bytes) {
+        HexDumpEncoder dump = new HexDumpEncoder();
+
+        synchronized (System.out) {
+            System.out.println(prefix);
+            try {
+                dump.encodeBuffer(bytes, System.out);
+            } catch (Exception e) {
+                // ignore
+            }
+            System.out.flush();
+        }
+    }
+
+    static void printHex(String prefix, ByteBuffer bb) {
+        HexDumpEncoder dump = new HexDumpEncoder();
+
+        synchronized (System.out) {
+            System.out.println(prefix);
+            try {
+                dump.encodeBuffer(bb.slice(), System.out);
+            } catch (Exception e) {
+                // ignore
+            }
+            System.out.flush();
+        }
+    }
+
+    static void printHex(String prefix, byte[] bytes, int offset, int length) {
+        HexDumpEncoder dump = new HexDumpEncoder();
+
+        synchronized (System.out) {
+            System.out.println(prefix);
+            try {
+                ByteBuffer bb = ByteBuffer.wrap(bytes, offset, length);
+                dump.encodeBuffer(bb, System.out);
+            } catch (Exception e) {
+                // ignore
+            }
+            System.out.flush();
+        }
+    }
 }
--- a/jdk/src/java.base/share/classes/sun/security/ssl/EngineArgs.java	Mon Jun 01 10:15:21 2015 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,238 +0,0 @@
-/*
- * Copyright (c) 2004, 2012, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package sun.security.ssl;
-
-import java.nio.*;
-
-/*
- * A multi-purpose class which handles all of the SSLEngine arguments.
- * It validates arguments, checks for RO conditions, does space
- * calculations, performs scatter/gather, etc.
- *
- * @author Brad R. Wetmore
- */
-class EngineArgs {
-
-    /*
-     * Keep track of the input parameters.
-     */
-    ByteBuffer netData;
-    ByteBuffer [] appData;
-
-    private int offset;         // offset/len for the appData array.
-    private int len;
-
-    /*
-     * The initial pos/limit conditions.  This is useful because we can
-     * quickly calculate the amount consumed/produced in successful
-     * operations, or easily return the buffers to their pre-error
-     * conditions.
-     */
-    private int netPos;
-    private int netLim;
-
-    private int [] appPoss;
-    private int [] appLims;
-
-    /*
-     * Sum total of the space remaining in all of the appData buffers
-     */
-    private int appRemaining = 0;
-
-    private boolean wrapMethod;
-
-    /*
-     * Called by the SSLEngine.wrap() method.
-     */
-    EngineArgs(ByteBuffer [] appData, int offset, int len,
-            ByteBuffer netData) {
-        this.wrapMethod = true;
-        init(netData, appData, offset, len);
-    }
-
-    /*
-     * Called by the SSLEngine.unwrap() method.
-     */
-    EngineArgs(ByteBuffer netData, ByteBuffer [] appData, int offset,
-            int len) {
-        this.wrapMethod = false;
-        init(netData, appData, offset, len);
-    }
-
-    /*
-     * The main initialization method for the arguments.  Most
-     * of them are pretty obvious as to what they do.
-     *
-     * Since we're already iterating over appData array for validity
-     * checking, we also keep track of how much remainging space is
-     * available.  Info is used in both unwrap (to see if there is
-     * enough space available in the destination), and in wrap (to
-     * determine how much more we can copy into the outgoing data
-     * buffer.
-     */
-    private void init(ByteBuffer netData, ByteBuffer [] appData,
-            int offset, int len) {
-
-        if ((netData == null) || (appData == null)) {
-            throw new IllegalArgumentException("src/dst is null");
-        }
-
-        if ((offset < 0) || (len < 0) || (offset > appData.length - len)) {
-            throw new IndexOutOfBoundsException();
-        }
-
-        if (wrapMethod && netData.isReadOnly()) {
-            throw new ReadOnlyBufferException();
-        }
-
-        netPos = netData.position();
-        netLim = netData.limit();
-
-        appPoss = new int [appData.length];
-        appLims = new int [appData.length];
-
-        for (int i = offset; i < offset + len; i++) {
-            if (appData[i] == null) {
-                throw new IllegalArgumentException(
-                    "appData[" + i + "] == null");
-            }
-
-            /*
-             * If we're unwrapping, then check to make sure our
-             * destination bufffers are writable.
-             */
-            if (!wrapMethod && appData[i].isReadOnly()) {
-                throw new ReadOnlyBufferException();
-            }
-
-            appRemaining += appData[i].remaining();
-
-            appPoss[i] = appData[i].position();
-            appLims[i] = appData[i].limit();
-        }
-
-        /*
-         * Ok, looks like we have a good set of args, let's
-         * store the rest of this stuff.
-         */
-        this.netData = netData;
-        this.appData = appData;
-        this.offset = offset;
-        this.len = len;
-    }
-
-    /*
-     * Given spaceLeft bytes to transfer, gather up that much data
-     * from the appData buffers (starting at offset in the array),
-     * and transfer it into the netData buffer.
-     *
-     * The user has already ensured there is enough room.
-     */
-    void gather(int spaceLeft) {
-        for (int i = offset; (i < (offset + len)) && (spaceLeft > 0); i++) {
-            int amount = Math.min(appData[i].remaining(), spaceLeft);
-            appData[i].limit(appData[i].position() + amount);
-            netData.put(appData[i]);
-            appRemaining -= amount;
-            spaceLeft -= amount;
-        }
-    }
-
-    /*
-     * Using the supplied buffer, scatter the data into the appData buffers
-     * (starting at offset in the array).
-     *
-     * The user has already ensured there is enough room.
-     */
-    void scatter(ByteBuffer readyData) {
-        int amountLeft = readyData.remaining();
-
-        for (int i = offset; (i < (offset + len)) && (amountLeft > 0);
-                i++) {
-            int amount = Math.min(appData[i].remaining(), amountLeft);
-            readyData.limit(readyData.position() + amount);
-            appData[i].put(readyData);
-            amountLeft -= amount;
-        }
-        assert(readyData.remaining() == 0);
-    }
-
-    int getAppRemaining() {
-        return appRemaining;
-    }
-
-    /*
-     * Calculate the bytesConsumed/byteProduced.  Aren't you glad
-     * we saved this off earlier?
-     */
-    int deltaNet() {
-        return (netData.position() - netPos);
-    }
-
-    /*
-     * Calculate the bytesConsumed/byteProduced.  Aren't you glad
-     * we saved this off earlier?
-     */
-    int deltaApp() {
-        int sum = 0;    // Only calculating 2^14 here, don't need a long.
-
-        for (int i = offset; i < offset + len; i++) {
-            sum += appData[i].position() - appPoss[i];
-        }
-
-        return sum;
-    }
-
-    /*
-     * In the case of Exception, we want to reset the positions
-     * to appear as though no data has been consumed or produced.
-     *
-     * Currently, this method is only called as we are preparing to
-     * fail out, and thus we don't need to actually recalculate
-     * appRemaining.  If that assumption changes, that variable should
-     * be updated here.
-     */
-    void resetPos() {
-        netData.position(netPos);
-        for (int i = offset; i < offset + len; i++) {
-            // See comment above about recalculating appRemaining.
-            appData[i].position(appPoss[i]);
-        }
-    }
-
-    /*
-     * We are doing lots of ByteBuffer manipulations, in which case
-     * we need to make sure that the limits get set back correctly.
-     * This is one of the last things to get done before returning to
-     * the user.
-     */
-    void resetLim() {
-        netData.limit(netLim);
-        for (int i = offset; i < offset + len; i++) {
-            appData[i].limit(appLims[i]);
-        }
-    }
-}
--- a/jdk/src/java.base/share/classes/sun/security/ssl/EngineInputRecord.java	Mon Jun 01 10:15:21 2015 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,427 +0,0 @@
-/*
- * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-
-package sun.security.ssl;
-
-import java.io.*;
-import java.nio.*;
-import javax.net.ssl.*;
-import javax.crypto.BadPaddingException;
-import sun.misc.HexDumpEncoder;
-
-
-/**
- * Wrapper class around InputRecord.
- *
- * Application data is kept external to the InputRecord,
- * but handshake data (alert/change_cipher_spec/handshake) will
- * be kept internally in the ByteArrayInputStream.
- *
- * @author Brad Wetmore
- */
-final class EngineInputRecord extends InputRecord {
-
-    private SSLEngineImpl engine;
-
-    /*
-     * A dummy ByteBuffer we'll pass back even when the data
-     * is stored internally.  It'll never actually be used.
-     */
-    static private ByteBuffer tmpBB = ByteBuffer.allocate(0);
-
-    /*
-     * Flag to tell whether the last read/parsed data resides
-     * internal in the ByteArrayInputStream, or in the external
-     * buffers.
-     */
-    private boolean internalData;
-
-    EngineInputRecord(SSLEngineImpl engine) {
-        super();
-        this.engine = engine;
-    }
-
-    @Override
-    byte contentType() {
-        if (internalData) {
-            return super.contentType();
-        } else {
-            return ct_application_data;
-        }
-    }
-
-    /*
-     * Check if there is enough inbound data in the ByteBuffer
-     * to make a inbound packet.  Look for both SSLv2 and SSLv3.
-     *
-     * @return -1 if there are not enough bytes to tell (small header),
-     */
-    int bytesInCompletePacket(ByteBuffer buf) throws SSLException {
-
-        /*
-         * SSLv2 length field is in bytes 0/1
-         * SSLv3/TLS length field is in bytes 3/4
-         */
-        if (buf.remaining() < 5) {
-            return -1;
-        }
-
-        int pos = buf.position();
-        byte byteZero = buf.get(pos);
-
-        int len = 0;
-
-        /*
-         * If we have already verified previous packets, we can
-         * ignore the verifications steps, and jump right to the
-         * determination.  Otherwise, try one last hueristic to
-         * see if it's SSL/TLS.
-         */
-        if (formatVerified ||
-                (byteZero == ct_handshake) ||
-                (byteZero == ct_alert)) {
-            /*
-             * Last sanity check that it's not a wild record
-             */
-            ProtocolVersion recordVersion =
-                ProtocolVersion.valueOf(buf.get(pos + 1), buf.get(pos + 2));
-
-            // check the record version
-            checkRecordVersion(recordVersion, false);
-
-            /*
-             * Reasonably sure this is a V3, disable further checks.
-             * We can't do the same in the v2 check below, because
-             * read still needs to parse/handle the v2 clientHello.
-             */
-            formatVerified = true;
-
-            /*
-             * One of the SSLv3/TLS message types.
-             */
-            len = ((buf.get(pos + 3) & 0xff) << 8) +
-                (buf.get(pos + 4) & 0xff) + headerSize;
-
-        } else {
-            /*
-             * Must be SSLv2 or something unknown.
-             * Check if it's short (2 bytes) or
-             * long (3) header.
-             *
-             * Internals can warn about unsupported SSLv2
-             */
-            boolean isShort = ((byteZero & 0x80) != 0);
-
-            if (isShort &&
-                    ((buf.get(pos + 2) == 1) || buf.get(pos + 2) == 4)) {
-
-                ProtocolVersion recordVersion =
-                    ProtocolVersion.valueOf(buf.get(pos + 3), buf.get(pos + 4));
-
-                // check the record version
-                checkRecordVersion(recordVersion, true);
-
-                /*
-                 * Client or Server Hello
-                 */
-                int mask = (isShort ? 0x7f : 0x3f);
-                len = ((byteZero & mask) << 8) + (buf.get(pos + 1) & 0xff) +
-                    (isShort ? 2 : 3);
-
-            } else {
-                // Gobblygook!
-                throw new SSLException(
-                    "Unrecognized SSL message, plaintext connection?");
-            }
-        }
-
-        return len;
-    }
-
-    /*
-     * Pass the data down if it's internally cached, otherwise
-     * do it here.
-     *
-     * If internal data, data is decrypted internally.
-     *
-     * If external data(app), return a new ByteBuffer with data to
-     * process.
-     */
-    ByteBuffer decrypt(Authenticator authenticator,
-            CipherBox box, ByteBuffer bb) throws BadPaddingException {
-
-        if (internalData) {
-            decrypt(authenticator, box);   // MAC is checked during decryption
-            return tmpBB;
-        }
-
-        BadPaddingException reservedBPE = null;
-        int tagLen =
-            (authenticator instanceof MAC) ? ((MAC)authenticator).MAClen() : 0;
-        int cipheredLength = bb.remaining();
-
-        if (!box.isNullCipher()) {
-            try {
-                // apply explicit nonce for AEAD/CBC cipher suites if needed
-                int nonceSize =
-                    box.applyExplicitNonce(authenticator, contentType(), bb);
-
-                // decrypt the content
-                if (box.isAEADMode()) {
-                    // DON'T encrypt the nonce_explicit for AEAD mode
-                    bb.position(bb.position() + nonceSize);
-                }   // The explicit IV for CBC mode can be decrypted.
-
-                // Note that the CipherBox.decrypt() does not change
-                // the capacity of the buffer.
-                box.decrypt(bb, tagLen);
-                bb.position(nonceSize); // We don't actually remove the nonce.
-            } catch (BadPaddingException bpe) {
-                // RFC 2246 states that decryption_failed should be used
-                // for this purpose. However, that allows certain attacks,
-                // so we just send bad record MAC. We also need to make
-                // sure to always check the MAC to avoid a timing attack
-                // for the same issue. See paper by Vaudenay et al and the
-                // update in RFC 4346/5246.
-                //
-                // Failover to message authentication code checking.
-                reservedBPE = bpe;
-            }
-        }
-
-        // Requires message authentication code for null, stream and block
-        // cipher suites.
-        if ((authenticator instanceof MAC) && (tagLen != 0)) {
-            MAC signer = (MAC)authenticator;
-            int macOffset = bb.limit() - tagLen;
-
-            // Note that although it is not necessary, we run the same MAC
-            // computation and comparison on the payload for both stream
-            // cipher and CBC block cipher.
-            if (bb.remaining() < tagLen) {
-                // negative data length, something is wrong
-                if (reservedBPE == null) {
-                    reservedBPE = new BadPaddingException("bad record");
-                }
-
-                // set offset of the dummy MAC
-                macOffset = cipheredLength - tagLen;
-                bb.limit(cipheredLength);
-            }
-
-            // Run MAC computation and comparison on the payload.
-            if (checkMacTags(contentType(), bb, signer, false)) {
-                if (reservedBPE == null) {
-                    reservedBPE = new BadPaddingException("bad record MAC");
-                }
-            }
-
-            // Run MAC computation and comparison on the remainder.
-            //
-            // It is only necessary for CBC block cipher.  It is used to get a
-            // constant time of MAC computation and comparison on each record.
-            if (box.isCBCMode()) {
-                int remainingLen = calculateRemainingLen(
-                                        signer, cipheredLength, macOffset);
-
-                // NOTE: here we use the InputRecord.buf because I did not find
-                // an effective way to work on ByteBuffer when its capacity is
-                // less than remainingLen.
-
-                // NOTE: remainingLen may be bigger (less than 1 block of the
-                // hash algorithm of the MAC) than the cipheredLength. However,
-                // We won't need to worry about it because we always use a
-                // maximum buffer for every record.  We need a change here if
-                // we use small buffer size in the future.
-                if (remainingLen > buf.length) {
-                    // unlikely to happen, just a placehold
-                    throw new RuntimeException(
-                        "Internal buffer capacity error");
-                }
-
-                // Won't need to worry about the result on the remainder. And
-                // then we won't need to worry about what's actual data to
-                // check MAC tag on.  We start the check from the header of the
-                // buffer so that we don't need to construct a new byte buffer.
-                checkMacTags(contentType(), buf, 0, remainingLen, signer, true);
-            }
-
-            bb.limit(macOffset);
-        }
-
-        // Is it a failover?
-        if (reservedBPE != null) {
-            throw reservedBPE;
-        }
-
-        return bb.slice();
-    }
-
-    /*
-     * Run MAC computation and comparison
-     *
-     * Please DON'T change the content of the ByteBuffer parameter!
-     */
-    private static boolean checkMacTags(byte contentType, ByteBuffer bb,
-            MAC signer, boolean isSimulated) {
-
-        int position = bb.position();
-        int tagLen = signer.MAClen();
-        int lim = bb.limit();
-        int macData = lim - tagLen;
-
-        bb.limit(macData);
-        byte[] hash = signer.compute(contentType, bb, isSimulated);
-        if (hash == null || tagLen != hash.length) {
-            // Something is wrong with MAC implementation.
-            throw new RuntimeException("Internal MAC error");
-        }
-
-        bb.position(macData);
-        bb.limit(lim);
-        try {
-            int[] results = compareMacTags(bb, hash);
-            return (results[0] != 0);
-        } finally {
-            // reset to the data
-            bb.position(position);
-            bb.limit(macData);
-        }
-    }
-
-    /*
-     * A constant-time comparison of the MAC tags.
-     *
-     * Please DON'T change the content of the ByteBuffer parameter!
-     */
-    private static int[] compareMacTags(ByteBuffer bb, byte[] tag) {
-
-        // An array of hits is used to prevent Hotspot optimization for
-        // the purpose of a constant-time check.
-        int[] results = {0, 0};     // {missed #, matched #}
-
-        // The caller ensures there are enough bytes available in the buffer.
-        // So we won't need to check the remaining of the buffer.
-        for (int i = 0; i < tag.length; i++) {
-            if (bb.get() != tag[i]) {
-                results[0]++;       // mismatched bytes
-            } else {
-                results[1]++;       // matched bytes
-            }
-        }
-
-        return results;
-    }
-
-    /*
-     * Override the actual write below.  We do things this way to be
-     * consistent with InputRecord.  InputRecord may try to write out
-     * data to the peer, and *then* throw an Exception.  This forces
-     * data to be generated/output before the exception is ever
-     * generated.
-     */
-    @Override
-    void writeBuffer(OutputStream s, byte [] buf, int off, int len)
-            throws IOException {
-        /*
-         * Copy data out of buffer, it's ready to go.
-         */
-        ByteBuffer netBB = ByteBuffer.allocate(len).put(buf, 0, len).flip();
-        engine.writer.putOutboundDataSync(netBB);
-    }
-
-    /*
-     * Delineate or read a complete packet from src.
-     *
-     * If internal data (hs, alert, ccs), the data is read and
-     * stored internally.
-     *
-     * If external data (app), return a new ByteBuffer which points
-     * to the data to process.
-     */
-    ByteBuffer read(ByteBuffer srcBB) throws IOException {
-        /*
-         * Could have a src == null/dst == null check here,
-         * but that was already checked by SSLEngine.unwrap before
-         * ever attempting to read.
-         */
-
-        /*
-         * If we have anything besides application data,
-         * or if we haven't even done the initial v2 verification,
-         * we send this down to be processed by the underlying
-         * internal cache.
-         */
-        if (!formatVerified ||
-                (srcBB.get(srcBB.position()) != ct_application_data)) {
-            internalData = true;
-            read(new ByteBufferInputStream(srcBB), (OutputStream) null);
-            return tmpBB;
-        }
-
-        internalData = false;
-
-        int srcPos = srcBB.position();
-        int srcLim = srcBB.limit();
-
-        ProtocolVersion recordVersion = ProtocolVersion.valueOf(
-                srcBB.get(srcPos + 1), srcBB.get(srcPos + 2));
-
-        // check the record version
-        checkRecordVersion(recordVersion, false);
-
-        /*
-         * It's really application data.  How much to consume?
-         * Jump over the header.
-         */
-        int len = bytesInCompletePacket(srcBB);
-        assert(len > 0);
-
-        if (debug != null && Debug.isOn("packet")) {
-            try {
-                HexDumpEncoder hd = new HexDumpEncoder();
-                ByteBuffer bb = srcBB.duplicate();  // Use copy of BB
-                bb.limit(srcPos + len);
-
-                System.out.println("[Raw read (bb)]: length = " + len);
-                hd.encodeBuffer(bb, System.out);
-            } catch (IOException e) { }
-        }
-
-        // Demarcate past header to end of packet.
-        srcBB.position(srcPos + headerSize);
-        srcBB.limit(srcPos + len);
-
-        // Protect remainder of buffer, create slice to actually
-        // operate on.
-        ByteBuffer bb = srcBB.slice();
-
-        srcBB.position(srcBB.limit());
-        srcBB.limit(srcLim);
-
-        return bb;
-    }
-}
--- a/jdk/src/java.base/share/classes/sun/security/ssl/EngineOutputRecord.java	Mon Jun 01 10:15:21 2015 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,329 +0,0 @@
-/*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-
-package sun.security.ssl;
-
-import java.io.*;
-import java.nio.*;
-
-/**
- * A OutputRecord class extension which uses external ByteBuffers
- * or the internal ByteArrayOutputStream for data manipulations.
- * <P>
- * Instead of rewriting this entire class
- * to use ByteBuffers, we leave things intact, so handshake, CCS,
- * and alerts will continue to use the internal buffers, but application
- * data will use external buffers.
- *
- * @author Brad Wetmore
- */
-final class EngineOutputRecord extends OutputRecord {
-
-    private SSLEngineImpl engine;
-    private EngineWriter writer;
-
-    private boolean finishedMsg = false;
-
-    /*
-     * All handshake hashing is done by the superclass
-     */
-
-    /*
-     * Default constructor makes a record supporting the maximum
-     * SSL record size.  It allocates the header bytes directly.
-     *
-     * @param type the content type for the record
-     */
-    EngineOutputRecord(byte type, SSLEngineImpl engine) {
-        super(type, recordSize(type));
-        this.engine = engine;
-        writer = engine.writer;
-    }
-
-    /**
-     * Get the size of the buffer we need for records of the specified
-     * type.
-     * <P>
-     * Application data buffers will provide their own byte buffers,
-     * and will not use the internal byte caching.
-     */
-    private static int recordSize(byte type) {
-        switch (type) {
-
-        case ct_change_cipher_spec:
-        case ct_alert:
-            return maxAlertRecordSize;
-
-        case ct_handshake:
-            return maxRecordSize;
-
-        case ct_application_data:
-            return 0;
-        }
-
-        throw new RuntimeException("Unknown record type: " + type);
-    }
-
-    void setFinishedMsg() {
-        finishedMsg = true;
-    }
-
-    @Override
-    public void flush() throws IOException {
-        finishedMsg = false;
-    }
-
-    boolean isFinishedMsg() {
-        return finishedMsg;
-    }
-
-    /*
-     * Override the actual write below.  We do things this way to be
-     * consistent with InputRecord.  InputRecord may try to write out
-     * data to the peer, and *then* throw an Exception.  This forces
-     * data to be generated/output before the exception is ever
-     * generated.
-     */
-    @Override
-    void writeBuffer(OutputStream s, byte [] buf, int off, int len,
-            int debugOffset) throws IOException {
-        /*
-         * Copy data out of buffer, it's ready to go.
-         */
-        ByteBuffer netBB = ByteBuffer.allocate(len).put(buf, off, len).flip();
-        writer.putOutboundData(netBB);
-    }
-
-    /*
-     * Main method for writing non-application data.
-     * We MAC/encrypt, then send down for processing.
-     */
-    void write(Authenticator authenticator, CipherBox writeCipher)
-            throws IOException {
-
-        /*
-         * Sanity check.
-         */
-        switch (contentType()) {
-            case ct_change_cipher_spec:
-            case ct_alert:
-            case ct_handshake:
-                break;
-            default:
-                throw new RuntimeException("unexpected byte buffers");
-        }
-
-        /*
-         * Don't bother to really write empty records.  We went this
-         * far to drive the handshake machinery, for correctness; not
-         * writing empty records improves performance by cutting CPU
-         * time and network resource usage.  Also, some protocol
-         * implementations are fragile and don't like to see empty
-         * records, so this increases robustness.
-         *
-         * (Even change cipher spec messages have a byte of data!)
-         */
-        if (!isEmpty()) {
-            // compress();              // eventually
-            encrypt(authenticator, writeCipher);
-
-            // send down for processing
-            write((OutputStream)null, false, (ByteArrayOutputStream)null);
-        }
-        return;
-    }
-
-    /**
-     * Main wrap/write driver.
-     */
-    void write(EngineArgs ea, Authenticator authenticator,
-            CipherBox writeCipher) throws IOException {
-        /*
-         * sanity check to make sure someone didn't inadvertantly
-         * send us an impossible combination we don't know how
-         * to process.
-         */
-        assert(contentType() == ct_application_data);
-
-        /*
-         * Have we set the MAC's yet?  If not, we're not ready
-         * to process application data yet.
-         */
-        if (authenticator == MAC.NULL) {
-            return;
-        }
-
-        /*
-         * Don't bother to really write empty records.  We went this
-         * far to drive the handshake machinery, for correctness; not
-         * writing empty records improves performance by cutting CPU
-         * time and network resource usage.  Also, some protocol
-         * implementations are fragile and don't like to see empty
-         * records, so this increases robustness.
-         */
-        if (ea.getAppRemaining() == 0) {
-            return;
-        }
-
-        /*
-         * By default, we counter chosen plaintext issues on CBC mode
-         * ciphersuites in SSLv3/TLS1.0 by sending one byte of application
-         * data in the first record of every payload, and the rest in
-         * subsequent record(s). Note that the issues have been solved in
-         * TLS 1.1 or later.
-         *
-         * It is not necessary to split the very first application record of
-         * a freshly negotiated TLS session, as there is no previous
-         * application data to guess.  To improve compatibility, we will not
-         * split such records.
-         *
-         * Because of the compatibility, we'd better produce no more than
-         * SSLSession.getPacketBufferSize() net data for each wrap. As we
-         * need a one-byte record at first, the 2nd record size should be
-         * equal to or less than Record.maxDataSizeMinusOneByteRecord.
-         *
-         * This avoids issues in the outbound direction.  For a full fix,
-         * the peer must have similar protections.
-         */
-        int length;
-        if (engine.needToSplitPayload(writeCipher, protocolVersion)) {
-            write(ea, authenticator, writeCipher, 0x01);
-            ea.resetLim();      // reset application data buffer limit
-            length = Math.min(ea.getAppRemaining(),
-                        maxDataSizeMinusOneByteRecord);
-        } else {
-            length = Math.min(ea.getAppRemaining(), maxDataSize);
-        }
-
-        // Don't bother to really write empty records.
-        if (length > 0) {
-            write(ea, authenticator, writeCipher, length);
-        }
-
-        return;
-    }
-
-    void write(EngineArgs ea, Authenticator authenticator,
-            CipherBox writeCipher, int length) throws IOException {
-        /*
-         * Copy out existing buffer values.
-         */
-        ByteBuffer dstBB = ea.netData;
-        int dstPos = dstBB.position();
-        int dstLim = dstBB.limit();
-
-        /*
-         * Where to put the data.  Jump over the header.
-         *
-         * Don't need to worry about SSLv2 rewrites, if we're here,
-         * that's long since done.
-         */
-        int dstData = dstPos + headerSize + writeCipher.getExplicitNonceSize();
-        dstBB.position(dstData);
-
-        /*
-         * transfer application data into the network data buffer
-         */
-        ea.gather(length);
-        dstBB.limit(dstBB.position());
-        dstBB.position(dstData);
-
-        /*
-         * "flip" but skip over header again, add MAC & encrypt
-         */
-        if (authenticator instanceof MAC) {
-            MAC signer = (MAC)authenticator;
-            if (signer.MAClen() != 0) {
-                byte[] hash = signer.compute(contentType(), dstBB, false);
-
-                /*
-                 * position was advanced to limit in compute above.
-                 *
-                 * Mark next area as writable (above layers should have
-                 * established that we have plenty of room), then write
-                 * out the hash.
-                 */
-                dstBB.limit(dstBB.limit() + hash.length);
-                dstBB.put(hash);
-
-                // reset the position and limit
-                dstBB.limit(dstBB.position());
-                dstBB.position(dstData);
-            }
-        }
-
-        if (!writeCipher.isNullCipher()) {
-            /*
-             * Requires explicit IV/nonce for CBC/AEAD cipher suites for TLS 1.1
-             * or later.
-             */
-            if (protocolVersion.v >= ProtocolVersion.TLS11.v &&
-                    (writeCipher.isCBCMode() || writeCipher.isAEADMode())) {
-                byte[] nonce = writeCipher.createExplicitNonce(
-                        authenticator, contentType(), dstBB.remaining());
-                dstBB.position(dstPos + headerSize);
-                dstBB.put(nonce);
-                if (!writeCipher.isAEADMode()) {
-                    // The explicit IV in TLS 1.1 and later can be encrypted.
-                    dstBB.position(dstPos + headerSize);
-                }   // Otherwise, DON'T encrypt the nonce_explicit for AEAD mode
-            }
-
-            /*
-             * Encrypt may pad, so again the limit may have changed.
-             */
-            writeCipher.encrypt(dstBB, dstLim);
-
-            if ((debug != null) && (Debug.isOn("record") ||
-                    (Debug.isOn("handshake") &&
-                        (contentType() == ct_change_cipher_spec)))) {
-                System.out.println(Thread.currentThread().getName()
-                    // v3.0/v3.1 ...
-                    + ", WRITE: " + protocolVersion
-                    + " " + InputRecord.contentName(contentType())
-                    + ", length = " + length);
-            }
-        } else {
-            dstBB.position(dstBB.limit());
-        }
-
-        int packetLength = dstBB.limit() - dstPos - headerSize;
-
-        /*
-         * Finish out the record header.
-         */
-        dstBB.put(dstPos, contentType());
-        dstBB.put(dstPos + 1, protocolVersion.major);
-        dstBB.put(dstPos + 2, protocolVersion.minor);
-        dstBB.put(dstPos + 3, (byte)(packetLength >> 8));
-        dstBB.put(dstPos + 4, (byte)packetLength);
-
-        /*
-         * Position was already set by encrypt() above.
-         */
-        dstBB.limit(dstLim);
-    }
-}
--- a/jdk/src/java.base/share/classes/sun/security/ssl/EngineWriter.java	Mon Jun 01 10:15:21 2015 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,244 +0,0 @@
-/*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package sun.security.ssl;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.util.LinkedList;
-import javax.net.ssl.SSLEngineResult.HandshakeStatus;
-import sun.misc.HexDumpEncoder;
-
-/**
- * A class to help abstract away SSLEngine writing synchronization.
- */
-final class EngineWriter {
-
-    /*
-     * Outgoing handshake Data waiting for a ride is stored here.
-     * Normal application data is written directly into the outbound
-     * buffer, but handshake data can be written out at any time,
-     * so we have buffer it somewhere.
-     *
-     * When wrap is called, we first check to see if there is
-     * any data waiting, then if we're in a data transfer state,
-     * we try to write app data.
-     *
-     * This will contain either ByteBuffers, or the marker
-     * HandshakeStatus.FINISHED to signify that a handshake just completed.
-     */
-    private LinkedList<Object> outboundList;
-
-    private boolean outboundClosed = false;
-
-    /* Class and subclass dynamic debugging support */
-    private static final Debug debug = Debug.getInstance("ssl");
-
-    EngineWriter() {
-        outboundList = new LinkedList<Object>();
-    }
-
-    /*
-     * Upper levels assured us we had room for at least one packet of data.
-     * As per the SSLEngine spec, we only return one SSL packets worth of
-     * data.
-     */
-    private HandshakeStatus getOutboundData(ByteBuffer dstBB) {
-
-        Object msg = outboundList.removeFirst();
-        assert(msg instanceof ByteBuffer);
-
-        ByteBuffer bbIn = (ByteBuffer) msg;
-        assert(dstBB.remaining() >= bbIn.remaining());
-
-        dstBB.put(bbIn);
-
-        /*
-         * If we have more data in the queue, it's either
-         * a finished message, or an indication that we need
-         * to call wrap again.
-         */
-        if (hasOutboundDataInternal()) {
-            msg = outboundList.getFirst();
-            if (msg == HandshakeStatus.FINISHED) {
-                outboundList.removeFirst();     // consume the message
-                return HandshakeStatus.FINISHED;
-            } else {
-                return HandshakeStatus.NEED_WRAP;
-            }
-        } else {
-            return null;
-        }
-    }
-
-    /*
-     * Properly orders the output of the data written to the wrap call.
-     * This is only handshake data, application data goes through the
-     * other writeRecord.
-     */
-    synchronized void writeRecord(EngineOutputRecord outputRecord,
-            Authenticator authenticator,
-            CipherBox writeCipher) throws IOException {
-
-        /*
-         * Only output if we're still open.
-         */
-        if (outboundClosed) {
-            throw new IOException("writer side was already closed.");
-        }
-
-        outputRecord.write(authenticator, writeCipher);
-
-        /*
-         * Did our handshakers notify that we just sent the
-         * Finished message?
-         *
-         * Add an "I'm finished" message to the queue.
-         */
-        if (outputRecord.isFinishedMsg()) {
-            outboundList.addLast(HandshakeStatus.FINISHED);
-        }
-    }
-
-    /*
-     * Output the packet info.
-     */
-    private void dumpPacket(EngineArgs ea, boolean hsData) {
-        try {
-            HexDumpEncoder hd = new HexDumpEncoder();
-
-            ByteBuffer bb = ea.netData.duplicate();
-
-            int pos = bb.position();
-            bb.position(pos - ea.deltaNet());
-            bb.limit(pos);
-
-            System.out.println("[Raw write" +
-                (hsData ? "" : " (bb)") + "]: length = " +
-                bb.remaining());
-            hd.encodeBuffer(bb, System.out);
-        } catch (IOException e) { }
-    }
-
-    /*
-     * Properly orders the output of the data written to the wrap call.
-     * Only app data goes through here, handshake data goes through
-     * the other writeRecord.
-     *
-     * Shouldn't expect to have an IOException here.
-     *
-     * Return any determined status.
-     */
-    synchronized HandshakeStatus writeRecord(
-            EngineOutputRecord outputRecord, EngineArgs ea,
-            Authenticator authenticator,
-            CipherBox writeCipher) throws IOException {
-
-        /*
-         * If we have data ready to go, output this first before
-         * trying to consume app data.
-         */
-        if (hasOutboundDataInternal()) {
-            HandshakeStatus hss = getOutboundData(ea.netData);
-
-            if (debug != null && Debug.isOn("packet")) {
-                /*
-                 * We could have put the dump in
-                 * OutputRecord.write(OutputStream), but let's actually
-                 * output when it's actually output by the SSLEngine.
-                 */
-                dumpPacket(ea, true);
-            }
-
-            return hss;
-        }
-
-        /*
-         * If we are closed, no more app data can be output.
-         * Only existing handshake data (above) can be obtained.
-         */
-        if (outboundClosed) {
-            throw new IOException("The write side was already closed");
-        }
-
-        outputRecord.write(ea, authenticator, writeCipher);
-
-        if (debug != null && Debug.isOn("packet")) {
-            dumpPacket(ea, false);
-        }
-
-        /*
-         * No way new outbound handshake data got here if we're
-         * locked properly.
-         *
-         * We don't have any status we can return.
-         */
-        return null;
-    }
-
-    /*
-     * We already hold "this" lock, this is the callback from the
-     * outputRecord.write() above.  We already know this
-     * writer can accept more data (outboundClosed == false),
-     * and the closure is sync'd.
-     */
-    void putOutboundData(ByteBuffer bytes) {
-        outboundList.addLast(bytes);
-    }
-
-    /*
-     * This is for the really rare case that someone is writing from
-     * the *InputRecord* before we know what to do with it.
-     */
-    synchronized void putOutboundDataSync(ByteBuffer bytes)
-            throws IOException {
-
-        if (outboundClosed) {
-            throw new IOException("Write side already closed");
-        }
-
-        outboundList.addLast(bytes);
-    }
-
-    /*
-     * Non-synch'd version of this method, called by internals
-     */
-    private boolean hasOutboundDataInternal() {
-        return (outboundList.size() != 0);
-    }
-
-    synchronized boolean hasOutboundData() {
-        return hasOutboundDataInternal();
-    }
-
-    synchronized boolean isOutboundDone() {
-        return outboundClosed && !hasOutboundDataInternal();
-    }
-
-    synchronized void closeOutbound() {
-        outboundClosed = true;
-    }
-
-}
--- a/jdk/src/java.base/share/classes/sun/security/ssl/HandshakeHash.java	Mon Jun 01 10:15:21 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/HandshakeHash.java	Tue Jun 02 09:15:47 2015 -0700
@@ -29,6 +29,7 @@
 import java.io.ByteArrayOutputStream;
 import java.security.*;
 import java.util.Locale;
+import java.nio.ByteBuffer;
 
 /**
  * Abstraction for the SSL/TLS hash of all handshake messages that is
@@ -99,6 +100,9 @@
     // For TLS 1.2
     private MessageDigest finMD;
 
+    // Cache for input record handshake hash computation
+    private ByteArrayOutputStream reserve = new ByteArrayOutputStream();
+
     /**
      * Create a new HandshakeHash. needCertificateVerify indicates whether
      * a hash for the certificate verify message is required.
@@ -107,7 +111,106 @@
         clonesNeeded = needCertificateVerify ? 3 : 2;
     }
 
+    void reserve(ByteBuffer input) {
+        if (input.hasArray()) {
+            reserve.write(input.array(),
+                    input.position() + input.arrayOffset(), input.remaining());
+        } else {
+            int inPos = input.position();
+            byte[] holder = new byte[input.remaining()];
+            input.get(holder);
+            input.position(inPos);
+            reserve.write(holder, 0, holder.length);
+        }
+    }
+
+    void reserve(byte[] b, int offset, int len) {
+        reserve.write(b, offset, len);
+    }
+
+    void reload() {
+        if (reserve.size() != 0) {
+            byte[] bytes = reserve.toByteArray();
+            reserve.reset();
+            update(bytes, 0, bytes.length);
+        }
+    }
+
+    void update(ByteBuffer input) {
+
+        // reload if there are reserved messages.
+        reload();
+
+        int inPos = input.position();
+        switch (version) {
+            case 1:
+                md5.update(input);
+                input.position(inPos);
+
+                sha.update(input);
+                input.position(inPos);
+
+                break;
+            default:
+                if (finMD != null) {
+                    finMD.update(input);
+                    input.position(inPos);
+                }
+                if (input.hasArray()) {
+                    data.write(input.array(),
+                            inPos + input.arrayOffset(), input.remaining());
+                } else {
+                    byte[] holder = new byte[input.remaining()];
+                    input.get(holder);
+                    input.position(inPos);
+                    data.write(holder, 0, holder.length);
+                }
+                break;
+        }
+    }
+
+    void update(byte handshakeType, byte[] handshakeBody) {
+
+        // reload if there are reserved messages.
+        reload();
+
+        switch (version) {
+            case 1:
+                md5.update(handshakeType);
+                sha.update(handshakeType);
+
+                md5.update((byte)((handshakeBody.length >> 16) & 0xFF));
+                sha.update((byte)((handshakeBody.length >> 16) & 0xFF));
+                md5.update((byte)((handshakeBody.length >> 8) & 0xFF));
+                sha.update((byte)((handshakeBody.length >> 8) & 0xFF));
+                md5.update((byte)(handshakeBody.length & 0xFF));
+                sha.update((byte)(handshakeBody.length & 0xFF));
+
+                md5.update(handshakeBody);
+                sha.update(handshakeBody);
+                break;
+            default:
+                if (finMD != null) {
+                    finMD.update(handshakeType);
+                    finMD.update((byte)((handshakeBody.length >> 16) & 0xFF));
+                    finMD.update((byte)((handshakeBody.length >> 8) & 0xFF));
+                    finMD.update((byte)(handshakeBody.length & 0xFF));
+                    finMD.update(handshakeBody);
+                }
+                data.write(handshakeType);
+                data.write((byte)((handshakeBody.length >> 16) & 0xFF));
+                data.write((byte)((handshakeBody.length >> 8) & 0xFF));
+                data.write((byte)(handshakeBody.length & 0xFF));
+                data.write(handshakeBody, 0, handshakeBody.length);
+                break;
+        }
+    }
+
     void update(byte[] b, int offset, int len) {
+
+        // reload if there are reserved messages.
+        reload();
+
         switch (version) {
             case 1:
                 md5.update(b, offset, len);
@@ -139,9 +242,15 @@
     void protocolDetermined(ProtocolVersion pv) {
 
         // Do not set again, will ignore
-        if (version != -1) return;
+        if (version != -1) {
+            return;
+        }
 
-        version = pv.compareTo(ProtocolVersion.TLS12) >= 0 ? 2 : 1;
+        if (pv.maybeDTLSProtocol()) {
+            version = pv.compareTo(ProtocolVersion.DTLS12) >= 0 ? 2 : 1;
+        } else {
+            version = pv.compareTo(ProtocolVersion.TLS12) >= 0 ? 2 : 1;
+        }
         switch (version) {
             case 1:
                 // initiate md5, sha and call update on saved array
--- a/jdk/src/java.base/share/classes/sun/security/ssl/HandshakeInStream.java	Mon Jun 01 10:15:21 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/HandshakeInStream.java	Tue Jun 02 09:15:47 2015 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -23,11 +23,11 @@
  * questions.
  */
 
-
 package sun.security.ssl;
 
-import java.io.InputStream;
+import java.io.ByteArrayInputStream;
 import java.io.IOException;
+import java.nio.ByteBuffer;
 
 import javax.net.ssl.SSLException;
 
@@ -38,154 +38,104 @@
  * Once a new handshake record arrives, it is buffered in this class until
  * processed by the Handshaker. The buffer may also contain incomplete
  * handshake messages in case the message is split across multiple records.
- * Handshaker.process_record deals with all that. It may also contain
+ * Handshaker.processRecord deals with all that. It may also contain
  * handshake messages larger than the default buffer size (e.g. large
- * certificate messages). The buffer is grown dynamically to handle that
- * (see InputRecord.queueHandshake()).
+ * certificate messages). The buffer is grown dynamically to handle that.
  *
- * Note that the InputRecord used as a buffer here is separate from the
- * AppInStream.r, which is where data from the socket is initially read
- * into. This is because once the initial handshake has been completed,
- * handshake and application data messages may be interleaved arbitrarily
- * and must be processed independently.
+ * Note that this class only handles Handshake messages in TLS format.
+ * DTLS Handshake messages should be converted into TLS format before
+ * calling into this method.
  *
  * @author David Brownell
  */
-public class HandshakeInStream extends InputStream {
 
-    InputRecord r;
+// This class is used to handle plain text handshake messages.
+//
+public final class HandshakeInStream extends ByteArrayInputStream {
 
     /*
      * Construct the stream; we'll be accumulating hashes of the
      * input records using two sets of digests.
      */
-    HandshakeInStream(HandshakeHash handshakeHash) {
-        r = new InputRecord();
-        r.setHandshakeHash(handshakeHash);
-    }
-
-
-    // overridden InputStream methods
-
-    /*
-     * Return the number of bytes available for read().
-     *
-     * Note that this returns the bytes remaining in the buffer, not
-     * the bytes remaining in the current handshake message.
-     */
-    @Override
-    public int available() {
-        return r.available();
-    }
-
-    /*
-     * Get a byte of handshake data.
-     */
-    @Override
-    public int read() throws IOException {
-        int n = r.read();
-        if (n == -1) {
-            throw new SSLException("Unexpected end of handshake data");
-        }
-        return n;
+    HandshakeInStream() {
+        super(new byte[0]);     // lazy to alloacte the internal buffer
     }
 
-    /*
-     * Get a bunch of bytes of handshake data.
-     */
+    //
+    // overridden ByteArrayInputStream methods
+    //
+
     @Override
-    public int read(byte b [], int off, int len) throws IOException {
-        // we read from a ByteArrayInputStream, it always returns the
-        // data in a single read if enough is available
-        int n = r.read(b, off, len);
-        if (n != len) {
+    public int read(byte[] b) throws IOException {
+        if (super.read(b) != b.length) {
             throw new SSLException("Unexpected end of handshake data");
         }
-        return n;
-    }
 
-    /*
-     * Skip some handshake data.
-     */
-    @Override
-    public long skip(long n) throws IOException {
-        return r.skip(n);
+        return b.length;
     }
 
-    /*
-     * Mark/ reset code, implemented using InputRecord mark/ reset.
-     *
-     * Note that it currently provides only a limited mark functionality
-     * and should be used with care (once a new handshake record has been
-     * read, data that has already been consumed is lost even if marked).
-     */
-
-    @Override
-    public void mark(int readlimit) {
-        r.mark(readlimit);
-    }
-
-    @Override
-    public void reset() throws IOException {
-        r.reset();
-    }
-
-    @Override
-    public boolean markSupported() {
-        return true;
-    }
-
-
-    // handshake management functions
+    //
+    // handshake input stream management functions
+    //
 
     /*
      * Here's an incoming record with handshake data.  Queue the contents;
      * it might be one or more entire messages, complete a message that's
      * partly queued, or both.
      */
-    void incomingRecord(InputRecord in) throws IOException {
-        r.queueHandshake(in);
+    void incomingRecord(ByteBuffer in) throws IOException {
+        int len;
+
+        // Move any unread data to the front of the buffer.
+        if (pos != 0) {
+            len = count - pos;
+            if (len != 0) {
+                System.arraycopy(buf, pos, buf, 0, len);
+            }
+            pos = 0;
+            count = len;
+        }
+
+        // Grow buffer if needed.
+        len = in.remaining() + count;
+        if (buf.length < len) {
+            byte[] newbuf = new byte[len];
+            if (count != 0) {
+                System.arraycopy(buf, 0, newbuf, 0, count);
+            }
+            buf = newbuf;
+        }
+
+        // Append the incoming record to the buffer
+        in.get(buf, count, in.remaining());
+        count = len;
     }
 
-    /*
-     * Hash any data we've consumed but not yet hashed.  Useful mostly
-     * for processing client certificate messages (so we can check the
-     * immediately following cert verify message) and finished messages
-     * (so we can compute our own finished message).
-     */
-    void digestNow() {
-        r.doHashes();
-    }
-
-    /*
-     * Do more than skip that handshake data ... totally ignore it.
-     * The difference is that the data does not get hashed.
-     */
-    void ignore(int n) {
-        r.ignore(n);
-    }
-
-
+    //
     // Message parsing methods
+    //
 
     /*
      * Read 8, 16, 24, and 32 bit SSL integer data types, encoded
      * in standard big-endian form.
      */
-
     int getInt8() throws IOException {
+        verifyLength(1);
         return read();
     }
 
     int getInt16() throws IOException {
+        verifyLength(2);
         return (getInt8() << 8) | getInt8();
     }
 
     int getInt24() throws IOException {
+        verifyLength(3);
         return (getInt8() << 16) | (getInt8() << 8) | getInt8();
     }
 
     int getInt32() throws IOException {
+        verifyLength(4);
         return (getInt8() << 24) | (getInt8() << 16)
              | (getInt8() << 8) | getInt8();
     }
@@ -193,13 +143,12 @@
     /*
      * Read byte vectors with 8, 16, and 24 bit length encodings.
      */
-
     byte[] getBytes8() throws IOException {
         int len = getInt8();
         verifyLength(len);
         byte b[] = new byte[len];
 
-        read(b, 0, len);
+        read(b);
         return b;
     }
 
@@ -208,7 +157,7 @@
         verifyLength(len);
         byte b[] = new byte[len];
 
-        read(b, 0, len);
+        read(b);
         return b;
     }
 
@@ -217,16 +166,14 @@
         verifyLength(len);
         byte b[] = new byte[len];
 
-        read(b, 0, len);
+        read(b);
         return b;
     }
 
     // Is a length greater than available bytes in the record?
     private void verifyLength(int len) throws SSLException {
         if (len > available()) {
-            throw new SSLException(
-                        "Not enough data to fill declared vector size");
+            throw new SSLException("Unexpected end of handshake data");
         }
     }
-
 }
--- a/jdk/src/java.base/share/classes/sun/security/ssl/HandshakeMessage.java	Mon Jun 01 10:15:21 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/HandshakeMessage.java	Tue Jun 02 09:15:47 2015 -0700
@@ -73,24 +73,43 @@
  */
 public abstract class HandshakeMessage {
 
-    HandshakeMessage() { }
+    /* Class and subclass dynamic debugging support */
+    public static final Debug debug = Debug.getInstance("ssl");
 
     // enum HandshakeType:
-    static final byte   ht_hello_request = 0;
-    static final byte   ht_client_hello = 1;
-    static final byte   ht_server_hello = 2;
+    static final byte   ht_hello_request          = 0;      // RFC 5246
+    static final byte   ht_client_hello           = 1;      // RFC 5246
+    static final byte   ht_server_hello           = 2;      // RFC 5246
+    static final byte   ht_hello_verify_request   = 3;      // RFC 6347
+    static final byte   ht_new_session_ticket     = 4;      // RFC 4507
+
+    static final byte   ht_certificate            = 11;     // RFC 5246
+    static final byte   ht_server_key_exchange    = 12;     // RFC 5246
+    static final byte   ht_certificate_request    = 13;     // RFC 5246
+    static final byte   ht_server_hello_done      = 14;     // RFC 5246
+    static final byte   ht_certificate_verify     = 15;     // RFC 5246
+    static final byte   ht_client_key_exchange    = 16;     // RFC 5246
 
-    static final byte   ht_certificate = 11;
-    static final byte   ht_server_key_exchange = 12;
-    static final byte   ht_certificate_request = 13;
-    static final byte   ht_server_hello_done = 14;
-    static final byte   ht_certificate_verify = 15;
-    static final byte   ht_client_key_exchange = 16;
+    static final byte   ht_finished               = 20;     // RFC 5246
+    static final byte   ht_certificate_url        = 21;     // RFC 6066
+    static final byte   ht_certificate_status     = 22;     // RFC 6066
+    static final byte   ht_supplemental_data      = 23;     // RFC 4680
+
+    static final byte   ht_not_applicable         = -1;     // N/A
 
-    static final byte   ht_finished = 20;
+    /*
+     * SSL 3.0 MAC padding constants.
+     * Also used by CertificateVerify and Finished during the handshake.
+     */
+    static final byte[] MD5_pad1 = genPad(0x36, 48);
+    static final byte[] MD5_pad2 = genPad(0x5c, 48);
 
-    /* Class and subclass dynamic debugging support */
-    public static final Debug debug = Debug.getInstance("ssl");
+    static final byte[] SHA_pad1 = genPad(0x36, 40);
+    static final byte[] SHA_pad2 = genPad(0x5c, 40);
+
+    // default constructor
+    HandshakeMessage() {
+    }
 
     /**
      * Utility method to convert a BigInteger to a byte array in unsigned
@@ -109,16 +128,6 @@
         return b;
     }
 
-    /*
-     * SSL 3.0 MAC padding constants.
-     * Also used by CertificateVerify and Finished during the handshake.
-     */
-    static final byte[] MD5_pad1 = genPad(0x36, 48);
-    static final byte[] MD5_pad2 = genPad(0x5c, 48);
-
-    static final byte[] SHA_pad1 = genPad(0x36, 40);
-    static final byte[] SHA_pad2 = genPad(0x5c, 40);
-
     private static byte[] genPad(int b, int count) {
         byte[] padding = new byte[count];
         Arrays.fill(padding, (byte)b);
@@ -141,6 +150,7 @@
         s.write(messageType());
         s.putInt24(len);
         send(s);
+        s.complete();
     }
 
     /*
@@ -199,6 +209,69 @@
 
 }
 
+/*
+ * HelloVerifyRequest ... SERVER --> CLIENT  [DTLS only]
+ *
+ * The definition of HelloVerifyRequest is as follows:
+ *
+ *     struct {
+ *       ProtocolVersion server_version;
+ *       opaque cookie<0..2^8-1>;
+ *     } HelloVerifyRequest;
+ *
+ * For DTLS protocols, once the client has transmitted the ClientHello message,
+ * it expects to see a HelloVerifyRequest from the server.  However, if the
+ * server's message is lost, the client knows that either the ClientHello or
+ * the HelloVerifyRequest has been lost and retransmits. [RFC 6347]
+ */
+static final class HelloVerifyRequest extends HandshakeMessage {
+    ProtocolVersion     protocolVersion;
+    byte[]              cookie;         // 1 to 2^8 - 1 bytes
+
+    HelloVerifyRequest(HelloCookieManager helloCookieManager,
+            ClientHello clientHelloMsg) {
+
+        this.protocolVersion = clientHelloMsg.protocolVersion;
+        this.cookie = helloCookieManager.getCookie(clientHelloMsg);
+    }
+
+    HelloVerifyRequest(
+            HandshakeInStream input, int messageLength) throws IOException {
+
+        this.protocolVersion =
+                ProtocolVersion.valueOf(input.getInt8(), input.getInt8());
+        this.cookie = input.getBytes8();
+
+        // Is it a valid cookie?
+        HelloCookieManager.checkCookie(protocolVersion, cookie);
+    }
+
+    @Override
+    int messageType() {
+        return ht_hello_verify_request;
+    }
+
+    @Override
+    int messageLength() {
+        return 2 + cookie.length;       // 2: the length of protocolVersion
+    }
+
+    @Override
+    void send(HandshakeOutStream hos) throws IOException {
+        hos.putInt8(protocolVersion.major);
+        hos.putInt8(protocolVersion.minor);
+        hos.putBytes8(cookie);
+    }
+
+    @Override
+    void print(PrintStream out) throws IOException {
+        out.println("*** HelloVerifyRequest");
+        if (debug != null && Debug.isOn("verbose")) {
+            out.println("server_version: " + protocolVersion);
+            Debug.println(out, "cookie", cookie);
+        }
+    }
+}
 
 /*
  * ClientHello ... CLIENT --> SERVER
@@ -213,22 +286,31 @@
  */
 static final class ClientHello extends HandshakeMessage {
 
-    ProtocolVersion     protocolVersion;
-    RandomCookie        clnt_random;
-    SessionId           sessionId;
-    private CipherSuiteList    cipherSuites;
-    byte[]              compression_methods;
+    ProtocolVersion             protocolVersion;
+    RandomCookie                clnt_random;
+    SessionId                   sessionId;
+    byte[]                      cookie;                     // DTLS only
+    private CipherSuiteList     cipherSuites;
+    private final boolean       isDTLS;
+    byte[]                      compression_methods;
 
     HelloExtensions extensions = new HelloExtensions();
 
     private final static byte[]  NULL_COMPRESSION = new byte[] {0};
 
     ClientHello(SecureRandom generator, ProtocolVersion protocolVersion,
-            SessionId sessionId, CipherSuiteList cipherSuites) {
+            SessionId sessionId, CipherSuiteList cipherSuites,
+            boolean isDTLS) {
 
+        this.isDTLS = isDTLS;
         this.protocolVersion = protocolVersion;
         this.sessionId = sessionId;
         this.cipherSuites = cipherSuites;
+        if (isDTLS) {
+            this.cookie = new byte[0];
+        } else {
+            this.cookie = null;
+        }
 
         if (cipherSuites.containsEC()) {
             extensions.add(SupportedEllipticCurvesExtension.DEFAULT);
@@ -239,11 +321,21 @@
         compression_methods = NULL_COMPRESSION;
     }
 
-    ClientHello(HandshakeInStream s, int messageLength) throws IOException {
+    ClientHello(HandshakeInStream s,
+            int messageLength, boolean isDTLS) throws IOException {
+
+        this.isDTLS = isDTLS;
+
         protocolVersion = ProtocolVersion.valueOf(s.getInt8(), s.getInt8());
         clnt_random = new RandomCookie(s);
         sessionId = new SessionId(s.getBytes8());
         sessionId.checkLength(protocolVersion);
+        if (isDTLS) {
+            cookie = s.getBytes8();
+        } else {
+            cookie = null;
+        }
+
         cipherSuites = new CipherSuiteList(s);
         compression_methods = s.getBytes8();
         if (messageLength() != messageLength) {
@@ -279,6 +371,28 @@
         extensions.add(signatureAlgorithm);
     }
 
+    void addMFLExtension(int maximumPacketSize) {
+        HelloExtension maxFragmentLength =
+                new MaxFragmentLengthExtension(maximumPacketSize);
+        extensions.add(maxFragmentLength);
+    }
+
+    void updateHelloCookie(MessageDigest cookieDigest) {
+        //
+        // Just use HandshakeOutStream to compute the hello verify cookie.
+        // Not actually used to output handshake message records.
+        //
+        HandshakeOutStream hos = new HandshakeOutStream(null);
+
+        try {
+            send(hos, false);    // Do not count hello verify cookie.
+        } catch (IOException ioe) {
+            // unlikely to happen
+        }
+
+        cookieDigest.update(hos.toByteArray());
+    }
+
     @Override
     int messageType() { return ht_client_hello; }
 
@@ -290,6 +404,7 @@
          */
         return (2 + 32 + 1 + 2 + 1
             + sessionId.length()                /* ... + variable parts */
+            + (isDTLS ? (1 + cookie.length) : 0)
             + (cipherSuites.size() * 2)
             + compression_methods.length)
             + extensions.length();
@@ -297,13 +412,7 @@
 
     @Override
     void send(HandshakeOutStream s) throws IOException {
-        s.putInt8(protocolVersion.major);
-        s.putInt8(protocolVersion.minor);
-        clnt_random.send(s);
-        s.putBytes8(sessionId.getId());
-        cipherSuites.send(s);
-        s.putBytes8(compression_methods);
-        extensions.send(s);
+        send(s, true);  // Count hello verify cookie.
     }
 
     @Override
@@ -317,6 +426,10 @@
             s.print("Session ID:  ");
             s.println(sessionId);
 
+            if (isDTLS) {
+                Debug.println(s, "cookie", cookie);
+            }
+
             s.println("Cipher Suites: " + cipherSuites);
 
             Debug.println(s, "Compression Methods", compression_methods);
@@ -324,6 +437,21 @@
             s.println("***");
         }
     }
+
+    private void send(HandshakeOutStream s,
+            boolean computeCookie) throws IOException {
+        s.putInt8(protocolVersion.major);
+        s.putInt8(protocolVersion.minor);
+        clnt_random.send(s);
+        s.putBytes8(sessionId.getId());
+        if (isDTLS && computeCookie) {
+            s.putBytes8(cookie);
+        }
+        cipherSuites.send(s);
+        s.putBytes8(compression_methods);
+        extensions.send(s);
+    }
+
 }
 
 /*
@@ -740,7 +868,7 @@
         setValues(obj);
 
         Signature sig;
-        if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+        if (protocolVersion.useTLS12PlusSpec()) {
             this.preferableSignatureAlgorithm = signAlgorithm;
             sig = JsseJce.getSignature(signAlgorithm.getAlgorithmName());
         } else {
@@ -801,7 +929,7 @@
                                              new BigInteger(1, dh_g)));
 
         // read the signature and hash algorithm
-        if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+        if (protocolVersion.useTLS12PlusSpec()) {
             int hash = input.getInt8();         // hash algorithm
             int signature = input.getInt8();    // signature algorithm
 
@@ -834,7 +962,7 @@
 
         Signature sig;
         String algorithm = publicKey.getAlgorithm();
-        if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+        if (protocolVersion.useTLS12PlusSpec()) {
             sig = JsseJce.getSignature(
                         preferableSignatureAlgorithm.getAlgorithmName());
         } else {
@@ -914,7 +1042,7 @@
         temp += dh_Ys.length;
 
         if (signature != null) {
-            if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+            if (protocolVersion.useTLS12PlusSpec()) {
                 temp += SignatureAndHashAlgorithm.sizeInRecord();
             }
 
@@ -934,7 +1062,7 @@
         s.putBytes16(dh_Ys);
 
         if (signature != null) {
-            if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+            if (protocolVersion.useTLS12PlusSpec()) {
                 s.putInt8(preferableSignatureAlgorithm.getHashValue());
                 s.putInt8(preferableSignatureAlgorithm.getSignatureValue());
             }
@@ -959,7 +1087,7 @@
             if (signature == null) {
                 s.println("Anonymous");
             } else {
-                if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+                if (protocolVersion.useTLS12PlusSpec()) {
                     s.println("Signature Algorithm " +
                         preferableSignatureAlgorithm.getAlgorithmName());
                 }
@@ -1021,7 +1149,7 @@
         }
 
         Signature sig;
-        if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+        if (protocolVersion.useTLS12PlusSpec()) {
             this.preferableSignatureAlgorithm = signAlgorithm;
             sig = JsseJce.getSignature(signAlgorithm.getAlgorithmName());
         } else {
@@ -1084,7 +1212,7 @@
         }
 
         // read the signature and hash algorithm
-        if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+        if (protocolVersion.useTLS12PlusSpec()) {
             int hash = input.getInt8();         // hash algorithm
             int signature = input.getInt8();    // signature algorithm
 
@@ -1105,7 +1233,7 @@
 
         // verify the signature
         Signature sig;
-        if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+        if (protocolVersion.useTLS12PlusSpec()) {
             sig = JsseJce.getSignature(
                         preferableSignatureAlgorithm.getAlgorithmName());
         } else {
@@ -1157,7 +1285,7 @@
         int sigLen = 0;
         if (signatureBytes != null) {
             sigLen = 2 + signatureBytes.length;
-            if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+            if (protocolVersion.useTLS12PlusSpec()) {
                 sigLen += SignatureAndHashAlgorithm.sizeInRecord();
             }
         }
@@ -1172,7 +1300,7 @@
         s.putBytes8(pointBytes);
 
         if (signatureBytes != null) {
-            if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+            if (protocolVersion.useTLS12PlusSpec()) {
                 s.putInt8(preferableSignatureAlgorithm.getHashValue());
                 s.putInt8(preferableSignatureAlgorithm.getSignatureValue());
             }
@@ -1189,7 +1317,7 @@
             if (signatureBytes == null) {
                 s.println("Anonymous");
             } else {
-                if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+                if (protocolVersion.useTLS12PlusSpec()) {
                     s.println("Signature Algorithm " +
                             preferableSignatureAlgorithm.getAlgorithmName());
                 }
@@ -1315,7 +1443,7 @@
         this.types = JsseJce.isEcAvailable() ? TYPES_ECC : TYPES_NO_ECC;
 
         // Use supported_signature_algorithms for TLS 1.2 or later.
-        if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+        if (protocolVersion.useTLS12PlusSpec()) {
             if (signAlgs == null || signAlgs.isEmpty()) {
                 throw new SSLProtocolException(
                         "No supported signature algorithms");
@@ -1339,7 +1467,7 @@
         types = input.getBytes8();
 
         // Read the supported_signature_algorithms for TLS 1.2 or later.
-        if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+        if (protocolVersion.useTLS12PlusSpec()) {
             algorithmsLen = input.getInt16();
             if (algorithmsLen < 2) {
                 throw new SSLProtocolException(
@@ -1406,7 +1534,7 @@
     int messageLength() {
         int len = 1 + types.length + 2;
 
-        if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+        if (protocolVersion.useTLS12PlusSpec()) {
             len += algorithmsLen + 2;
         }
 
@@ -1423,7 +1551,7 @@
         output.putBytes8(types);
 
         // put supported_signature_algorithms
-        if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+        if (protocolVersion.useTLS12PlusSpec()) {
             output.putInt16(algorithmsLen);
             for (SignatureAndHashAlgorithm algorithm : algorithms) {
                 output.putInt8(algorithm.getHashValue());      // hash
@@ -1478,7 +1606,7 @@
             }
             s.println();
 
-            if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+            if (protocolVersion.useTLS12PlusSpec()) {
                 StringBuilder sb = new StringBuilder();
                 boolean opened = false;
                 for (SignatureAndHashAlgorithm signAlg : algorithms) {
@@ -1576,7 +1704,7 @@
 
         String algorithm = privateKey.getAlgorithm();
         Signature sig = null;
-        if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+        if (protocolVersion.useTLS12PlusSpec()) {
             this.preferableSignatureAlgorithm = signAlgorithm;
             sig = JsseJce.getSignature(signAlgorithm.getAlgorithmName());
         } else {
@@ -1598,7 +1726,7 @@
         this.protocolVersion = protocolVersion;
 
         // read the signature and hash algorithm
-        if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+        if (protocolVersion.useTLS12PlusSpec()) {
             int hashAlg = input.getInt8();         // hash algorithm
             int signAlg = input.getInt8();         // signature algorithm
 
@@ -1634,7 +1762,7 @@
             SecretKey masterSecret) throws GeneralSecurityException {
         String algorithm = publicKey.getAlgorithm();
         Signature sig = null;
-        if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+        if (protocolVersion.useTLS12PlusSpec()) {
             sig = JsseJce.getSignature(
                         preferableSignatureAlgorithm.getAlgorithmName());
         } else {
@@ -1676,11 +1804,11 @@
             throws SignatureException {
 
         if (algorithm.equals("RSA")) {
-            if (protocolVersion.v < ProtocolVersion.TLS12.v) { // TLS1.1-
+            if (!protocolVersion.useTLS12PlusSpec()) {  // TLS1.1-
                 MessageDigest md5Clone = handshakeHash.getMD5Clone();
                 MessageDigest shaClone = handshakeHash.getSHAClone();
 
-                if (protocolVersion.v < ProtocolVersion.TLS10.v) { // SSLv3
+                if (!protocolVersion.useTLS10PlusSpec()) {  // SSLv3
                     updateDigest(md5Clone, MD5_pad1, MD5_pad2, masterKey);
                     updateDigest(shaClone, SHA_pad1, SHA_pad2, masterKey);
                 }
@@ -1692,10 +1820,10 @@
                 sig.update(handshakeHash.getAllHandshakeMessages());
             }
         } else { // DSA, ECDSA
-            if (protocolVersion.v < ProtocolVersion.TLS12.v) { // TLS1.1-
+            if (!protocolVersion.useTLS12PlusSpec()) {  // TLS1.1-
                 MessageDigest shaClone = handshakeHash.getSHAClone();
 
-                if (protocolVersion.v < ProtocolVersion.TLS10.v) { // SSLv3
+                if (!protocolVersion.useTLS10PlusSpec()) {  // SSLv3
                     updateDigest(shaClone, SHA_pad1, SHA_pad2, masterKey);
                 }
 
@@ -1811,7 +1939,7 @@
     int messageLength() {
         int temp = 2;
 
-        if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+        if (protocolVersion.useTLS12PlusSpec()) {
             temp += SignatureAndHashAlgorithm.sizeInRecord();
         }
 
@@ -1820,7 +1948,7 @@
 
     @Override
     void send(HandshakeOutStream s) throws IOException {
-        if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+        if (protocolVersion.useTLS12PlusSpec()) {
             s.putInt8(preferableSignatureAlgorithm.getHashValue());
             s.putInt8(preferableSignatureAlgorithm.getSignatureValue());
         }
@@ -1833,7 +1961,7 @@
         s.println("*** CertificateVerify");
 
         if (debug != null && Debug.isOn("verbose")) {
-            if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+            if (protocolVersion.useTLS12PlusSpec()) {
                 s.println("Signature Algorithm " +
                         preferableSignatureAlgorithm.getAlgorithmName());
             }
@@ -1899,7 +2027,7 @@
             CipherSuite cipherSuite) throws IOException {
         this.protocolVersion = protocolVersion;
         this.cipherSuite = cipherSuite;
-        int msgLen = (protocolVersion.v >= ProtocolVersion.TLS10.v) ? 12 : 36;
+        int msgLen = protocolVersion.useTLS10PlusSpec() ?  12 : 36;
         verifyData = new byte[msgLen];
         input.read(verifyData);
     }
@@ -1932,7 +2060,7 @@
             throw new RuntimeException("Invalid sender: " + sender);
         }
 
-        if (protocolVersion.v >= ProtocolVersion.TLS10.v) {
+        if (protocolVersion.useTLS10PlusSpec()) {
             // TLS 1.0+
             try {
                 byte [] seed;
@@ -1940,14 +2068,14 @@
                 PRF prf;
 
                 // Get the KeyGenerator alg and calculate the seed.
-                if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
-                    // TLS 1.2
+                if (protocolVersion.useTLS12PlusSpec()) {
+                    // TLS 1.2+ or DTLS 1.2+
                     seed = handshakeHash.getFinishedHash();
 
                     prfAlg = "SunTls12Prf";
                     prf = cipherSuite.prfAlg;
                 } else {
-                    // TLS 1.0/1.1
+                    // TLS 1.0/1.1, DTLS 1.0
                     MessageDigest md5Clone = handshakeHash.getMD5Clone();
                     MessageDigest shaClone = handshakeHash.getSHAClone();
                     seed = new byte[36];
--- a/jdk/src/java.base/share/classes/sun/security/ssl/HandshakeOutStream.java	Mon Jun 01 10:15:21 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/HandshakeOutStream.java	Tue Jun 02 09:15:47 2015 -0700
@@ -23,10 +23,9 @@
  * questions.
  */
 
-
 package sun.security.ssl;
 
-import java.io.OutputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 
 /**
@@ -40,197 +39,113 @@
  *
  * @author  David Brownell
  */
-public class HandshakeOutStream extends OutputStream {
-
-    private SSLSocketImpl socket;
-    private SSLEngineImpl engine;
-
-    OutputRecord r;
+public class HandshakeOutStream extends ByteArrayOutputStream {
 
-    HandshakeOutStream(ProtocolVersion protocolVersion,
-            ProtocolVersion helloVersion, HandshakeHash handshakeHash,
-            SSLSocketImpl socket) {
-        this.socket = socket;
-        r = new OutputRecord(Record.ct_handshake);
-        init(protocolVersion, helloVersion, handshakeHash);
-    }
+    OutputRecord outputRecord;      // May be null if not actually used to
+                                    // output handshake message records.
 
-    HandshakeOutStream(ProtocolVersion protocolVersion,
-            ProtocolVersion helloVersion, HandshakeHash handshakeHash,
-            SSLEngineImpl engine) {
-        this.engine = engine;
-        r = new EngineOutputRecord(Record.ct_handshake, engine);
-        init(protocolVersion, helloVersion, handshakeHash);
-    }
-
-    private void init(ProtocolVersion protocolVersion,
-            ProtocolVersion helloVersion, HandshakeHash handshakeHash) {
-        r.setVersion(protocolVersion);
-        r.setHelloVersion(helloVersion);
-        r.setHandshakeHash(handshakeHash);
+    HandshakeOutStream(OutputRecord outputRecord) {
+        super();
+        this.outputRecord = outputRecord;
     }
 
+    // Complete a handshakin message writing. Called by HandshakeMessage.
+    void complete() throws IOException {
+        if (size() < 4) {       // 4: handshake message header size
+            // internal_error alert will be triggered
+            throw new RuntimeException("handshake message is not available");
+        }
 
-    /*
-     * Update the handshake data hashes ... mostly for use after a
-     * client cert has been sent, so the cert verify message can be
-     * constructed correctly yet without forcing extra I/O.  In all
-     * other cases, automatic hash calculation suffices.
-     */
-    void doHashes() {
-        r.doHashes();
+        // outputRecord cannot be null
+        outputRecord.encodeHandshake(buf, 0, count);
+
+        // reset the byte array output stream
+        reset();
     }
 
-    /*
-     * Write some data out onto the stream ... buffers as much as possible.
-     * Hashes are updated automatically if something gets flushed to the
-     * network (e.g. a big cert message etc).
-     */
-    @Override
-    public void write(byte buf[], int off, int len) throws IOException {
-        while (len > 0) {
-            int howmuch = Math.min(len, r.availableDataBytes());
+    //
+    // overridden ByteArrayOutputStream methods
+    //
 
-            if (howmuch == 0) {
-                flush();
-            } else {
-                r.write(buf, off, howmuch);
-                off += howmuch;
-                len -= howmuch;
-            }
-        }
-    }
-
-    /*
-     * write-a-byte
-     */
     @Override
-    public void write(int i) throws IOException {
-        if (r.availableDataBytes() < 1) {
-            flush();
-        }
-        r.write(i);
+    public void write(byte[] b, int off, int len) {
+        // The maximum fragment size is 24 bytes.
+        checkOverflow(len, Record.OVERFLOW_OF_INT24);
+        super.write(b, off, len);
     }
 
     @Override
     public void flush() throws IOException {
-        if (socket != null) {
-            try {
-                socket.writeRecord(r);
-            } catch (IOException e) {
-                // Had problems writing; check if there was an
-                // alert from peer. If alert received, waitForClose
-                // will throw an exception for the alert
-                socket.waitForClose(true);
-
-                // No alert was received, just rethrow exception
-                throw e;
-            }
-        } else {  // engine != null
-            /*
-             * Even if record might be empty, flush anyway in case
-             * there is a finished handshake message that we need
-             * to queue.
-             */
-            engine.writeRecord((EngineOutputRecord)r);
-        }
+        outputRecord.flush();
     }
 
-    /*
-     * Tell the OutputRecord that a finished message was
-     * contained either in this record or the one immeiately
-     * preceding it.  We need to reliably pass back notifications
-     * that a finish message occurred.
-     */
-    void setFinishedMsg() {
-        assert(socket == null);
-
-        ((EngineOutputRecord)r).setFinishedMsg();
-    }
+    //
+    // handshake output stream management functions
+    //
 
     /*
      * Put integers encoded in standard 8, 16, 24, and 32 bit
      * big endian formats. Note that OutputStream.write(int) only
      * writes the least significant 8 bits and ignores the rest.
      */
-
     void putInt8(int i) throws IOException {
         checkOverflow(i, Record.OVERFLOW_OF_INT08);
-        r.write(i);
+        super.write(i);
     }
 
     void putInt16(int i) throws IOException {
         checkOverflow(i, Record.OVERFLOW_OF_INT16);
-        if (r.availableDataBytes() < 2) {
-            flush();
-        }
-        r.write(i >> 8);
-        r.write(i);
+        super.write(i >> 8);
+        super.write(i);
     }
 
     void putInt24(int i) throws IOException {
         checkOverflow(i, Record.OVERFLOW_OF_INT24);
-        if (r.availableDataBytes() < 3) {
-            flush();
-        }
-        r.write(i >> 16);
-        r.write(i >> 8);
-        r.write(i);
-    }
-
-    void putInt32(int i) throws IOException {
-        if (r.availableDataBytes() < 4) {
-            flush();
-        }
-        r.write(i >> 24);
-        r.write(i >> 16);
-        r.write(i >> 8);
-        r.write(i);
+        super.write(i >> 16);
+        super.write(i >> 8);
+        super.write(i);
     }
 
     /*
      * Put byte arrays with length encoded as 8, 16, 24 bit
      * integers in big-endian format.
      */
-    void putBytes8(byte b[]) throws IOException {
+    void putBytes8(byte[] b) throws IOException {
         if (b == null) {
             putInt8(0);
-            return;
         } else {
-            checkOverflow(b.length, Record.OVERFLOW_OF_INT08);
+            putInt8(b.length);
+            super.write(b, 0, b.length);
         }
-        putInt8(b.length);
-        write(b, 0, b.length);
     }
 
     public void putBytes16(byte b[]) throws IOException {
         if (b == null) {
             putInt16(0);
-            return;
         } else {
-            checkOverflow(b.length, Record.OVERFLOW_OF_INT16);
+            putInt16(b.length);
+            super.write(b, 0, b.length);
         }
-        putInt16(b.length);
-        write(b, 0, b.length);
     }
 
     void putBytes24(byte b[]) throws IOException {
         if (b == null) {
             putInt24(0);
-            return;
         } else {
-            checkOverflow(b.length, Record.OVERFLOW_OF_INT24);
+            putInt24(b.length);
+            super.write(b, 0, b.length);
         }
-        putInt24(b.length);
-        write(b, 0, b.length);
     }
 
-    private void checkOverflow(int length, int overflow) {
-        if (length >= overflow) {
+    /*
+     * Does the specified length overflow the limitation?
+     */
+    private static void checkOverflow(int length, int limit) {
+        if (length >= limit) {
             // internal_error alert will be triggered
             throw new RuntimeException(
                     "Field length overflow, the field length (" +
-                    length + ") should be less than " + overflow);
+                    length + ") should be less than " + limit);
         }
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/HandshakeStateManager.java	Tue Jun 02 09:15:47 2015 -0700
@@ -0,0 +1,925 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ssl;
+
+import java.util.LinkedList;
+import java.util.HashMap;
+import javax.net.ssl.SSLProtocolException;
+
+import sun.security.ssl.HandshakeMessage.*;
+
+import static sun.security.ssl.CipherSuite.KeyExchange;
+import static sun.security.ssl.CipherSuite.KeyExchange.*;
+import static sun.security.ssl.HandshakeStateManager.HandshakeState.*;
+import static sun.security.ssl.HandshakeMessage.*;
+
+/*
+ * Handshake state manager.
+ *
+ * Messages flow for a full handshake:
+ *
+ *      -                                                         -
+ *      |          HelloRequest       (No.0, RFC 5246) [*]        |
+ *      |     <--------------------------------------------       |
+ *      |                                                         |
+ *      |          ClientHello        (No.1, RFC 5246)            |
+ *      |     -------------------------------------------->       |
+ *      |                                                         |
+ *      |   -      HelloVerifyRequest (No.3, RFC 6347)      -     |
+ *      | D | <-------------------------------------------- | D   |
+ *      | T |                                               | T   |
+ *      | L |      ClientHello        (No.1, RFC 5246)      | L   |
+ *      | S | --------------------------------------------> | S   |
+ *      |   -                                               -     |
+ *      |                                                         |
+ *   C  |          ServerHello        (No.2, RFC 5246)            |  S
+ *   L  |          SupplementalData   (No.23, RFC4680) [*]        |  E
+ *   I  |          Certificate        (No.11, RFC 5246) [*]       |  R
+ *   E  |          CertificateStatus  (No.22, RFC 6066) [*]       |  V
+ *   N  |          ServerKeyExchange  (No.12, RFC 5246) [*]       |  E
+ *   T  |          CertificateRequest (No.13, RFC 5246) [*]       |  R
+ *      |          ServerHelloDone    (No.14, RFC 5246)           |
+ *      |     <--------------------------------------------       |
+ *      |                                                         |
+ *      |          SupplementalData   (No.23, RFC4680) [*]        |
+ *      |          Certificate        (No.11, RFC 5246) [*] Or    |
+ *      |              CertificateURL (No.21, RFC6066) [*]        |
+ *      |          ClientKeyExchange  (No.16, RFC 5246)           |
+ *      |          CertificateVerify  (No.15, RFC 5246) [*]       |
+ *      |          [ChangeCipherSpec] (RFC 5246)                  |
+ *      |          Finished           (No.20, RFC 5246)           |
+ *      |     -------------------------------------------->       |
+ *      |                                                         |
+ *      |          NewSessionTicket   (No.4, RFC4507) [*]         |
+ *      |          [ChangeCipherSpec] (RFC 5246)                  |
+ *      |          Finished           (No.20, RFC 5246)           |
+ *      |     <--------------------------------------------       |
+ *      -                                                         -
+ * [*] Indicates optional or situation-dependent messages that are not
+ * always sent.
+ *
+ * Message flow for an abbreviated handshake:
+ *      -                                                         -
+ *      |          ClientHello        (No.1, RFC 5246)            |
+ *      |     -------------------------------------------->       |
+ *      |                                                         |
+ *   C  |          ServerHello        (No.2, RFC 5246)            |  S
+ *   L  |          NewSessionTicket   (No.4, RFC4507) [*]         |  E
+ *   I  |          [ChangeCipherSpec] (RFC 5246)                  |  R
+ *   E  |          Finished           (No.20, RFC 5246)           |  V
+ *   N  |     <--------------------------------------------       |  E
+ *   T  |                                                         |  R
+ *      |          [ChangeCipherSpec] (RFC 5246)                  |
+ *      |          Finished           (No.20, RFC 5246)           |
+ *      |     -------------------------------------------->       |
+ *      -                                                         -
+ *
+ *
+ * State machine of handshake states:
+ *
+ *                   +--------------+
+ *      START -----> | HelloRequest |
+ *        |          +--------------+
+ *        |               |
+ *        v               v
+ *     +---------------------+   -->  +---------------------+
+ *     |    ClientHello      |        | HelloVerifyRequest  |
+ *     +---------------------+   <--  +---------------------+
+ *               |
+ *               |
+ * =========================================================================
+ *               |
+ *               v
+ *     +---------------------+
+ *     |    ServerHello      |  ----------------------------------+------+
+ *     +---------------------+  -->  +-------------------------+  |      |
+ *                    |              | Server SupplementalData |  |      |
+ *                    |              +-------------------------+  |      |
+ *                    |                |                          |      |
+ *                    v                v                          |      |
+ *                +---------------------+                         |      |
+ *         +----  | Server Certificate  |                         |      |
+ *         |      +---------------------+                         |      |
+ *         |          |                                           |      |
+ *         |          |   +--------------------+                  |      |
+ *         |          +-> | CertificateStatus  |                  |      |
+ *         |          |   +--------------------+                  v      |
+ *         |          |      |          |     +--------------------+     |
+ *         |          v      v          +-->  | ServerKeyExchange  |     |
+ *         |  +---------------------+   |     +--------------------+     |
+ *         |  | CertificateRequest  |   |         |                      |
+ *         |  +---------------------+ <-+---------+                      |
+ *         |            |               |         |                      |
+ *         v            v               |         |                      |
+ *     +---------------------+  <-------+         |                      |
+ *     |  ServerHelloDone    |  <-----------------+                      |
+ *     +---------------------+                                           |
+ *       |         |                                                     |
+ *       |         |                                                     |
+ *       |         |                                                     |
+ * =========================================================================
+ *       |         |                                                     |
+ *       |         v                                                     |
+ *       |   +-------------------------+                                 |
+ *       |   | Client SupplementalData | --------------+                 |
+ *       |   +-------------------------+               |                 |
+ *       |             |                               |                 |
+ *       |             v                               |                 |
+ *       |   +--------------------+                    |                 |
+ *       +-> | Client Certificate | ALT.               |                 |
+ *       |   +--------------------+----------------+   |                 |
+ *       |                        | CertificateURL |   |                 |
+ *       |                        +----------------+   |                 |
+ *       v                                             |                 |
+ *     +-------------------+  <------------------------+                 |
+ *     | ClientKeyExchange |                                             |
+ *     +-------------------+                                             |
+ *          |           |                                                |
+ *          |           v                                                |
+ *          |      +-------------------+                                 |
+ *          |      | CertificateVerify |                                 |
+ *          |      +-------------------+                                 |
+ *          |          |                                                 |
+ *          v          v                                                 |
+ *     +-------------------------+                                       |
+ *     | Client ChangeCipherSpec |  <---------------+                    |
+ *     +-------------------------+                  |                    |
+ *               |                                  |                    |
+ *               v                                  |                    |
+ *     +-----------------+  (abbreviated)           |                    |
+ *     | Client Finished |  -------------> END      |                    |
+ *     +-----------------+  (Abbreviated handshake) |                    |
+ *                      |                           |                    |
+ *                      | (full)                    |                    |
+ *                      |                           |                    |
+ * ================================                 |                    |
+ *                      |                           |                    |
+ *                      |                   ================================
+ *                      |                           |                    |
+ *                      v                           |                    |
+ *                 +------------------+             |    (abbreviated)   |
+ *                 | NewSessionTicket | <--------------------------------+
+ *                 +------------------+             |                    |
+ *                      |                           |                    |
+ *                      v                           |                    |
+ *     +-------------------------+                  |    (abbreviated)   |
+ *     | Server ChangeCipherSpec | <-------------------------------------+
+ *     +-------------------------+                  |
+ *               |                                  |
+ *               v                                  |
+ *     +-----------------+    (abbreviated)         |
+ *     | Server Finished | -------------------------+
+ *     +-----------------+
+ *            | (full)
+ *            v
+ *        END (Full handshake)
+ *
+ *
+ * The scenarios of the use of this class:
+ * 1. Create an instance of HandshakeStateManager during the initializtion
+ *    handshake.
+ * 2. If receiving a handshake message, call HandshakeStateManager.check()
+ *    to make sure that the message is of the expected handshake type.  And
+ *    then call HandshakeStateManager.update() in case handshake states may
+ *    be impacted by this new incoming handshake message.
+ * 3. On delivering a handshake message, call HandshakeStateManager.update()
+ *    in case handshake states may by thie new outgoing handshake message.
+ * 4. On receiving and delivering ChangeCipherSpec message, call
+ *    HandshakeStateManager.changeCipherSpec() to check the present sequence
+ *    of this message, and update the states if necessary.
+ */
+final class HandshakeStateManager {
+    // upcoming handshake states.
+    private LinkedList<HandshakeState> upcomingStates;
+    private LinkedList<HandshakeState> alternatives;
+
+    private boolean isDTLS;
+
+    private final static boolean debugIsOn;
+
+    private final static HashMap<Byte, String> handshakeTypes;
+
+    static {
+        debugIsOn = (Handshaker.debug != null) &&
+                Debug.isOn("handshake") && Debug.isOn("verbose");
+        handshakeTypes = new HashMap<>(15);
+
+        handshakeTypes.put(ht_hello_request,            "hello_request");
+        handshakeTypes.put(ht_client_hello,             "client_hello");
+        handshakeTypes.put(ht_server_hello,             "server_hello");
+        handshakeTypes.put(ht_hello_verify_request,     "hello_verify_request");
+        handshakeTypes.put(ht_new_session_ticket,       "session_ticket");
+        handshakeTypes.put(ht_certificate,              "certificate");
+        handshakeTypes.put(ht_server_key_exchange,      "server_key_exchange");
+        handshakeTypes.put(ht_certificate_request,      "certificate_request");
+        handshakeTypes.put(ht_server_hello_done,        "server_hello_done");
+        handshakeTypes.put(ht_certificate_verify,       "certificate_verify");
+        handshakeTypes.put(ht_client_key_exchange,      "client_key_exchange");
+        handshakeTypes.put(ht_finished,                 "finished");
+        handshakeTypes.put(ht_certificate_url,          "certificate_url");
+        handshakeTypes.put(ht_certificate_status,       "certificate_status");
+        handshakeTypes.put(ht_supplemental_data,        "supplemental_data");
+    }
+
+    HandshakeStateManager(boolean isDTLS) {
+        this.upcomingStates = new LinkedList<>();
+        this.alternatives = new LinkedList<>();
+        this.isDTLS = isDTLS;
+    }
+
+    //
+    // enumation of handshake type
+    //
+    static enum HandshakeState {
+        HS_HELLO_REQUEST(
+                "hello_request",
+                HandshakeMessage.ht_hello_request),
+        HS_CLIENT_HELLO(
+                "client_hello",
+                HandshakeMessage.ht_client_hello),
+        HS_HELLO_VERIFY_REQUEST(
+                "hello_verify_request",
+                HandshakeMessage.ht_hello_verify_request),
+        HS_SERVER_HELLO(
+                "server_hello",
+                HandshakeMessage.ht_server_hello),
+        HS_SERVER_SUPPLEMENTAL_DATA(
+                "server supplemental_data",
+                HandshakeMessage.ht_supplemental_data, true),
+        HS_SERVER_CERTIFICATE(
+                "server certificate",
+                HandshakeMessage.ht_certificate),
+        HS_CERTIFICATE_STATUS(
+                "certificate_status",
+                HandshakeMessage.ht_certificate_status, true),
+        HS_SERVER_KEY_EXCHANGE(
+                "server_key_exchange",
+                HandshakeMessage.ht_server_key_exchange, true),
+        HS_CERTIFICATE_REQUEST(
+                "certificate_request",
+                HandshakeMessage.ht_certificate_request, true),
+        HS_SERVER_HELLO_DONE(
+                "server_hello_done",
+                HandshakeMessage.ht_server_hello_done),
+        HS_CLIENT_SUPPLEMENTAL_DATA(
+                "client supplemental_data",
+                HandshakeMessage.ht_supplemental_data, true),
+        HS_CLIENT_CERTIFICATE(
+                "client certificate",
+                HandshakeMessage.ht_certificate, true),
+        HS_CERTIFICATE_URL(
+                "certificate_url",
+                HandshakeMessage.ht_certificate_url, true),
+        HS_CLIENT_KEY_EXCHANGE(
+                "client_key_exchange",
+                HandshakeMessage.ht_client_key_exchange),
+        HS_CERTIFICATE_VERIFY(
+                "certificate_verify",
+                HandshakeMessage.ht_certificate_verify, true),
+        HS_CLIENT_CHANGE_CIPHER_SPEC(
+                "client change_cipher_spec",
+                HandshakeMessage.ht_not_applicable),
+        HS_CLEINT_FINISHED(
+                "client finished",
+                HandshakeMessage.ht_finished),
+        HS_NEW_SESSION_TICKET(
+                "session_ticket",
+                HandshakeMessage.ht_new_session_ticket),
+        HS_SERVER_CHANGE_CIPHER_SPEC(
+                "server change_cipher_spec",
+                HandshakeMessage.ht_not_applicable),
+        HS_SERVER_FINISHDE(
+                "server finished",
+                HandshakeMessage.ht_finished);
+
+        final String description;
+        final byte handshakeType;
+        final boolean isOptional;
+
+        HandshakeState(String description, byte handshakeType) {
+            this.description = description;
+            this.handshakeType = handshakeType;
+            this.isOptional = false;
+        }
+
+        HandshakeState(String description,
+                byte handshakeType, boolean isOptional) {
+
+            this.description = description;
+            this.handshakeType = handshakeType;
+            this.isOptional = isOptional;
+        }
+
+        public String toString() {
+            return description + "[" + handshakeType + "]" +
+                    (isOptional ? "(optional)" : "");
+        }
+    }
+
+    boolean isEmpty() {
+        return upcomingStates.isEmpty();
+    }
+
+    void check(byte handshakeType) throws SSLProtocolException {
+        String exceptionMsg =
+                 "Handshake message sequence violation, " + handshakeType;
+
+        if (debugIsOn) {
+            System.out.println(
+                    "check handshake state: " + toString(handshakeType));
+        }
+
+        if (upcomingStates.isEmpty()) {
+            // Is it a kickstart message?
+            if ((handshakeType != HandshakeMessage.ht_hello_request) &&
+                (handshakeType != HandshakeMessage.ht_client_hello)) {
+
+                throw new SSLProtocolException(
+                    "Handshake message sequence violation, " + handshakeType);
+            }
+
+            // It is a kickstart message.
+            return;
+        }
+
+        // Ignore the checking for HelloRequest messages as they are
+        // may be sent by the server at any time.
+        if (handshakeType == HandshakeMessage.ht_hello_request) {
+            return;
+        }
+
+        for (HandshakeState handshakeState : upcomingStates) {
+            if (handshakeState.handshakeType == handshakeType) {
+                // It's the expected next handshake type.
+                return;
+            }
+
+            if (handshakeState.isOptional) {
+                continue;
+            } else {
+                for (HandshakeState alternative : alternatives) {
+                    if (alternative.handshakeType == handshakeType) {
+                        return;
+                    }
+
+                    if (alternative.isOptional) {
+                        continue;
+                    } else {
+                        throw new SSLProtocolException(exceptionMsg);
+                    }
+                }
+            }
+
+            throw new SSLProtocolException(exceptionMsg);
+        }
+
+        // Not an expected Handshake message.
+        throw new SSLProtocolException(
+                "Handshake message sequence violation, " + handshakeType);
+    }
+
+    void update(HandshakeMessage handshakeMessage,
+            boolean isAbbreviated) throws SSLProtocolException {
+
+        byte handshakeType = (byte)handshakeMessage.messageType();
+        String exceptionMsg =
+                 "Handshake message sequence violation, " + handshakeType;
+
+        if (debugIsOn) {
+            System.out.println(
+                    "update handshake state: " + toString(handshakeType));
+        }
+
+        boolean hasPresentState = false;
+        switch (handshakeType) {
+        case HandshakeMessage.ht_hello_request:
+            //
+            // State machine:
+            //     PRESENT: START
+            //        TO  : ClientHello
+            //
+
+            // No old state to update.
+
+            // Add the upcoming states.
+            if (!upcomingStates.isEmpty()) {
+                // A ClientHello message should be followed.
+                upcomingStates.add(HS_CLIENT_HELLO);
+
+            }   // Otherwise, ignore this HelloRequest message.
+
+            break;
+
+        case HandshakeMessage.ht_client_hello:
+            //
+            // State machine:
+            //     PRESENT: START
+            //              HS_CLIENT_HELLO
+            //        TO  : HS_HELLO_VERIFY_REQUEST (DTLS)
+            //              HS_SERVER_HELLO
+            //
+
+            // Check and update the present state.
+            if (!upcomingStates.isEmpty()) {
+                // The current state should be HS_CLIENT_HELLO.
+                HandshakeState handshakeState = upcomingStates.pop();
+                if (handshakeState != HS_CLIENT_HELLO) {
+                    throw new SSLProtocolException(exceptionMsg);
+                }
+            }
+
+            // Add the upcoming states.
+            ClientHello clientHello = (ClientHello)handshakeMessage;
+            if (isDTLS) {
+                // Is it an initial ClientHello message?
+                if (clientHello.cookie == null ||
+                        clientHello.cookie.length == 0) {
+                    // Is it an abbreviated handshake?
+                    if (clientHello.sessionId.length() != 0) {
+                        // A HelloVerifyRequest message or a ServerHello
+                        // message may follow the abbreviated session
+                        // resuming handshake request.
+                        upcomingStates.add(HS_HELLO_VERIFY_REQUEST);
+                        alternatives.add(HS_SERVER_HELLO);
+                    } else {
+                        // A HelloVerifyRequest message should follow
+                        // the initial ClientHello message.
+                        upcomingStates.add(HS_HELLO_VERIFY_REQUEST);
+                    }
+                } else {
+                    // A HelloVerifyRequest may be followed if the cookie
+                    // cannot be verified.
+                    upcomingStates.add(HS_SERVER_HELLO);
+                    alternatives.add(HS_HELLO_VERIFY_REQUEST);
+                }
+            } else {
+                upcomingStates.add(HS_SERVER_HELLO);
+            }
+
+            break;
+
+        case HandshakeMessage.ht_hello_verify_request:
+            //
+            // State machine:
+            //     PRESENT: HS_HELLO_VERIFY_REQUEST
+            //        TO  : HS_CLIENT_HELLO
+            //
+            // Note that this state may have an alternative option.
+
+            // Check and update the present state.
+            if (!upcomingStates.isEmpty()) {
+                // The current state should be HS_HELLO_VERIFY_REQUEST.
+                HandshakeState handshakeState = upcomingStates.pop();
+                HandshakeState alternative = null;
+                if (!alternatives.isEmpty()) {
+                    alternative = alternatives.pop();
+                }
+
+                if ((handshakeState != HS_HELLO_VERIFY_REQUEST) &&
+                        (alternative != HS_HELLO_VERIFY_REQUEST)) {
+
+                    throw new SSLProtocolException(exceptionMsg);
+                }
+            } else {
+                // No present state.
+                throw new SSLProtocolException(exceptionMsg);
+            }
+
+            // Add the upcoming states.
+            upcomingStates.add(HS_CLIENT_HELLO);
+
+            break;
+
+        case HandshakeMessage.ht_server_hello:
+            //
+            // State machine:
+            //     PRESENT: HS_SERVER_HELLO
+            //        TO  :
+            //          Full handshake state stacks
+            //              (ServerHello Flight)
+            //              HS_SERVER_SUPPLEMENTAL_DATA [optional]
+            //          --> HS_SERVER_CERTIFICATE [optional]
+            //          --> HS_CERTIFICATE_STATUS [optional]
+            //          --> HS_SERVER_KEY_EXCHANGE [optional]
+            //          --> HS_CERTIFICATE_REQUEST [optional]
+            //          --> HS_SERVER_HELLO_DONE
+            //              (Client ClientKeyExchange Flight)
+            //          --> HS_CLIENT_SUPPLEMENTAL_DATA [optional]
+            //          --> HS_CLIENT_CERTIFICATE or
+            //              HS_CERTIFICATE_URL
+            //          --> HS_CLIENT_KEY_EXCHANGE
+            //          --> HS_CERTIFICATE_VERIFY [optional]
+            //          --> HS_CLIENT_CHANGE_CIPHER_SPEC
+            //          --> HS_CLEINT_FINISHED
+            //              (Server Finished Flight)
+            //          --> HS_CLIENT_SUPPLEMENTAL_DATA [optional]
+            //
+            //          Abbreviated handshake state stacks
+            //              (Server Finished Flight)
+            //              HS_NEW_SESSION_TICKET
+            //          --> HS_SERVER_CHANGE_CIPHER_SPEC
+            //          --> HS_SERVER_FINISHDE
+            //              (Client Finished Flight)
+            //          --> HS_CLIENT_CHANGE_CIPHER_SPEC
+            //          --> HS_CLEINT_FINISHED
+            //
+            // Note that this state may have an alternative option.
+
+            // Check and update the present state.
+            if (!upcomingStates.isEmpty()) {
+                // The current state should be HS_SERVER_HELLO
+                HandshakeState handshakeState = upcomingStates.pop();
+                HandshakeState alternative = null;
+                if (!alternatives.isEmpty()) {
+                    alternative = alternatives.pop();
+                }
+
+                if ((handshakeState != HS_SERVER_HELLO) &&
+                        (alternative != HS_SERVER_HELLO)) {
+
+                    throw new SSLProtocolException(exceptionMsg);
+                }
+            } else {
+                // No present state.
+                throw new SSLProtocolException(exceptionMsg);
+            }
+
+            // Add the upcoming states.
+            ServerHello serverHello = (ServerHello)handshakeMessage;
+            HelloExtensions hes = serverHello.extensions;
+
+
+            // Not support SessionTicket extension yet.
+            //
+            // boolean hasSessionTicketExt =
+            //     (hes.get(HandshakeMessage.ht_new_session_ticket) != null);
+
+            if (isAbbreviated) {
+                // Not support SessionTicket extension yet.
+                //
+                // // Mandatory NewSessionTicket message
+                // if (hasSessionTicketExt) {
+                //     upcomingStates.add(HS_NEW_SESSION_TICKET);
+                // }
+
+                // Mandatory server ChangeCipherSpec and Finished messages
+                upcomingStates.add(HS_SERVER_CHANGE_CIPHER_SPEC);
+                upcomingStates.add(HS_SERVER_FINISHDE);
+
+                // Mandatory client ChangeCipherSpec and Finished messages
+                upcomingStates.add(HS_CLIENT_CHANGE_CIPHER_SPEC);
+                upcomingStates.add(HS_CLEINT_FINISHED);
+            } else {
+                // Not support SupplementalData extension yet.
+                //
+                // boolean hasSupplementalDataExt =
+                //     (hes.get(HandshakeMessage.ht_supplemental_data) != null);
+
+                // Not support CertificateStatus extension yet.
+                //
+                // boolean hasCertificateStatusExt =
+                //    (hes.get(HandshakeMessage.ht_certificate_status) != null);
+
+                // Not support CertificateURL extension yet.
+                //
+                // boolean hasCertificateUrlExt =
+                //     (hes.get(HandshakeMessage.ht_certificate_url) != null);
+
+                // Not support SupplementalData extension yet.
+                //
+                // // Optional SupplementalData message
+                // if (hasSupplementalDataExt) {
+                //     upcomingStates.add(HS_SERVER_SUPPLEMENTAL_DATA);
+                // }
+
+                // Need server Certificate message or not?
+                KeyExchange keyExchange = serverHello.cipherSuite.keyExchange;
+                if ((keyExchange != K_KRB5) &&
+                        (keyExchange != K_KRB5_EXPORT) &&
+                        (keyExchange != K_DH_ANON) &&
+                        (keyExchange != K_ECDH_ANON)) {
+                    // Mandatory Certificate message
+                    upcomingStates.add(HS_SERVER_CERTIFICATE);
+                }
+
+                // Not support CertificateStatus extension yet.
+                //
+                // // Optional CertificateStatus message
+                // if (hasCertificateStatusExt) {
+                //     upcomingStates.add(HS_CERTIFICATE_STATUS);
+                // }
+
+                // Need ServerKeyExchange message or not?
+                if ((keyExchange == K_RSA_EXPORT) ||
+                        (keyExchange == K_DHE_RSA) ||
+                        (keyExchange == K_DHE_DSS) ||
+                        (keyExchange == K_DH_ANON) ||
+                        (keyExchange == K_ECDHE_RSA) ||
+                        (keyExchange == K_ECDHE_ECDSA) ||
+                        (keyExchange == K_ECDH_ANON)) {
+                    // Optional ServerKeyExchange message
+                    upcomingStates.add(HS_SERVER_KEY_EXCHANGE);
+                }
+
+                // Optional CertificateRequest message
+                upcomingStates.add(HS_CERTIFICATE_REQUEST);
+
+                // Mandatory ServerHelloDone message
+                upcomingStates.add(HS_SERVER_HELLO_DONE);
+
+                // Not support SupplementalData extension yet.
+                //
+                // // Optional SupplementalData message
+                // if (hasSupplementalDataExt) {
+                //     upcomingStates.add(HS_CLIENT_SUPPLEMENTAL_DATA);
+                // }
+
+                // Optional client Certificate message
+                upcomingStates.add(HS_CLIENT_CERTIFICATE);
+
+                // Not support CertificateURL extension yet.
+                //
+                // // Alternative CertificateURL message, optional too.
+                // //
+                // // Please put CertificateURL rather than Certificate
+                // // message in the alternatives list.  So that we can
+                // // simplify the process of this alternative pair later.
+                // if (hasCertificateUrlExt) {
+                //     alternatives.add(HS_CERTIFICATE_URL);
+                // }
+
+                // Mandatory ClientKeyExchange message
+                upcomingStates.add(HS_CLIENT_KEY_EXCHANGE);
+
+                // Optional CertificateVerify message
+                upcomingStates.add(HS_CERTIFICATE_VERIFY);
+
+                // Mandatory client ChangeCipherSpec and Finished messages
+                upcomingStates.add(HS_CLIENT_CHANGE_CIPHER_SPEC);
+                upcomingStates.add(HS_CLEINT_FINISHED);
+
+                // Not support SessionTicket extension yet.
+                //
+                // // Mandatory NewSessionTicket message
+                // if (hasSessionTicketExt) {
+                //     upcomingStates.add(HS_NEW_SESSION_TICKET);
+                // }
+
+                // Mandatory server ChangeCipherSpec and Finished messages
+                upcomingStates.add(HS_SERVER_CHANGE_CIPHER_SPEC);
+                upcomingStates.add(HS_SERVER_FINISHDE);
+            }
+
+            break;
+
+        case HandshakeMessage.ht_certificate:
+            //
+            // State machine:
+            //     PRESENT: HS_CERTIFICATE_URL or
+            //              HS_CLIENT_CERTIFICATE
+            //        TO  : HS_CLIENT_KEY_EXCHANGE
+            //
+            //     Or
+            //
+            //     PRESENT: HS_SERVER_CERTIFICATE
+            //        TO  : HS_CERTIFICATE_STATUS [optional]
+            //              HS_SERVER_KEY_EXCHANGE [optional]
+            //              HS_CERTIFICATE_REQUEST [optional]
+            //              HS_SERVER_HELLO_DONE
+            //
+            // Note that this state may have an alternative option.
+
+            // Check and update the present state.
+            while (!upcomingStates.isEmpty()) {
+                HandshakeState handshakeState = upcomingStates.pop();
+                if (handshakeState.handshakeType == handshakeType) {
+                    hasPresentState = true;
+
+                    // The current state should be HS_CLIENT_CERTIFICATE or
+                    // HS_SERVER_CERTIFICATE.
+                    //
+                    // Note that we won't put HS_CLIENT_CERTIFICATE into
+                    // the alternative list.
+                    if ((handshakeState != HS_CLIENT_CERTIFICATE) &&
+                            (handshakeState != HS_SERVER_CERTIFICATE)) {
+                        throw new SSLProtocolException(exceptionMsg);
+                    }
+
+                    // Is it an expected client Certificate message?
+                    boolean isClientMessage = false;
+                    if (!upcomingStates.isEmpty()) {
+                        // If the next expected message is ClientKeyExchange,
+                        // this one should be an expected client Certificate
+                        // message.
+                        HandshakeState nextState = upcomingStates.getFirst();
+                        if (nextState == HS_CLIENT_KEY_EXCHANGE) {
+                            isClientMessage = true;
+                        }
+                    }
+
+                    if (isClientMessage) {
+                        if (handshakeState != HS_CLIENT_CERTIFICATE) {
+                            throw new SSLProtocolException(exceptionMsg);
+                        }
+
+                        // Not support CertificateURL extension yet.
+                        /*******************************************
+                        // clear up the alternatives list
+                        if (!alternatives.isEmpty()) {
+                            HandshakeState alternative = alternatives.pop();
+
+                            if (alternative != HS_CERTIFICATE_URL) {
+                                throw new SSLProtocolException(exceptionMsg);
+                            }
+                        }
+                        ********************************************/
+                    } else {
+                        if ((handshakeState != HS_SERVER_CERTIFICATE)) {
+                            throw new SSLProtocolException(exceptionMsg);
+                        }
+                    }
+
+                    break;
+                } else if (!handshakeState.isOptional) {
+                    throw new SSLProtocolException(exceptionMsg);
+                }   // Otherwise, looking for next state track.
+            }
+
+            // No present state.
+            if (!hasPresentState) {
+                throw new SSLProtocolException(exceptionMsg);
+            }
+
+            // no new upcoming states.
+
+            break;
+
+        // Not support CertificateURL extension yet.
+        /*************************************************/
+        case HandshakeMessage.ht_certificate_url:
+            //
+            // State machine:
+            //     PRESENT: HS_CERTIFICATE_URL or
+            //              HS_CLIENT_CERTIFICATE
+            //        TO  : HS_CLIENT_KEY_EXCHANGE
+            //
+            // Note that this state may have an alternative option.
+
+            // Check and update the present state.
+            while (!upcomingStates.isEmpty()) {
+                // The current state should be HS_CLIENT_CERTIFICATE.
+                //
+                // Note that we won't put HS_CLIENT_CERTIFICATE into
+                // the alternative list.
+                HandshakeState handshakeState = upcomingStates.pop();
+                if (handshakeState.handshakeType ==
+                        HS_CLIENT_CERTIFICATE.handshakeType) {
+                    hasPresentState = true;
+
+                    // Look for HS_CERTIFICATE_URL state track.
+                    if (!alternatives.isEmpty()) {
+                        HandshakeState alternative = alternatives.pop();
+
+                        if (alternative != HS_CERTIFICATE_URL) {
+                            throw new SSLProtocolException(exceptionMsg);
+                        }
+                    } else {
+                        // No alternative CertificateUR state track.
+                        throw new SSLProtocolException(exceptionMsg);
+                    }
+
+                    if ((handshakeState != HS_CLIENT_CERTIFICATE)) {
+                        throw new SSLProtocolException(exceptionMsg);
+                    }
+
+                    break;
+                } else if (!handshakeState.isOptional) {
+                    throw new SSLProtocolException(exceptionMsg);
+                }   // Otherwise, looking for next state track.
+
+            }
+
+            // No present state.
+            if (!hasPresentState) {
+                // No present state.
+                throw new SSLProtocolException(exceptionMsg);
+            }
+
+            // no new upcoming states.
+
+            break;
+        /*************************************************/
+
+        default:
+            // Check and update the present state.
+            while (!upcomingStates.isEmpty()) {
+                HandshakeState handshakeState = upcomingStates.pop();
+                if (handshakeState.handshakeType == handshakeType) {
+                    hasPresentState = true;
+                    break;
+                } else if (!handshakeState.isOptional) {
+                    throw new SSLProtocolException(exceptionMsg);
+                }   // Otherwise, looking for next state track.
+            }
+
+            // No present state.
+            if (!hasPresentState) {
+                throw new SSLProtocolException(exceptionMsg);
+            }
+
+            // no new upcoming states.
+        }
+
+        if (debugIsOn) {
+            for (HandshakeState handshakeState : upcomingStates) {
+                System.out.println(
+                    "upcoming handshake states: " + handshakeState);
+            }
+            for (HandshakeState handshakeState : alternatives) {
+                System.out.println(
+                    "upcoming handshake alternative state: " + handshakeState);
+            }
+        }
+    }
+
+    void changeCipherSpec(boolean isInput,
+            boolean isClient) throws SSLProtocolException {
+
+        if (debugIsOn) {
+            System.out.println(
+                    "update handshake state: change_cipher_spec");
+        }
+
+        String exceptionMsg = "ChangeCipherSpec message sequence violation";
+
+        HandshakeState expectedState;
+        if ((isClient && isInput) || (!isClient && !isInput)) {
+            expectedState = HS_SERVER_CHANGE_CIPHER_SPEC;
+        } else {
+            expectedState = HS_CLIENT_CHANGE_CIPHER_SPEC;
+        }
+
+        boolean hasPresentState = false;
+
+        // Check and update the present state.
+        while (!upcomingStates.isEmpty()) {
+            HandshakeState handshakeState = upcomingStates.pop();
+            if (handshakeState == expectedState) {
+                hasPresentState = true;
+                break;
+            } else if (!handshakeState.isOptional) {
+                throw new SSLProtocolException(exceptionMsg);
+            }   // Otherwise, looking for next state track.
+        }
+
+        // No present state.
+        if (!hasPresentState) {
+            throw new SSLProtocolException(exceptionMsg);
+        }
+
+        // no new upcoming states.
+
+        if (debugIsOn) {
+            for (HandshakeState handshakeState : upcomingStates) {
+                System.out.println(
+                    "upcoming handshake states: " + handshakeState);
+            }
+            for (HandshakeState handshakeState : alternatives) {
+                System.out.println(
+                    "upcoming handshake alternative state: " + handshakeState);
+            }
+        }
+    }
+
+    private static String toString(byte handshakeType) {
+        String s = handshakeTypes.get(handshakeType);
+        if (s == null) {
+            s = "unknown";
+        }
+        return (s + "[" + handshakeType + "]");
+    }
+}
+
--- a/jdk/src/java.base/share/classes/sun/security/ssl/Handshaker.java	Mon Jun 01 10:15:21 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/Handshaker.java	Tue Jun 02 09:15:47 2015 -0700
@@ -29,6 +29,7 @@
 import java.io.*;
 import java.util.*;
 import java.security.*;
+import java.nio.ByteBuffer;
 import java.security.NoSuchAlgorithmException;
 import java.security.AccessController;
 import java.security.AlgorithmConstraints;
@@ -83,7 +84,7 @@
     private CipherSuiteList     enabledCipherSuites;
 
     // The endpoint identification protocol
-    String              identificationProtocol;
+    String                      identificationProtocol;
 
     // The cryptographic algorithm constraints
     private AlgorithmConstraints    algorithmConstraints = null;
@@ -109,12 +110,15 @@
      * Active cipher suites is a subset of enabled cipher suites, and will
      * contain only those cipher suites available for the active protocols.
      */
-    private CipherSuiteList    activeCipherSuites;
+    private CipherSuiteList     activeCipherSuites;
 
     // The server name indication and matchers
     List<SNIServerName> serverNames = Collections.<SNIServerName>emptyList();
     Collection<SNIMatcher> sniMatchers = Collections.<SNIMatcher>emptyList();
 
+    // The maximum expected network packet size for SSL/TLS/DTLS records.
+    int                         maximumPacketSize = 0;
+
     private boolean             isClient;
     private boolean             needCertVerify;
 
@@ -124,11 +128,16 @@
     HandshakeHash               handshakeHash;
     HandshakeInStream           input;
     HandshakeOutStream          output;
-    int                         state;
     SSLContextImpl              sslContext;
     RandomCookie                clnt_random, svr_random;
     SSLSessionImpl              session;
 
+    HandshakeStateManager       handshakeState;
+    boolean                     clientHelloDelivered;
+    boolean                     serverHelloRequested;
+    boolean                     handshakeActivated;
+    boolean                     handshakeFinished;
+
     // current CipherSuite. Never null, initially SSL_NULL_WITH_NULL_NULL
     CipherSuite         cipherSuite;
 
@@ -141,10 +150,6 @@
     // True if it's OK to start a new SSL session
     boolean             enableNewSession;
 
-    // True if session keys have been calculated and the caller may receive
-    // and process a ChangeCipherSpec message
-    private boolean sessKeysCalculated;
-
     // Whether local cipher suites preference should be honored during
     // handshaking?
     //
@@ -207,12 +212,18 @@
     // need to dispose the object when it is invalidated
     boolean invalidated;
 
+    /*
+     * Is this an instance for Datagram Transport Layer Security (DTLS)?
+     */
+    final boolean isDTLS;
+
     Handshaker(SSLSocketImpl c, SSLContextImpl context,
             ProtocolList enabledProtocols, boolean needCertVerify,
             boolean isClient, ProtocolVersion activeProtocolVersion,
             boolean isInitialHandshake, boolean secureRenegotiation,
             byte[] clientVerifyData, byte[] serverVerifyData) {
         this.conn = c;
+        this.isDTLS = false;
         init(context, enabledProtocols, needCertVerify, isClient,
             activeProtocolVersion, isInitialHandshake, secureRenegotiation,
             clientVerifyData, serverVerifyData);
@@ -222,8 +233,10 @@
             ProtocolList enabledProtocols, boolean needCertVerify,
             boolean isClient, ProtocolVersion activeProtocolVersion,
             boolean isInitialHandshake, boolean secureRenegotiation,
-            byte[] clientVerifyData, byte[] serverVerifyData) {
+            byte[] clientVerifyData, byte[] serverVerifyData,
+            boolean isDTLS) {
         this.engine = engine;
+        this.isDTLS = isDTLS;
         init(context, enabledProtocols, needCertVerify, isClient,
             activeProtocolVersion, isInitialHandshake, secureRenegotiation,
             clientVerifyData, serverVerifyData);
@@ -251,9 +264,13 @@
         this.secureRenegotiation = secureRenegotiation;
         this.clientVerifyData = clientVerifyData;
         this.serverVerifyData = serverVerifyData;
-        enableNewSession = true;
-        invalidated = false;
-        sessKeysCalculated = false;
+        this.enableNewSession = true;
+        this.invalidated = false;
+        this.handshakeState = new HandshakeStateManager(isDTLS);
+        this.clientHelloDelivered = false;
+        this.serverHelloRequested = false;
+        this.handshakeActivated = false;
+        this.handshakeFinished = false;
 
         setCipherSuite(CipherSuite.C_NULL);
         setEnabledProtocols(enabledProtocols);
@@ -263,22 +280,6 @@
         } else {        // engine != null
             algorithmConstraints = new SSLAlgorithmConstraints(engine, true);
         }
-
-
-        //
-        // In addition to the connection state machine, controlling
-        // how the connection deals with the different sorts of records
-        // that get sent (notably handshake transitions!), there's
-        // also a handshaking state machine that controls message
-        // sequencing.
-        //
-        // It's a convenient artifact of the protocol that this can,
-        // with only a couple of minor exceptions, be driven by the
-        // type constant for the last message seen:  except for the
-        // client's cert verify, those constants are in a convenient
-        // order to drastically simplify state machine checking.
-        //
-        state = -2;  // initialized but not activated
     }
 
     /*
@@ -360,14 +361,6 @@
         }
     }
 
-    final boolean receivedChangeCipherSpec() {
-        if (conn != null) {
-            return conn.receivedChangeCipherSpec();
-        } else {
-            return engine.receivedChangeCipherSpec();
-        }
-    }
-
     String getEndpointIdentificationAlgorithmSE() {
         SSLParameters paras;
         if (conn != null) {
@@ -395,8 +388,6 @@
     void setVersion(ProtocolVersion protocolVersion) {
         this.protocolVersion = protocolVersion;
         setVersionSE(protocolVersion);
-
-        output.r.setVersion(protocolVersion);
     }
 
     /**
@@ -483,6 +474,13 @@
     }
 
     /**
+     * Sets the maximum packet size of the handshaking.
+     */
+    void setMaximumPacketSize(int maximumPacketSize) {
+        this.maximumPacketSize = maximumPacketSize;
+    }
+
+    /**
      * Sets the cipher suites preference.
      */
     void setUseCipherSuitesOrder(boolean on) {
@@ -532,23 +530,29 @@
         handshakeHash = new HandshakeHash(needCertVerify);
 
         // Generate handshake input/output stream.
-        input = new HandshakeInStream(handshakeHash);
         if (conn != null) {
-            output = new HandshakeOutStream(protocolVersion, helloVersion,
-                                        handshakeHash, conn);
-            conn.getAppInputStream().r.setHandshakeHash(handshakeHash);
-            conn.getAppInputStream().r.setHelloVersion(helloVersion);
-            conn.getAppOutputStream().r.setHelloVersion(helloVersion);
-        } else {
-            output = new HandshakeOutStream(protocolVersion, helloVersion,
-                                        handshakeHash, engine);
+            input = new HandshakeInStream();
+            output = new HandshakeOutStream(conn.outputRecord);
+
+            conn.inputRecord.setHandshakeHash(handshakeHash);
+            conn.inputRecord.setHelloVersion(helloVersion);
+
+            conn.outputRecord.setHandshakeHash(handshakeHash);
+            conn.outputRecord.setHelloVersion(helloVersion);
+            conn.outputRecord.setVersion(protocolVersion);
+        } else if (engine != null) {
+            input = new HandshakeInStream();
+            output = new HandshakeOutStream(engine.outputRecord);
+
             engine.inputRecord.setHandshakeHash(handshakeHash);
             engine.inputRecord.setHelloVersion(helloVersion);
+
+            engine.outputRecord.setHandshakeHash(handshakeHash);
             engine.outputRecord.setHelloVersion(helloVersion);
+            engine.outputRecord.setVersion(protocolVersion);
         }
 
-        // move state to activated
-        state = -1;
+        handshakeActivated = true;
     }
 
     /**
@@ -637,15 +641,15 @@
             if (!(activeProtocols.collection().isEmpty()) &&
                     activeProtocols.min.v != ProtocolVersion.NONE.v) {
                 for (CipherSuite suite : enabledCipherSuites.collection()) {
-                    if (suite.obsoleted > activeProtocols.min.v &&
-                            suite.supported <= activeProtocols.max.v) {
+                    if (!activeProtocols.min.obsoletes(suite) &&
+                            activeProtocols.max.supports(suite)) {
                         if (algorithmConstraints.permits(
                                 EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
                                 suite.name, null)) {
                             suites.add(suite);
                         }
                     } else if (debug != null && Debug.isOn("verbose")) {
-                        if (suite.obsoleted <= activeProtocols.min.v) {
+                        if (activeProtocols.min.obsoletes(suite)) {
                             System.out.println(
                                 "Ignoring obsoleted cipher suite: " + suite);
                         } else {
@@ -700,8 +704,8 @@
 
                 boolean found = false;
                 for (CipherSuite suite : enabledCipherSuites.collection()) {
-                    if (suite.isAvailable() && suite.obsoleted > protocol.v &&
-                                               suite.supported <= protocol.v) {
+                    if (suite.isAvailable() && (!protocol.obsoletes(suite)) &&
+                                               protocol.supports(suite)) {
                         if (algorithmConstraints.permits(
                                 EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
                                 suite.name, null)) {
@@ -837,7 +841,7 @@
      * this freshly created session can become the current one.
      */
     boolean isDone() {
-        return state == HandshakeMessage.ht_finished;
+        return started() && handshakeState.isEmpty() && handshakeFinished;
     }
 
 
@@ -861,6 +865,14 @@
         }
     }
 
+    void expectingFinishFlightSE() {
+        if (conn != null) {
+            conn.expectingFinishFlight();
+        } else {
+            engine.expectingFinishFlight();
+        }
+    }
+
     /*
      * Returns true if renegotiation is in use for this connection.
      */
@@ -886,8 +898,8 @@
      * This routine is fed SSL handshake records when they become available,
      * and processes messages found therein.
      */
-    void process_record(InputRecord r, boolean expectingFinished)
-            throws IOException {
+    void processRecord(ByteBuffer record,
+            boolean expectingFinished) throws IOException {
 
         checkThrown();
 
@@ -895,7 +907,7 @@
          * Store the incoming handshake data, then see if we can
          * now process any completed handshake messages
          */
-        input.incomingRecord(r);
+        input.incomingRecord(record);
 
         /*
          * We don't need to create a separate delegatable task
@@ -946,6 +958,13 @@
                 return;
             }
 
+            // Set the flags in the message receiving side.
+            if (messageType == HandshakeMessage.ht_client_hello) {
+                clientHelloDelivered = true;
+            } else if (messageType == HandshakeMessage.ht_hello_request) {
+                serverHelloRequested = true;
+            }
+
             /*
              * Process the message.  We require
              * that processMessage() consumes the entire message.  In
@@ -961,14 +980,16 @@
              * Also, note that hello request messages are never hashed;
              * that includes the hello request header, too.
              */
-            if (messageType == HandshakeMessage.ht_hello_request) {
-                input.reset();
-                processMessage(messageType, messageLen);
-                input.ignore(4 + messageLen);
-            } else {
-                input.mark(messageLen);
-                processMessage(messageType, messageLen);
-                input.digestNow();
+            processMessage(messageType, messageLen);
+
+            // Reload if this message has been reserved.
+            //
+            // Note: in the implementation, only certificate_verify and
+            // finished messages are reserved.
+            if ((messageType == HandshakeMessage.ht_finished) ||
+                (messageType == HandshakeMessage.ht_certificate_verify)) {
+
+                handshakeHash.reload();
             }
         }
     }
@@ -980,29 +1001,29 @@
      * In activated state, the handshaker may not send any messages out.
      */
     boolean activated() {
-        return state >= -1;
+        return handshakeActivated;
     }
 
     /**
      * Returns true iff the handshaker has sent any messages.
      */
     boolean started() {
-        return state >= 0;  // 0: HandshakeMessage.ht_hello_request
-                            // 1: HandshakeMessage.ht_client_hello
+        return (serverHelloRequested || clientHelloDelivered);
     }
 
-
     /*
      * Used to kickstart the negotiation ... either writing a
      * ClientHello or a HelloRequest as appropriate, whichever
      * the subclass returns.  NOP if handshaking's already started.
      */
     void kickstart() throws IOException {
-        if (state >= 0) {
+        if ((isClient && clientHelloDelivered) ||
+                (!isClient && serverHelloRequested)) {
             return;
         }
 
         HandshakeMessage m = getKickstartMessage();
+        handshakeState.update(m, resumingSession);
 
         if (debug != null && Debug.isOn("handshake")) {
             m.print(System.out);
@@ -1010,7 +1031,13 @@
         m.write(output);
         output.flush();
 
-        state = m.messageType();
+        // Set the flags in the message delivering side.
+        int handshakeType = m.messageType();
+        if (handshakeType == HandshakeMessage.ht_hello_request) {
+            serverHelloRequested = true;
+        } else {        // HandshakeMessage.ht_client_hello
+            clientHelloDelivered = true;
+        }
     }
 
     /**
@@ -1052,24 +1079,16 @@
          * We already hold SSLEngine/SSLSocket "this" by virtue
          * of this being called from the readRecord code.
          */
-        OutputRecord r;
-        if (conn != null) {
-            r = new OutputRecord(Record.ct_change_cipher_spec);
-        } else {
-            r = new EngineOutputRecord(Record.ct_change_cipher_spec, engine);
-        }
-
-        r.setVersion(protocolVersion);
-        r.write(1);     // single byte of data
-
         if (conn != null) {
             conn.writeLock.lock();
             try {
-                conn.writeRecord(r);
+                handshakeState.changeCipherSpec(false, isClient);
                 conn.changeWriteCiphers();
                 if (debug != null && Debug.isOn("handshake")) {
                     mesg.print(System.out);
                 }
+
+                handshakeState.update(mesg, resumingSession);
                 mesg.write(output);
                 output.flush();
             } finally {
@@ -1077,19 +1096,25 @@
             }
         } else {
             synchronized (engine.writeLock) {
-                engine.writeRecord((EngineOutputRecord)r);
+                handshakeState.changeCipherSpec(false, isClient);
                 engine.changeWriteCiphers();
                 if (debug != null && Debug.isOn("handshake")) {
                     mesg.print(System.out);
                 }
+
+                handshakeState.update(mesg, resumingSession);
                 mesg.write(output);
-
-                if (lastMessage) {
-                    output.setFinishedMsg();
-                }
                 output.flush();
             }
         }
+
+        if (lastMessage) {
+            handshakeFinished = true;
+        }
+    }
+
+    void receiveChangeCipherSpec() throws IOException {
+        handshakeState.changeCipherSpec(true, isClient);
     }
 
     /*
@@ -1131,12 +1156,31 @@
         String masterAlg;
         PRF prf;
 
-        if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
-            masterAlg = "SunTls12MasterSecret";
-            prf = cipherSuite.prfAlg;
+        byte majorVersion = protocolVersion.major;
+        byte minorVersion = protocolVersion.minor;
+        if (protocolVersion.isDTLSProtocol()) {
+            // Use TLS version number for DTLS key calculation
+            if (protocolVersion.v == ProtocolVersion.DTLS10.v) {
+                majorVersion = ProtocolVersion.TLS11.major;
+                minorVersion = ProtocolVersion.TLS11.minor;
+
+                masterAlg = "SunTlsMasterSecret";
+                prf = P_NONE;
+            } else {    // DTLS 1.2
+                majorVersion = ProtocolVersion.TLS12.major;
+                minorVersion = ProtocolVersion.TLS12.minor;
+
+                masterAlg = "SunTls12MasterSecret";
+                prf = cipherSuite.prfAlg;
+            }
         } else {
-            masterAlg = "SunTlsMasterSecret";
-            prf = P_NONE;
+            if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+                masterAlg = "SunTls12MasterSecret";
+                prf = cipherSuite.prfAlg;
+            } else {
+                masterAlg = "SunTlsMasterSecret";
+                prf = P_NONE;
+            }
         }
 
         String prfHashAlg = prf.getPRFHashAlg();
@@ -1145,7 +1189,7 @@
 
         @SuppressWarnings("deprecation")
         TlsMasterSecretParameterSpec spec = new TlsMasterSecretParameterSpec(
-                preMasterSecret, protocolVersion.major, protocolVersion.minor,
+                preMasterSecret, (majorVersion & 0xFF), (minorVersion & 0xFF),
                 clnt_random.random_bytes, svr_random.random_bytes,
                 prfHashAlg, prfHashLength, prfBlockSize);
 
@@ -1196,36 +1240,55 @@
         String keyMaterialAlg;
         PRF prf;
 
-        if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
-            keyMaterialAlg = "SunTls12KeyMaterial";
-            prf = cipherSuite.prfAlg;
+        byte majorVersion = protocolVersion.major;
+        byte minorVersion = protocolVersion.minor;
+        if (protocolVersion.isDTLSProtocol()) {
+            // Use TLS version number for DTLS key calculation
+            if (protocolVersion.v == ProtocolVersion.DTLS10.v) {
+                majorVersion = ProtocolVersion.TLS11.major;
+                minorVersion = ProtocolVersion.TLS11.minor;
+
+                keyMaterialAlg = "SunTlsKeyMaterial";
+                prf = P_NONE;
+            } else {    // DTLS 1.2+
+                majorVersion = ProtocolVersion.TLS12.major;
+                minorVersion = ProtocolVersion.TLS12.minor;
+
+                keyMaterialAlg = "SunTls12KeyMaterial";
+                prf = cipherSuite.prfAlg;
+            }
         } else {
-            keyMaterialAlg = "SunTlsKeyMaterial";
-            prf = P_NONE;
+            if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+                keyMaterialAlg = "SunTls12KeyMaterial";
+                prf = cipherSuite.prfAlg;
+            } else {
+                keyMaterialAlg = "SunTlsKeyMaterial";
+                prf = P_NONE;
+            }
         }
 
         String prfHashAlg = prf.getPRFHashAlg();
         int prfHashLength = prf.getPRFHashLength();
         int prfBlockSize = prf.getPRFBlockSize();
 
-        // TLS v1.1 or later uses an explicit IV in CBC cipher suites to
+        // TLS v1.1+ and DTLS use an explicit IV in CBC cipher suites to
         // protect against the CBC attacks.  AEAD/GCM cipher suites in TLS
         // v1.2 or later use a fixed IV as the implicit part of the partially
         // implicit nonce technique described in RFC 5116.
         int ivSize = cipher.ivSize;
         if (cipher.cipherType == AEAD_CIPHER) {
             ivSize = cipher.fixedIvSize;
-        } else if (protocolVersion.v >= ProtocolVersion.TLS11.v &&
-                cipher.cipherType == BLOCK_CIPHER) {
+        } else if ((cipher.cipherType == BLOCK_CIPHER) &&
+                protocolVersion.useTLS11PlusSpec()) {
             ivSize = 0;
         }
 
         TlsKeyMaterialParameterSpec spec = new TlsKeyMaterialParameterSpec(
-            masterKey, protocolVersion.major, protocolVersion.minor,
-            clnt_random.random_bytes, svr_random.random_bytes,
-            cipher.algorithm, cipher.keySize, expandedKeySize,
-            ivSize, hashSize,
-            prfHashAlg, prfHashLength, prfBlockSize);
+                masterKey, (majorVersion & 0xFF), (minorVersion & 0xFF),
+                clnt_random.random_bytes, svr_random.random_bytes,
+                cipher.algorithm, cipher.keySize, expandedKeySize,
+                ivSize, hashSize,
+                prfHashAlg, prfHashLength, prfBlockSize);
 
         try {
             KeyGenerator kg = JsseJce.getKeyGenerator(keyMaterialAlg);
@@ -1247,10 +1310,6 @@
             throw new ProviderException(e);
         }
 
-        // Mark a flag that allows outside entities (like SSLSocket/SSLEngine)
-        // determine if a ChangeCipherSpec message could be processed.
-        sessKeysCalculated = true;
-
         //
         // Dump the connection keys as they're generated.
         //
@@ -1293,7 +1352,7 @@
                     System.out.println("Server write IV:");
                     printHex(dump, svrWriteIV.getIV());
                 } else {
-                    if (protocolVersion.v >= ProtocolVersion.TLS11.v) {
+                    if (protocolVersion.useTLS11PlusSpec()) {
                         System.out.println(
                                 "... no IV derived for this protocol");
                     } else {
@@ -1305,15 +1364,6 @@
         }
     }
 
-    /**
-     * Return whether or not the Handshaker has derived session keys for
-     * this handshake.  This is used for determining readiness to process
-     * an incoming ChangeCipherSpec message.
-     */
-    boolean sessionKeysCalculated() {
-        return sessKeysCalculated;
-    }
-
     private static void printHex(HexDumpEncoder dump, byte[] bytes) {
         if (bytes == null) {
             System.out.println("(key bytes not available)");
@@ -1326,19 +1376,6 @@
         }
     }
 
-    /**
-     * Throw an SSLException with the specified message and cause.
-     * Shorthand until a new SSLException constructor is added.
-     * This method never returns.
-     */
-    static void throwSSLException(String msg, Throwable cause)
-            throws SSLException {
-        SSLException e = new SSLException(msg);
-        e.initCause(cause);
-        throw e;
-    }
-
-
     /*
      * Implement a simple task delegator.
      *
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/HelloCookieManager.java	Tue Jun 02 09:15:47 2015 -0700
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ssl;
+
+import java.io.IOException;
+import javax.net.ssl.SSLProtocolException;
+import java.security.MessageDigest;
+import java.security.SecureRandom;
+
+import sun.security.ssl.HandshakeMessage.ClientHello;
+
+/*
+ * HelloVerifyRequest cookie manager
+ */
+final class HelloCookieManager {
+    // the cookie secret life time
+    private static long COOKIE_TIMING_WINDOW = 3600000;     // in milliseconds
+    private static int  COOKIE_MAX_LENGTH_DTLS10 = 32;      // 32 bytes
+    private static int  COOKIE_MAX_LENGTH_DTLS12 = 0xFF;    // 2^8 -1 bytes
+
+    private final SecureRandom          secureRandom;
+    private final MessageDigest         cookieDigest;
+
+    private int                         cookieVersion;      // allow to wrap
+    private long                        secretLifetime;
+    private byte[]                      cookieSecret;
+
+    private int                         prevCookieVersion;
+    private byte[]                      prevCookieSecret;
+
+    HelloCookieManager(SecureRandom secureRandom) {
+        this.secureRandom = secureRandom;
+        this.cookieDigest = JsseJce.getMessageDigest("SHA-256");
+
+        this.cookieVersion = secureRandom.nextInt();
+        this.secretLifetime = 0;
+        this.cookieSecret = null;
+
+        this.prevCookieVersion = 0;
+        this.prevCookieSecret = null;
+    }
+
+    // Used by server side to generate cookies in HelloVerifyRequest message.
+    synchronized byte[] getCookie(ClientHello clientHelloMsg) {
+        if (secretLifetime < System.currentTimeMillis()) {
+            if (cookieSecret != null) {
+                prevCookieVersion = cookieVersion;
+                prevCookieSecret = cookieSecret.clone();
+            } else {
+                cookieSecret = new byte[32];
+            }
+
+            cookieVersion++;
+            secureRandom.nextBytes(cookieSecret);
+            secretLifetime = System.currentTimeMillis() + COOKIE_TIMING_WINDOW;
+        }
+
+        clientHelloMsg.updateHelloCookie(cookieDigest);
+        byte[] cookie = cookieDigest.digest(cookieSecret);      // 32 bytes
+        cookie[0] = (byte)((cookieVersion >> 24) & 0xFF);
+        cookie[1] = (byte)((cookieVersion >> 16) & 0xFF);
+        cookie[2] = (byte)((cookieVersion >> 8) & 0xFF);
+        cookie[3] = (byte)(cookieVersion & 0xFF);
+
+        return cookie;
+    }
+
+    // Used by server side to check the cookie in ClientHello message.
+    synchronized boolean isValid(ClientHello clientHelloMsg) {
+        byte[] cookie = clientHelloMsg.cookie;
+
+        // no cookie exchange or not a valid cookie length
+        if ((cookie == null) || (cookie.length != 32)) {
+            return false;
+        }
+
+        int version = ((cookie[0] & 0xFF) << 24) |
+                      ((cookie[1] & 0xFF) << 16) |
+                      ((cookie[2] & 0xFF) << 8) |
+                       (cookie[3] & 0xFF);
+
+        byte[] secret;
+        if (version == cookieVersion) {
+            secret = cookieSecret;
+        } else if (version == prevCookieVersion) {
+            secret = prevCookieSecret;
+        } else {
+            return false;       // may be out of the timing window
+        }
+
+        clientHelloMsg.updateHelloCookie(cookieDigest);
+        byte[] target = cookieDigest.digest(secret);            // 32 bytes
+
+        for (int i = 4; i < 32; i++) {
+            if (cookie[i] != target[i]) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    // Used by client side to check the cookie in HelloVerifyRequest message.
+    static void checkCookie(ProtocolVersion protocolVersion,
+            byte[] cookie) throws IOException {
+        if (cookie != null && cookie.length != 0) {
+            int limit = COOKIE_MAX_LENGTH_DTLS12;
+            if (protocolVersion.v == ProtocolVersion.DTLS10.v) {
+                limit = COOKIE_MAX_LENGTH_DTLS10;
+            }
+
+            if (cookie.length > COOKIE_MAX_LENGTH_DTLS10) {
+                throw new SSLProtocolException(
+                        "Invalid HelloVerifyRequest.cookie (length = " +
+                         cookie.length + " bytes)");
+            }
+        }
+
+        // Otherwise, no cookie exchange.
+    }
+}
--- a/jdk/src/java.base/share/classes/sun/security/ssl/HelloExtensions.java	Mon Jun 01 10:15:21 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/HelloExtensions.java	Tue Jun 02 09:15:47 2015 -0700
@@ -85,6 +85,8 @@
                         new SupportedEllipticPointFormatsExtension(s, extlen);
             } else if (extType == ExtensionType.EXT_RENEGOTIATION_INFO) {
                 extension = new RenegotiationInfoExtension(s, extlen);
+            } else if (extType == ExtensionType.EXT_MAX_FRAGMENT_LENGTH) {
+                extension = new MaxFragmentLengthExtension(s, extlen);
             } else {
                 extension = new UnknownExtension(s, extlen, extType);
             }
--- a/jdk/src/java.base/share/classes/sun/security/ssl/InputRecord.java	Mon Jun 01 10:15:21 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/InputRecord.java	Tue Jun 02 09:15:47 2015 -0700
@@ -23,11 +23,11 @@
  * questions.
  */
 
-
 package sun.security.ssl;
 
 import java.io.*;
 import java.nio.*;
+import java.util.*;
 
 import javax.crypto.BadPaddingException;
 
@@ -37,66 +37,35 @@
 
 
 /**
- * SSL 3.0 records, as pulled off a TCP stream.  Input records are
- * basically buffers tied to a particular input stream ... a layer
- * above this must map these records into the model of a continuous
- * stream of data.
- *
- * Since this returns SSL 3.0 records, it's the layer that needs to
- * map SSL 2.0 style handshake records into SSL 3.0 ones for those
- * "old" clients that interop with both V2 and V3 servers.  Not as
- * pretty as might be desired.
- *
- * NOTE:  During handshaking, each message must be hashed to support
- * verification that the handshake process wasn't compromised.
+ * {@code InputRecord} takes care of the management of SSL/TLS/DTLS input
+ * records, including buffering, decryption, handshake messages marshal, etc.
  *
  * @author David Brownell
  */
-class InputRecord extends ByteArrayInputStream implements Record {
+class InputRecord implements Record, Closeable {
+
+    /* Class and subclass dynamic debugging support */
+    static final Debug debug = Debug.getInstance("ssl");
 
-    private HandshakeHash       handshakeHash;
-    private int                 lastHashed;
-    boolean                     formatVerified = true;  // SSLv2 ruled out?
-    private boolean             isClosed;
-    private boolean             appDataValid;
+    Authenticator       readAuthenticator;
+    CipherBox           readCipher;
+
+    HandshakeHash       handshakeHash;
+    boolean             isClosed;
 
     // The ClientHello version to accept. If set to ProtocolVersion.SSL20Hello
     // and the first message we read is a ClientHello in V2 format, we convert
     // it to V3. Otherwise we throw an exception when encountering a V2 hello.
-    private ProtocolVersion     helloVersion;
-
-    /* Class and subclass dynamic debugging support */
-    static final Debug debug = Debug.getInstance("ssl");
+    ProtocolVersion     helloVersion;
 
-    /* The existing record length */
-    private int exlen;
-
-    /* V2 handshake message */
-    private byte v2Buf[];
+    // fragment size
+    int                 fragmentSize;
 
-    /*
-     * Construct the record to hold the maximum sized input record.
-     * Data will be filled in separately.
-     *
-     * The structure of the byte buffer looks like:
-     *
-     *     |--------+---------+---------------------------------|
-     *     | header |   IV    | content, MAC/TAG, padding, etc. |
-     *     | headerPlusIVSize |
-     *
-     * header: the header of an SSL records
-     * IV:     the optional IV/nonce field, it is only required for block
-     *         (TLS 1.1 or later) and AEAD cipher suites.
-     *
-     */
     InputRecord() {
-        super(new byte[maxRecordSize]);
-        setHelloVersion(ProtocolVersion.DEFAULT_HELLO);
-        pos = headerSize;
-        count = headerSize;
-        lastHashed = count;
-        exlen = 0;
-        v2Buf = null;
+        this.readCipher = CipherBox.NULL;
+        this.readAuthenticator = null;      // Please override this assignment.
+        this.helloVersion = ProtocolVersion.DEFAULT_HELLO;
+        this.fragmentSize = Record.maxDataSize;
     }
 
     void setHelloVersion(ProtocolVersion helloVersion) {
@@ -108,70 +77,332 @@
     }
 
     /*
-     * Enable format checks if initial handshaking hasn't completed
-     */
-    void enableFormatChecks() {
-        formatVerified = false;
-    }
-
-    // return whether the data in this record is valid, decrypted data
-    boolean isAppDataValid() {
-        return appDataValid;
-    }
-
-    void setAppDataValid(boolean value) {
-        appDataValid = value;
-    }
-
-    /*
-     * Return the content type of the record.
-     */
-    byte contentType() {
-        return buf[0];
-    }
-
-    /*
+     * Set instance for the computation of handshake hashes.
+     *
      * For handshaking, we need to be able to hash every byte above the
      * record marking layer.  This is where we're guaranteed to see those
      * bytes, so this is where we can hash them ... especially in the
      * case of hashing the initial V2 message!
      */
     void setHandshakeHash(HandshakeHash handshakeHash) {
+        if (handshakeHash != null) {
+            byte[] reserved = null;
+            if (this.handshakeHash != null) {
+                reserved = this.handshakeHash.getAllHandshakeMessages();
+            }
+            if ((reserved != null) && (reserved.length != 0)) {
+                handshakeHash.update(reserved, 0, reserved.length);
+
+               if (debug != null && Debug.isOn("data")) {
+                    Debug.printHex(
+                        "[reserved] handshake hash: len = " + reserved.length,
+                        reserved);
+               }
+            }
+        }
+
         this.handshakeHash = handshakeHash;
     }
 
-    HandshakeHash getHandshakeHash() {
-        return handshakeHash;
+    boolean seqNumIsHuge() {
+        return (readAuthenticator != null) &&
+                        readAuthenticator.seqNumIsHuge();
+    }
+
+    boolean isEmpty() {
+        return false;
+    }
+
+    // apply to DTLS SSLEngine
+    void expectingFinishFlight() {
+        // blank
+    }
+
+    /**
+     * Prevent any more data from being read into this record,
+     * and flag the record as holding no data.
+     */
+    @Override
+    synchronized public void close() throws IOException {
+        if (!isClosed) {
+            isClosed = true;
+            readCipher.dispose();
+        }
+    }
+
+    // apply to SSLSocket and SSLEngine
+    void changeReadCiphers(
+            Authenticator readAuthenticator, CipherBox readCipher) {
+
+        /*
+         * Dispose of any intermediate state in the underlying cipher.
+         * For PKCS11 ciphers, this will release any attached sessions,
+         * and thus make finalization faster.
+         *
+         * Since MAC's doFinal() is called for every SSL/TLS packet, it's
+         * not necessary to do the same with MAC's.
+         */
+        readCipher.dispose();
+
+        this.readAuthenticator = readAuthenticator;
+        this.readCipher = readCipher;
+    }
+
+    // change fragment size
+    void changeFragmentSize(int fragmentSize) {
+        this.fragmentSize = fragmentSize;
+    }
+
+    /*
+     * Check if there is enough inbound data in the ByteBuffer to make
+     * a inbound packet.
+     *
+     * @return -1 if there are not enough bytes to tell (small header),
+     */
+    // apply to SSLEngine only
+    int bytesInCompletePacket(ByteBuffer buf) throws SSLException {
+        throw new UnsupportedOperationException();
+    }
+
+    // apply to SSLSocket only
+    int bytesInCompletePacket(InputStream is) throws IOException {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Return true if the specified record protocol version is out of the
+     * range of the possible supported versions.
+     */
+    void checkRecordVersion(ProtocolVersion version,
+            boolean allowSSL20Hello) throws SSLException {
+        // blank
+    }
+
+    // apply to DTLS SSLEngine only
+    Plaintext acquirePlaintext()
+            throws IOException, BadPaddingException {
+        throw new UnsupportedOperationException();
+    }
+
+    // read, decrypt and decompress the network record.
+    //
+    // apply to SSLEngine only
+    Plaintext decode(ByteBuffer netData)
+            throws IOException, BadPaddingException {
+        throw new UnsupportedOperationException();
+    }
+
+    // apply to SSLSocket only
+    Plaintext decode(InputStream is, ByteBuffer destination)
+            throws IOException, BadPaddingException {
+        throw new UnsupportedOperationException();
+    }
+
+    // apply to SSLSocket only
+    void setDeliverStream(OutputStream outputStream) {
+        throw new UnsupportedOperationException();
+    }
+
+    // calculate plaintext fragment size
+    //
+    // apply to SSLEngine only
+    int estimateFragmentSize(int packetSize) {
+        throw new UnsupportedOperationException();
     }
 
-    void decrypt(Authenticator authenticator,
-            CipherBox box) throws BadPaddingException {
+    //
+    // shared helpers
+    //
+
+    // Not apply to DTLS
+    static ByteBuffer convertToClientHello(ByteBuffer packet) {
+
+        int srcPos = packet.position();
+        int srcLim = packet.limit();
+
+        byte firstByte = packet.get();
+        byte secondByte = packet.get();
+        int recordLen = (((firstByte & 0x7F) << 8) | (secondByte & 0xFF)) + 2;
+
+        packet.position(srcPos + 3);        // the V2ClientHello record header
+
+        byte majorVersion = packet.get();
+        byte minorVersion = packet.get();
+
+        int cipherSpecLen = ((packet.get() & 0xFF) << 8) +
+                             (packet.get() & 0xFF);
+        int sessionIdLen  = ((packet.get() & 0xFF) << 8) +
+                             (packet.get() & 0xFF);
+        int nonceLen      = ((packet.get() & 0xFF) << 8) +
+                             (packet.get() & 0xFF);
+
+        // Required space for the target SSLv3 ClientHello message.
+        //  5: record header size
+        //  4: handshake header size
+        //  2: ClientHello.client_version
+        // 32: ClientHello.random
+        //  1: length byte of ClientHello.session_id
+        //  2: empty ClientHello.compression_methods
+        int requiredSize = 46 + sessionIdLen + ((cipherSpecLen * 2 ) / 3 );
+        byte[] converted = new byte[requiredSize];
+
+        /*
+         * Build the first part of the V3 record header from the V2 one
+         * that's now buffered up.  (Lengths are fixed up later).
+         */
+        // Note: need not to set the header actually.
+        converted[0] = ct_handshake;
+        converted[1] = majorVersion;
+        converted[2] = minorVersion;
+        // header [3..4] for handshake message length
+        // required size is 5;
+
+        /*
+         * Store the generic V3 handshake header:  4 bytes
+         */
+        converted[5] = 1;    // HandshakeMessage.ht_client_hello
+        // buf [6..8] for length of ClientHello (int24)
+        // required size += 4;
+
+        /*
+         * ClientHello header starts with SSL version
+         */
+        converted[9] = majorVersion;
+        converted[10] = minorVersion;
+        // required size += 2;
+        int pointer = 11;
+
+        /*
+         * Copy Random value/nonce ... if less than the 32 bytes of
+         * a V3 "Random", right justify and zero pad to the left.  Else
+         * just take the last 32 bytes.
+         */
+        int offset = srcPos + 11 + cipherSpecLen + sessionIdLen;
+
+        if (nonceLen < 32) {
+            for (int i = 0; i < (32 - nonceLen); i++) {
+                converted[pointer++] = 0;
+            }
+            packet.position(offset);
+            packet.get(converted, pointer, nonceLen);
+
+            pointer += nonceLen;
+        } else {
+            packet.position(offset + nonceLen - 32);
+            packet.get(converted, pointer, 32);
+
+            pointer += 32;
+        }
+
+        /*
+         * Copy session ID (only one byte length!)
+         */
+        offset -= sessionIdLen;
+        converted[pointer++] = (byte)(sessionIdLen & 0xFF);
+        packet.position(offset);
+        packet.get(converted, pointer, sessionIdLen);
+
+        /*
+         * Copy and translate cipher suites ... V2 specs with first byte zero
+         * are really V3 specs (in the last 2 bytes), just copy those and drop
+         * the other ones.  Preference order remains unchanged.
+         *
+         * Example:  Netscape Navigator 3.0 (exportable) says:
+         *
+         * 0/3,     SSL_RSA_EXPORT_WITH_RC4_40_MD5
+         * 0/6,     SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5
+         *
+         * Microsoft Internet Explorer 3.0 (exportable) supports only
+         *
+         * 0/3,     SSL_RSA_EXPORT_WITH_RC4_40_MD5
+         */
+        int j;
+
+        offset -= cipherSpecLen;
+        packet.position(offset);
+
+        j = pointer + 2;
+        for (int i = 0; i < cipherSpecLen; i += 3) {
+            if (packet.get() != 0) {
+                // Ignore version 2.0 specifix cipher suite.  Clients
+                // should also include the version 3.0 equivalent in
+                // the V2ClientHello message.
+                packet.get();           // ignore the 2nd byte
+                packet.get();           // ignore the 3rd byte
+                continue;
+            }
+
+            converted[j++] = packet.get();
+            converted[j++] = packet.get();
+        }
+
+        j -= pointer + 2;
+        converted[pointer++] = (byte)((j >>> 8) & 0xFF);
+        converted[pointer++] = (byte)(j & 0xFF);
+        pointer += j;
+
+        /*
+         * Append compression methods (default/null only)
+         */
+        converted[pointer++] = 1;
+        converted[pointer++] = 0;      // Session.compression_null
+
+        /*
+         * Fill in lengths of the messages we synthesized (nested:
+         * V3 handshake message within V3 record).
+         */
+        // Note: need not to set the header actually.
+        int fragLen = pointer - 5;                      // TLSPlaintext.length
+        converted[3] = (byte)((fragLen >>> 8) & 0xFF);
+        converted[4] = (byte)(fragLen & 0xFF);
+
+        /*
+         * Handshake.length, length of ClientHello message
+         */
+        fragLen = pointer - 9;                          // Handshake.length
+        converted[6] = (byte)((fragLen >>> 16) & 0xFF);
+        converted[7] = (byte)((fragLen >>> 8) & 0xFF);
+        converted[8] = (byte)(fragLen & 0xFF);
+
+        // consume the full record
+        packet.position(srcPos + recordLen);
+
+        // Need no header bytes.
+        return ByteBuffer.wrap(converted, 5, pointer - 5);  // 5: header size
+    }
+
+    static ByteBuffer decrypt(Authenticator authenticator, CipherBox box,
+            byte contentType, ByteBuffer bb) throws BadPaddingException {
+
+        return decrypt(authenticator, box, contentType, bb, null);
+    }
+
+    static ByteBuffer decrypt(Authenticator authenticator,
+            CipherBox box, byte contentType, ByteBuffer bb,
+            byte[] sequence) throws BadPaddingException {
+
         BadPaddingException reservedBPE = null;
         int tagLen =
             (authenticator instanceof MAC) ? ((MAC)authenticator).MAClen() : 0;
-        int cipheredLength = count - headerSize;
-
+        int cipheredLength = bb.remaining();
+        int srcPos = bb.position();
         if (!box.isNullCipher()) {
             try {
                 // apply explicit nonce for AEAD/CBC cipher suites if needed
-                int nonceSize = box.applyExplicitNonce(authenticator,
-                        contentType(), buf, headerSize, cipheredLength);
-                pos = headerSize + nonceSize;
-                lastHashed = pos;   // don't digest the explicit nonce
+                int nonceSize = box.applyExplicitNonce(
+                        authenticator, contentType, bb, sequence);
 
                 // decrypt the content
-                int offset = headerSize;
                 if (box.isAEADMode()) {
-                    // DON'T encrypt the nonce_explicit for AEAD mode
-                    offset += nonceSize;
+                    // DON'T decrypt the nonce_explicit for AEAD mode
+                    bb.position(srcPos + nonceSize);
                 }   // The explicit IV for CBC mode can be decrypted.
 
                 // Note that the CipherBox.decrypt() does not change
                 // the capacity of the buffer.
-                count = offset +
-                    box.decrypt(buf, offset, count - offset, tagLen);
-
-                // Note that we don't remove the nonce from the buffer.
+                box.decrypt(bb, tagLen);
+                // We don't actually remove the nonce.
+                bb.position(srcPos + nonceSize);
             } catch (BadPaddingException bpe) {
                 // RFC 2246 states that decryption_failed should be used
                 // for this purpose. However, that allows certain attacks,
@@ -187,10 +418,9 @@
 
         // Requires message authentication code for null, stream and block
         // cipher suites.
-        if (authenticator instanceof MAC && tagLen != 0) {
+        if ((authenticator instanceof MAC) && (tagLen != 0)) {
             MAC signer = (MAC)authenticator;
-            int macOffset = count - tagLen;
-            int contentLen = macOffset - pos;
+            int contentLen = bb.remaining() - tagLen;
 
             // Note that although it is not necessary, we run the same MAC
             // computation and comparison on the payload for both stream
@@ -202,19 +432,14 @@
                 }
 
                 // set offset of the dummy MAC
-                macOffset = headerSize + cipheredLength - tagLen;
-                contentLen = macOffset - headerSize;
+                contentLen = cipheredLength - tagLen;
+                bb.limit(srcPos + cipheredLength);
             }
 
-            count -= tagLen;  // Set the count before any MAC checking
-                              // exception occurs, so that the following
-                              // process can read the actual decrypted
-                              // content (minus the MAC) in the fragment
-                              // if necessary.
-
             // Run MAC computation and comparison on the payload.
-            if (checkMacTags(contentType(),
-                    buf, pos, contentLen, signer, false)) {
+            //
+            // MAC data would be stripped off during the check.
+            if (checkMacTags(contentType, bb, signer, sequence, false)) {
                 if (reservedBPE == null) {
                     reservedBPE = new BadPaddingException("bad record MAC");
                 }
@@ -229,21 +454,18 @@
                                         signer, cipheredLength, contentLen);
 
                 // NOTE: remainingLen may be bigger (less than 1 block of the
-                // hash algorithm of the MAC) than the cipheredLength. However,
-                // We won't need to worry about it because we always use a
-                // maximum buffer for every record.  We need a change here if
-                // we use small buffer size in the future.
-                if (remainingLen > buf.length) {
-                    // unlikely to happen, just a placehold
-                    throw new RuntimeException(
-                        "Internal buffer capacity error");
-                }
+                // hash algorithm of the MAC) than the cipheredLength.
+                //
+                // Is it possible to use a static buffer, rather than allocate
+                // it dynamically?
+                remainingLen += signer.MAClen();
+                ByteBuffer temporary = ByteBuffer.allocate(remainingLen);
 
                 // Won't need to worry about the result on the remainder. And
                 // then we won't need to worry about what's actual data to
                 // check MAC tag on.  We start the check from the header of the
                 // buffer so that we don't need to construct a new byte buffer.
-                checkMacTags(contentType(), buf, 0, remainingLen, signer, true);
+                checkMacTags(contentType, temporary, signer, sequence, true);
             }
         }
 
@@ -251,6 +473,63 @@
         if (reservedBPE != null) {
             throw reservedBPE;
         }
+
+        return bb.slice();
+    }
+
+    /*
+     * Run MAC computation and comparison
+     *
+     */
+    private static boolean checkMacTags(byte contentType, ByteBuffer bb,
+            MAC signer, byte[] sequence, boolean isSimulated) {
+
+        int tagLen = signer.MAClen();
+        int position = bb.position();
+        int lim = bb.limit();
+        int macOffset = lim - tagLen;
+
+        bb.limit(macOffset);
+        byte[] hash = signer.compute(contentType, bb, sequence, isSimulated);
+        if (hash == null || tagLen != hash.length) {
+            // Something is wrong with MAC implementation.
+            throw new RuntimeException("Internal MAC error");
+        }
+
+        bb.position(macOffset);
+        bb.limit(lim);
+        try {
+            int[] results = compareMacTags(bb, hash);
+            return (results[0] != 0);
+        } finally {
+            // reset to the data
+            bb.position(position);
+            bb.limit(macOffset);
+        }
+    }
+
+    /*
+     * A constant-time comparison of the MAC tags.
+     *
+     * Please DON'T change the content of the ByteBuffer parameter!
+     */
+    private static int[] compareMacTags(ByteBuffer bb, byte[] tag) {
+
+        // An array of hits is used to prevent Hotspot optimization for
+        // the purpose of a constant-time check.
+        int[] results = {0, 0};     // {missed #, matched #}
+
+        // The caller ensures there are enough bytes available in the buffer.
+        // So we won't need to check the remaining of the buffer.
+        for (int i = 0; i < tag.length; i++) {
+            if (bb.get() != tag[i]) {
+                results[0]++;       // mismatched bytes
+            } else {
+                results[1]++;       // matched bytes
+            }
+        }
+
+        return results;
     }
 
     /*
@@ -258,7 +537,7 @@
      *
      * Please DON'T change the content of the byte buffer parameter!
      */
-    static boolean checkMacTags(byte contentType, byte[] buffer,
+    private static boolean checkMacTags(byte contentType, byte[] buffer,
             int offset, int contentLen, MAC signer, boolean isSimulated) {
 
         int tagLen = signer.MAClen();
@@ -304,7 +583,7 @@
      *
      * The caller MUST ensure that the fullLen is not less than usedLen.
      */
-    static int calculateRemainingLen(
+    private static int calculateRemainingLen(
             MAC signer, int fullLen, int usedLen) {
 
         int blockLen = signer.hashBlockLen();
@@ -322,551 +601,7 @@
         // that the return value is positive.  The extra one byte does
         // not impact the overall MAC compression function evaluations.
         return 0x01 + (int)(Math.ceil(fullLen/(1.0d * blockLen)) -
-                Math.ceil(usedLen/(1.0d * blockLen))) * signer.hashBlockLen();
-    }
-
-    /*
-     * Well ... hello_request messages are _never_ hashed since we can't
-     * know when they'd appear in the sequence.
-     */
-    void ignore(int bytes) {
-        if (bytes > 0) {
-            pos += bytes;
-            lastHashed = pos;
-        }
-    }
-
-    /*
-     * We hash the (plaintext) we've processed, but only on demand.
-     *
-     * There is one place where we want to access the hash in the middle
-     * of a record:  client cert message gets hashed, and part of the
-     * same record is the client cert verify message which uses that hash.
-     * So we track how much we've read and hashed.
-     */
-    void doHashes() {
-        int len = pos - lastHashed;
-
-        if (len > 0) {
-            hashInternal(buf, lastHashed, len);
-            lastHashed = pos;
-        }
-    }
-
-    /*
-     * Need a helper function so we can hash the V2 hello correctly
-     */
-    private void hashInternal(byte databuf [], int offset, int len) {
-        if (debug != null && Debug.isOn("data")) {
-            try {
-                HexDumpEncoder hd = new HexDumpEncoder();
-
-                System.out.println("[read] MD5 and SHA1 hashes:  len = "
-                    + len);
-                hd.encodeBuffer(new ByteArrayInputStream(databuf, offset, len),
-                    System.out);
-            } catch (IOException e) { }
-        }
-        handshakeHash.update(databuf, offset, len);
-    }
-
-
-    /*
-     * Handshake messages may cross record boundaries.  We "queue"
-     * these in big buffers if we need to cope with this problem.
-     * This is not anticipated to be a common case; if this turns
-     * out to be wrong, this can readily be sped up.
-     */
-    void queueHandshake(InputRecord r) throws IOException {
-        int len;
-
-        /*
-         * Hash any data that's read but unhashed.
-         */
-        doHashes();
-
-        /*
-         * Move any unread data to the front of the buffer,
-         * flagging it all as unhashed.
-         */
-        if (pos > headerSize) {
-            len = count - pos;
-            if (len != 0) {
-                System.arraycopy(buf, pos, buf, headerSize, len);
-            }
-            pos = headerSize;
-            lastHashed = pos;
-            count = headerSize + len;
-        }
-
-        /*
-         * Grow "buf" if needed
-         */
-        len = r.available() + count;
-        if (buf.length < len) {
-            byte        newbuf [];
-
-            newbuf = new byte [len];
-            System.arraycopy(buf, 0, newbuf, 0, count);
-            buf = newbuf;
-        }
-
-        /*
-         * Append the new buffer to this one.
-         */
-        System.arraycopy(r.buf, r.pos, buf, count, len - count);
-        count = len;
-
-        /*
-         * Adjust lastHashed; important for now with clients which
-         * send SSL V2 client hellos.  This will go away eventually,
-         * by buffer code cleanup.
-         */
-        len = r.lastHashed - r.pos;
-        if (pos == headerSize) {
-            lastHashed += len;
-        } else {
-            throw new SSLProtocolException("?? confused buffer hashing ??");
-        }
-        // we've read the record, advance the pointers
-        r.pos = r.count;
-    }
-
-
-    /**
-     * Prevent any more data from being read into this record,
-     * and flag the record as holding no data.
-     */
-    @Override
-    public void close() {
-        appDataValid = false;
-        isClosed = true;
-        mark = 0;
-        pos = 0;
-        count = 0;
+                Math.ceil(usedLen/(1.0d * blockLen))) * blockLen;
     }
-
-
-    /*
-     * We may need to send this SSL v2 "No Cipher" message back, if we
-     * are faced with an SSLv2 "hello" that's not saying "I talk v3".
-     * It's the only one documented in the V2 spec as a fatal error.
-     */
-    private static final byte[] v2NoCipher = {
-        (byte)0x80, (byte)0x03, // unpadded 3 byte record
-        (byte)0x00,             // ... error message
-        (byte)0x00, (byte)0x01  // ... NO_CIPHER error
-    };
-
-    private int readFully(InputStream s, byte b[], int off, int len)
-            throws IOException {
-        int n = 0;
-        while (n < len) {
-            int readLen = s.read(b, off + n, len - n);
-            if (readLen < 0) {
-                return readLen;
-            }
-
-            if (debug != null && Debug.isOn("packet")) {
-                try {
-                    HexDumpEncoder hd = new HexDumpEncoder();
-                    ByteBuffer bb = ByteBuffer.wrap(b, off + n, readLen);
-
-                    System.out.println("[Raw read]: length = " +
-                        bb.remaining());
-                    hd.encodeBuffer(bb, System.out);
-                } catch (IOException e) { }
-            }
-
-            n += readLen;
-            exlen += readLen;
-        }
-
-        return n;
-    }
-
-    /*
-     * Read the SSL V3 record ... first time around, check to see if it
-     * really IS a V3 record.  Handle SSL V2 clients which can talk V3.0,
-     * as well as real V3 record format; otherwise report an error.
-     */
-    void read(InputStream s, OutputStream o) throws IOException {
-        if (isClosed) {
-            return;
-        }
-
-        /*
-         * For SSL it really _is_ an error if the other end went away
-         * so ungracefully as to not shut down cleanly.
-         */
-        if(exlen < headerSize) {
-            int really = readFully(s, buf, exlen, headerSize - exlen);
-            if (really < 0) {
-                throw new EOFException("SSL peer shut down incorrectly");
-            }
-
-            pos = headerSize;
-            count = headerSize;
-            lastHashed = pos;
-        }
-
-        /*
-         * The first record might use some other record marking convention,
-         * typically SSL v2 header.  (PCT could also be detected here.)
-         * This case is currently common -- Navigator 3.0 usually works
-         * this way, as do IE 3.0 and other products.
-         */
-        if (!formatVerified) {
-            formatVerified = true;
-            /*
-             * The first record must either be a handshake record or an
-             * alert message. If it's not, it is either invalid or an
-             * SSLv2 message.
-             */
-            if (buf[0] != ct_handshake && buf[0] != ct_alert) {
-                handleUnknownRecord(s, o);
-            } else {
-                readV3Record(s, o);
-            }
-        } else { // formatVerified == true
-            readV3Record(s, o);
-        }
-    }
-
-    /**
-     * Return true if the specified record protocol version is out of the
-     * range of the possible supported versions.
-     */
-    static void checkRecordVersion(ProtocolVersion version,
-            boolean allowSSL20Hello) throws SSLException {
-        // Check if the record version is too old (currently not possible)
-        // or if the major version does not match.
-        //
-        // The actual version negotiation is in the handshaker classes
-        if ((version.v < ProtocolVersion.MIN.v) ||
-            ((version.major & 0xFF) > (ProtocolVersion.MAX.major & 0xFF))) {
-
-            // if it's not SSLv2, we're out of here.
-            if (!allowSSL20Hello ||
-                    (version.v != ProtocolVersion.SSL20Hello.v)) {
-                throw new SSLException("Unsupported record version " + version);
-            }
-        }
-    }
-
-    /**
-     * Read a SSL/TLS record. Throw an IOException if the format is invalid.
-     */
-    private void readV3Record(InputStream s, OutputStream o)
-            throws IOException {
-        ProtocolVersion recordVersion = ProtocolVersion.valueOf(buf[1], buf[2]);
-
-        // check the record version
-        checkRecordVersion(recordVersion, false);
-
-        /*
-         * Get and check length, then the data.
-         */
-        int contentLen = ((buf[3] & 0x0ff) << 8) + (buf[4] & 0xff);
-
-        /*
-         * Check for upper bound.
-         */
-        if (contentLen < 0 || contentLen > maxLargeRecordSize - headerSize) {
-            throw new SSLProtocolException("Bad InputRecord size"
-                + ", count = " + contentLen
-                + ", buf.length = " + buf.length);
-        }
-
-        /*
-         * Grow "buf" if needed. Since buf is maxRecordSize by default,
-         * this only occurs when we receive records which violate the
-         * SSL specification. This is a workaround for a Microsoft SSL bug.
-         */
-        if (contentLen > buf.length - headerSize) {
-            byte[] newbuf = new byte[contentLen + headerSize];
-            System.arraycopy(buf, 0, newbuf, 0, headerSize);
-            buf = newbuf;
-        }
+}
 
-        if (exlen < contentLen + headerSize) {
-            int really = readFully(
-                s, buf, exlen, contentLen + headerSize - exlen);
-            if (really < 0) {
-                throw new SSLException("SSL peer shut down incorrectly");
-            }
-        }
-
-        // now we've got a complete record.
-        count = contentLen + headerSize;
-        exlen = 0;
-
-        if (debug != null && Debug.isOn("record")) {
-            if (count < 0 || count > (maxRecordSize - headerSize)) {
-                System.out.println(Thread.currentThread().getName()
-                    + ", Bad InputRecord size" + ", count = " + count);
-            }
-            System.out.println(Thread.currentThread().getName()
-                + ", READ: " + recordVersion + " "
-                + contentName(contentType()) + ", length = " + available());
-        }
-        /*
-         * then caller decrypts, verifies, and uncompresses
-         */
-    }
-
-    /**
-     * Deal with unknown records. Called if the first data we read on this
-     * connection does not look like an SSL/TLS record. It could a SSLv2
-     * message, or just garbage.
-     */
-    private void handleUnknownRecord(InputStream s, OutputStream o)
-            throws IOException {
-        /*
-         * No?  Oh well; does it look like a V2 "ClientHello"?
-         * That'd be an unpadded handshake message; we don't
-         * bother checking length just now.
-         */
-        if (((buf[0] & 0x080) != 0) && buf[2] == 1) {
-            /*
-             * if the user has disabled SSLv2Hello (using
-             * setEnabledProtocol) then throw an
-             * exception
-             */
-            if (helloVersion != ProtocolVersion.SSL20Hello) {
-                throw new SSLHandshakeException("SSLv2Hello is disabled");
-            }
-
-            ProtocolVersion recordVersion =
-                                ProtocolVersion.valueOf(buf[3], buf[4]);
-
-            if (recordVersion == ProtocolVersion.SSL20Hello) {
-                /*
-                 * Looks like a V2 client hello, but not one saying
-                 * "let's talk SSLv3".  So we send an SSLv2 error
-                 * message, one that's treated as fatal by clients.
-                 * (Otherwise we'll hang.)
-                 */
-                try {
-                    writeBuffer(o, v2NoCipher, 0, v2NoCipher.length);
-                } catch (Exception e) {
-                    /* NOTHING */
-                }
-                throw new SSLException("Unsupported SSL v2.0 ClientHello");
-            }
-
-            /*
-             * If we can map this into a V3 ClientHello, read and
-             * hash the rest of the V2 handshake, turn it into a
-             * V3 ClientHello message, and pass it up.
-             */
-            int len = ((buf[0] & 0x7f) << 8) +
-                (buf[1] & 0xff) - 3;
-            if (v2Buf == null) {
-                v2Buf = new byte[len];
-            }
-            if (exlen < len + headerSize) {
-                int really = readFully(
-                        s, v2Buf, exlen - headerSize, len + headerSize - exlen);
-                if (really < 0) {
-                    throw new EOFException("SSL peer shut down incorrectly");
-                }
-            }
-
-            // now we've got a complete record.
-            exlen = 0;
-
-            hashInternal(buf, 2, 3);
-            hashInternal(v2Buf, 0, len);
-            V2toV3ClientHello(v2Buf);
-            v2Buf = null;
-            lastHashed = count;
-
-            if (debug != null && Debug.isOn("record"))  {
-                System.out.println(
-                    Thread.currentThread().getName()
-                    + ", READ:  SSL v2, contentType = "
-                    + contentName(contentType())
-                    + ", translated length = " + available());
-            }
-            return;
-
-        } else {
-            /*
-             * Does it look like a V2 "ServerHello"?
-             */
-            if (((buf [0] & 0x080) != 0) && buf [2] == 4) {
-                throw new SSLException(
-                    "SSL V2.0 servers are not supported.");
-            }
-
-            /*
-             * If this is a V2 NoCipher message then this means
-             * the other server doesn't support V3. Otherwise, we just
-             * don't understand what it's saying.
-             */
-            for (int i = 0; i < v2NoCipher.length; i++) {
-                if (buf[i] != v2NoCipher[i]) {
-                    throw new SSLException(
-                        "Unrecognized SSL message, plaintext connection?");
-                }
-            }
-
-            throw new SSLException("SSL V2.0 servers are not supported.");
-        }
-    }
-
-    /*
-     * Actually do the write here.  For SSLEngine's HS data,
-     * we'll override this method and let it take the appropriate
-     * action.
-     */
-    void writeBuffer(OutputStream s, byte [] buf, int off, int len)
-            throws IOException {
-        s.write(buf, 0, len);
-        s.flush();
-    }
-
-    /*
-     * Support "old" clients which are capable of SSL V3.0 protocol ... for
-     * example, Navigator 3.0 clients.  The V2 message is in the header and
-     * the bytes passed as parameter.  This routine translates the V2 message
-     * into an equivalent V3 one.
-     */
-    private void V2toV3ClientHello(byte v2Msg []) throws SSLException
-    {
-        int i;
-
-        /*
-         * Build the first part of the V3 record header from the V2 one
-         * that's now buffered up.  (Lengths are fixed up later).
-         */
-        buf [0] = ct_handshake;
-        buf [1] = buf [3];      // V3.x
-        buf[2] = buf[4];
-        // header [3..4] for handshake message length
-        // count = 5;
-
-        /*
-         * Store the generic V3 handshake header:  4 bytes
-         */
-        buf [5] = 1;    // HandshakeMessage.ht_client_hello
-        // buf [6..8] for length of ClientHello (int24)
-        // count += 4;
-
-        /*
-         * ClientHello header starts with SSL version
-         */
-        buf [9] = buf [1];
-        buf [10] = buf [2];
-        // count += 2;
-        count = 11;
-
-        /*
-         * Start parsing the V2 message ...
-         */
-        int      cipherSpecLen, sessionIdLen, nonceLen;
-
-        cipherSpecLen = ((v2Msg [0] & 0xff) << 8) + (v2Msg [1] & 0xff);
-        sessionIdLen  = ((v2Msg [2] & 0xff) << 8) + (v2Msg [3] & 0xff);
-        nonceLen   = ((v2Msg [4] & 0xff) << 8) + (v2Msg [5] & 0xff);
-
-        /*
-         * Copy Random value/nonce ... if less than the 32 bytes of
-         * a V3 "Random", right justify and zero pad to the left.  Else
-         * just take the last 32 bytes.
-         */
-        int      offset = 6 + cipherSpecLen + sessionIdLen;
-
-        if (nonceLen < 32) {
-            for (i = 0; i < (32 - nonceLen); i++)
-                buf [count++] = 0;
-            System.arraycopy(v2Msg, offset, buf, count, nonceLen);
-            count += nonceLen;
-        } else {
-            System.arraycopy(v2Msg, offset + (nonceLen - 32),
-                    buf, count, 32);
-            count += 32;
-        }
-
-        /*
-         * Copy Session ID (only one byte length!)
-         */
-        offset -= sessionIdLen;
-        buf [count++] = (byte) sessionIdLen;
-
-        System.arraycopy(v2Msg, offset, buf, count, sessionIdLen);
-        count += sessionIdLen;
-
-        /*
-         * Copy and translate cipher suites ... V2 specs with first byte zero
-         * are really V3 specs (in the last 2 bytes), just copy those and drop
-         * the other ones.  Preference order remains unchanged.
-         *
-         * Example:  Netscape Navigator 3.0 (exportable) says:
-         *
-         * 0/3,     SSL_RSA_EXPORT_WITH_RC4_40_MD5
-         * 0/6,     SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5
-         *
-         * Microsoft Internet Explorer 3.0 (exportable) supports only
-         *
-         * 0/3,     SSL_RSA_EXPORT_WITH_RC4_40_MD5
-         */
-        int j;
-
-        offset -= cipherSpecLen;
-        j = count + 2;
-
-        for (i = 0; i < cipherSpecLen; i += 3) {
-            if (v2Msg [offset + i] != 0)
-                continue;
-            buf [j++] = v2Msg [offset + i + 1];
-            buf [j++] = v2Msg [offset + i + 2];
-        }
-
-        j -= count + 2;
-        buf [count++] = (byte) (j >>> 8);
-        buf [count++] = (byte) j;
-        count += j;
-
-        /*
-         * Append compression methods (default/null only)
-         */
-        buf [count++] = 1;
-        buf [count++] = 0;      // Session.compression_null
-
-        /*
-         * Fill in lengths of the messages we synthesized (nested:
-         * V3 handshake message within V3 record) and then return
-         */
-        buf [3] = (byte) (count - headerSize);
-        buf [4] = (byte) ((count - headerSize) >>> 8);
-
-        buf [headerSize + 1] = 0;
-        buf [headerSize + 2] = (byte) (((count - headerSize) - 4) >>> 8);
-        buf [headerSize + 3] = (byte) ((count - headerSize) - 4);
-
-        pos = headerSize;
-    }
-
-    /**
-     * Return a description for the given content type. This method should be
-     * in Record, but since that is an interface this is not possible.
-     * Called from InputRecord and OutputRecord.
-     */
-    static String contentName(int contentType) {
-        switch (contentType) {
-        case ct_change_cipher_spec:
-            return "Change Cipher Spec";
-        case ct_alert:
-            return "Alert";
-        case ct_handshake:
-            return "Handshake";
-        case ct_application_data:
-            return "Application Data";
-        default:
-            return "contentType = " + contentType;
-        }
-    }
-
-}
--- a/jdk/src/java.base/share/classes/sun/security/ssl/JsseJce.java	Mon Jun 01 10:15:21 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/JsseJce.java	Tue Jun 02 09:15:47 2015 -0700
@@ -64,24 +64,9 @@
     // If true, then all the Kerberos-based crypto we need is available.
     private final static boolean kerberosAvailable;
     static {
-        boolean temp;
-        try {
-            AccessController.doPrivileged(
-                new PrivilegedExceptionAction<Void>() {
-                    @Override
-                    public Void run() throws Exception {
-                        // Test for Kerberos using the bootstrap class loader
-                        Class.forName("sun.security.krb5.PrincipalName", true,
-                                null);
-                        return null;
-                    }
-                });
-            temp = true;
-
-        } catch (Exception e) {
-            temp = false;
-        }
-        kerberosAvailable = temp;
+        ClientKeyExchangeService p =
+                ClientKeyExchangeService.find("KRB5");
+        kerberosAvailable = (p != null);
     }
 
     static {
--- a/jdk/src/java.base/share/classes/sun/security/ssl/KerberosClientKeyExchange.java	Mon Jun 01 10:15:21 2015 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,154 +0,0 @@
-/*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package sun.security.ssl;
-
-import java.io.IOException;
-import java.io.PrintStream;
-import java.security.AccessController;
-import java.security.AccessControlContext;
-import java.security.Principal;
-import java.security.PrivilegedAction;
-import java.security.SecureRandom;
-import javax.crypto.SecretKey;
-
-/**
- * A helper class that calls the KerberosClientKeyExchange implementation.
- */
-public class KerberosClientKeyExchange extends HandshakeMessage {
-
-    private static final String IMPL_CLASS =
-        "sun.security.ssl.krb5.KerberosClientKeyExchangeImpl";
-
-    private static final Class<?> implClass = AccessController.doPrivileged(
-            new PrivilegedAction<Class<?>>() {
-                @Override
-                public Class<?> run() {
-                    try {
-                        return Class.forName(IMPL_CLASS, true, null);
-                    } catch (ClassNotFoundException cnf) {
-                        return null;
-                    }
-                }
-            }
-        );
-    private final KerberosClientKeyExchange impl = createImpl();
-
-    private KerberosClientKeyExchange createImpl() {
-        if (implClass != null &&
-                getClass() == KerberosClientKeyExchange.class) {
-            try {
-                return (KerberosClientKeyExchange)implClass.newInstance();
-            } catch (InstantiationException e) {
-                throw new AssertionError(e);
-            } catch (IllegalAccessException e) {
-                throw new AssertionError(e);
-            }
-        }
-        return null;
-    }
-
-    // This constructor will be called when constructing an instance of its
-    // subclass -- KerberosClientKeyExchangeImpl.  Please won't check the
-    // value of impl variable in this constructor.
-    protected KerberosClientKeyExchange() {
-        // please won't check the value of impl variable
-    }
-
-    public KerberosClientKeyExchange(String serverName,
-        AccessControlContext acc, ProtocolVersion protocolVersion,
-        SecureRandom rand) throws IOException {
-
-        if (impl != null) {
-            init(serverName, acc, protocolVersion, rand);
-        } else {
-            throw new IllegalStateException("Kerberos is unavailable");
-        }
-    }
-
-    public KerberosClientKeyExchange(ProtocolVersion protocolVersion,
-            ProtocolVersion clientVersion, SecureRandom rand,
-            HandshakeInStream input, AccessControlContext acc,
-            Object serverKeys) throws IOException {
-
-        if (impl != null) {
-            init(protocolVersion, clientVersion, rand, input, acc, serverKeys);
-        } else {
-            throw new IllegalStateException("Kerberos is unavailable");
-        }
-    }
-
-    @Override
-    int messageType() {
-        return ht_client_key_exchange;
-    }
-
-    @Override
-    public int messageLength() {
-        return impl.messageLength();
-    }
-
-    @Override
-    public void send(HandshakeOutStream s) throws IOException {
-        impl.send(s);
-    }
-
-    @Override
-    public void print(PrintStream p) throws IOException {
-        impl.print(p);
-    }
-
-    public void init(String serverName,
-        AccessControlContext acc, ProtocolVersion protocolVersion,
-        SecureRandom rand) throws IOException {
-
-        if (impl != null) {
-            impl.init(serverName, acc, protocolVersion, rand);
-        }
-    }
-
-    public void init(ProtocolVersion protocolVersion,
-            ProtocolVersion clientVersion, SecureRandom rand,
-            HandshakeInStream input, AccessControlContext acc,
-            Object ServiceCreds) throws IOException {
-
-        if (impl != null) {
-            impl.init(protocolVersion, clientVersion,
-                                    rand, input, acc, ServiceCreds);
-        }
-    }
-
-    public byte[] getUnencryptedPreMasterSecret() {
-        return impl.getUnencryptedPreMasterSecret();
-    }
-
-    public Principal getPeerPrincipal(){
-        return impl.getPeerPrincipal();
-    }
-
-    public Principal getLocalPrincipal(){
-        return impl.getLocalPrincipal();
-    }
-}
--- a/jdk/src/java.base/share/classes/sun/security/ssl/Krb5Helper.java	Mon Jun 01 10:15:21 2015 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,135 +0,0 @@
-/*
- * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package sun.security.ssl;
-
-import java.security.AccessControlContext;
-import java.security.AccessController;
-import java.security.Permission;
-import java.security.Principal;
-import java.security.PrivilegedAction;
-import javax.crypto.SecretKey;
-import javax.security.auth.Subject;
-import javax.security.auth.login.LoginException;
-
-/**
- * A helper class for Kerberos APIs.
- */
-public final class Krb5Helper {
-
-    private Krb5Helper() { }
-
-    // loads Krb5Proxy implementation class if available
-    private static final String IMPL_CLASS =
-        "sun.security.ssl.krb5.Krb5ProxyImpl";
-
-    private static final Krb5Proxy proxy =
-        AccessController.doPrivileged(new PrivilegedAction<Krb5Proxy>() {
-            @Override
-            public Krb5Proxy run() {
-                try {
-                    Class<?> c = Class.forName(IMPL_CLASS, true, null);
-                    return (Krb5Proxy)c.newInstance();
-                } catch (ClassNotFoundException cnf) {
-                    return null;
-                } catch (InstantiationException e) {
-                    throw new AssertionError(e);
-                } catch (IllegalAccessException e) {
-                    throw new AssertionError(e);
-                }
-            }});
-
-    /**
-     * Returns true if Kerberos is available.
-     */
-    public static boolean isAvailable() {
-        return proxy != null;
-    }
-
-    private static void ensureAvailable() {
-        if (proxy == null)
-            throw new AssertionError("Kerberos should have been available");
-    }
-
-    /**
-     * Returns the Subject associated with client-side of the SSL socket.
-     */
-    public static Subject getClientSubject(AccessControlContext acc)
-            throws LoginException {
-        ensureAvailable();
-        return proxy.getClientSubject(acc);
-    }
-
-    /**
-     * Returns the Subject associated with server-side of the SSL socket.
-     */
-    public static Subject getServerSubject(AccessControlContext acc)
-            throws LoginException {
-        ensureAvailable();
-        return proxy.getServerSubject(acc);
-    }
-
-    /**
-     * Returns the KerberosKeys for the default server-side principal.
-     */
-    public static Object getServiceCreds(AccessControlContext acc)
-            throws LoginException {
-        ensureAvailable();
-        return proxy.getServiceCreds(acc);
-    }
-
-    /**
-     * Returns the server-side principal name associated with the KerberosKey.
-     */
-    public static String getServerPrincipalName(Object serviceCreds) {
-        ensureAvailable();
-        return proxy.getServerPrincipalName(serviceCreds);
-    }
-
-    /**
-     * Returns the hostname embedded in the principal name.
-     */
-    public static String getPrincipalHostName(Principal principal) {
-        ensureAvailable();
-        return proxy.getPrincipalHostName(principal);
-    }
-
-    /**
-     * Returns a ServicePermission for the principal name and action.
-     */
-    public static Permission getServicePermission(String principalName,
-            String action) {
-        ensureAvailable();
-        return proxy.getServicePermission(principalName, action);
-    }
-
-    /**
-     * Determines if the Subject might contain creds for princ.
-     */
-    public static boolean isRelated(Subject subject, Principal princ) {
-        ensureAvailable();
-        return proxy.isRelated(subject, princ);
-    }
-}
--- a/jdk/src/java.base/share/classes/sun/security/ssl/Krb5Proxy.java	Mon Jun 01 10:15:21 2015 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,76 +0,0 @@
-/*
- * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package sun.security.ssl;
-
-import java.security.AccessControlContext;
-import java.security.Permission;
-import java.security.Principal;
-import javax.crypto.SecretKey;
-import javax.security.auth.Subject;
-import javax.security.auth.login.LoginException;
-
-/**
- * An interface to a subset of the Kerberos APIs to avoid a static dependency
- * on the types defined by these APIs.
- */
-public interface Krb5Proxy {
-
-    /**
-     * Returns the Subject associated with the client-side of the SSL socket.
-     */
-    Subject getClientSubject(AccessControlContext acc) throws LoginException;
-
-    /**
-     * Returns the Subject associated with the server-side of the SSL socket.
-     */
-    Subject getServerSubject(AccessControlContext acc) throws LoginException;
-
-
-    /**
-     * Returns the Kerberos ServiceCreds for the default server-side principal.
-     */
-    Object getServiceCreds(AccessControlContext acc) throws LoginException;
-
-    /**
-     * Returns the server-side principal name associated with the KerberosKey.
-     */
-    String getServerPrincipalName(Object serviceCreds);
-
-    /**
-     * Returns the hostname embedded in the principal name.
-     */
-    String getPrincipalHostName(Principal principal);
-
-    /**
-     * Returns a ServicePermission for the principal name and action.
-     */
-    Permission getServicePermission(String principalName, String action);
-
-    /**
-     * Determines if the Subject might contain creds for princ.
-     */
-    boolean isRelated(Subject subject, Principal princ);
-}
--- a/jdk/src/java.base/share/classes/sun/security/ssl/MAC.java	Mon Jun 01 10:15:21 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/MAC.java	Tue Jun 02 09:15:47 2015 -0700
@@ -23,7 +23,6 @@
  * questions.
  */
 
-
 package sun.security.ssl;
 
 import java.security.InvalidKeyException;
@@ -50,24 +49,26 @@
  */
 final class MAC extends Authenticator {
 
-    final static MAC NULL = new MAC();
+    final static MAC TLS_NULL = new MAC(false);
 
     // Value of the null MAC is fixed
     private static final byte nullMAC[] = new byte[0];
 
     // internal identifier for the MAC algorithm
-    private final MacAlg        macAlg;
+    private final MacAlg macAlg;
 
     // JCE Mac object
     private final Mac mac;
 
-    private MAC() {
+    MAC(boolean isDTLS) {
+        super(isDTLS);
+
         macAlg = M_NULL;
         mac = null;
     }
 
     /**
-     * Set up, configured for the given SSL/TLS MAC type and version.
+     * Set up, configured for the given MAC type and version.
      */
     MAC(MacAlg macAlg, ProtocolVersion protocolVersion, SecretKey key)
             throws NoSuchAlgorithmException, InvalidKeyException {
@@ -75,12 +76,14 @@
         this.macAlg = macAlg;
 
         String algorithm;
-        boolean tls = (protocolVersion.v >= ProtocolVersion.TLS10.v);
+
+        // using SSL MAC computation?
+        boolean useSSLMac = (protocolVersion.v < ProtocolVersion.TLS10.v);
 
         if (macAlg == M_MD5) {
-            algorithm = tls ? "HmacMD5" : "SslMacMD5";
+            algorithm = useSSLMac ? "SslMacMD5" : "HmacMD5";
         } else if (macAlg == M_SHA) {
-            algorithm = tls ? "HmacSHA1" : "SslMacSHA1";
+            algorithm = useSSLMac ? "SslMacSHA1" : "HmacSHA1";
         } else if (macAlg == M_SHA256) {
             algorithm = "HmacSHA256";    // TLS 1.2+
         } else if (macAlg == M_SHA384) {
@@ -122,6 +125,8 @@
      * @param offset start of compressed record data
      * @param len the size of the compressed record
      * @param isSimulated if true, simulate the MAC computation
+     *
+     * @return the MAC result
      */
     final byte[] compute(byte type, byte buf[],
             int offset, int len, boolean isSimulated) {
@@ -130,7 +135,8 @@
         }
 
         if (!isSimulated) {
-            byte[] additional = acquireAuthenticationBytes(type, len);
+            // Uses the implicit sequence number for the computation.
+            byte[] additional = acquireAuthenticationBytes(type, len, null);
             mac.update(additional);
         }
         mac.update(buf, offset, len);
@@ -149,15 +155,22 @@
      * @param bb a ByteBuffer in which the position and limit
      *          demarcate the data to be MAC'd.
      * @param isSimulated if true, simulate the MAC computation
+     * @param sequence the explicit sequence number, or null if using
+     *        the implicit sequence number for the computation
+     *
+     * @return the MAC result
      */
-    final byte[] compute(byte type, ByteBuffer bb, boolean isSimulated) {
+    final byte[] compute(byte type, ByteBuffer bb,
+            byte[] sequence, boolean isSimulated) {
+
         if (macAlg.size == 0) {
             return nullMAC;
         }
 
         if (!isSimulated) {
+            // Uses the explicit sequence number for the computation.
             byte[] additional =
-                    acquireAuthenticationBytes(type, bb.remaining());
+                    acquireAuthenticationBytes(type, bb.remaining(), sequence);
             mac.update(additional);
         }
         mac.update(bb);
@@ -165,5 +178,22 @@
         return mac.doFinal();
     }
 
+    /**
+     * Compute and returns the MAC for the remaining data
+     * in this ByteBuffer.
+     *
+     * On return, the bb position == limit, and limit will
+     * have not changed.
+     *
+     * @param type record type
+     * @param bb a ByteBuffer in which the position and limit
+     *        demarcate the data to be MAC'd.
+     * @param isSimulated if true, simulate the the MAC computation
+     *
+     * @return the MAC result
+     */
+    final byte[] compute(byte type, ByteBuffer bb, boolean isSimulated) {
+        // Uses the implicit sequence number for the computation.
+        return compute(type, bb, null, isSimulated);
+    }
 }
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/MaxFragmentLengthExtension.java	Tue Jun 02 09:15:47 2015 -0700
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ssl;
+
+import java.io.IOException;
+import javax.net.ssl.SSLProtocolException;
+
+/*
+ * [RFC6066] TLS specifies a fixed maximum plaintext fragment length of
+ * 2^14 bytes.  It may be desirable for constrained clients to negotiate
+ * a smaller maximum fragment length due to memory limitations or bandwidth
+ * limitations.
+ *
+ * In order to negotiate smaller maximum fragment lengths, clients MAY
+ * include an extension of type "max_fragment_length" in the (extended)
+ * client hello.  The "extension_data" field of this extension SHALL
+ * contain:
+ *
+ *    enum{
+ *        2^9(1), 2^10(2), 2^11(3), 2^12(4), (255)
+ *    } MaxFragmentLength;
+ *
+ * whose value is the desired maximum fragment length.
+ */
+final class MaxFragmentLengthExtension extends HelloExtension {
+
+    private static final int MAX_FRAGMENT_LENGTH_512  = 1;      // 2^9
+    private static final int MAX_FRAGMENT_LENGTH_1024 = 2;      // 2^10
+    private static final int MAX_FRAGMENT_LENGTH_2048 = 3;      // 2^11
+    private static final int MAX_FRAGMENT_LENGTH_4096 = 4;      // 2^12
+
+    final int maxFragmentLength;
+
+    MaxFragmentLengthExtension(int fragmentSize) {
+        super(ExtensionType.EXT_MAX_FRAGMENT_LENGTH);
+
+        if (fragmentSize < 1024) {
+            maxFragmentLength = MAX_FRAGMENT_LENGTH_512;
+        } else if (fragmentSize < 2048) {
+            maxFragmentLength = MAX_FRAGMENT_LENGTH_1024;
+        } else if (fragmentSize < 4096) {
+            maxFragmentLength = MAX_FRAGMENT_LENGTH_2048;
+        } else {
+            maxFragmentLength = MAX_FRAGMENT_LENGTH_4096;
+        }
+    }
+
+    MaxFragmentLengthExtension(HandshakeInStream s, int len)
+                throws IOException {
+        super(ExtensionType.EXT_MAX_FRAGMENT_LENGTH);
+
+        // check the extension length
+        if (len != 1) {
+            throw new SSLProtocolException("Invalid " + type + " extension");
+        }
+
+        maxFragmentLength = s.getInt8();
+        if ((maxFragmentLength > 4) || (maxFragmentLength < 1)) {
+            throw new SSLProtocolException("Invalid " + type + " extension");
+        }
+    }
+
+    // Length of the encoded extension, including the type and length fields
+    @Override
+    int length() {
+        return 5;               // 4: extension type and length fields
+                                // 1: MaxFragmentLength field
+    }
+
+    @Override
+    void send(HandshakeOutStream s) throws IOException {
+        s.putInt16(type.id);
+        s.putInt16(1);
+        s.putInt8(maxFragmentLength);
+    }
+
+    int getMaxFragLen() {
+        switch (maxFragmentLength) {
+            case MAX_FRAGMENT_LENGTH_512:
+                return 512;
+            case MAX_FRAGMENT_LENGTH_1024:
+                return 1024;
+            case MAX_FRAGMENT_LENGTH_2048:
+                return 2048;
+            case MAX_FRAGMENT_LENGTH_4096:
+                return 4096;
+        }
+
+        // unlikely to happen
+        return -1;
+    }
+
+    static boolean needFragLenNego(int fragmentSize) {
+        return (fragmentSize > 0) && (fragmentSize <= 4096);
+    }
+
+    static int getValidMaxFragLen(int fragmentSize) {
+        if (fragmentSize < 1024) {
+            return 512;
+        } else if (fragmentSize < 2048) {
+            return 1024;
+        } else if (fragmentSize < 4096) {
+            return 2048;
+        } else if (fragmentSize == 4096) {
+            return 4096;
+        } else {
+            return 16384;
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "Extension " + type + ", max_fragment_length: " +
+                "(2^" + (maxFragmentLength + 8) + ")";
+    }
+}
--- a/jdk/src/java.base/share/classes/sun/security/ssl/OutputRecord.java	Mon Jun 01 10:15:21 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/OutputRecord.java	Tue Jun 02 09:15:47 2015 -0700
@@ -23,7 +23,6 @@
  * questions.
  */
 
-
 package sun.security.ssl;
 
 import java.io.*;
@@ -35,92 +34,61 @@
 
 
 /**
- * SSL 3.0 records, as written to a TCP stream.
- *
- * Each record has a message area that starts out with data supplied by the
- * application.  It may grow/shrink due to compression and will be modified
- * in place for mac-ing and encryption.
- *
- * Handshake records have additional needs, notably accumulation of a set
- * of hashes which are used to establish that handshaking was done right.
- * Handshake records usually have several handshake messages each, and we
- * need message-level control over what's hashed.
+ * {@code OutputRecord} takes care of the management of SSL/TLS/DTLS output
+ * records, including buffering, encryption, handshake messages marshal, etc.
  *
  * @author David Brownell
  */
-class OutputRecord extends ByteArrayOutputStream implements Record {
+abstract class OutputRecord extends ByteArrayOutputStream
+            implements Record, Closeable {
+
+    /* Class and subclass dynamic debugging support */
+    static final Debug          debug = Debug.getInstance("ssl");
 
-    private HandshakeHash       handshakeHash;
-    private int                 lastHashed;
-    private boolean             firstMessage;
-    final private byte          contentType;
-    private int                 headerOffset;
+    Authenticator               writeAuthenticator;
+    CipherBox                   writeCipher;
+
+    HandshakeHash               handshakeHash;
+    boolean                     firstMessage;
 
     // current protocol version, sent as record version
-    ProtocolVersion     protocolVersion;
+    ProtocolVersion             protocolVersion;
 
     // version for the ClientHello message. Only relevant if this is a
     // client handshake record. If set to ProtocolVersion.SSL20Hello,
     // the V3 client hello is converted to V2 format.
-    private ProtocolVersion     helloVersion;
+    ProtocolVersion             helloVersion;
+
+    // Is it the first application record to write?
+    boolean                     isFirstAppOutputRecord = true;
 
-    /* Class and subclass dynamic debugging support */
-    static final Debug debug = Debug.getInstance("ssl");
+    // packet size
+    int                         packetSize;
+
+    // fragment size
+    int                         fragmentSize;
+
+    // closed or not?
+    boolean                     isClosed;
 
     /*
-     * Default constructor makes a record supporting the maximum
-     * SSL record size.  It allocates the header bytes directly.
-     *
-     * The structure of the byte buffer looks like:
-     *
-     *     |---------+--------+-------+---------------------------------|
-     *     | unused  | header |  IV   | content, MAC/TAG, padding, etc. |
-     *     |    headerPlusMaxIVSize   |
-     *
-     * unused: unused part of the buffer of size
-     *
-     *             headerPlusMaxIVSize - header size - IV size
-     *
-     *         When this object is created, we don't know the protocol
-     *         version number, IV length, etc., so reserve space in front
-     *         to avoid extra data movement (copies).
-     * header: the header of an SSL record
-     * IV:     the optional IV/nonce field, it is only required for block
-     *         (TLS 1.1 or later) and AEAD cipher suites.
-     *
-     * @param type the content type for the record
+     * Mappings from V3 cipher suite encodings to their pure V2 equivalents.
+     * This is taken from the SSL V3 specification, Appendix E.
      */
-    OutputRecord(byte type, int size) {
-        super(size);
-        this.protocolVersion = ProtocolVersion.DEFAULT;
-        this.helloVersion = ProtocolVersion.DEFAULT_HELLO;
-        firstMessage = true;
-        count = headerPlusMaxIVSize;
-        contentType = type;
-        lastHashed = count;
-        headerOffset = headerPlusMaxIVSize - headerSize;
+    private static int[] V3toV2CipherMap1 =
+        {-1, -1, -1, 0x02, 0x01, -1, 0x04, 0x05, -1, 0x06, 0x07};
+    private static int[] V3toV2CipherMap3 =
+        {-1, -1, -1, 0x80, 0x80, -1, 0x80, 0x80, -1, 0x40, 0xC0};
+
+    OutputRecord() {
+        this.writeCipher = CipherBox.NULL;
+        this.firstMessage = true;
+        this.fragmentSize = Record.maxDataSize;
+
+        // Please set packetSize and protocolVersion in the implementation.
     }
 
-    OutputRecord(byte type) {
-        this(type, recordSize(type));
-    }
-
-    /**
-     * Get the size of the buffer we need for records of the specified
-     * type.
-     */
-    private static int recordSize(byte type) {
-        if ((type == ct_change_cipher_spec) || (type == ct_alert)) {
-            return maxAlertRecordSize;
-        } else {
-            return maxRecordSize;
-        }
-    }
-
-    /*
-     * Updates the SSL version of this record.
-     */
-    synchronized void setVersion(ProtocolVersion protocolVersion) {
+    void setVersion(ProtocolVersion protocolVersion) {
         this.protocolVersion = protocolVersion;
     }
 
@@ -132,441 +100,364 @@
     }
 
     /*
-     * Reset the record so that it can be refilled, starting
-     * immediately after the header.
-     */
-    @Override
-    public synchronized void reset() {
-        super.reset();
-        count = headerPlusMaxIVSize;
-        lastHashed = count;
-        headerOffset = headerPlusMaxIVSize - headerSize;
-    }
-
-    /*
      * For handshaking, we need to be able to hash every byte above the
      * record marking layer.  This is where we're guaranteed to see those
      * bytes, so this is where we can hash them.
      */
     void setHandshakeHash(HandshakeHash handshakeHash) {
-        assert(contentType == ct_handshake);
         this.handshakeHash = handshakeHash;
     }
 
     /*
-     * We hash (the plaintext) on demand.  There is one place where
-     * we want to access the hash in the middle of a record:  client
-     * cert message gets hashed, and part of the same record is the
-     * client cert verify message which uses that hash.  So we track
-     * how much of each record we've hashed so far.
-     */
-    void doHashes() {
-        int len = count - lastHashed;
-
-        if (len > 0) {
-            hashInternal(buf, lastHashed, len);
-            lastHashed = count;
-        }
-    }
-
-    /*
-     * Need a helper function so we can hash the V2 hello correctly
-     */
-    private void hashInternal(byte buf [], int offset, int len) {
-        if (debug != null && Debug.isOn("data")) {
-            try {
-                HexDumpEncoder hd = new HexDumpEncoder();
-
-                System.out.println("[write] MD5 and SHA1 hashes:  len = "
-                    + len);
-                hd.encodeBuffer(new ByteArrayInputStream(buf,
-                    lastHashed, len), System.out);
-            } catch (IOException e) { }
-        }
-
-        handshakeHash.update(buf, lastHashed, len);
-        lastHashed = count;
-    }
-
-    /*
      * Return true iff the record is empty -- to avoid doing the work
      * of sending empty records over the network.
      */
     boolean isEmpty() {
-        return count == headerPlusMaxIVSize;
-    }
-
-    /*
-     * Return true if the record is of an alert of the given description.
-     *
-     * Per SSL/TLS specifications, alert messages convey the severity of the
-     * message (warning or fatal) and a description of the alert. An alert
-     * is defined with a two bytes struct, {byte level, byte description},
-     * following after the header bytes.
-     */
-    boolean isAlert(byte description) {
-        if ((count > (headerPlusMaxIVSize + 1)) && (contentType == ct_alert)) {
-            return buf[headerPlusMaxIVSize + 1] == description;
-        }
-
         return false;
     }
 
-    /*
-     * Encrypt ... length may grow due to block cipher padding, or
-     * message authentication code or tag.
-     */
-    void encrypt(Authenticator authenticator, CipherBox box)
-            throws IOException {
+    boolean seqNumIsHuge() {
+        return (writeAuthenticator != null) &&
+                        writeAuthenticator.seqNumIsHuge();
+    }
 
-        // In case we are automatically flushing a handshake stream, make
-        // sure we have hashed the message first.
-        //
-        // when we support compression, hashing can't go here
-        // since it'll need to be done on the uncompressed data,
-        // and the MAC applies to the compressed data.
-        if (contentType == ct_handshake) {
-            doHashes();
-        }
+    // SSLEngine and SSLSocket
+    abstract void encodeAlert(byte level, byte description) throws IOException;
+
+    // SSLEngine and SSLSocket
+    abstract void encodeHandshake(byte[] buffer,
+            int offset, int length) throws IOException;
+
+    // SSLEngine and SSLSocket
+    abstract void encodeChangeCipherSpec() throws IOException;
 
-        // Requires message authentication code for stream and block
-        // cipher suites.
-        if (authenticator instanceof MAC) {
-            MAC signer = (MAC)authenticator;
-            if (signer.MAClen() != 0) {
-                byte[] hash = signer.compute(contentType, buf,
-                    headerPlusMaxIVSize, count - headerPlusMaxIVSize, false);
-                write(hash);
-            }
-        }
+    // apply to SSLEngine only
+    Ciphertext encode(ByteBuffer[] sources, int offset, int length,
+            ByteBuffer destination) throws IOException {
+        throw new UnsupportedOperationException();
+    }
+
+    // apply to SSLEngine only
+    void encodeV2NoCipher() throws IOException {
+        throw new UnsupportedOperationException();
+    }
 
-        if (!box.isNullCipher()) {
-            // Requires explicit IV/nonce for CBC/AEAD cipher suites for
-            // TLS 1.1 or later.
-            if ((protocolVersion.v >= ProtocolVersion.TLS11.v) &&
-                                    (box.isCBCMode() || box.isAEADMode())) {
-                byte[] nonce = box.createExplicitNonce(authenticator,
-                                    contentType, count - headerPlusMaxIVSize);
-                int offset = headerPlusMaxIVSize - nonce.length;
-                System.arraycopy(nonce, 0, buf, offset, nonce.length);
-                headerOffset = offset - headerSize;
-            } else {
-                headerOffset = headerPlusMaxIVSize - headerSize;
-            }
+    // apply to SSLSocket only
+    void deliver(byte[] source, int offset, int length) throws IOException {
+        throw new UnsupportedOperationException();
+    }
 
-            // encrypt the content
-            int offset = headerPlusMaxIVSize;
-            if (!box.isAEADMode()) {
-                // The explicit IV can be encrypted.
-                offset = headerOffset + headerSize;
-            }   // Otherwise, DON'T encrypt the nonce_explicit for AEAD mode
+    // apply to SSLSocket only
+    void setDeliverStream(OutputStream outputStream) {
+        throw new UnsupportedOperationException();
+    }
 
-            count = offset + box.encrypt(buf, offset, count - offset);
-        }
+    // apply to SSLEngine only
+    Ciphertext acquireCiphertext(ByteBuffer destination) throws IOException {
+        throw new UnsupportedOperationException();
     }
 
-    /*
-     * Tell how full the buffer is ... for filling it with application or
-     * handshake data.
-     */
-    final int availableDataBytes() {
-        int dataSize = count - headerPlusMaxIVSize;
-        return maxDataSize - dataSize;
+    void changeWriteCiphers(Authenticator writeAuthenticator,
+            CipherBox writeCipher) throws IOException {
+
+        encodeChangeCipherSpec();
+
+        /*
+         * Dispose of any intermediate state in the underlying cipher.
+         * For PKCS11 ciphers, this will release any attached sessions,
+         * and thus make finalization faster.
+         *
+         * Since MAC's doFinal() is called for every SSL/TLS packet, it's
+         * not necessary to do the same with MAC's.
+         */
+        writeCipher.dispose();
+
+        this.writeAuthenticator = writeAuthenticator;
+        this.writeCipher = writeCipher;
+        this.isFirstAppOutputRecord = true;
     }
 
-    /*
-     * Increases the capacity if necessary to ensure that it can hold
-     * at least the number of elements specified by the minimum
-     * capacity argument.
-     *
-     * Note that the increased capacity is only can be used for held
-     * record buffer. Please DO NOT update the availableDataBytes()
-     * according to the expended buffer capacity.
-     *
-     * @see availableDataBytes()
-     */
-    private void ensureCapacity(int minCapacity) {
-        // overflow-conscious code
-        if (minCapacity > buf.length) {
-            buf = Arrays.copyOf(buf, minCapacity);
+    void changePacketSize(int packetSize) {
+        this.packetSize = packetSize;
+    }
+
+    void changeFragmentSize(int fragmentSize) {
+        this.fragmentSize = fragmentSize;
+    }
+
+    int getMaxPacketSize() {
+        return packetSize;
+    }
+
+    // apply to DTLS SSLEngine
+    void initHandshaker() {
+        // blank
+    }
+
+    @Override
+    synchronized public void close() throws IOException {
+        if (!isClosed) {
+            isClosed = true;
+            writeCipher.dispose();
         }
     }
 
-    /*
-     * Return the type of SSL record that's buffered here.
-     */
-    final byte contentType() {
-        return contentType;
-    }
+    //
+    // shared helpers
+    //
+
+    // Encrypt a fragment and wrap up a record.
+    //
+    // To be consistent with the spec of SSLEngine.wrap() methods, the
+    // destination ByteBuffer's position is updated to reflect the amount
+    // of data produced.  The limit remains the same.
+    static long encrypt(Authenticator authenticator,
+            CipherBox encCipher, byte contentType, ByteBuffer destination,
+            int headerOffset, int dstLim, int headerSize,
+            ProtocolVersion protocolVersion, boolean isDTLS) {
+
+        byte[] sequenceNumber = null;
+        int dstContent = destination.position();
 
-    /*
-     * Write the record out on the stream.  Note that you must have (in
-     * order) compressed the data, appended the MAC, and encrypted it in
-     * order for the record to be understood by the other end.  (Some of
-     * those steps will be null early in handshaking.)
-     *
-     * Note that this does no locking for the connection, it's required
-     * that synchronization be done elsewhere.  Also, this does its work
-     * in a single low level write, for efficiency.
-     */
-    void write(OutputStream s, boolean holdRecord,
-            ByteArrayOutputStream heldRecordBuffer) throws IOException {
+        // Acquire the current sequence number before using.
+        if (isDTLS) {
+            sequenceNumber = authenticator.sequenceNumber();
+        }
+
+        // "flip" but skip over header again, add MAC & encrypt
+        if (authenticator instanceof MAC) {
+            MAC signer = (MAC)authenticator;
+            if (signer.MAClen() != 0) {
+                byte[] hash = signer.compute(contentType, destination, false);
 
-        /*
-         * Don't emit content-free records.  (Even change cipher spec
-         * messages have a byte of data!)
-         */
-        if (count == headerPlusMaxIVSize) {
-            return;
+                /*
+                 * position was advanced to limit in MAC compute above.
+                 *
+                 * Mark next area as writable (above layers should have
+                 * established that we have plenty of room), then write
+                 * out the hash.
+                 */
+                destination.limit(destination.limit() + hash.length);
+                destination.put(hash);
+
+                // reset the position and limit
+                destination.limit(destination.position());
+                destination.position(dstContent);
+            }
         }
 
-        int length = count - headerOffset - headerSize;
-        // "should" really never write more than about 14 Kb...
-        if (length < 0) {
-            throw new SSLException("output record size too small: "
-                + length);
+        if (!encCipher.isNullCipher()) {
+            if (protocolVersion.useTLS11PlusSpec() &&
+                    (encCipher.isCBCMode() || encCipher.isAEADMode())) {
+                byte[] nonce = encCipher.createExplicitNonce(
+                        authenticator, contentType, destination.remaining());
+                destination.position(headerOffset + headerSize);
+                destination.put(nonce);
+            }
+            if (!encCipher.isAEADMode()) {
+                // The explicit IV in TLS 1.1 and later can be encrypted.
+                destination.position(headerOffset + headerSize);
+            }   // Otherwise, DON'T encrypt the nonce_explicit for AEAD mode
+
+            // Encrypt may pad, so again the limit may be changed.
+            encCipher.encrypt(destination, dstLim);
+        } else {
+            destination.position(destination.limit());
+        }
+
+        // Finish out the record header.
+        int fragLen = destination.limit() - headerOffset - headerSize;
+
+        destination.put(headerOffset, contentType);         // content type
+        destination.put(headerOffset + 1, protocolVersion.major);
+        destination.put(headerOffset + 2, protocolVersion.minor);
+        if (!isDTLS) {
+            // fragment length
+            destination.put(headerOffset + 3, (byte)(fragLen >> 8));
+            destination.put(headerOffset + 4, (byte)fragLen);
+        } else {
+            // epoch and sequence_number
+            destination.put(headerOffset + 3, sequenceNumber[0]);
+            destination.put(headerOffset + 4, sequenceNumber[1]);
+            destination.put(headerOffset + 5, sequenceNumber[2]);
+            destination.put(headerOffset + 6, sequenceNumber[3]);
+            destination.put(headerOffset + 7, sequenceNumber[4]);
+            destination.put(headerOffset + 8, sequenceNumber[5]);
+            destination.put(headerOffset + 9, sequenceNumber[6]);
+            destination.put(headerOffset + 10, sequenceNumber[7]);
+
+            // fragment length
+            destination.put(headerOffset + 11, (byte)(fragLen >> 8));
+            destination.put(headerOffset + 12, (byte)fragLen);
+
+            // Increase the sequence number for next use.
+            authenticator.increaseSequenceNumber();
         }
 
-        if (debug != null
-                && (Debug.isOn("record") || Debug.isOn("handshake"))) {
-            if ((debug != null && Debug.isOn("record"))
-                    || contentType() == ct_change_cipher_spec)
-                System.out.println(Thread.currentThread().getName()
-                    // v3.0/v3.1 ...
-                    + ", WRITE: " + protocolVersion
-                    + " " + InputRecord.contentName(contentType())
-                    + ", length = " + length);
+        // Update destination position to reflect the amount of data produced.
+        destination.position(destination.limit());
+
+        return Authenticator.toLong(sequenceNumber);
+    }
+
+    // Encrypt a fragment and wrap up a record.
+    //
+    // Uses the internal expandable buf variable and the current
+    // protocolVersion variable.
+    void encrypt(Authenticator authenticator,
+            CipherBox encCipher, byte contentType, int headerSize) {
+
+        int position = headerSize + writeCipher.getExplicitNonceSize();
+
+        // "flip" but skip over header again, add MAC & encrypt
+        int macLen = 0;
+        if (authenticator instanceof MAC) {
+            MAC signer = (MAC)authenticator;
+            macLen = signer.MAClen();
+            if (macLen != 0) {
+                byte[] hash = signer.compute(contentType,
+                        buf, position, (count - position), false);
+
+                write(hash, 0, hash.length);
+            }
         }
 
-        /*
-         * If this is the initial ClientHello on this connection and
-         * we're not trying to resume a (V3) session then send a V2
-         * ClientHello instead so we can detect V2 servers cleanly.
-         */
-         if (firstMessage && useV2Hello()) {
-            byte[] v3Msg = new byte[length - 4];
-            System.arraycopy(buf, headerPlusMaxIVSize + 4,
-                                        v3Msg, 0, v3Msg.length);
-            headerOffset = 0;   // reset the header offset
-            V3toV2ClientHello(v3Msg);
-            handshakeHash.reset();
-            lastHashed = 2;
-            doHashes();
-            if (debug != null && Debug.isOn("record"))  {
-                System.out.println(
-                    Thread.currentThread().getName()
-                    + ", WRITE: SSLv2 client hello message"
-                    + ", length = " + (count - 2)); // 2 byte SSLv2 header
+        if (!encCipher.isNullCipher()) {
+            // Requires explicit IV/nonce for CBC/AEAD cipher suites for
+            // TLS 1.1 or later.
+            if (protocolVersion.useTLS11PlusSpec() &&
+                    (encCipher.isCBCMode() || encCipher.isAEADMode())) {
+
+                byte[] nonce = encCipher.createExplicitNonce(
+                        authenticator, contentType, (count - position));
+                int noncePos = position - nonce.length;
+                System.arraycopy(nonce, 0, buf, noncePos, nonce.length);
+            }
+
+            if (!encCipher.isAEADMode()) {
+                // The explicit IV in TLS 1.1 and later can be encrypted.
+                position = headerSize;
+            }   // Otherwise, DON'T encrypt the nonce_explicit for AEAD mode
+
+            // increase buf capacity if necessary
+            int fragSize = count - position;
+            int packetSize =
+                    encCipher.calculatePacketSize(fragSize, macLen, headerSize);
+            if (packetSize > (buf.length - position)) {
+                byte[] newBuf = new byte[position + packetSize];
+                System.arraycopy(buf, 0, newBuf, 0, count);
+                buf = newBuf;
             }
-        } else {
-            /*
-             * Fill out the header, write it and the message.
-             */
-            buf[headerOffset + 0] = contentType;
-            buf[headerOffset + 1] = protocolVersion.major;
-            buf[headerOffset + 2] = protocolVersion.minor;
-            buf[headerOffset + 3] = (byte)(length >> 8);
-            buf[headerOffset + 4] = (byte)(length);
+
+            // Encrypt may pad, so again the count may be changed.
+            count = position +
+                    encCipher.encrypt(buf, position, (count - position));
         }
-        firstMessage = false;
+
+        // Fill out the header, write it and the message.
+        int fragLen = count - headerSize;
+        buf[0] = contentType;
+        buf[1] = protocolVersion.major;
+        buf[2] = protocolVersion.minor;
+        buf[3] = (byte)((fragLen >> 8) & 0xFF);
+        buf[4] = (byte)(fragLen & 0xFF);
+    }
+
+    static ByteBuffer encodeV2ClientHello(
+            byte[] fragment, int offset, int length) throws IOException {
+
+        int v3SessIdLenOffset = offset + 34;      //  2: client_version
+                                                  // 32: random
+
+        int v3SessIdLen = fragment[v3SessIdLenOffset];
+        int v3CSLenOffset = v3SessIdLenOffset + 1 + v3SessIdLen;
+        int v3CSLen = ((fragment[v3CSLenOffset] & 0xff) << 8) +
+                       (fragment[v3CSLenOffset + 1] & 0xff);
+        int cipherSpecs = v3CSLen / 2;        // 2: cipher spec size
+
+        // Estimate the max V2ClientHello message length
+        //
+        // 11: header size
+        // (cipherSpecs * 6): cipher_specs
+        //    6: one cipher suite may need 6 bytes, see V3toV2CipherSuite.
+        // 3: placeholder for the TLS_EMPTY_RENEGOTIATION_INFO_SCSV
+        //    signaling cipher suite
+        // 32: challenge size
+        int v2MaxMsgLen = 11 + (cipherSpecs * 6) + 3 + 32;
+
+        // Create a ByteBuffer backed by an accessible byte array.
+        byte[] dstBytes = new byte[v2MaxMsgLen];
+        ByteBuffer dstBuf = ByteBuffer.wrap(dstBytes);
 
         /*
-         * The upper levels may want us to delay sending this packet so
-         * multiple TLS Records can be sent in one (or more) TCP packets.
-         * If so, add this packet to the heldRecordBuffer.
-         *
-         * NOTE:  all writes have been synchronized by upper levels.
+         * Copy over the cipher specs. We don't care about actually
+         * translating them for use with an actual V2 server since
+         * we only talk V3.  Therefore, just copy over the V3 cipher
+         * spec values with a leading 0.
          */
-        int debugOffset = 0;
-        if (holdRecord) {
-            /*
-             * If holdRecord is true, we must have a heldRecordBuffer.
-             *
-             * Don't worry about the override of writeBuffer(), because
-             * when holdRecord is true, the implementation in this class
-             * will be used.
-             */
-            writeBuffer(heldRecordBuffer,
-                        buf, headerOffset, count - headerOffset, debugOffset);
-        } else {
-            // It's time to send, do we have buffered data?
-            // May or may not have a heldRecordBuffer.
-            if (heldRecordBuffer != null && heldRecordBuffer.size() > 0) {
-                int heldLen = heldRecordBuffer.size();
-
-                // Ensure the capacity of this buffer.
-                int newCount = count + heldLen - headerOffset;
-                ensureCapacity(newCount);
-
-                // Slide everything in the buffer to the right.
-                System.arraycopy(buf, headerOffset,
-                                    buf, heldLen, count - headerOffset);
-
-                // Prepend the held record to the buffer.
-                System.arraycopy(
-                    heldRecordBuffer.toByteArray(), 0, buf, 0, heldLen);
-                count = newCount;
-                headerOffset = 0;
-
-                // Clear the held buffer.
-                heldRecordBuffer.reset();
-
-                // The held buffer has been dumped, set the debug dump offset.
-                debugOffset = heldLen;
-            }
-            writeBuffer(s, buf, headerOffset,
-                        count - headerOffset, debugOffset);
-        }
-
-        reset();
-    }
+        int v3CSOffset = v3CSLenOffset + 2;   // skip length field
+        int v2CSLen = 0;
 
-    /*
-     * Actually do the write here.  For SSLEngine's HS data,
-     * we'll override this method and let it take the appropriate
-     * action.
-     */
-    void writeBuffer(OutputStream s, byte [] buf, int off, int len,
-            int debugOffset) throws IOException {
-        s.write(buf, off, len);
-        s.flush();
-
-        // Output only the record from the specified debug offset.
-        if (debug != null && Debug.isOn("packet")) {
-            try {
-                HexDumpEncoder hd = new HexDumpEncoder();
-
-                System.out.println("[Raw write]: length = " +
-                                                    (len - debugOffset));
-                hd.encodeBuffer(new ByteArrayInputStream(buf,
-                    off + debugOffset, len - debugOffset), System.out);
-            } catch (IOException e) { }
-        }
-    }
-
-    /*
-     * Return whether the buffer contains a ClientHello message that should
-     * be converted to V2 format.
-     */
-    private boolean useV2Hello() {
-        return firstMessage
-            && (helloVersion == ProtocolVersion.SSL20Hello)
-            && (contentType == ct_handshake)
-            && (buf[headerOffset + 5] == HandshakeMessage.ht_client_hello)
-                                            //  5: recode header size
-            && (buf[headerPlusMaxIVSize + 4 + 2 + 32] == 0);
-                                            // V3 session ID is empty
-                                            //  4: handshake header size
-                                            //  2: client_version in ClientHello
-                                            // 32: random in ClientHello
-    }
-
-    /*
-     * Detect "old" servers which are capable of SSL V2.0 protocol ... for
-     * example, Netscape Commerce 1.0 servers.  The V3 message is in the
-     * header and the bytes passed as parameter.  This routine translates
-     * the V3 message into an equivalent V2 one.
-     *
-     * Note that the translation will strip off all hello extensions as
-     * SSL V2.0 does not support hello extension.
-     */
-    private void V3toV2ClientHello(byte v3Msg []) throws SSLException {
-        int v3SessionIdLenOffset = 2 + 32; // version + nonce
-        int v3SessionIdLen = v3Msg[v3SessionIdLenOffset];
-        int v3CipherSpecLenOffset = v3SessionIdLenOffset + 1 + v3SessionIdLen;
-        int v3CipherSpecLen = ((v3Msg[v3CipherSpecLenOffset] & 0xff) << 8) +
-          (v3Msg[v3CipherSpecLenOffset + 1] & 0xff);
-        int cipherSpecs = v3CipherSpecLen / 2; // 2 bytes each in V3
-
-        /*
-         * Copy over the cipher specs. We don't care about actually translating
-         * them for use with an actual V2 server since we only talk V3.
-         * Therefore, just copy over the V3 cipher spec values with a leading
-         * 0.
-         */
-        int v3CipherSpecOffset = v3CipherSpecLenOffset + 2; // skip length
-        int v2CipherSpecLen = 0;
-        count = 11;
+        dstBuf.position(11);
         boolean containsRenegoInfoSCSV = false;
         for (int i = 0; i < cipherSpecs; i++) {
             byte byte1, byte2;
 
-            byte1 = v3Msg[v3CipherSpecOffset++];
-            byte2 = v3Msg[v3CipherSpecOffset++];
-            v2CipherSpecLen += V3toV2CipherSuite(byte1, byte2);
+            byte1 = fragment[v3CSOffset++];
+            byte2 = fragment[v3CSOffset++];
+            v2CSLen += V3toV2CipherSuite(dstBuf, byte1, byte2);
             if (!containsRenegoInfoSCSV &&
-                        byte1 == (byte)0x00 && byte2 == (byte)0xFF) {
+                    byte1 == (byte)0x00 && byte2 == (byte)0xFF) {
                 containsRenegoInfoSCSV = true;
             }
         }
 
         if (!containsRenegoInfoSCSV) {
-            v2CipherSpecLen += V3toV2CipherSuite((byte)0x00, (byte)0xFF);
+            v2CSLen += V3toV2CipherSuite(dstBuf, (byte)0x00, (byte)0xFF);
         }
 
         /*
+         * Copy in the nonce.
+         */
+        dstBuf.put(fragment, (offset + 2), 32);
+
+        /*
          * Build the first part of the V3 record header from the V2 one
          * that's now buffered up.  (Lengths are fixed up later).
          */
-        buf[2] = HandshakeMessage.ht_client_hello;
-        buf[3] = v3Msg[0];      // major version
-        buf[4] = v3Msg[1];      // minor version
-        buf[5] = (byte)(v2CipherSpecLen >>> 8);
-        buf[6] = (byte)v2CipherSpecLen;
-        buf[7] = 0;
-        buf[8] = 0;             // always no session
-        buf[9] = 0;
-        buf[10] = 32;           // nonce length (always 32 in V3)
+        int msgLen = dstBuf.position() - 2;   // Exclude the legth field itself
+        dstBuf.position(0);
+        dstBuf.put((byte)(0x80 | ((msgLen >>> 8) & 0xFF)));  // pos: 0
+        dstBuf.put((byte)(msgLen & 0xFF));                   // pos: 1
+        dstBuf.put(HandshakeMessage.ht_client_hello);        // pos: 2
+        dstBuf.put(fragment[offset]);         // major version, pos: 3
+        dstBuf.put(fragment[offset + 1]);     // minor version, pos: 4
+        dstBuf.put((byte)(v2CSLen >>> 8));                   // pos: 5
+        dstBuf.put((byte)(v2CSLen & 0xFF));                  // pos: 6
+        dstBuf.put((byte)0x00);           // session_id_length, pos: 7
+        dstBuf.put((byte)0x00);                              // pos: 8
+        dstBuf.put((byte)0x00);           // challenge_length,  pos: 9
+        dstBuf.put((byte)32);                                // pos: 10
 
-        /*
-         * Copy in the nonce.
-         */
-        System.arraycopy(v3Msg, 2, buf, count, 32);
-        count += 32;
+        dstBuf.position(0);
+        dstBuf.limit(msgLen + 2);
 
-        /*
-         * Set the length of the message.
-         */
-        count -= 2; // don't include length field itself
-        buf[0] = (byte)(count >>> 8);
-        buf[0] |= 0x80;
-        buf[1] = (byte)(count);
-        count += 2;
+        return dstBuf;
     }
 
-    /*
-     * Mappings from V3 cipher suite encodings to their pure V2 equivalents.
-     * This is taken from the SSL V3 specification, Appendix E.
-     */
-    private static int[] V3toV2CipherMap1 =
-        {-1, -1, -1, 0x02, 0x01, -1, 0x04, 0x05, -1, 0x06, 0x07};
-    private static int[] V3toV2CipherMap3 =
-        {-1, -1, -1, 0x80, 0x80, -1, 0x80, 0x80, -1, 0x40, 0xC0};
+    private static int V3toV2CipherSuite(ByteBuffer dstBuf,
+            byte byte1, byte byte2) {
+        dstBuf.put((byte)0);
+        dstBuf.put(byte1);
+        dstBuf.put(byte2);
 
-    /*
-     * See which matching pure-V2 cipher specs we need to include.
-     * We are including these not because we are actually prepared
-     * to talk V2 but because the Oracle Web Server insists on receiving
-     * at least 1 "pure V2" cipher suite that it supports and returns an
-     * illegal_parameter alert unless one is present. Rather than mindlessly
-     * claiming to implement all documented pure V2 cipher suites the code below
-     * just claims to implement the V2 cipher suite that is "equivalent"
-     * in terms of cipher algorithm & exportability with the actual V3 cipher
-     * suite that we do support.
-     */
-    private int V3toV2CipherSuite(byte byte1, byte byte2) {
-        buf[count++] = 0;
-        buf[count++] = byte1;
-        buf[count++] = byte2;
-
-        if (((byte2 & 0xff) > 0xA) ||
-                (V3toV2CipherMap1[byte2] == -1)) {
+        if (((byte2 & 0xff) > 0xA) || (V3toV2CipherMap1[byte2] == -1)) {
             return 3;
         }
 
-        buf[count++] = (byte)V3toV2CipherMap1[byte2];
-        buf[count++] = 0;
-        buf[count++] = (byte)V3toV2CipherMap3[byte2];
+        dstBuf.put((byte)V3toV2CipherMap1[byte2]);
+        dstBuf.put((byte)0);
+        dstBuf.put((byte)V3toV2CipherMap3[byte2]);
 
         return 6;
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/Plaintext.java	Tue Jun 02 09:15:47 2015 -0700
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ssl;
+
+import java.nio.ByteBuffer;
+import javax.net.ssl.SSLEngineResult.HandshakeStatus;
+
+/*
+ * Plaintext
+ */
+final class Plaintext {
+    final static Plaintext PLAINTEXT_NULL = new Plaintext();
+
+    byte            contentType;
+    byte            majorVersion;
+    byte            minorVersion;
+    int             recordEpoch;    // incremented on every cipher state change
+    long            recordSN;
+    ByteBuffer      fragment;       // null if need to be reassembled
+
+    HandshakeStatus handshakeStatus;    // null if not used or not handshaking
+
+    Plaintext() {
+        this.contentType = 0;
+        this.majorVersion = 0;
+        this.minorVersion = 0;
+        this.recordEpoch = -1;
+        this.recordSN = -1;
+        this.fragment = null;
+        this.handshakeStatus = null;
+    }
+
+    Plaintext(byte contentType, byte majorVersion, byte minorVersion,
+            int recordEpoch, long recordSN, ByteBuffer fragment) {
+
+        this.contentType = contentType;
+        this.majorVersion = majorVersion;
+        this.minorVersion = minorVersion;
+        this.recordEpoch = recordEpoch;
+        this.recordSN = recordSN;
+        this.fragment = fragment;
+
+        this.handshakeStatus = null;
+    }
+
+    public String toString() {
+        return "contentType: " + contentType + "/" +
+               "majorVersion: " + majorVersion + "/" +
+               "minorVersion: " + minorVersion + "/" +
+               "recordEpoch: " + recordEpoch + "/" +
+               "recordSN: 0x" + Long.toHexString(recordSN) + "/" +
+               "fragment: " + fragment;
+    }
+}
--- a/jdk/src/java.base/share/classes/sun/security/ssl/ProtocolList.java	Mon Jun 01 10:15:21 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/ProtocolList.java	Tue Jun 02 09:15:47 2015 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -124,8 +124,9 @@
     ProtocolVersion selectProtocolVersion(ProtocolVersion protocolVersion) {
         ProtocolVersion selectedVersion = null;
         for (ProtocolVersion pv : protocols) {
-            if (pv.v > protocolVersion.v) {
-                break;  // Safe to break here as this.protocols is sorted
+            if (pv.compareTo(protocolVersion) > 0) {
+                break;      // Safe to break here as this.protocols is sorted,
+                            // and DTLS and SSL/TLS protocols are not mixed.
             }
             selectedVersion = pv;
         }
--- a/jdk/src/java.base/share/classes/sun/security/ssl/ProtocolVersion.java	Mon Jun 01 10:15:21 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/ProtocolVersion.java	Tue Jun 02 09:15:47 2015 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -27,6 +27,7 @@
 
 import java.util.*;
 import java.security.CryptoPrimitive;
+import sun.security.ssl.CipherSuite.*;
 
 /**
  * Type safe enum for an SSL/TLS protocol version. Instances are obtained
@@ -61,9 +62,9 @@
     // Dummy protocol version value for invalid SSLSession
     final static ProtocolVersion NONE = new ProtocolVersion(-1, "NONE");
 
-    // If enabled, send/ accept SSLv2 hello messages
-    final static ProtocolVersion SSL20Hello = new ProtocolVersion(0x0002,
-                                                                "SSLv2Hello");
+    // If enabled, send/accept SSLv2 hello messages
+    final static ProtocolVersion SSL20Hello =
+                                new ProtocolVersion(0x0002, "SSLv2Hello");
 
     // SSL 3.0
     final static ProtocolVersion SSL30 = new ProtocolVersion(0x0300, "SSLv3");
@@ -77,6 +78,19 @@
     // TLS 1.2
     final static ProtocolVersion TLS12 = new ProtocolVersion(0x0303, "TLSv1.2");
 
+    // DTLS 1.0
+    // {254, 255}, the version value of DTLS 1.0.
+    final static ProtocolVersion DTLS10 =
+                                new ProtocolVersion(0xFEFF, "DTLSv1.0");
+
+    // No DTLS 1.1, that version number was skipped in order to harmonize
+    // version numbers with TLS.
+
+    // DTLS 1.2
+    // {254, 253}, the version value of DTLS 1.2.
+    final static ProtocolVersion DTLS12 =
+                                new ProtocolVersion(0xFEFD, "DTLSv1.2");
+
     private static final boolean FIPS = SunJSSE.isFIPS();
 
     // minimum version we implement (SSL 3.0)
@@ -85,8 +99,11 @@
     // maximum version we implement (TLS 1.2)
     final static ProtocolVersion MAX = TLS12;
 
-    // ProtocolVersion to use by default (TLS 1.2)
-    final static ProtocolVersion DEFAULT = TLS12;
+    // SSL/TLS ProtocolVersion to use by default (TLS 1.2)
+    final static ProtocolVersion DEFAULT_TLS = TLS12;
+
+    // DTLS ProtocolVersion to use by default (TLS 1.2)
+    final static ProtocolVersion DEFAULT_DTLS = DTLS12;
 
     // Default version for hello messages (SSLv2Hello)
     final static ProtocolVersion DEFAULT_HELLO = FIPS ? TLS10 : SSL30;
@@ -108,10 +125,10 @@
 
     // Initialize the available protocols.
     static {
-        Set<ProtocolVersion> protocols = new HashSet<>(5);
+        Set<ProtocolVersion> protocols = new HashSet<>(7);
 
         ProtocolVersion[] pvs = new ProtocolVersion[] {
-                SSL20Hello, SSL30, TLS10, TLS11, TLS12};
+                SSL20Hello, SSL30, TLS10, TLS11, TLS12, DTLS10, DTLS12};
         EnumSet<CryptoPrimitive> cryptoPrimitives =
             EnumSet.<CryptoPrimitive>of(CryptoPrimitive.KEY_AGREEMENT);
         for (ProtocolVersion p : pvs) {
@@ -145,6 +162,10 @@
             return TLS12;
         } else if (v == SSL20Hello.v) {
             return SSL20Hello;
+        } else if (v == DTLS10.v) {
+            return DTLS10;
+        } else if (v == DTLS12.v) {
+            return DTLS12;
         } else {
             int major = (v >>> 8) & 0xFF;
             int minor = v & 0xFF;
@@ -172,8 +193,8 @@
         }
 
         if (FIPS && (name.equals(SSL30.name) || name.equals(SSL20Hello.name))) {
-            throw new IllegalArgumentException
-                ("Only TLS 1.0 or later allowed in FIPS mode");
+            throw new IllegalArgumentException(
+                    "Only TLS 1.0 or later allowed in FIPS mode");
         }
 
         if (name.equals(SSL30.name)) {
@@ -186,6 +207,10 @@
             return TLS12;
         } else if (name.equals(SSL20Hello.name)) {
             return SSL20Hello;
+        } else if (name.equals(DTLS10.name)) {
+            return DTLS10;
+        } else if (name.equals(DTLS12.name)) {
+            return DTLS12;
         } else {
             throw new IllegalArgumentException(name);
         }
@@ -201,6 +226,90 @@
      */
     @Override
     public int compareTo(ProtocolVersion protocolVersion) {
-        return this.v - protocolVersion.v;
+        if (maybeDTLSProtocol()) {
+            if (!protocolVersion.maybeDTLSProtocol()) {
+                throw new IllegalArgumentException("Not DTLS protocol");
+            }
+
+            return protocolVersion.v - this.v;
+        } else {
+            if (protocolVersion.maybeDTLSProtocol()) {
+                throw new IllegalArgumentException("Not TLS protocol");
+            }
+
+            return this.v - protocolVersion.v;
+        }
+    }
+
+    /**
+     * Returns true if a ProtocolVersion represents a DTLS protocol.
+     */
+    boolean isDTLSProtocol() {
+        return this.v == DTLS12.v || this.v == DTLS10.v;
+    }
+
+    /**
+     * Returns true if a ProtocolVersion may represent a DTLS protocol.
+     */
+    boolean maybeDTLSProtocol() {
+        return (this.major & 0x80) != 0;
+    }
+
+    boolean useTLS12PlusSpec() {
+        return maybeDTLSProtocol() ? (this.v <= DTLS12.v) : (this.v >= TLS12.v);
+    }
+
+    boolean useTLS11PlusSpec() {
+        return maybeDTLSProtocol() ? true : (this.v >= TLS11.v);
+    }
+
+    boolean useTLS10PlusSpec() {
+        return maybeDTLSProtocol() ? true : (this.v >= TLS10.v);
+    }
+
+    boolean obsoletes(CipherSuite suite) {
+        ProtocolVersion proto = this;
+        if (proto.isDTLSProtocol()) {
+            // DTLS bans stream ciphers.
+            if (suite.cipher.cipherType == CipherType.STREAM_CIPHER) {
+                return true;
+            }
+
+            proto = mapToTLSProtocol(this);
+        }
+
+        return (proto.v >= suite.obsoleted);
+    }
+
+    boolean supports(CipherSuite suite) {
+        ProtocolVersion proto = this;
+        if (proto.isDTLSProtocol()) {
+            // DTLS bans stream ciphers.
+            if (suite.cipher.cipherType == CipherType.STREAM_CIPHER) {
+                return false;
+            }
+
+            proto = mapToTLSProtocol(this);
+        }
+
+        return (proto.v >= suite.supported);
+    }
+
+    // Map a specified protocol to the corresponding TLS version.
+    //
+    // DTLS 1.2 -> TLS 1.2
+    // DTLS 1.0 -> TLS 1.1
+    private static ProtocolVersion mapToTLSProtocol(
+            ProtocolVersion protocolVersion) {
+
+        if (protocolVersion.isDTLSProtocol()) {
+            if (protocolVersion.v == DTLS10.v) {
+                protocolVersion = TLS11;
+            } else {    // DTLS12
+                protocolVersion = TLS12;
+            }
+        }
+
+        return protocolVersion;
     }
 }
--- a/jdk/src/java.base/share/classes/sun/security/ssl/RSAClientKeyExchange.java	Mon Jun 01 10:15:21 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/RSAClientKeyExchange.java	Tue Jun 02 09:15:47 2015 -0700
@@ -73,8 +73,8 @@
         this.protocolVersion = protocolVersion;
 
         try {
-            String s = ((protocolVersion.v >= ProtocolVersion.TLS12.v) ?
-                "SunTls12RsaPremasterSecret" : "SunTlsRsaPremasterSecret");
+            String s = protocolVersion.useTLS12PlusSpec() ?
+                "SunTls12RsaPremasterSecret" : "SunTlsRsaPremasterSecret";
             KeyGenerator kg = JsseJce.getKeyGenerator(s);
             kg.init(new TlsRsaPremasterSecretParameterSpec(
                     maxVersion.v, protocolVersion.v), generator);
@@ -103,7 +103,7 @@
             throw new SSLKeyException("Private key not of type RSA");
         }
 
-        if (currentVersion.v >= ProtocolVersion.TLS10.v) {
+        if (currentVersion.useTLS10PlusSpec()) {
             encrypted = input.getBytes16();
         } else {
             encrypted = new byte [messageSize];
@@ -142,7 +142,7 @@
 
     @Override
     int messageLength() {
-        if (protocolVersion.v >= ProtocolVersion.TLS10.v) {
+        if (protocolVersion.useTLS10PlusSpec()) {
             return encrypted.length + 2;
         } else {
             return encrypted.length;
@@ -151,7 +151,7 @@
 
     @Override
     void send(HandshakeOutStream s) throws IOException {
-        if (protocolVersion.v >= ProtocolVersion.TLS10.v) {
+        if (protocolVersion.useTLS10PlusSpec()) {
             s.putBytes16(encrypted);
         } else {
             s.write(encrypted);
--- a/jdk/src/java.base/share/classes/sun/security/ssl/RandomCookie.java	Mon Jun 01 10:15:21 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/RandomCookie.java	Tue Jun 02 09:15:47 2015 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2007, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -70,10 +70,10 @@
     void print(PrintStream s) {
         int i, gmt_unix_time;
 
-        gmt_unix_time = random_bytes[0] << 24;
-        gmt_unix_time += random_bytes[1] << 16;
-        gmt_unix_time += random_bytes[2] << 8;
-        gmt_unix_time += random_bytes[3];
+        gmt_unix_time = ((random_bytes[0] & 0xFF) << 24) |
+                        ((random_bytes[1] & 0xFF) << 16) |
+                        ((random_bytes[2] & 0xFF) << 8) |
+                         (random_bytes[3] & 0xFF);
 
         s.print("GMT: " + gmt_unix_time + " ");
         s.print("bytes = { ");
--- a/jdk/src/java.base/share/classes/sun/security/ssl/Record.java	Mon Jun 01 10:15:21 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/Record.java	Tue Jun 02 09:15:47 2015 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -23,21 +23,20 @@
  * questions.
  */
 
-
 package sun.security.ssl;
 
-
 /**
- * SSL/TLS records, as pulled off (and put onto) a TCP stream.  This is
+ * SSL/TLS/DTLS records, as pulled off (and put onto) a TCP stream.  This is
  * the base interface, which defines common information and interfaces
  * used by both Input and Output records.
  *
  * @author David Brownell
  */
 interface Record {
+
     /*
-     * There are four SSL record types, which are part of the interface
-     * to this level (along with the maximum record size)
+     * There are four record types, which are part of the interface
+     * to this level (along with the maximum record size).
      *
      * enum { change_cipher_spec(20), alert(21), handshake(22),
      *      application_data(23), (255) } ContentType;
@@ -47,75 +46,20 @@
     static final byte   ct_handshake = 22;
     static final byte   ct_application_data = 23;
 
-    static final int    headerSize = 5;         // SSLv3 record header
-    static final int    maxExpansion = 1024;    // for bad compression
-    static final int    trailerSize = 20;       // SHA1 hash size
+    static final int    maxMacSize = 48;        // the max supported MAC or
+                                                // AEAD tag size
     static final int    maxDataSize = 16384;    // 2^14 bytes of data
     static final int    maxPadding = 256;       // block cipher padding
-    static final int    maxIVLength = 256;      // IV length
-
-    /*
-     * The size of the header plus the max IV length
-     */
-    static final int    headerPlusMaxIVSize =
-                                      headerSize        // header
-                                    + maxIVLength;      // iv
+    static final int    maxIVLength = 16;       // the max supported IV length
 
-    /*
-     * SSL has a maximum record size.  It's header, (compressed) data,
-     * padding, and a trailer for the message authentication information (MAC
-     * for block and stream ciphers, and message authentication tag for AEAD
-     * ciphers).
-     *
-     * Some compression algorithms have rare cases where they expand the data.
-     * As we don't support compression at this time, leave that out.
-     */
-    static final int    maxRecordSize =
-                                      headerPlusMaxIVSize   // header + iv
-                                    + maxDataSize           // data
-                                    + maxPadding            // padding
-                                    + trailerSize;          // MAC or AEAD tag
-
-    static final boolean enableCBCProtection =
-            Debug.getBooleanProperty("jsse.enableCBCProtection", true);
+    static final int    maxFragmentSize = 18432;    // the max fragment size
+                                                    // 2^14 + 2048
 
     /*
-     * For CBC protection in SSL3/TLS1, we break some plaintext into two
-     * packets.  Max application data size for the second packet.
+     * System property to enable/disable CBC protection in SSL3/TLS1.
      */
-    static final int    maxDataSizeMinusOneByteRecord =
-                                  maxDataSize       // max data size
-                                - (                 // max one byte record size
-                                      headerPlusMaxIVSize   // header + iv
-                                    + 1             // one byte data
-                                    + maxPadding    // padding
-                                    + trailerSize   // MAC
-                                  );
-
-    /*
-     * The maximum large record size.
-     *
-     * Some SSL/TLS implementations support large fragment upto 2^15 bytes,
-     * such as Microsoft. We support large incoming fragments.
-     *
-     * The maximum large record size is defined as maxRecordSize plus 2^14,
-     * this is the amount OpenSSL is using.
-     */
-    static final int    maxLargeRecordSize =
-                maxRecordSize   // Max size with a conforming implementation
-              + maxDataSize;    // extra 2^14 bytes for large data packets.
-
-
-    /*
-     * Maximum record size for alert and change cipher spec records.
-     * They only contain 2 and 1 bytes of data, respectively.
-     * Allocate a smaller array.
-     */
-    static final int    maxAlertRecordSize =
-                                      headerPlusMaxIVSize   // header + iv
-                                    + 2                     // alert
-                                    + maxPadding            // padding
-                                    + trailerSize;          // MAC
+    static final boolean enableCBCProtection =
+            Debug.getBooleanProperty("jsse.enableCBCProtection", true);
 
     /*
      * The overflow values of integers of 8, 16 and 24 bits.
@@ -123,4 +67,27 @@
     static final int OVERFLOW_OF_INT08 = (1 << 8);
     static final int OVERFLOW_OF_INT16 = (1 << 16);
     static final int OVERFLOW_OF_INT24 = (1 << 24);
+
+    /**
+     * Return a description for the given content type.
+     */
+    static String contentName(byte contentType) {
+        switch (contentType) {
+        case ct_change_cipher_spec:
+            return "Change Cipher Spec";
+        case ct_alert:
+            return "Alert";
+        case ct_handshake:
+            return "Handshake";
+        case ct_application_data:
+            return "Application Data";
+        default:
+            return "contentType = " + contentType;
+        }
+    }
+
+    static boolean isValidContentType(byte contentType) {
+        return (contentType == 20) || (contentType == 21) ||
+               (contentType == 22) || (contentType == 23);
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/RecordType.java	Tue Jun 02 09:15:47 2015 -0700
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ssl;
+
+/*
+ * enumation of record type
+ */
+enum RecordType {
+
+    RECORD_CHANGE_CIPHER_SPEC   (Record.ct_change_cipher_spec,
+                                    HandshakeMessage.ht_not_applicable),
+    RECORD_ALERT                (Record.ct_alert,
+                                    HandshakeMessage.ht_not_applicable),
+    RECORD_HELLO_REQUEST        (Record.ct_handshake,
+                                    HandshakeMessage.ht_hello_request),
+    RECORD_CLIENT_HELLO         (Record.ct_handshake,
+                                    HandshakeMessage.ht_client_hello),
+    RECORD_SERVER_HELLO         (Record.ct_handshake,
+                                    HandshakeMessage.ht_server_hello),
+    RECORD_HELLO_VERIFY_REQUEST (Record.ct_handshake,
+                                    HandshakeMessage.ht_hello_verify_request),
+    RECORD_NEW_SESSION_TICKET   (Record.ct_handshake,
+                                    HandshakeMessage.ht_new_session_ticket),
+    RECORD_CERTIFICATE          (Record.ct_handshake,
+                                    HandshakeMessage.ht_certificate),
+    RECORD_SERVER_KEY_EXCHANGE  (Record.ct_handshake,
+                                    HandshakeMessage.ht_server_key_exchange),
+    RECORD_CERTIFICATE_REQUEST  (Record.ct_handshake,
+                                    HandshakeMessage.ht_certificate_request),
+    RECORD_SERVER_HELLO_DONE    (Record.ct_handshake,
+                                    HandshakeMessage.ht_server_hello_done),
+    RECORD_CERTIFICATE_VERIFY   (Record.ct_handshake,
+                                    HandshakeMessage.ht_certificate_verify),
+    RECORD_CLIENT_KEY_EXCHANGE  (Record.ct_handshake,
+                                    HandshakeMessage.ht_client_key_exchange),
+    RECORD_FINISHED             (Record.ct_handshake,
+                                    HandshakeMessage.ht_finished),
+    RECORD_CERTIFICATE_URL      (Record.ct_handshake,
+                                    HandshakeMessage.ht_certificate_url),
+    RECORD_CERTIFICATE_STATUS   (Record.ct_handshake,
+                                    HandshakeMessage.ht_certificate_status),
+    RECORD_SUPPLIEMENTAL_DATA   (Record.ct_handshake,
+                                    HandshakeMessage.ht_supplemental_data),
+    RECORD_APPLICATION_DATA     (Record.ct_application_data,
+                                    HandshakeMessage.ht_not_applicable);
+
+    byte            contentType;
+    byte            handshakeType;
+
+    private RecordType(byte contentType, byte handshakeType) {
+        this.contentType = contentType;
+        this.handshakeType = handshakeType;
+    }
+
+    static RecordType valueOf(byte contentType, byte handshakeType) {
+        if (contentType == Record.ct_change_cipher_spec) {
+            return RECORD_CHANGE_CIPHER_SPEC;
+        } else if (contentType == Record.ct_alert) {
+            return RECORD_ALERT;
+        } else if (contentType == Record.ct_application_data) {
+            return RECORD_APPLICATION_DATA;
+        } else if (handshakeType == HandshakeMessage.ht_hello_request) {
+            return RECORD_HELLO_REQUEST;
+        } else if (handshakeType == HandshakeMessage.ht_client_hello) {
+            return RECORD_CLIENT_HELLO;
+        } else if (handshakeType == HandshakeMessage.ht_server_hello) {
+            return RECORD_SERVER_HELLO;
+        } else if (handshakeType == HandshakeMessage.ht_hello_verify_request) {
+            return RECORD_HELLO_VERIFY_REQUEST;
+        } else if (handshakeType == HandshakeMessage.ht_new_session_ticket) {
+            return RECORD_NEW_SESSION_TICKET;
+        } else if (handshakeType == HandshakeMessage.ht_certificate) {
+            return RECORD_CERTIFICATE;
+        } else if (handshakeType == HandshakeMessage.ht_server_key_exchange) {
+            return RECORD_SERVER_KEY_EXCHANGE;
+        } else if (handshakeType == HandshakeMessage.ht_certificate_request) {
+            return RECORD_CERTIFICATE_REQUEST;
+        } else if (handshakeType == HandshakeMessage.ht_server_hello_done) {
+            return RECORD_SERVER_HELLO_DONE;
+        } else if (handshakeType == HandshakeMessage.ht_certificate_verify) {
+            return RECORD_CERTIFICATE_VERIFY;
+        } else if (handshakeType == HandshakeMessage.ht_client_key_exchange) {
+            return RECORD_CLIENT_KEY_EXCHANGE;
+        } else if (handshakeType == HandshakeMessage.ht_finished) {
+            return RECORD_FINISHED;
+        } else if (handshakeType == HandshakeMessage.ht_certificate_url) {
+            return RECORD_CERTIFICATE_URL;
+        } else if (handshakeType == HandshakeMessage.ht_certificate_status) {
+            return RECORD_CERTIFICATE_STATUS;
+        } else if (handshakeType == HandshakeMessage.ht_supplemental_data) {
+            return RECORD_SUPPLIEMENTAL_DATA;
+        }
+
+        // otherwise, invalid record type
+        throw new IllegalArgumentException(
+                "Invalid record type (ContentType:" + contentType +
+                ", HandshakeType:" + handshakeType + ")");
+    }
+}
--- a/jdk/src/java.base/share/classes/sun/security/ssl/SSLAlgorithmConstraints.java	Mon Jun 01 10:15:21 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/SSLAlgorithmConstraints.java	Tue Jun 02 09:15:47 2015 -0700
@@ -352,18 +352,13 @@
                         components.add("ECDH_ANON");
                     }
                     break;
-                case K_KRB5:
-                    if (!forCertPathOnly) {
-                        components.add("KRB5");
+                default:
+                    if (ClientKeyExchangeService.find(keyExchange.name) != null) {
+                        if (!forCertPathOnly) {
+                            components.add(keyExchange.name);
+                        }
                     }
-                    break;
-                case K_KRB5_EXPORT:
-                    if (!forCertPathOnly) {
-                        components.add("KRB5_EXPORT");
-                    }
-                    break;
-                default:
-                    // ignore
+                    // otherwise ignore
             }
 
             return components;
--- a/jdk/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java	Mon Jun 01 10:15:21 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java	Tue Jun 02 09:15:47 2015 -0700
@@ -62,6 +62,9 @@
     private CipherSuiteList defaultClientCipherSuiteList;
     private CipherSuiteList supportedCipherSuiteList;
 
+    // DTLS cookie exchange manager
+    private HelloCookieManager helloCookieManager;
+
     SSLContextImpl() {
         ephemeralKeyManager = new EphemeralKeyManager();
         clientCache = new SSLSessionContextImpl();
@@ -175,11 +178,29 @@
         return DummyX509KeyManager.INSTANCE;
     }
 
+    abstract SSLEngine createSSLEngineImpl();
+    abstract SSLEngine createSSLEngineImpl(String host, int port);
+
+    @Override
+    protected SSLEngine engineCreateSSLEngine() {
+        if (!isInitialized) {
+            throw new IllegalStateException("SSLContext is not initialized");
+        }
+        return createSSLEngineImpl();
+    }
+
+    @Override
+    protected SSLEngine engineCreateSSLEngine(String host, int port) {
+        if (!isInitialized) {
+            throw new IllegalStateException("SSLContext is not initialized");
+        }
+        return createSSLEngineImpl(host, port);
+    }
+
     @Override
     protected SSLSocketFactory engineGetSocketFactory() {
         if (!isInitialized) {
-            throw new IllegalStateException(
-                "SSLContextImpl is not initialized");
+            throw new IllegalStateException("SSLContext is not initialized");
         }
        return new SSLSocketFactoryImpl(this);
     }
@@ -193,24 +214,6 @@
     }
 
     @Override
-    protected SSLEngine engineCreateSSLEngine() {
-        if (!isInitialized) {
-            throw new IllegalStateException(
-                "SSLContextImpl is not initialized");
-        }
-        return new SSLEngineImpl(this);
-    }
-
-    @Override
-    protected SSLEngine engineCreateSSLEngine(String host, int port) {
-        if (!isInitialized) {
-            throw new IllegalStateException(
-                "SSLContextImpl is not initialized");
-        }
-        return new SSLEngineImpl(this, host, port);
-    }
-
-    @Override
     protected SSLSessionContext engineGetClientSessionContext() {
         return clientCache;
     }
@@ -236,6 +239,23 @@
         return ephemeralKeyManager;
     }
 
+    HelloCookieManager getHelloCookieManager() {
+        if (!isInitialized) {
+            throw new IllegalStateException("SSLContext is not initialized");
+        }
+
+        if (helloCookieManager == null) {
+            helloCookieManager = getHelloCookieManager(secureRandom);
+        }
+
+        return helloCookieManager;
+    }
+
+    HelloCookieManager getHelloCookieManager(SecureRandom secureRandom) {
+        throw new UnsupportedOperationException(
+                "Cookie exchange applies to DTLS only");
+    }
+
     abstract SSLParameters getDefaultServerSSLParams();
     abstract SSLParameters getDefaultClientSSLParams();
     abstract SSLParameters getSupportedSSLParams();
@@ -319,12 +339,20 @@
                (protocols == defaultClientProtocolList);
     }
 
+    /**
+     * Return whether a protocol list is the original default enabled
+     * protocols.  See: SSLSocket/SSLEngine.setEnabledProtocols()
+     */
+    boolean isDefaultCipherSuiteList(CipherSuiteList cipherSuites) {
+        return (cipherSuites == defaultClientCipherSuiteList) ||
+               (cipherSuites == defaultServerCipherSuiteList);
+    }
 
     /*
      * Return the list of all available CipherSuites with a priority of
      * minPriority or above.
      */
-    private CipherSuiteList getApplicableCipherSuiteList(
+    private static CipherSuiteList getApplicableCipherSuiteList(
             ProtocolList protocols, boolean onlyEnabled) {
 
         int minPriority = CipherSuite.SUPPORTED_SUITES_PRIORITY;
@@ -344,8 +372,8 @@
                 }
 
                 if (suite.isAvailable() &&
-                        suite.obsoleted > protocols.min.v &&
-                        suite.supported <= protocols.max.v) {
+                        !protocols.min.obsoletes(suite) &&
+                        protocols.max.supports(suite)) {
                     if (SSLAlgorithmConstraints.DEFAULT.permits(
                             EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
                             suite.name, null)) {
@@ -353,10 +381,10 @@
                     }
                 } else if (debug != null &&
                         Debug.isOn("sslctx") && Debug.isOn("verbose")) {
-                    if (suite.obsoleted <= protocols.min.v) {
+                    if (protocols.min.obsoletes(suite)) {
                         System.out.println(
                             "Ignoring obsoleted cipher suite: " + suite);
-                    } else if (suite.supported > protocols.max.v) {
+                    } else if (!protocols.max.supports(suite)) {
                         System.out.println(
                             "Ignoring unsupported cipher suite: " + suite);
                     } else {
@@ -388,8 +416,25 @@
         }
     }
 
+    private static String[] getAvailableProtocols(
+            ProtocolVersion[] protocolCandidates) {
+
+        List<String> availableProtocols = Collections.<String>emptyList();
+        if (protocolCandidates !=  null && protocolCandidates.length != 0) {
+            availableProtocols = new ArrayList<>(protocolCandidates.length);
+            for (ProtocolVersion p : protocolCandidates) {
+                if (ProtocolVersion.availableProtocols.contains(p)) {
+                    availableProtocols.add(p.name);
+                }
+            }
+        }
+
+        return availableProtocols.toArray(new String[0]);
+    }
+
+
     /*
-     * The SSLContext implementation for TLS/SSL algorithm
+     * The SSLContext implementation for SSL/(D)TLS algorithm
      *
      * SSL/TLS protocols specify the forward compatibility and version
      * roll-back attack protections, however, a number of SSL/TLS server
@@ -418,14 +463,15 @@
      */
 
     /*
-     * The base abstract SSLContext implementation.
+     * The base abstract SSLContext implementation for the Transport Layer
+     * Security (TLS) protocols.
      *
      * This abstract class encapsulates supported and the default server
-     * SSL parameters.
+     * SSL/TLS parameters.
      *
      * @see SSLContext
      */
-    private abstract static class AbstractSSLContext extends SSLContextImpl {
+    private abstract static class AbstractTLSContext extends SSLContextImpl {
         // parameters
         private static final SSLParameters defaultServerSSLParams;
         private static final SSLParameters supportedSSLParams;
@@ -482,20 +528,14 @@
             return supportedSSLParams;
         }
 
-        static String[] getAvailableProtocols(
-                ProtocolVersion[] protocolCandidates) {
+        @Override
+        SSLEngine createSSLEngineImpl() {
+            return new SSLEngineImpl(this, false);
+        }
 
-            List<String> availableProtocols = Collections.<String>emptyList();
-            if (protocolCandidates !=  null && protocolCandidates.length != 0) {
-                availableProtocols = new ArrayList<>(protocolCandidates.length);
-                for (ProtocolVersion p : protocolCandidates) {
-                    if (ProtocolVersion.availableProtocols.contains(p)) {
-                        availableProtocols.add(p.name);
-                    }
-                }
-            }
-
-            return availableProtocols.toArray(new String[0]);
+        @Override
+        SSLEngine createSSLEngineImpl(String host, int port) {
+            return new SSLEngineImpl(this, host, port, false);
         }
     }
 
@@ -504,7 +544,7 @@
      *
      * @see SSLContext
      */
-    public static final class TLS10Context extends AbstractSSLContext {
+    public static final class TLS10Context extends AbstractTLSContext {
         private static final SSLParameters defaultClientSSLParams;
 
         static {
@@ -537,7 +577,7 @@
      *
      * @see SSLContext
      */
-    public static final class TLS11Context extends AbstractSSLContext {
+    public static final class TLS11Context extends AbstractTLSContext {
         private static final SSLParameters defaultClientSSLParams;
 
         static {
@@ -572,7 +612,7 @@
      *
      * @see SSLContext
      */
-    public static final class TLS12Context extends AbstractSSLContext {
+    public static final class TLS12Context extends AbstractTLSContext {
         private static final SSLParameters defaultClientSSLParams;
 
         static {
@@ -605,12 +645,73 @@
     }
 
     /*
+     * The interface for the customized SSL/(D)TLS SSLContext.
+     *
+     * @see SSLContext
+     */
+    private static class CustomizedSSLProtocols {
+        private final static String PROPERTY_NAME = "jdk.tls.client.protocols";
+        static IllegalArgumentException reservedException = null;
+        static ArrayList<ProtocolVersion>
+                                customizedProtocols = new ArrayList<>();
+
+        // Don't want a java.lang.LinkageError for illegal system property.
+        //
+        // Please don't throw exception in this static block.  Otherwise,
+        // java.lang.LinkageError may be thrown during the instantiation of
+        // the provider service. Instead, please handle the initialization
+        // exception in the caller's constructor.
+        static {
+            String property = AccessController.doPrivileged(
+                    new GetPropertyAction(PROPERTY_NAME));
+            if (property != null && property.length() != 0) {
+                // remove double quote marks from beginning/end of the property
+                if (property.length() > 1 && property.charAt(0) == '"' &&
+                        property.charAt(property.length() - 1) == '"') {
+                    property = property.substring(1, property.length() - 1);
+                }
+            }
+
+            if (property != null && property.length() != 0) {
+                String[] protocols = property.split(",");
+                for (int i = 0; i < protocols.length; i++) {
+                    protocols[i] = protocols[i].trim();
+                    // Is it a supported protocol name?
+                    try {
+                        ProtocolVersion pro =
+                                ProtocolVersion.valueOf(protocols[i]);
+
+                        if (SunJSSE.isFIPS() &&
+                                ((pro.v == ProtocolVersion.SSL30.v) ||
+                                 (pro.v == ProtocolVersion.SSL20Hello.v))) {
+                            reservedException = new IllegalArgumentException(
+                                    PROPERTY_NAME + ": " + pro +
+                                    " is not FIPS compliant");
+
+                            break;
+                        }
+
+                        // ignore duplicated protocols
+                        if (!customizedProtocols.contains(pro)) {
+                            customizedProtocols.add(pro);
+                        }
+                    } catch (IllegalArgumentException iae) {
+                        reservedException = new IllegalArgumentException(
+                                PROPERTY_NAME + ": " + protocols[i] +
+                                " is not a standard SSL protocol name", iae);
+                    }
+                }
+            }
+        }
+    }
+
+    /*
      * The SSLContext implementation for customized TLS protocols
      *
      * @see SSLContext
      */
-    private static class CustomizedSSLContext extends AbstractSSLContext {
-        private static final String PROPERTY_NAME = "jdk.tls.client.protocols";
+    private static class CustomizedTLSContext extends AbstractTLSContext {
+
         private static final SSLParameters defaultClientSSLParams;
         private static IllegalArgumentException reservedException = null;
 
@@ -621,78 +722,52 @@
         // the provider service. Instead, let's handle the initialization
         // exception in constructor.
         static {
-            // candidates for available protocols
-            ProtocolVersion[] candidates;
-
-            String property = AccessController.doPrivileged(
-                    new GetPropertyAction(PROPERTY_NAME));
-            if (property == null || property.length() == 0) {
-                // the default enabled client TLS protocols
-                if (SunJSSE.isFIPS()) {
-                    candidates = new ProtocolVersion[] {
-                        ProtocolVersion.TLS10,
-                        ProtocolVersion.TLS11,
-                        ProtocolVersion.TLS12
-                    };
-                } else {
-                    candidates = new ProtocolVersion[] {
-                        ProtocolVersion.SSL30,
-                        ProtocolVersion.TLS10,
-                        ProtocolVersion.TLS11,
-                        ProtocolVersion.TLS12
-                    };
-                }
-            } else {
-                // remove double quote marks from beginning/end of the property
-                if (property.length() > 1 && property.charAt(0) == '"' &&
-                        property.charAt(property.length() - 1) == '"') {
-                    property = property.substring(1, property.length() - 1);
-                }
-
-                String[] protocols = null;
-                if (property != null && property.length() != 0) {
-                    protocols = property.split(",");
-                } else {
-                    reservedException = new IllegalArgumentException(
-                        "No protocol specified in " +
-                        PROPERTY_NAME + " system property");
-                    protocols = new String[0];
-                }
-
-                candidates = new ProtocolVersion[protocols.length];
-                for (int i = 0; i < protocols.length; i++) {
-                    protocols[i] = protocols[i].trim();
-                    // Is it a supported protocol name?
-                    try {
-                        candidates[i] = ProtocolVersion.valueOf(protocols[i]);
-                    } catch (IllegalArgumentException iae) {
-                        reservedException = new IllegalArgumentException(
-                            PROPERTY_NAME + ": " + protocols[i] +
-                            " is not a standard SSL/TLS protocol name", iae);
-                        break;
+            reservedException = CustomizedSSLProtocols.reservedException;
+            if (reservedException == null) {
+                ArrayList<ProtocolVersion>
+                        customizedTLSProtocols = new ArrayList<>();
+                for (ProtocolVersion protocol :
+                        CustomizedSSLProtocols.customizedProtocols) {
+                    if (!protocol.isDTLSProtocol()) {
+                        customizedTLSProtocols.add(protocol);
                     }
                 }
 
-                if ((reservedException == null) && SunJSSE.isFIPS()) {
-                    for (ProtocolVersion protocolVersion : candidates) {
-                        if (ProtocolVersion.SSL20Hello.v == protocolVersion.v ||
-                                ProtocolVersion.SSL30.v == protocolVersion.v) {
-                            reservedException = new IllegalArgumentException(
-                                    PROPERTY_NAME + ": " + protocolVersion +
-                                    " is not FIPS compliant");
-                        }
+                // candidates for available protocols
+                ProtocolVersion[] candidates;
+                if (customizedTLSProtocols.isEmpty()) {
+                    // Use the default enabled client protocols if no
+                    // customized TLS protocols.
+                    if (SunJSSE.isFIPS()) {
+                        candidates = new ProtocolVersion[] {
+                            ProtocolVersion.TLS10,
+                            ProtocolVersion.TLS11,
+                            ProtocolVersion.TLS12
+                        };
+                    } else {
+                        candidates = new ProtocolVersion[] {
+                            ProtocolVersion.SSL30,
+                            ProtocolVersion.TLS10,
+                            ProtocolVersion.TLS11,
+                            ProtocolVersion.TLS12
+                        };
                     }
+                } else {
+                    // Use the customized TLS protocols.
+                    candidates =
+                            new ProtocolVersion[customizedTLSProtocols.size()];
+                    candidates = customizedTLSProtocols.toArray(candidates);
                 }
-            }
 
-            defaultClientSSLParams = new SSLParameters();
-            if (reservedException == null) {
+                defaultClientSSLParams = new SSLParameters();
                 defaultClientSSLParams.setProtocols(
                         getAvailableProtocols(candidates));
+            } else {
+                defaultClientSSLParams = null;  // unlikely to be used
             }
         }
 
-        protected CustomizedSSLContext() {
+        protected CustomizedTLSContext() {
             if (reservedException != null) {
                 throw reservedException;
             }
@@ -709,7 +784,7 @@
      *
      * @see SSLContext
      */
-    public static final class TLSContext extends CustomizedSSLContext {
+    public static final class TLSContext extends CustomizedTLSContext {
         // use the default constructor and methods
     }
 
@@ -718,7 +793,7 @@
      *
      * @see SSLContext
      */
-    public static final class DefaultSSLContext extends CustomizedSSLContext {
+    public static final class DefaultSSLContext extends CustomizedTLSContext {
         private static final String NONE = "NONE";
         private static final String P11KEYSTORE = "PKCS11";
 
@@ -879,6 +954,193 @@
         }
     }
 
+    /*
+     * The base abstract SSLContext implementation for the Datagram Transport
+     * Layer Security (DTLS) protocols.
+     *
+     * This abstract class encapsulates supported and the default server DTLS
+     * parameters.
+     *
+     * @see SSLContext
+     */
+    private abstract static class AbstractDTLSContext extends SSLContextImpl {
+        // parameters
+        private static final SSLParameters defaultServerSSLParams;
+        private static final SSLParameters supportedSSLParams;
+
+        static {
+            // supported SSL parameters
+            supportedSSLParams = new SSLParameters();
+
+            // Both DTLSv1.0 and DTLSv1.2 can be used in FIPS mode.
+            supportedSSLParams.setProtocols(new String[] {
+                ProtocolVersion.DTLS10.name,
+                ProtocolVersion.DTLS12.name
+            });
+
+            // candidates for available protocols
+            ProtocolVersion[] candidates = new ProtocolVersion[] {
+                ProtocolVersion.DTLS10,
+                ProtocolVersion.DTLS12
+            };
+
+            defaultServerSSLParams = new SSLParameters();
+            defaultServerSSLParams.setProtocols(
+                    getAvailableProtocols(candidates));
+        }
+
+        @Override
+        SSLParameters getDefaultServerSSLParams() {
+            return defaultServerSSLParams;
+        }
+
+        @Override
+        SSLParameters getSupportedSSLParams() {
+            return supportedSSLParams;
+        }
+
+        @Override
+        SSLEngine createSSLEngineImpl() {
+            return new SSLEngineImpl(this, true);
+        }
+
+        @Override
+        SSLEngine createSSLEngineImpl(String host, int port) {
+            return new SSLEngineImpl(this, host, port, true);
+        }
+
+        @Override
+        HelloCookieManager getHelloCookieManager(SecureRandom secureRandom) {
+            return new HelloCookieManager(secureRandom);
+        }
+    }
+
+    /*
+     * The SSLContext implementation for DTLSv1.0 algorithm.
+     *
+     * @see SSLContext
+     */
+    public static final class DTLS10Context extends AbstractDTLSContext {
+        private final static SSLParameters defaultClientSSLParams;
+
+        static {
+            // candidates for available protocols
+            ProtocolVersion[] candidates = new ProtocolVersion[] {
+                ProtocolVersion.DTLS10
+            };
+
+            defaultClientSSLParams = new SSLParameters();
+            defaultClientSSLParams.setProtocols(
+                    getAvailableProtocols(candidates));
+        }
+
+        @Override
+        SSLParameters getDefaultClientSSLParams() {
+            return defaultClientSSLParams;
+        }
+    }
+
+    /*
+     * The SSLContext implementation for DTLSv1.2 algorithm.
+     *
+     * @see SSLContext
+     */
+    public static final class DTLS12Context extends AbstractDTLSContext {
+        private final static SSLParameters defaultClientSSLParams;
+
+        static {
+            // candidates for available protocols
+            ProtocolVersion[] candidates = new ProtocolVersion[] {
+                ProtocolVersion.DTLS10,
+                ProtocolVersion.DTLS12
+            };
+
+            defaultClientSSLParams = new SSLParameters();
+            defaultClientSSLParams.setProtocols(
+                    getAvailableProtocols(candidates));
+        }
+
+        @Override
+        SSLParameters getDefaultClientSSLParams() {
+            return defaultClientSSLParams;
+        }
+    }
+
+    /*
+     * The SSLContext implementation for customized TLS protocols
+     *
+     * @see SSLContext
+     */
+    private static class CustomizedDTLSContext extends AbstractDTLSContext {
+        private final static SSLParameters defaultClientSSLParams;
+        private static IllegalArgumentException reservedException = null;
+
+        // Don't want a java.lang.LinkageError for illegal system property.
+        //
+        // Please don't throw exception in this static block.  Otherwise,
+        // java.lang.LinkageError may be thrown during the instantiation of
+        // the provider service. Instead, let's handle the initialization
+        // exception in constructor.
+        static {
+            reservedException = CustomizedSSLProtocols.reservedException;
+            if (reservedException == null) {
+                ArrayList<ProtocolVersion>
+                        customizedDTLSProtocols = new ArrayList<>();
+                for (ProtocolVersion protocol :
+                        CustomizedSSLProtocols.customizedProtocols) {
+                    if (protocol.isDTLSProtocol()) {
+                        customizedDTLSProtocols.add(protocol);
+                    }
+                }
+
+                // candidates for available protocols
+                ProtocolVersion[] candidates;
+                if (customizedDTLSProtocols.isEmpty()) {
+                    // Use the default enabled client protocols if no
+                    // customized TLS protocols.
+                    //
+                    // Both DTLSv1.0 and DTLSv1.2 can be used in FIPS mode.
+                    candidates = new ProtocolVersion[] {
+                        ProtocolVersion.DTLS10,
+                        ProtocolVersion.DTLS12
+                    };
+
+                } else {
+                    // Use the customized TLS protocols.
+                    candidates =
+                            new ProtocolVersion[customizedDTLSProtocols.size()];
+                    candidates = customizedDTLSProtocols.toArray(candidates);
+                }
+
+                defaultClientSSLParams = new SSLParameters();
+                defaultClientSSLParams.setProtocols(
+                        getAvailableProtocols(candidates));
+            } else {
+                defaultClientSSLParams = null;   // unlikely to be used
+            }
+        }
+
+        protected CustomizedDTLSContext() {
+            if (reservedException != null) {
+                throw reservedException;
+            }
+        }
+
+        @Override
+        SSLParameters getDefaultClientSSLParams() {
+            return defaultClientSSLParams;
+        }
+    }
+
+    /*
+     * The SSLContext implementation for default "DTLS" algorithm
+     *
+     * @see SSLContext
+     */
+    public static final class DTLSContext extends CustomizedDTLSContext {
+        // use the default constructor and methods
+    }
+
 }
 
 
@@ -961,7 +1223,7 @@
             ProtocolVersion protocolVersion =
                 ProtocolVersion.valueOf(session.getProtocol());
             AlgorithmConstraints constraints = null;
-            if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+            if (protocolVersion.useTLS12PlusSpec()) {
                 if (session instanceof ExtendedSSLSession) {
                     ExtendedSSLSession extSession =
                                     (ExtendedSSLSession)session;
@@ -1003,7 +1265,7 @@
             ProtocolVersion protocolVersion =
                 ProtocolVersion.valueOf(session.getProtocol());
             AlgorithmConstraints constraints = null;
-            if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+            if (protocolVersion.useTLS12PlusSpec()) {
                 if (session instanceof ExtendedSSLSession) {
                     ExtendedSSLSession extSession =
                                     (ExtendedSSLSession)session;
--- a/jdk/src/java.base/share/classes/sun/security/ssl/SSLEngineImpl.java	Mon Jun 01 10:15:21 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/SSLEngineImpl.java	Tue Jun 02 09:15:47 2015 -0700
@@ -54,57 +54,6 @@
  * before thread1, and sends the data.  The receiving side would see an
  * out-of-order error.
  *
- * Handshaking is still done the same way as SSLSocket using the normal
- * InputStream/OutputStream abstactions.  We create
- * ClientHandshakers/ServerHandshakers, which produce/consume the
- * handshaking data.  The transfer of the data is largely handled by the
- * HandshakeInStream/HandshakeOutStreams.  Lastly, the
- * InputRecord/OutputRecords still have the same functionality, except
- * that they are overridden with EngineInputRecord/EngineOutputRecord,
- * which provide SSLEngine-specific functionality.
- *
- * Some of the major differences are:
- *
- * EngineInputRecord/EngineOutputRecord/EngineWriter:
- *
- *      In order to avoid writing whole new control flows for
- *      handshaking, and to reuse most of the same code, we kept most
- *      of the actual handshake code the same.  As usual, reading
- *      handshake data may trigger output of more handshake data, so
- *      what we do is write this data to internal buffers, and wait for
- *      wrap() to be called to give that data a ride.
- *
- *      All data is routed through
- *      EngineInputRecord/EngineOutputRecord.  However, all handshake
- *      data (ct_alert/ct_change_cipher_spec/ct_handshake) are passed
- *      through to the underlying InputRecord/OutputRecord, and
- *      the data uses the internal buffers.
- *
- *      Application data is handled slightly different, we copy the data
- *      directly from the src to the dst buffers, and do all operations
- *      on those buffers, saving the overhead of multiple copies.
- *
- *      In the case of an inbound record, unwrap passes the inbound
- *      ByteBuffer to the InputRecord.  If the data is handshake data,
- *      the data is read into the InputRecord's internal buffer.  If
- *      the data is application data, the data is decoded directly into
- *      the dst buffer.
- *
- *      In the case of an outbound record, when the write to the
- *      "real" OutputStream's would normally take place, instead we
- *      call back up to the EngineOutputRecord's version of
- *      writeBuffer, at which time we capture the resulting output in a
- *      ByteBuffer, and send that back to the EngineWriter for internal
- *      storage.
- *
- *      EngineWriter is responsible for "handling" all outbound
- *      data, be it handshake or app data, and for returning the data
- *      to wrap() in the proper order.
- *
- * ClientHandshaker/ServerHandshaker/Handshaker:
- *      Methods which relied on SSLSocket now have work on either
- *      SSLSockets or SSLEngines.
- *
  * @author Brad Wetmore
  */
 final public class SSLEngineImpl extends SSLEngine {
@@ -175,11 +124,10 @@
     /*
      * Once we're in state cs_CLOSED, we can continue to
      * wrap/unwrap until we finish sending/receiving the messages
-     * for close_notify.  EngineWriter handles outboundDone.
+     * for close_notify.
      */
     private boolean             inboundDone = false;
-
-    EngineWriter                writer;
+    private boolean             outboundDone = false;
 
     /*
      * The authentication context holds all information used to establish
@@ -201,21 +149,6 @@
     private SSLSessionImpl              sess;
     private volatile SSLSessionImpl     handshakeSession;
 
-
-    /*
-     * Client authentication be off, requested, or required.
-     *
-     * This will be used by both this class and SSLSocket's variants.
-     */
-    static final byte           clauth_none = 0;
-    static final byte           clauth_requested = 1;
-    static final byte           clauth_required = 2;
-
-    /*
-     * Flag indicating that the engine has received a ChangeCipherSpec message.
-     */
-    private boolean             receivedCCS;
-
     /*
      * Flag indicating if the next record we receive MUST be a Finished
      * message. Temporarily set during the handshake to ensure that
@@ -243,11 +176,12 @@
      * Per-connection private state that doesn't change when the
      * session is changed.
      */
-    private byte                        doClientAuth;
-    private boolean                     enableSessionCreation = true;
-    EngineInputRecord                   inputRecord;
-    EngineOutputRecord                  outputRecord;
-    private AccessControlContext        acc;
+    private ClientAuthType          doClientAuth =
+                                            ClientAuthType.CLIENT_AUTH_NONE;
+    private boolean                 enableSessionCreation = true;
+    InputRecord                     inputRecord;
+    OutputRecord                    outputRecord;
+    private AccessControlContext    acc;
 
     // The cipher suites enabled for use on this connection.
     private CipherSuiteList             enabledCipherSuites;
@@ -280,14 +214,7 @@
     /*
      * The SSL version associated with this connection.
      */
-    private ProtocolVersion     protocolVersion = ProtocolVersion.DEFAULT;
-
-    /*
-     * Crypto state that's reinitialized when the session changes.
-     */
-    private Authenticator       readAuthenticator, writeAuthenticator;
-    private CipherBox           readCipher, writeCipher;
-    // NOTE: compression state would be saved here
+    private ProtocolVersion     protocolVersion;
 
     /*
      * security parameters for secure renegotiation.
@@ -320,17 +247,27 @@
     Object                      writeLock;
 
     /*
-     * Is it the first application record to write?
-     */
-    private boolean isFirstAppOutputRecord = true;
-
-    /*
      * Whether local cipher suites preference in server side should be
      * honored during handshaking?
      */
     private boolean preferLocalCipherSuites = false;
 
     /*
+     * whether DTLS handshake retransmissions should be enabled?
+     */
+    private boolean enableRetransmissions = false;
+
+    /*
+     * The maximum expected network packet size for SSL/TLS/DTLS records.
+     */
+    private int maximumPacketSize = 0;
+
+    /*
+     * Is this an instance for Datagram Transport Layer Security (DTLS)?
+     */
+    private final boolean isDTLS;
+
+    /*
      * Class and subclass dynamic debugging support
      */
     private static final Debug debug = Debug.getInstance("ssl");
@@ -344,23 +281,25 @@
      * host/port hints.  This Engine will not be able to cache
      * sessions, but must renegotiate everything by hand.
      */
-    SSLEngineImpl(SSLContextImpl ctx) {
+    SSLEngineImpl(SSLContextImpl ctx, boolean isDTLS) {
         super();
-        init(ctx);
+        this.isDTLS = isDTLS;
+        init(ctx, isDTLS);
     }
 
     /**
      * Constructor for an SSLEngine from SSLContext.
      */
-    SSLEngineImpl(SSLContextImpl ctx, String host, int port) {
+    SSLEngineImpl(SSLContextImpl ctx, String host, int port, boolean isDTLS) {
         super(host, port);
-        init(ctx);
+        this.isDTLS = isDTLS;
+        init(ctx, isDTLS);
     }
 
     /**
      * Initializes the Engine
      */
-    private void init(SSLContextImpl ctx) {
+    private void init(SSLContextImpl ctx, boolean isDTLS) {
         if (debug != null && Debug.isOn("ssl")) {
             System.out.println("Using SSLEngineImpl.");
         }
@@ -368,6 +307,8 @@
         sslContext = ctx;
         sess = SSLSessionImpl.nullSession;
         handshakeSession = null;
+        protocolVersion = isDTLS ?
+                ProtocolVersion.DEFAULT_DTLS : ProtocolVersion.DEFAULT_TLS;
 
         /*
          * State is cs_START until we initialize the handshaker.
@@ -377,22 +318,11 @@
          */
         roleIsServer = true;
         connectionState = cs_START;
-        receivedCCS = false;
 
         // default server name indication
         serverNames =
             Utilities.addToSNIServerNameList(serverNames, getPeerHost());
 
-        /*
-         * default read and write side cipher and MAC support
-         *
-         * Note:  compression support would go here too
-         */
-        readCipher = CipherBox.NULL;
-        readAuthenticator = MAC.NULL;
-        writeCipher = CipherBox.NULL;
-        writeAuthenticator = MAC.NULL;
-
         // default security parameters for secure renegotiation
         secureRenegotiation = false;
         clientVerifyData = new byte[0];
@@ -421,12 +351,19 @@
          * elsewhere.  All inbound data goes through this one
          * input record.
          */
-        outputRecord =
-            new EngineOutputRecord(Record.ct_application_data, this);
-        inputRecord = new EngineInputRecord(this);
-        inputRecord.enableFormatChecks();
+        if (isDTLS) {
+            enableRetransmissions = true;
+
+            // SSLEngine needs no record local buffer
+            outputRecord = new DTLSOutputRecord();
+            inputRecord = new DTLSInputRecord();
 
-        writer = new EngineWriter();
+        } else {
+            outputRecord = new SSLEngineOutputRecord();
+            inputRecord = new SSLEngineInputRecord();
+        }
+
+        maximumPacketSize = outputRecord.getMaxPacketSize();
     }
 
     /**
@@ -480,18 +417,23 @@
             handshaker = new ServerHandshaker(this, sslContext,
                     enabledProtocols, doClientAuth,
                     protocolVersion, connectionState == cs_HANDSHAKE,
-                    secureRenegotiation, clientVerifyData, serverVerifyData);
+                    secureRenegotiation, clientVerifyData, serverVerifyData,
+                    isDTLS);
             handshaker.setSNIMatchers(sniMatchers);
             handshaker.setUseCipherSuitesOrder(preferLocalCipherSuites);
         } else {
             handshaker = new ClientHandshaker(this, sslContext,
                     enabledProtocols,
                     protocolVersion, connectionState == cs_HANDSHAKE,
-                    secureRenegotiation, clientVerifyData, serverVerifyData);
+                    secureRenegotiation, clientVerifyData, serverVerifyData,
+                    isDTLS);
             handshaker.setSNIServerNames(serverNames);
         }
+        handshaker.setMaximumPacketSize(maximumPacketSize);
         handshaker.setEnabledCipherSuites(enabledCipherSuites);
         handshaker.setEnableSessionCreation(enableSessionCreation);
+
+        outputRecord.initHandshaker();
     }
 
     /*
@@ -504,11 +446,14 @@
         }
 
         synchronized (this) {
-            if (writer.hasOutboundData()) {
+            if (!outputRecord.isEmpty()) {
+                // If no handshaking, special case to wrap alters.
                 return HandshakeStatus.NEED_WRAP;
             } else if (handshaker != null) {
                 if (handshaker.taskOutstanding()) {
                     return HandshakeStatus.NEED_TASK;
+                } else if (isDTLS && !inputRecord.isEmpty()) {
+                    return HandshakeStatus.NEED_UNWRAP_AGAIN;
                 } else {
                     return HandshakeStatus.NEED_UNWRAP;
                 }
@@ -572,67 +517,15 @@
     }
 
     /*
-     * When a connection finishes handshaking by enabling use of a newly
-     * negotiated session, each end learns about it in two halves (read,
-     * and write).  When both read and write ciphers have changed, and the
-     * last handshake message has been read, the connection has joined
-     * (rejoined) the new session.
-     *
-     * NOTE:  The SSLv3 spec is rather unclear on the concepts here.
-     * Sessions don't change once they're established (including cipher
-     * suite and master secret) but connections can join them (and leave
-     * them).  They're created by handshaking, though sometime handshaking
-     * causes connections to join up with pre-established sessions.
-     *
-     * Synchronized on "this" from readRecord.
-     */
-    private void changeReadCiphers() throws SSLException {
-        if (connectionState != cs_HANDSHAKE
-                && connectionState != cs_RENEGOTIATE) {
-            throw new SSLProtocolException(
-                "State error, change cipher specs");
-        }
-
-        // ... create decompressor
-
-        CipherBox oldCipher = readCipher;
-
-        try {
-            readCipher = handshaker.newReadCipher();
-            readAuthenticator = handshaker.newReadAuthenticator();
-        } catch (GeneralSecurityException e) {
-            // "can't happen"
-            throw new SSLException("Algorithm missing:  ", e);
-        }
-
-        /*
-         * Dispose of any intermediate state in the underlying cipher.
-         * For PKCS11 ciphers, this will release any attached sessions,
-         * and thus make finalization faster.
-         *
-         * Since MAC's doFinal() is called for every SSL/TLS packet, it's
-         * not necessary to do the same with MAC's.
-         */
-        oldCipher.dispose();
-    }
-
-    /*
      * used by Handshaker to change the active write cipher, follows
      * the output of the CCS message.
      *
      * Also synchronized on "this" from readRecord/delegatedTask.
      */
-    void changeWriteCiphers() throws SSLException {
-        if (connectionState != cs_HANDSHAKE
-                && connectionState != cs_RENEGOTIATE) {
-            throw new SSLProtocolException(
-                "State error, change cipher specs");
-        }
+    void changeWriteCiphers() throws IOException {
 
-        // ... create compressor
-
-        CipherBox oldCipher = writeCipher;
-
+        Authenticator writeAuthenticator;
+        CipherBox writeCipher;
         try {
             writeCipher = handshaker.newWriteCipher();
             writeAuthenticator = handshaker.newWriteAuthenticator();
@@ -641,11 +534,7 @@
             throw new SSLException("Algorithm missing:  ", e);
         }
 
-        // See comment above.
-        oldCipher.dispose();
-
-        // reset the flag of the first application record
-        isFirstAppOutputRecord = true;
+        outputRecord.changeWriteCiphers(writeAuthenticator, writeCipher);
     }
 
     /*
@@ -716,10 +605,6 @@
         //
         // Kickstart handshake state machine if we need to ...
         //
-        // Note that handshaker.kickstart() writes the message
-        // to its HandshakeOutStream, which calls back into
-        // SSLSocketImpl.writeRecord() to send it.
-        //
         if (!handshaker.activated()) {
              // prior to handshaking, activate the handshake
             if (connectionState == cs_RENEGOTIATE) {
@@ -738,10 +623,6 @@
                 } else {
                     // we want to renegotiate, send hello request
                     handshaker.kickstart();
-
-                    // hello request is not included in the handshake
-                    // hashes, reset them
-                    handshaker.handshakeHash.reset();
                 }
             }
         }
@@ -771,15 +652,20 @@
      * the unwrapLock, which blocks multiple unwraps from occurring.
      */
     @Override
-    public SSLEngineResult unwrap(ByteBuffer netData, ByteBuffer [] appData,
+    public SSLEngineResult unwrap(ByteBuffer netData, ByteBuffer[] appData,
             int offset, int length) throws SSLException {
 
-        EngineArgs ea = new EngineArgs(netData, appData, offset, length);
+        // check engine parameters
+        checkEngineParas(netData, appData, offset, length, false);
 
         try {
             synchronized (unwrapLock) {
-                return readNetRecord(ea);
+                return readNetRecord(netData, appData, offset, length);
             }
+        } catch (SSLProtocolException spe) {
+            // may be an unexpected handshake message
+            fatal(Alerts.alert_unexpected_message, spe.getMessage(), spe);
+            return null;  // make compiler happy
         } catch (Exception e) {
             /*
              * Don't reset position so it looks like we didn't
@@ -790,11 +676,39 @@
             fatal(Alerts.alert_internal_error,
                 "problem unwrapping net record", e);
             return null;  // make compiler happy
-        } finally {
+        }
+    }
+
+    private static void checkEngineParas(ByteBuffer netData,
+            ByteBuffer[] appData, int offset, int len, boolean isForWrap) {
+
+        if ((netData == null) || (appData == null)) {
+            throw new IllegalArgumentException("src/dst is null");
+        }
+
+        if ((offset < 0) || (len < 0) || (offset > appData.length - len)) {
+            throw new IndexOutOfBoundsException();
+        }
+
+        /*
+         * If wrapping, make sure the destination bufffer is writable.
+         */
+        if (isForWrap && netData.isReadOnly()) {
+            throw new ReadOnlyBufferException();
+        }
+
+        for (int i = offset; i < offset + len; i++) {
+            if (appData[i] == null) {
+                throw new IllegalArgumentException(
+                        "appData[" + i + "] == null");
+            }
+
             /*
-             * Just in case something failed to reset limits properly.
+             * If unwrapping, make sure the destination bufffers are writable.
              */
-            ea.resetLim();
+            if (!isForWrap && appData[i].isReadOnly()) {
+                throw new ReadOnlyBufferException();
+            }
         }
     }
 
@@ -802,7 +716,8 @@
      * Makes additional checks for unwrap, but this time more
      * specific to this packet and the current state of the machine.
      */
-    private SSLEngineResult readNetRecord(EngineArgs ea) throws IOException {
+    private SSLEngineResult readNetRecord(ByteBuffer netData,
+            ByteBuffer[] appData, int offset, int length) throws IOException {
 
         Status status = null;
         HandshakeStatus hsStatus = null;
@@ -857,54 +772,117 @@
          * message which would change the ciphers.
          */
         if (hsStatus == HandshakeStatus.NEED_TASK) {
+            return new SSLEngineResult(Status.OK, hsStatus, 0, 0);
+        }
+
+        if (hsStatus == SSLEngineResult.HandshakeStatus.NEED_UNWRAP_AGAIN) {
+            Plaintext plainText = null;
+            try {
+                plainText = readRecord(null, null, 0, 0);
+            } catch (SSLException e) {
+                throw e;
+            } catch (IOException e) {
+                throw new SSLException("readRecord", e);
+            }
+
+            status = (isInboundDone() ? Status.CLOSED : Status.OK);
+            hsStatus = getHSStatus(plainText.handshakeStatus);
+
             return new SSLEngineResult(
-                Status.OK, hsStatus, 0, 0);
+                    status, hsStatus, 0, 0, plainText.recordSN);
         }
 
         /*
          * Check the packet to make sure enough is here.
          * This will also indirectly check for 0 len packets.
          */
-        int packetLen = inputRecord.bytesInCompletePacket(ea.netData);
+        int packetLen = 0;
+        try {
+            packetLen = inputRecord.bytesInCompletePacket(netData);
+        } catch (SSLException ssle) {
+            // Need to discard invalid records for DTLS protocols.
+            if (isDTLS) {
+                if (debug != null && Debug.isOn("ssl")) {
+                    System.out.println(
+                        Thread.currentThread().getName() +
+                        " discard invalid record: " + ssle);
+                }
+
+                // invalid, discard the entire data [section 4.1.2.7, RFC 6347]
+                int deltaNet = netData.remaining();
+                netData.position(netData.limit());
+
+                status = (isInboundDone() ? Status.CLOSED : Status.OK);
+                hsStatus = getHSStatus(hsStatus);
+
+                return new SSLEngineResult(status, hsStatus, deltaNet, 0, -1L);
+            } else {
+                throw ssle;
+            }
+        }
 
         // Is this packet bigger than SSL/TLS normally allows?
         if (packetLen > sess.getPacketBufferSize()) {
-            if (packetLen > Record.maxLargeRecordSize) {
-                throw new SSLProtocolException(
-                    "Input SSL/TLS record too big: max = " +
-                    Record.maxLargeRecordSize +
-                    " len = " + packetLen);
-            } else {
+            int largestRecordSize = isDTLS ?
+                    DTLSRecord.maxRecordSize : SSLRecord.maxLargeRecordSize;
+            if ((packetLen <= largestRecordSize) && !isDTLS) {
                 // Expand the expected maximum packet/application buffer
                 // sizes.
+                //
+                // Only apply to SSL/TLS protocols.
+
+                // Old behavior: shall we honor the System Property
+                // "jsse.SSLEngine.acceptLargeFragments" if it is "false"?
                 sess.expandBufferSizes();
             }
+
+            // check the packet again
+            largestRecordSize = sess.getPacketBufferSize();
+            if (packetLen > largestRecordSize) {
+                throw new SSLProtocolException(
+                        "Input record too big: max = " +
+                        largestRecordSize + " len = " + packetLen);
+            }
+        }
+
+        int netPos = netData.position();
+        int appRemains = 0;
+        for (int i = offset; i < offset + length; i++) {
+            if (appData[i] == null) {
+                throw new IllegalArgumentException(
+                        "appData[" + i + "] == null");
+            }
+            appRemains += appData[i].remaining();
         }
 
         /*
          * Check for OVERFLOW.
          *
-         * To be considered: We could delay enforcing the application buffer
-         * free space requirement until after the initial handshaking.
+         * Delay enforcing the application buffer free space requirement
+         * until after the initial handshaking.
          */
-        if ((packetLen - Record.headerSize) > ea.getAppRemaining()) {
-            return new SSLEngineResult(Status.BUFFER_OVERFLOW, hsStatus, 0, 0);
+        // synchronize connectionState?
+        if ((connectionState == cs_DATA) ||
+                (connectionState == cs_RENEGOTIATE)) {
+
+            int FragLen = inputRecord.estimateFragmentSize(packetLen);
+            if (FragLen > appRemains) {
+                return new SSLEngineResult(
+                        Status.BUFFER_OVERFLOW, hsStatus, 0, 0);
+            }
         }
 
         // check for UNDERFLOW.
-        if ((packetLen == -1) || (ea.netData.remaining() < packetLen)) {
-            return new SSLEngineResult(
-                Status.BUFFER_UNDERFLOW, hsStatus, 0, 0);
+        if ((packetLen == -1) || (netData.remaining() < packetLen)) {
+            return new SSLEngineResult(Status.BUFFER_UNDERFLOW, hsStatus, 0, 0);
         }
 
         /*
          * We're now ready to actually do the read.
-         * The only result code we really need to be exactly
-         * right is the HS finished, for signaling to
-         * HandshakeCompletedListeners.
          */
+        Plaintext plainText = null;
         try {
-            hsStatus = readRecord(ea);
+            plainText = readRecord(netData, appData, offset, length);
         } catch (SSLException e) {
             throw e;
         } catch (IOException e) {
@@ -922,10 +900,21 @@
          * status above should cover:  FINISHED, NEED_TASK
          */
         status = (isInboundDone() ? Status.CLOSED : Status.OK);
-        hsStatus = getHSStatus(hsStatus);
+        hsStatus = getHSStatus(plainText.handshakeStatus);
+
+        int deltaNet = netData.position() - netPos;
+        int deltaApp = appRemains;
+        for (int i = offset; i < offset + length; i++) {
+            deltaApp -= appData[i].remaining();
+        }
 
-        return new SSLEngineResult(status, hsStatus,
-            ea.deltaNet(), ea.deltaApp());
+        return new SSLEngineResult(
+                status, hsStatus, deltaNet, deltaApp, plainText.recordSN);
+    }
+
+    // the caller have synchronized readLock
+    void expectingFinishFlight() {
+        inputRecord.expectingFinishFlight();
     }
 
     /*
@@ -936,220 +925,266 @@
      * that a handshake just completed.
      *
      * It would be nice to be symmetrical with the write side and move
-     * the majority of this to EngineInputRecord, but there's too much
+     * the majority of this to SSLInputRecord, but there's too much
      * SSLEngine state to do that cleanly.  It must still live here.
      */
-    private HandshakeStatus readRecord(EngineArgs ea) throws IOException {
-
-        HandshakeStatus hsStatus = null;
+    private Plaintext readRecord(ByteBuffer netData,
+            ByteBuffer[] appData, int offset, int length) throws IOException {
 
         /*
          * The various operations will return new sliced BB's,
          * this will avoid having to worry about positions and
          * limits in the netBB.
          */
-        ByteBuffer readBB = null;
-        ByteBuffer decryptedBB = null;
+        Plaintext plainText = null;
 
-        if (getConnectionState() != cs_ERROR) {
+        if (getConnectionState() == cs_ERROR) {
+            return Plaintext.PLAINTEXT_NULL;
+        }
 
-            /*
-             * Read a record ... maybe emitting an alert if we get a
-             * comprehensible but unsupported "hello" message during
-             * format checking (e.g. V2).
-             */
-            try {
-                readBB = inputRecord.read(ea.netData);
-            } catch (IOException e) {
-                fatal(Alerts.alert_unexpected_message, e);
+        /*
+         * Read a record ... maybe emitting an alert if we get a
+         * comprehensible but unsupported "hello" message during
+         * format checking (e.g. V2).
+         */
+        try {
+            if (isDTLS) {
+                // Don't process the incoming record until all of the
+                // buffered records get handled.
+                plainText = inputRecord.acquirePlaintext();
             }
 
+            if ((!isDTLS || plainText == null) && netData != null) {
+                plainText = inputRecord.decode(netData);
+            }
+        } catch (UnsupportedOperationException unsoe) {         // SSLv2Hello
+            // Hack code to deliver SSLv2 error message for SSL/TLS connections.
+            if (!isDTLS) {
+                outputRecord.encodeV2NoCipher();
+            }
+
+            fatal(Alerts.alert_unexpected_message, unsoe);
+        } catch (BadPaddingException e) {
             /*
              * The basic SSLv3 record protection involves (optional)
              * encryption for privacy, and an integrity check ensuring
              * data origin authentication.  We do them both here, and
              * throw a fatal alert if the integrity check fails.
              */
-            try {
-                decryptedBB = inputRecord.decrypt(
-                                    readAuthenticator, readCipher, readBB);
-            } catch (BadPaddingException e) {
-                byte alertType = (inputRecord.contentType() ==
-                    Record.ct_handshake) ?
-                        Alerts.alert_handshake_failure :
-                        Alerts.alert_bad_record_mac;
-                fatal(alertType, e.getMessage(), e);
-            }
+            byte alertType = (connectionState != cs_DATA) ?
+                    Alerts.alert_handshake_failure :
+                    Alerts.alert_bad_record_mac;
+            fatal(alertType, e.getMessage(), e);
+        } catch (SSLHandshakeException she) {
+            // may be record sequence number overflow
+            fatal(Alerts.alert_handshake_failure, she);
+        } catch (IOException ioe) {
+            fatal(Alerts.alert_unexpected_message, ioe);
+        }
+
+        // plainText should never be null for TLS protocols
+        HandshakeStatus hsStatus = null;
+        if (!isDTLS || plainText != null) {
+            hsStatus = processInputRecord(plainText, appData, offset, length);
+        }
 
-            // if (!inputRecord.decompress(c))
-            //     fatal(Alerts.alert_decompression_failure,
-            //     "decompression failure");
+        if (hsStatus == null) {
+            hsStatus = getHSStatus(null);
+        }
+
+        if (plainText == null) {
+            plainText = new Plaintext();
+        }
+        plainText.handshakeStatus = hsStatus;
 
+        return plainText;
+    }
 
-            /*
-             * Process the record.
-             */
+    /*
+     * Process the record.
+     */
+    private synchronized HandshakeStatus processInputRecord(
+            Plaintext plainText,
+            ByteBuffer[] appData, int offset, int length) throws IOException {
 
-            synchronized (this) {
-                switch (inputRecord.contentType()) {
-                case Record.ct_handshake:
-                    /*
-                     * Handshake messages always go to a pending session
-                     * handshaker ... if there isn't one, create one.  This
-                     * must work asynchronously, for renegotiation.
-                     *
-                     * NOTE that handshaking will either resume a session
-                     * which was in the cache (and which might have other
-                     * connections in it already), or else will start a new
-                     * session (new keys exchanged) with just this connection
-                     * in it.
-                     */
-                    initHandshaker();
-                    if (!handshaker.activated()) {
-                        // prior to handshaking, activate the handshake
-                        if (connectionState == cs_RENEGOTIATE) {
-                            // don't use SSLv2Hello when renegotiating
-                            handshaker.activate(protocolVersion);
-                        } else {
-                            handshaker.activate(null);
-                        }
+        HandshakeStatus hsStatus = null;
+        switch (plainText.contentType) {
+            case Record.ct_handshake:
+                /*
+                 * Handshake messages always go to a pending session
+                 * handshaker ... if there isn't one, create one.  This
+                 * must work asynchronously, for renegotiation.
+                 *
+                 * NOTE that handshaking will either resume a session
+                 * which was in the cache (and which might have other
+                 * connections in it already), or else will start a new
+                 * session (new keys exchanged) with just this connection
+                 * in it.
+                 */
+                initHandshaker();
+                if (!handshaker.activated()) {
+                    // prior to handshaking, activate the handshake
+                    if (connectionState == cs_RENEGOTIATE) {
+                        // don't use SSLv2Hello when renegotiating
+                        handshaker.activate(protocolVersion);
+                    } else {
+                        handshaker.activate(null);
+                    }
+                }
+
+                /*
+                 * process the handshake record ... may contain just
+                 * a partial handshake message or multiple messages.
+                 *
+                 * The handshaker state machine will ensure that it's
+                 * a finished message.
+                 */
+                handshaker.processRecord(plainText.fragment, expectingFinished);
+                expectingFinished = false;
+
+                if (handshaker.invalidated) {
+                    finishHandshake();
+
+                    // if state is cs_RENEGOTIATE, revert it to cs_DATA
+                    if (connectionState == cs_RENEGOTIATE) {
+                        connectionState = cs_DATA;
+                    }
+                } else if (handshaker.isDone()) {
+                    // reset the parameters for secure renegotiation.
+                    secureRenegotiation =
+                                handshaker.isSecureRenegotiation();
+                    clientVerifyData = handshaker.getClientVerifyData();
+                    serverVerifyData = handshaker.getServerVerifyData();
+
+                    sess = handshaker.getSession();
+                    handshakeSession = null;
+                    if (outputRecord.isEmpty()) {
+                        hsStatus = finishHandshake();
+                        connectionState = cs_DATA;
                     }
 
-                    /*
-                     * process the handshake record ... may contain just
-                     * a partial handshake message or multiple messages.
-                     *
-                     * The handshaker state machine will ensure that it's
-                     * a finished message.
-                     */
-                    handshaker.process_record(inputRecord, expectingFinished);
-                    expectingFinished = false;
+                    // No handshakeListeners here.  That's a
+                    // SSLSocket thing.
+                } else if (handshaker.taskOutstanding()) {
+                    hsStatus = HandshakeStatus.NEED_TASK;
+                }
+                break;
 
-                    if (handshaker.invalidated) {
-                        handshaker = null;
-                        receivedCCS = false;
-                        // if state is cs_RENEGOTIATE, revert it to cs_DATA
-                        if (connectionState == cs_RENEGOTIATE) {
-                            connectionState = cs_DATA;
-                        }
-                    } else if (handshaker.isDone()) {
-                        // reset the parameters for secure renegotiation.
-                        secureRenegotiation =
-                                        handshaker.isSecureRenegotiation();
-                        clientVerifyData = handshaker.getClientVerifyData();
-                        serverVerifyData = handshaker.getServerVerifyData();
-
-                        sess = handshaker.getSession();
-                        handshakeSession = null;
-                        if (!writer.hasOutboundData()) {
-                            hsStatus = HandshakeStatus.FINISHED;
-                        }
-                        handshaker = null;
-                        connectionState = cs_DATA;
-                        receivedCCS = false;
-
-                        // No handshakeListeners here.  That's a
-                        // SSLSocket thing.
-                    } else if (handshaker.taskOutstanding()) {
-                        hsStatus = HandshakeStatus.NEED_TASK;
-                    }
-                    break;
-
-                case Record.ct_application_data:
-                    // Pass this right back up to the application.
-                    if ((connectionState != cs_DATA)
-                            && (connectionState != cs_RENEGOTIATE)
-                            && (connectionState != cs_CLOSED)) {
-                        throw new SSLProtocolException(
+            case Record.ct_application_data:
+                // Pass this right back up to the application.
+                if ((connectionState != cs_DATA)
+                        && (connectionState != cs_RENEGOTIATE)
+                        && (connectionState != cs_CLOSED)) {
+                    throw new SSLProtocolException(
                             "Data received in non-data state: " +
                             connectionState);
-                    }
-
-                    if (expectingFinished) {
-                        throw new SSLProtocolException
-                                ("Expecting finished message, received data");
-                    }
-
-                    /*
-                     * Don't return data once the inbound side is
-                     * closed.
-                     */
-                    if (!inboundDone) {
-                        ea.scatter(decryptedBB.slice());
-                    }
-                    break;
-
-                case Record.ct_alert:
-                    recvAlert();
-                    break;
+                }
 
-                case Record.ct_change_cipher_spec:
-                    if ((connectionState != cs_HANDSHAKE
-                                && connectionState != cs_RENEGOTIATE)
-                            || !handshaker.sessionKeysCalculated()
-                            || receivedCCS) {
-                        // For the CCS message arriving in the wrong state
-                        fatal(Alerts.alert_unexpected_message,
-                                "illegal change cipher spec msg, conn state = "
-                                + connectionState + ", handshake state = "
-                                + handshaker.state);
-                    } else if (inputRecord.available() != 1
-                            || inputRecord.read() != 1) {
-                        // For structural/content issues with the CCS
-                        fatal(Alerts.alert_unexpected_message,
-                                "Malformed change cipher spec msg");
-                    }
-
-                    // Once we've received CCS, update the flag.
-                    // If the remote endpoint sends it again in this handshake
-                    // we won't process it.
-                    receivedCCS = true;
+                if (expectingFinished) {
+                    throw new SSLProtocolException
+                            ("Expecting finished message, received data");
+                }
 
-                    //
-                    // The first message after a change_cipher_spec
-                    // record MUST be a "Finished" handshake record,
-                    // else it's a protocol violation.  We force this
-                    // to be checked by a minor tweak to the state
-                    // machine.
-                    //
-                    changeReadCiphers();
-                    // next message MUST be a finished message
-                    expectingFinished = true;
-                    break;
+                if (!inboundDone) {
+                    ByteBuffer fragment = plainText.fragment;
+                    int remains = fragment.remaining();
 
-                default:
-                    //
-                    // TLS requires that unrecognized records be ignored.
-                    //
-                    if (debug != null && Debug.isOn("ssl")) {
-                        System.out.println(Thread.currentThread().getName() +
-                            ", Received record type: "
-                            + inputRecord.contentType());
-                    }
-                    break;
-                } // switch
-
-                /*
-                 * We only need to check the sequence number state for
-                 * non-handshaking record.
-                 *
-                 * Note that in order to maintain the handshake status
-                 * properly, we check the sequence number after the last
-                 * record reading process. As we request renegotiation
-                 * or close the connection for wrapped sequence number
-                 * when there is enough sequence number space left to
-                 * handle a few more records, so the sequence number
-                 * of the last record cannot be wrapped.
-                 */
-                hsStatus = getHSStatus(hsStatus);
-                if (connectionState < cs_ERROR && !isInboundDone() &&
-                        (hsStatus == HandshakeStatus.NOT_HANDSHAKING)) {
-                    if (checkSequenceNumber(readAuthenticator,
-                            inputRecord.contentType())) {
-                        hsStatus = getHSStatus(null);
+                    // Should have enough room in appData.
+                    for (int i = offset;
+                            ((i < (offset + length)) && (remains > 0)); i++) {
+                        int amount = Math.min(appData[i].remaining(), remains);
+                        fragment.limit(fragment.position() + amount);
+                        appData[i].put(fragment);
+                        remains -= amount;
                     }
                 }
-            } // synchronized (this)
+
+                break;
+
+            case Record.ct_alert:
+                recvAlert(plainText.fragment);
+                break;
+
+            case Record.ct_change_cipher_spec:
+                if ((connectionState != cs_HANDSHAKE
+                        && connectionState != cs_RENEGOTIATE)) {
+                    // For the CCS message arriving in the wrong state
+                    fatal(Alerts.alert_unexpected_message,
+                            "illegal change cipher spec msg, conn state = "
+                            + connectionState);
+                } else if (plainText.fragment.remaining() != 1
+                        || plainText.fragment.get() != 1) {
+                    // For structural/content issues with the CCS
+                    fatal(Alerts.alert_unexpected_message,
+                            "Malformed change cipher spec msg");
+                }
+
+                //
+                // The first message after a change_cipher_spec
+                // record MUST be a "Finished" handshake record,
+                // else it's a protocol violation.  We force this
+                // to be checked by a minor tweak to the state
+                // machine.
+                //
+                handshaker.receiveChangeCipherSpec();
+
+                CipherBox readCipher;
+                Authenticator readAuthenticator;
+                try {
+                    readCipher = handshaker.newReadCipher();
+                    readAuthenticator = handshaker.newReadAuthenticator();
+                } catch (GeneralSecurityException e) {
+                    // can't happen
+                    throw new SSLException("Algorithm missing:  ", e);
+                }
+                inputRecord.changeReadCiphers(readAuthenticator, readCipher);
+
+                // next message MUST be a finished message
+                expectingFinished = true;
+                break;
+
+            default:
+                //
+                // TLS requires that unrecognized records be ignored.
+                //
+                if (debug != null && Debug.isOn("ssl")) {
+                    System.out.println(Thread.currentThread().getName() +
+                            ", Received record type: " + plainText.contentType);
+                }
+                break;
+        } // switch
+
+        /*
+         * We only need to check the sequence number state for
+         * non-handshaking record.
+         *
+         * Note that in order to maintain the handshake status
+         * properly, we check the sequence number after the last
+         * record reading process. As we request renegotiation
+         * or close the connection for wrapped sequence number
+         * when there is enough sequence number space left to
+         * handle a few more records, so the sequence number
+         * of the last record cannot be wrapped.
+         */
+        hsStatus = getHSStatus(hsStatus);
+        if (connectionState < cs_ERROR && !isInboundDone() &&
+                (hsStatus == HandshakeStatus.NOT_HANDSHAKING) &&
+                (inputRecord.seqNumIsHuge())) {
+            /*
+             * Ask for renegotiation when need to renew sequence number.
+             *
+             * Don't bother to kickstart the renegotiation when the local is
+             * asking for it.
+             */
+            if (debug != null && Debug.isOn("ssl")) {
+                System.out.println(Thread.currentThread().getName() +
+                        ", request renegotiation " +
+                        "to avoid sequence number overflow");
+            }
+
+            beginHandshake();
+
+            hsStatus = getHSStatus(null);
         }
 
         return hsStatus;
@@ -1166,36 +1201,33 @@
      * the wrapLock, which blocks multiple wraps from occurring.
      */
     @Override
-    public SSLEngineResult wrap(ByteBuffer [] appData,
+    public SSLEngineResult wrap(ByteBuffer[] appData,
             int offset, int length, ByteBuffer netData) throws SSLException {
 
-        EngineArgs ea = new EngineArgs(appData, offset, length, netData);
+        // check engine parameters
+        checkEngineParas(netData, appData, offset, length, true);
 
         /*
          * We can be smarter about using smaller buffer sizes later.
-         * For now, force it to be large enough to handle any
-         * valid SSL/TLS record.
+         * For now, force it to be large enough to handle any valid record.
          */
-        if (netData.remaining() < EngineOutputRecord.maxRecordSize) {
+        if (netData.remaining() < sess.getPacketBufferSize()) {
             return new SSLEngineResult(
                 Status.BUFFER_OVERFLOW, getHSStatus(null), 0, 0);
         }
 
         try {
             synchronized (wrapLock) {
-                return writeAppRecord(ea);
+                return writeAppRecord(appData, offset, length, netData);
             }
+        } catch (SSLProtocolException spe) {
+            // may be an unexpected handshake message
+            fatal(Alerts.alert_unexpected_message, spe.getMessage(), spe);
+            return null;  // make compiler happy
         } catch (Exception e) {
-            ea.resetPos();
-
             fatal(Alerts.alert_internal_error,
                 "problem wrapping app data", e);
             return null;  // make compiler happy
-        } finally {
-            /*
-             * Just in case something didn't reset limits properly.
-             */
-            ea.resetLim();
         }
     }
 
@@ -1203,7 +1235,8 @@
      * Makes additional checks for unwrap, but this time more
      * specific to this packet and the current state of the machine.
      */
-    private SSLEngineResult writeAppRecord(EngineArgs ea) throws IOException {
+    private SSLEngineResult writeAppRecord(ByteBuffer[] appData,
+            int offset, int length, ByteBuffer netData) throws IOException {
 
         Status status = null;
         HandshakeStatus hsStatus = null;
@@ -1216,7 +1249,7 @@
         /*
          * short circuit if we're closed/closing.
          */
-        if (writer.isOutboundDone()) {
+        if (isOutboundDone()) {
             return new SSLEngineResult(Status.CLOSED, getHSStatus(null), 0, 0);
         }
 
@@ -1226,7 +1259,8 @@
          */
         synchronized (this) {
             if ((connectionState == cs_HANDSHAKE) ||
-                    (connectionState == cs_START)) {
+                (connectionState == cs_START)) {
+
                 kickstartHandshake();
 
                 /*
@@ -1234,9 +1268,18 @@
                  * without trying to wrap anything.
                  */
                 hsStatus = getHSStatus(null);
+                if (hsStatus == HandshakeStatus.NEED_UNWRAP) {
+                    /*
+                     * For DTLS, if the handshake state is
+                     * HandshakeStatus.NEED_UNWRAP, a call to SSLEngine.wrap()
+                     * means that the previous handshake packets (if delivered)
+                     * get lost, and need retransmit the handshake messages.
+                     */
+                    if (!isDTLS || !enableRetransmissions ||
+                            (handshaker == null) || outputRecord.firstMessage) {
 
-                if (hsStatus == HandshakeStatus.NEED_UNWRAP) {
-                    return new SSLEngineResult(Status.OK, hsStatus, 0, 0);
+                        return new SSLEngineResult(Status.OK, hsStatus, 0, 0);
+                    }   // otherwise, need retransmission
                 }
             }
         }
@@ -1258,17 +1301,33 @@
          * message which would change the ciphers.
          */
         if (hsStatus == HandshakeStatus.NEED_TASK) {
-            return new SSLEngineResult(
-                Status.OK, hsStatus, 0, 0);
+            return new SSLEngineResult(Status.OK, hsStatus, 0, 0);
         }
 
         /*
          * This will obtain any waiting outbound data, or will
          * process the outbound appData.
          */
+        int netPos = netData.position();
+        int appRemains = 0;
+        for (int i = offset; i < offset + length; i++) {
+            if (appData[i] == null) {
+                throw new IllegalArgumentException(
+                        "appData[" + i + "] == null");
+            }
+            appRemains += appData[i].remaining();
+        }
+
+        Ciphertext ciphertext = null;
         try {
-            synchronized (writeLock) {
-                hsStatus = writeRecord(outputRecord, ea);
+            if (appRemains != 0) {
+                synchronized (writeLock) {
+                    ciphertext = writeRecord(appData, offset, length, netData);
+                }
+            } else {
+                synchronized (writeLock) {
+                    ciphertext = writeRecord(null, 0, 0, netData);
+                }
             }
         } catch (SSLException e) {
             throw e;
@@ -1283,21 +1342,62 @@
          * status above should cover:  NEED_WRAP/FINISHED
          */
         status = (isOutboundDone() ? Status.CLOSED : Status.OK);
-        hsStatus = getHSStatus(hsStatus);
+        hsStatus = getHSStatus(ciphertext.handshakeStatus);
 
-        return new SSLEngineResult(status, hsStatus,
-            ea.deltaApp(), ea.deltaNet());
+        int deltaNet = netData.position() - netPos;
+        int deltaApp = appRemains;
+        for (int i = offset; i < offset + length; i++) {
+            deltaApp -= appData[i].remaining();
+        }
+
+        return new SSLEngineResult(
+                status, hsStatus, deltaApp, deltaNet, ciphertext.recordSN);
     }
 
     /*
      * Central point to write/get all of the outgoing data.
      */
-    private HandshakeStatus writeRecord(EngineOutputRecord eor,
-            EngineArgs ea) throws IOException {
+    private Ciphertext writeRecord(ByteBuffer[] appData,
+            int offset, int length, ByteBuffer netData) throws IOException {
+
+        Ciphertext ciphertext = null;
+        try {
+            // Acquire the buffered to-be-delivered records or retransmissions.
+            //
+            // May have buffered records, or need retransmission if handshaking.
+            if (!outputRecord.isEmpty() || (handshaker != null)) {
+                ciphertext = outputRecord.acquireCiphertext(netData);
+            }
+
+            if ((ciphertext == null) && (appData != null)) {
+                ciphertext = outputRecord.encode(
+                        appData, offset, length, netData);
+            }
+        } catch (SSLHandshakeException she) {
+            // may be record sequence number overflow
+            fatal(Alerts.alert_handshake_failure, she);
 
-        // eventually compress as well.
-        HandshakeStatus hsStatus =
-                writer.writeRecord(eor, ea, writeAuthenticator, writeCipher);
+            return Ciphertext.CIPHERTEXT_NULL;   // make the complier happy
+        } catch (IOException e) {
+            fatal(Alerts.alert_unexpected_message, e);
+
+            return Ciphertext.CIPHERTEXT_NULL;   // make the complier happy
+        }
+
+        if (ciphertext == null) {
+            return Ciphertext.CIPHERTEXT_NULL;
+        }
+
+        HandshakeStatus hsStatus = null;
+        Ciphertext.RecordType recordType = ciphertext.recordType;
+        if ((handshaker != null) &&
+                (recordType.contentType == Record.ct_handshake) &&
+                (recordType.handshakeType == HandshakeMessage.ht_finished) &&
+                handshaker.isDone() && outputRecord.isEmpty()) {
+
+            hsStatus = finishHandshake();
+            connectionState = cs_DATA;
+        }   // Otherwise, the followed call to getHSStatus() will help.
 
         /*
          * We only need to check the sequence number state for
@@ -1313,129 +1413,41 @@
          */
         hsStatus = getHSStatus(hsStatus);
         if (connectionState < cs_ERROR && !isOutboundDone() &&
-                (hsStatus == HandshakeStatus.NOT_HANDSHAKING)) {
-            if (checkSequenceNumber(writeAuthenticator, eor.contentType())) {
-                hsStatus = getHSStatus(null);
-            }
-        }
-
-        /*
-         * turn off the flag of the first application record if we really
-         * consumed at least byte.
-         */
-        if (isFirstAppOutputRecord && ea.deltaApp() > 0) {
-            isFirstAppOutputRecord = false;
-        }
-
-        return hsStatus;
-    }
-
-    /*
-     * Need to split the payload except the following cases:
-     *
-     * 1. protocol version is TLS 1.1 or later;
-     * 2. bulk cipher does not use CBC mode, including null bulk cipher suites.
-     * 3. the payload is the first application record of a freshly
-     *    negotiated TLS session.
-     * 4. the CBC protection is disabled;
-     *
-     * More details, please refer to
-     * EngineOutputRecord.write(EngineArgs, MAC, CipherBox).
-     */
-    boolean needToSplitPayload(CipherBox cipher, ProtocolVersion protocol) {
-        return (protocol.v <= ProtocolVersion.TLS10.v) &&
-                cipher.isCBCMode() && !isFirstAppOutputRecord &&
-                Record.enableCBCProtection;
-    }
-
-    /*
-     * Non-application OutputRecords go through here.
-     */
-    void writeRecord(EngineOutputRecord eor) throws IOException {
-        // eventually compress as well.
-        writer.writeRecord(eor, writeAuthenticator, writeCipher);
-
-        /*
-         * Check the sequence number state
-         *
-         * Note that in order to maintain the connection I/O
-         * properly, we check the sequence number after the last
-         * record writing process. As we request renegotiation
-         * or close the connection for wrapped sequence number
-         * when there is enough sequence number space left to
-         * handle a few more records, so the sequence number
-         * of the last record cannot be wrapped.
-         */
-        if ((connectionState < cs_ERROR) && !isOutboundDone()) {
-            checkSequenceNumber(writeAuthenticator, eor.contentType());
-        }
-    }
-
-    //
-    // Close code
-    //
-
-    /**
-     * Check the sequence number state
-     *
-     * RFC 4346 states that, "Sequence numbers are of type uint64 and
-     * may not exceed 2^64-1.  Sequence numbers do not wrap. If a TLS
-     * implementation would need to wrap a sequence number, it must
-     * renegotiate instead."
-     *
-     * Return true if the handshake status may be changed.
-     */
-    private boolean checkSequenceNumber(Authenticator authenticator, byte type)
-            throws IOException {
-
-        /*
-         * Don't bother to check the sequence number for error or
-         * closed connections, or NULL MAC
-         */
-        if (connectionState >= cs_ERROR || authenticator == MAC.NULL) {
-            return false;
-        }
-
-        /*
-         * Conservatively, close the connection immediately when the
-         * sequence number is close to overflow
-         */
-        if (authenticator.seqNumOverflow()) {
+                (hsStatus == HandshakeStatus.NOT_HANDSHAKING) &&
+                (outputRecord.seqNumIsHuge())) {
             /*
-             * TLS protocols do not define a error alert for sequence
-             * number overflow. We use handshake_failure error alert
-             * for handshaking and bad_record_mac for other records.
+             * Ask for renegotiation when need to renew sequence number.
+             *
+             * Don't bother to kickstart the renegotiation when the local is
+             * asking for it.
              */
             if (debug != null && Debug.isOn("ssl")) {
                 System.out.println(Thread.currentThread().getName() +
-                    ", sequence number extremely close to overflow " +
-                    "(2^64-1 packets). Closing connection.");
-            }
-
-            fatal(Alerts.alert_handshake_failure, "sequence number overflow");
-
-            return true; // make the compiler happy
-        }
-
-        /*
-         * Ask for renegotiation when need to renew sequence number.
-         *
-         * Don't bother to kickstart the renegotiation when the local is
-         * asking for it.
-         */
-        if ((type != Record.ct_handshake) && authenticator.seqNumIsHuge()) {
-            if (debug != null && Debug.isOn("ssl")) {
-                System.out.println(Thread.currentThread().getName() +
                         ", request renegotiation " +
                         "to avoid sequence number overflow");
             }
 
             beginHandshake();
-            return true;
+
+            hsStatus = getHSStatus(null);
         }
+        ciphertext.handshakeStatus = hsStatus;
+
+        return ciphertext;
+    }
 
-        return false;
-    }
+    private HandshakeStatus finishHandshake() {
+        handshaker = null;
+        inputRecord.setHandshakeHash(null);
+        outputRecord.setHandshakeHash(null);
+        connectionState = cs_DATA;
+
+       return HandshakeStatus.FINISHED;
+   }
+
+    //
+    // Close code
+    //
 
     /**
      * Signals that no more outbound application data will be sent
@@ -1451,7 +1463,7 @@
         /*
          * Already closed, ignore
          */
-        if (writer.isOutboundDone()) {
+        if (outboundDone) {
             return;
         }
 
@@ -1461,7 +1473,18 @@
          * If we haven't even started yet, don't bother reading inbound.
          */
         case cs_START:
-            writer.closeOutbound();
+            try {
+                outputRecord.close();
+            } catch (IOException ioe) {
+               // ignore
+            }
+            outboundDone = true;
+
+            try {
+                inputRecord.close();
+            } catch (IOException ioe) {
+               // ignore
+            }
             inboundDone = true;
             break;
 
@@ -1477,13 +1500,15 @@
         // case cs_RENEGOTIATE:
         default:
             warning(Alerts.alert_close_notify);
-            writer.closeOutbound();
+            try {
+                outputRecord.close();
+            } catch (IOException ioe) {
+               // ignore
+            }
+            outboundDone = true;
             break;
         }
 
-        // See comment in changeReadCiphers()
-        writeCipher.dispose();
-
         connectionState = cs_CLOSED;
     }
 
@@ -1505,7 +1530,7 @@
      */
     @Override
     public boolean isOutboundDone() {
-        return writer.isOutboundDone();
+        return outboundDone && outputRecord.isEmpty();
     }
 
     /**
@@ -1527,11 +1552,14 @@
         }
 
         closeOutboundInternal();
+
+        try {
+            inputRecord.close();
+        } catch (IOException ioe) {
+           // ignore
+        }
         inboundDone = true;
 
-        // See comment in changeReadCiphers()
-        readCipher.dispose();
-
         connectionState = cs_CLOSED;
     }
 
@@ -1602,6 +1630,10 @@
     }
 
     synchronized void setHandshakeSession(SSLSessionImpl session) {
+        // update the fragment size, which may be negotiated during handshaking
+        inputRecord.changeFragmentSize(session.getNegotiatedMaxFragSize());
+        outputRecord.changeFragmentSize(session.getNegotiatedMaxFragSize());
+
         handshakeSession = session;
     }
 
@@ -1701,6 +1733,11 @@
         int oldState = connectionState;
         connectionState = cs_ERROR;
 
+        try {
+            inputRecord.close();
+        } catch (IOException ioe) {
+           // ignore
+        }
         inboundDone = true;
 
         sess.invalidate();
@@ -1728,14 +1765,15 @@
                 Alerts.getSSLException(description, cause, diagnostic);
         }
 
-        writer.closeOutbound();
+        try {
+            outputRecord.close();
+        } catch (IOException ioe) {
+           // ignore
+        }
+        outboundDone = true;
 
         connectionState = cs_CLOSED;
 
-        // See comment in changeReadCiphers()
-        readCipher.dispose();
-        writeCipher.dispose();
-
         if (cause instanceof RuntimeException) {
             throw (RuntimeException)cause;
         } else {
@@ -1747,9 +1785,10 @@
      * Process an incoming alert ... caller must already have synchronized
      * access to "this".
      */
-    private void recvAlert() throws IOException {
-        byte level = (byte)inputRecord.read();
-        byte description = (byte)inputRecord.read();
+    private void recvAlert(ByteBuffer fragment) throws IOException {
+        byte level = fragment.get();
+        byte description = fragment.get();
+
         if (description == -1) { // check for short message
             fatal(Alerts.alert_illegal_parameter, "Short alert message");
         }
@@ -1813,40 +1852,18 @@
 
         // For initial handshaking, don't send alert message to peer if
         // handshaker has not started.
-        if (connectionState == cs_HANDSHAKE &&
-            (handshaker == null || !handshaker.started())) {
+        //
+        // Shall we send an fatal alter to terminate the connection gracefully?
+        if (connectionState <= cs_HANDSHAKE &&
+                (handshaker == null || !handshaker.started() ||
+                        !handshaker.activated())) {
             return;
         }
 
-        EngineOutputRecord r = new EngineOutputRecord(Record.ct_alert, this);
-        r.setVersion(protocolVersion);
-
-        boolean useDebug = debug != null && Debug.isOn("ssl");
-        if (useDebug) {
-            synchronized (System.out) {
-                System.out.print(Thread.currentThread().getName());
-                System.out.print(", SEND " + protocolVersion + " ALERT:  ");
-                if (level == Alerts.alert_fatal) {
-                    System.out.print("fatal, ");
-                } else if (level == Alerts.alert_warning) {
-                    System.out.print("warning, ");
-                } else {
-                    System.out.print("<level = " + (0x0ff & level) + ">, ");
-                }
-                System.out.println("description = "
-                        + Alerts.alertDescription(description));
-            }
-        }
-
-        r.write(level);
-        r.write(description);
         try {
-            writeRecord(r);
-        } catch (IOException e) {
-            if (useDebug) {
-                System.out.println(Thread.currentThread().getName() +
-                    ", Exception sending alert: " + e);
-            }
+            outputRecord.encodeAlert(level, description);
+        } catch (IOException ioe) {
+            // ignore
         }
     }
 
@@ -1894,7 +1911,8 @@
     @Override
     synchronized public void setNeedClientAuth(boolean flag) {
         doClientAuth = (flag ?
-            SSLEngineImpl.clauth_required : SSLEngineImpl.clauth_none);
+                ClientAuthType.CLIENT_AUTH_REQUIRED :
+                ClientAuthType.CLIENT_AUTH_NONE);
 
         if ((handshaker != null) &&
                 (handshaker instanceof ServerHandshaker) &&
@@ -1905,7 +1923,7 @@
 
     @Override
     synchronized public boolean getNeedClientAuth() {
-        return (doClientAuth == SSLEngineImpl.clauth_required);
+        return (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUIRED);
     }
 
     /**
@@ -1919,7 +1937,8 @@
     @Override
     synchronized public void setWantClientAuth(boolean flag) {
         doClientAuth = (flag ?
-            SSLEngineImpl.clauth_requested : SSLEngineImpl.clauth_none);
+                ClientAuthType.CLIENT_AUTH_REQUESTED :
+                ClientAuthType.CLIENT_AUTH_NONE);
 
         if ((handshaker != null) &&
                 (handshaker instanceof ServerHandshaker) &&
@@ -1930,7 +1949,7 @@
 
     @Override
     synchronized public boolean getWantClientAuth() {
-        return (doClientAuth == SSLEngineImpl.clauth_requested);
+        return (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUESTED);
     }
 
 
@@ -1946,13 +1965,21 @@
 
         case cs_START:
             /*
-             * If we need to change the engine mode and the enabled
-             * protocols haven't specifically been set by the user,
-             * change them to the corresponding default ones.
+             * If we need to change the socket mode and the enabled
+             * protocols and cipher suites haven't specifically been
+             * set by the user, change them to the corresponding
+             * default ones.
              */
-            if (roleIsServer != (!flag) &&
-                    sslContext.isDefaultProtocolList(enabledProtocols)) {
-                enabledProtocols = sslContext.getDefaultProtocolList(!flag);
+            if (roleIsServer != (!flag)) {
+                if (sslContext.isDefaultProtocolList(enabledProtocols)) {
+                    enabledProtocols =
+                            sslContext.getDefaultProtocolList(!flag);
+                }
+
+                if (sslContext.isDefaultCipherSuiteList(enabledCipherSuites)) {
+                    enabledCipherSuites =
+                            sslContext.getDefaultCipherSuiteList(!flag);
+                }
             }
 
             roleIsServer = !flag;
@@ -1970,13 +1997,22 @@
             assert(handshaker != null);
             if (!handshaker.activated()) {
                 /*
-                 * If we need to change the engine mode and the enabled
-                 * protocols haven't specifically been set by the user,
-                 * change them to the corresponding default ones.
+                 * If we need to change the socket mode and the enabled
+                 * protocols and cipher suites haven't specifically been
+                 * set by the user, change them to the corresponding
+                 * default ones.
                  */
-                if (roleIsServer != (!flag) &&
-                        sslContext.isDefaultProtocolList(enabledProtocols)) {
-                    enabledProtocols = sslContext.getDefaultProtocolList(!flag);
+                if (roleIsServer != (!flag)) {
+                    if (sslContext.isDefaultProtocolList(enabledProtocols)) {
+                        enabledProtocols =
+                                sslContext.getDefaultProtocolList(!flag);
+                    }
+
+                    if (sslContext.isDefaultCipherSuiteList(
+                                                    enabledCipherSuites)) {
+                        enabledCipherSuites =
+                            sslContext.getDefaultCipherSuiteList(!flag);
+                    }
                 }
 
                 roleIsServer = !flag;
@@ -2102,6 +2138,8 @@
         params.setSNIMatchers(sniMatchers);
         params.setServerNames(serverNames);
         params.setUseCipherSuitesOrder(preferLocalCipherSuites);
+        params.setEnableRetransmissions(enableRetransmissions);
+        params.setMaximumPacketSize(maximumPacketSize);
 
         return params;
     }
@@ -2117,6 +2155,15 @@
         identificationProtocol = params.getEndpointIdentificationAlgorithm();
         algorithmConstraints = params.getAlgorithmConstraints();
         preferLocalCipherSuites = params.getUseCipherSuitesOrder();
+        enableRetransmissions = params.getEnableRetransmissions();
+        maximumPacketSize = params.getMaximumPacketSize();
+
+        if (maximumPacketSize != 0) {
+            outputRecord.changePacketSize(maximumPacketSize);
+        } else {
+            // use the implicit maximum packet size.
+            maximumPacketSize = outputRecord.getMaxPacketSize();
+        }
 
         List<SNIServerName> sniNames = params.getServerNames();
         if (sniNames != null) {
@@ -2131,6 +2178,7 @@
         if ((handshaker != null) && !handshaker.started()) {
             handshaker.setIdentificationProtocol(identificationProtocol);
             handshaker.setAlgorithmConstraints(algorithmConstraints);
+            handshaker.setMaximumPacketSize(maximumPacketSize);
             if (roleIsServer) {
                 handshaker.setSNIMatchers(sniMatchers);
                 handshaker.setUseCipherSuitesOrder(preferLocalCipherSuites);
@@ -2141,14 +2189,6 @@
     }
 
     /**
-     * Returns a boolean indicating whether the ChangeCipherSpec message
-     * has been received for this handshake.
-     */
-    boolean receivedChangeCipherSpec() {
-        return receivedCCS;
-    }
-
-    /**
      * Returns a printable representation of this end of the connection.
      */
     @Override
@@ -2162,6 +2202,7 @@
         retval.append((host == null) ? "null" : host);
         retval.append(" port=");
         retval.append(Integer.toString(getPeerPort()));
+        retval.append(" role=" + (roleIsServer ? "Server" : "Client"));
         retval.append("] ");
         retval.append(getSession().getCipherSuite());
         retval.append("]");
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/SSLEngineInputRecord.java	Tue Jun 02 09:15:47 2015 -0700
@@ -0,0 +1,409 @@
+/*
+ * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ssl;
+
+import java.io.*;
+import java.nio.*;
+
+import javax.crypto.BadPaddingException;
+
+import javax.net.ssl.*;
+
+import sun.misc.HexDumpEncoder;
+
+
+/**
+ * {@code InputRecord} implementation for {@code SSLEngine}.
+ */
+final class SSLEngineInputRecord extends InputRecord implements SSLRecord {
+    // used by handshake hash computation for handshake fragment
+    private byte prevType = -1;
+    private int hsMsgOff = 0;
+    private int hsMsgLen = 0;
+
+    private boolean formatVerified = false;     // SSLv2 ruled out?
+
+    SSLEngineInputRecord() {
+        this.readAuthenticator = MAC.TLS_NULL;
+    }
+
+    @Override
+    int estimateFragmentSize(int packetSize) {
+        int macLen = 0;
+        if (readAuthenticator instanceof MAC) {
+            macLen = ((MAC)readAuthenticator).MAClen();
+        }
+
+        if (packetSize > 0) {
+            return readCipher.estimateFragmentSize(
+                    packetSize, macLen, headerSize);
+        } else {
+            return Record.maxDataSize;
+        }
+    }
+
+    @Override
+    int bytesInCompletePacket(ByteBuffer packet) throws SSLException {
+        /*
+         * SSLv2 length field is in bytes 0/1
+         * SSLv3/TLS length field is in bytes 3/4
+         */
+        if (packet.remaining() < 5) {
+            return -1;
+        }
+
+        int pos = packet.position();
+        byte byteZero = packet.get(pos);
+
+        int len = 0;
+
+        /*
+         * If we have already verified previous packets, we can
+         * ignore the verifications steps, and jump right to the
+         * determination.  Otherwise, try one last hueristic to
+         * see if it's SSL/TLS.
+         */
+        if (formatVerified ||
+                (byteZero == ct_handshake) || (byteZero == ct_alert)) {
+            /*
+             * Last sanity check that it's not a wild record
+             */
+            ProtocolVersion recordVersion = ProtocolVersion.valueOf(
+                                    packet.get(pos + 1), packet.get(pos + 2));
+
+            // check the record version
+            checkRecordVersion(recordVersion, false);
+
+            /*
+             * Reasonably sure this is a V3, disable further checks.
+             * We can't do the same in the v2 check below, because
+             * read still needs to parse/handle the v2 clientHello.
+             */
+            formatVerified = true;
+
+            /*
+             * One of the SSLv3/TLS message types.
+             */
+            len = ((packet.get(pos + 3) & 0xFF) << 8) +
+                   (packet.get(pos + 4) & 0xFF) + headerSize;
+
+        } else {
+            /*
+             * Must be SSLv2 or something unknown.
+             * Check if it's short (2 bytes) or
+             * long (3) header.
+             *
+             * Internals can warn about unsupported SSLv2
+             */
+            boolean isShort = ((byteZero & 0x80) != 0);
+
+            if (isShort &&
+                    ((packet.get(pos + 2) == 1) || packet.get(pos + 2) == 4)) {
+
+                ProtocolVersion recordVersion = ProtocolVersion.valueOf(
+                                    packet.get(pos + 3), packet.get(pos + 4));
+
+                // check the record version
+                checkRecordVersion(recordVersion, true);
+
+                /*
+                 * Client or Server Hello
+                 */
+                int mask = (isShort ? 0x7F : 0x3F);
+                len = ((byteZero & mask) << 8) +
+                        (packet.get(pos + 1) & 0xFF) + (isShort ? 2 : 3);
+
+            } else {
+                // Gobblygook!
+                throw new SSLException(
+                        "Unrecognized SSL message, plaintext connection?");
+            }
+        }
+
+        return len;
+    }
+
+    @Override
+    void checkRecordVersion(ProtocolVersion recordVersion,
+            boolean allowSSL20Hello) throws SSLException {
+
+        if (recordVersion.maybeDTLSProtocol()) {
+            throw new SSLException(
+                    "Unrecognized record version " + recordVersion +
+                    " , DTLS packet?");
+        }
+
+        // Check if the record version is too old.
+        if ((recordVersion.v < ProtocolVersion.MIN.v)) {
+            // if it's not SSLv2, we're out of here.
+            if (!allowSSL20Hello ||
+                    (recordVersion.v != ProtocolVersion.SSL20Hello.v)) {
+                throw new SSLException(
+                    "Unsupported record version " + recordVersion);
+            }
+        }
+    }
+
+    @Override
+    Plaintext decode(ByteBuffer packet)
+            throws IOException, BadPaddingException {
+
+        if (isClosed) {
+            return null;
+        }
+
+        if (debug != null && Debug.isOn("packet")) {
+             Debug.printHex(
+                    "[Raw read]: length = " + packet.remaining(), packet);
+        }
+
+        // The caller should have validated the record.
+        if (!formatVerified) {
+            formatVerified = true;
+
+            /*
+             * The first record must either be a handshake record or an
+             * alert message. If it's not, it is either invalid or an
+             * SSLv2 message.
+             */
+            int pos = packet.position();
+            byte byteZero = packet.get(pos);
+            if (byteZero != ct_handshake && byteZero != ct_alert) {
+                return handleUnknownRecord(packet);
+            }
+        }
+
+        return decodeInputRecord(packet);
+    }
+
+    private Plaintext decodeInputRecord(ByteBuffer packet)
+            throws IOException, BadPaddingException {
+
+        //
+        // The packet should be a complete record, or more.
+        //
+
+        int srcPos = packet.position();
+        int srcLim = packet.limit();
+
+        byte contentType = packet.get();                   // pos: 0
+        byte majorVersion = packet.get();                  // pos: 1
+        byte minorVersion = packet.get();                  // pos: 2
+        int contentLen = ((packet.get() & 0xFF) << 8) +
+                          (packet.get() & 0xFF);           // pos: 3, 4
+
+        if (debug != null && Debug.isOn("record")) {
+             System.out.println(Thread.currentThread().getName() +
+                    ", READ: " +
+                    ProtocolVersion.valueOf(majorVersion, minorVersion) +
+                    " " + Record.contentName(contentType) + ", length = " +
+                    contentLen);
+        }
+
+        //
+        // Check for upper bound.
+        //
+        // Note: May check packetSize limit in the future.
+        if (contentLen < 0 || contentLen > maxLargeRecordSize - headerSize) {
+            throw new SSLProtocolException(
+                "Bad input record size, TLSCiphertext.length = " + contentLen);
+        }
+
+        //
+        // check for handshake fragment
+        //
+        if ((contentType != ct_handshake) && (hsMsgOff != hsMsgLen)) {
+            throw new SSLProtocolException(
+                    "Expected to get a handshake fragment");
+        }
+
+        //
+        // Decrypt the fragment
+        //
+        int recLim = srcPos + SSLRecord.headerSize + contentLen;
+        packet.limit(recLim);
+        packet.position(srcPos + SSLRecord.headerSize);
+
+        ByteBuffer plaintext;
+        try {
+            plaintext =
+                decrypt(readAuthenticator, readCipher, contentType, packet);
+        } finally {
+            // comsume a complete record
+            packet.limit(srcLim);
+            packet.position(recLim);
+        }
+
+        //
+        // handshake hashing
+        //
+        if (contentType == ct_handshake) {
+            int pltPos = plaintext.position();
+            int pltLim = plaintext.limit();
+            int frgPos = pltPos;
+            for (int remains = plaintext.remaining(); remains > 0;) {
+                int howmuch;
+                byte handshakeType;
+                if (hsMsgOff < hsMsgLen) {
+                    // a fragment of the handshake message
+                    howmuch = Math.min((hsMsgLen - hsMsgOff), remains);
+                    handshakeType = prevType;
+
+                    hsMsgOff += howmuch;
+                    if (hsMsgOff == hsMsgLen) {
+                        // Now is a complete handshake message.
+                        hsMsgOff = 0;
+                        hsMsgLen = 0;
+                    }
+                } else {    // hsMsgOff == hsMsgLen, a new handshake message
+                    handshakeType = plaintext.get();
+                    int handshakeLen = ((plaintext.get() & 0xFF) << 16) |
+                                       ((plaintext.get() & 0xFF) << 8) |
+                                        (plaintext.get() & 0xFF);
+                    plaintext.position(frgPos);
+                    if (remains < (handshakeLen + 1)) { // 1: handshake type
+                        // This handshake message is fragmented.
+                        prevType = handshakeType;
+                        hsMsgOff = remains - 4;         // 4: handshake header
+                        hsMsgLen = handshakeLen;
+                    }
+
+                    howmuch = Math.min(handshakeLen + 4, remains);
+                }
+
+                plaintext.limit(frgPos + howmuch);
+
+                if (handshakeType == HandshakeMessage.ht_hello_request) {
+                    // omitted from handshake hash computation
+                } else if ((handshakeType != HandshakeMessage.ht_finished) &&
+                    (handshakeType != HandshakeMessage.ht_certificate_verify)) {
+
+                    if (handshakeHash == null) {
+                        // used for cache only
+                        handshakeHash = new HandshakeHash(false);
+                    }
+                    handshakeHash.update(plaintext);
+                } else {
+                    // Reserve until this handshake message has been processed.
+                    if (handshakeHash == null) {
+                        // used for cache only
+                        handshakeHash = new HandshakeHash(false);
+                    }
+                    handshakeHash.reserve(plaintext);
+                }
+
+                plaintext.position(frgPos + howmuch);
+                plaintext.limit(pltLim);
+
+                frgPos += howmuch;
+                remains -= howmuch;
+            }
+
+            plaintext.position(pltPos);
+        }
+
+        return new Plaintext(contentType,
+                majorVersion, minorVersion, -1, -1L, plaintext);
+                // recordEpoch, recordSeq, plaintext);
+    }
+
+    private Plaintext handleUnknownRecord(ByteBuffer packet)
+            throws IOException, BadPaddingException {
+
+        //
+        // The packet should be a complete record.
+        //
+        int srcPos = packet.position();
+        int srcLim = packet.limit();
+
+        byte firstByte = packet.get(srcPos);
+        byte thirdByte = packet.get(srcPos + 2);
+
+        // Does it look like a Version 2 client hello (V2ClientHello)?
+        if (((firstByte & 0x80) != 0) && (thirdByte == 1)) {
+            /*
+             * If SSLv2Hello is not enabled, throw an exception.
+             */
+            if (helloVersion != ProtocolVersion.SSL20Hello) {
+                throw new SSLHandshakeException("SSLv2Hello is not enabled");
+            }
+
+            byte majorVersion = packet.get(srcPos + 3);
+            byte minorVersion = packet.get(srcPos + 4);
+
+            if ((majorVersion == ProtocolVersion.SSL20Hello.major) &&
+                (minorVersion == ProtocolVersion.SSL20Hello.minor)) {
+
+                /*
+                 * Looks like a V2 client hello, but not one saying
+                 * "let's talk SSLv3".  So we need to send an SSLv2
+                 * error message, one that's treated as fatal by
+                 * clients (Otherwise we'll hang.)
+                 */
+                if (debug != null && Debug.isOn("record")) {
+                     System.out.println(Thread.currentThread().getName() +
+                            "Requested to negotiate unsupported SSLv2!");
+                }
+
+                // hack code, the exception is caught in SSLEngineImpl
+                // so that SSLv2 error message can be delivered properly.
+                throw new UnsupportedOperationException(        // SSLv2Hello
+                        "Unsupported SSL v2.0 ClientHello");
+            }
+
+            /*
+             * If we can map this into a V3 ClientHello, read and
+             * hash the rest of the V2 handshake, turn it into a
+             * V3 ClientHello message, and pass it up.
+             */
+            packet.position(srcPos + 2);        // exclude the header
+
+            if (handshakeHash == null) {
+                // used for cache only
+                handshakeHash = new HandshakeHash(false);
+            }
+            handshakeHash.update(packet);
+            packet.position(srcPos);
+
+            ByteBuffer converted = convertToClientHello(packet);
+
+            if (debug != null && Debug.isOn("packet")) {
+                 Debug.printHex(
+                        "[Converted] ClientHello", converted);
+            }
+
+            return new Plaintext(ct_handshake,
+                majorVersion, minorVersion, -1, -1L, converted);
+        } else {
+            if (((firstByte & 0x80) != 0) && (thirdByte == 4)) {
+                throw new SSLException("SSL V2.0 servers are not supported.");
+            }
+
+            throw new SSLException("Unsupported or unrecognized SSL message");
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/SSLEngineOutputRecord.java	Tue Jun 02 09:15:47 2015 -0700
@@ -0,0 +1,570 @@
+/*
+ * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ssl;
+
+import java.io.*;
+import java.nio.*;
+import java.util.*;
+
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLHandshakeException;
+import sun.misc.HexDumpEncoder;
+import static sun.security.ssl.Ciphertext.RecordType;
+
+/**
+ * {@code OutputRecord} implementation for {@code SSLEngine}.
+ */
+final class SSLEngineOutputRecord extends OutputRecord implements SSLRecord {
+
+    private HandshakeFragment fragmenter = null;
+    private LinkedList<RecordMemo> alertMemos = new LinkedList<>();
+    private boolean isTalkingToV2 = false;      // SSLv2Hello
+    private ByteBuffer v2ClientHello = null;    // SSLv2Hello
+
+    private boolean isCloseWaiting = false;
+
+    SSLEngineOutputRecord() {
+        this.writeAuthenticator = MAC.TLS_NULL;
+
+        this.packetSize = SSLRecord.maxRecordSize;
+        this.protocolVersion = ProtocolVersion.DEFAULT_TLS;
+    }
+
+    @Override
+    synchronized public void close() throws IOException {
+        if (!isClosed) {
+            if (alertMemos != null && !alertMemos.isEmpty()) {
+                isCloseWaiting = true;
+            } else {
+                super.close();
+            }
+        }
+    }
+
+    @Override
+    void encodeAlert(byte level, byte description) throws IOException {
+        RecordMemo memo = new RecordMemo();
+
+        memo.contentType = Record.ct_alert;
+        memo.majorVersion = protocolVersion.major;
+        memo.minorVersion = protocolVersion.minor;
+        memo.encodeCipher = writeCipher;
+        memo.encodeAuthenticator = writeAuthenticator;
+
+        memo.fragment = new byte[2];
+        memo.fragment[0] = level;
+        memo.fragment[1] = description;
+
+        alertMemos.add(memo);
+    }
+
+    @Override
+    void encodeHandshake(byte[] source,
+            int offset, int length) throws IOException {
+
+        if (fragmenter == null) {
+           fragmenter = new HandshakeFragment();
+        }
+
+        if (firstMessage) {
+            firstMessage = false;
+
+            if ((helloVersion == ProtocolVersion.SSL20Hello) &&
+                (source[offset] == HandshakeMessage.ht_client_hello) &&
+                                            //  5: recode header size
+                (source[offset + 4 + 2 + 32] == 0)) {
+                                            // V3 session ID is empty
+                                            //  4: handshake header size
+                                            //  2: client_version in ClientHello
+                                            // 32: random in ClientHello
+
+                // Double space should be big enough for the converted message.
+                v2ClientHello = encodeV2ClientHello(
+                        source, (offset + 4), (length - 4));
+
+                v2ClientHello.position(2);     // exclude the header
+                handshakeHash.update(v2ClientHello);
+                v2ClientHello.position(0);
+
+                return;
+            }
+        }
+
+        byte handshakeType = source[offset];
+        if (handshakeType != HandshakeMessage.ht_hello_request) {
+            handshakeHash.update(source, offset, length);
+        }
+
+        fragmenter.queueUpFragment(source, offset, length);
+    }
+
+    @Override
+    void encodeChangeCipherSpec() throws IOException {
+        if (fragmenter == null) {
+           fragmenter = new HandshakeFragment();
+        }
+        fragmenter.queueUpChangeCipherSpec();
+    }
+
+    @Override
+    void encodeV2NoCipher() throws IOException {
+        isTalkingToV2 = true;
+    }
+
+    @Override
+    Ciphertext encode(ByteBuffer[] sources, int offset, int length,
+            ByteBuffer destination) throws IOException {
+
+        if (writeAuthenticator.seqNumOverflow()) {
+            if (debug != null && Debug.isOn("ssl")) {
+                System.out.println(Thread.currentThread().getName() +
+                    ", sequence number extremely close to overflow " +
+                    "(2^64-1 packets). Closing connection.");
+            }
+
+            throw new SSLHandshakeException("sequence number overflow");
+        }
+
+        int macLen = 0;
+        if (writeAuthenticator instanceof MAC) {
+            macLen = ((MAC)writeAuthenticator).MAClen();
+        }
+
+        int dstLim = destination.limit();
+        boolean isFirstRecordOfThePayload = true;
+        int packetLeftSize = Math.min(maxRecordSize, packetSize);
+        boolean needMorePayload = true;
+        long recordSN = 0L;
+        while (needMorePayload) {
+            int fragLen;
+            if (isFirstRecordOfThePayload && needToSplitPayload()) {
+                needMorePayload = true;
+
+                fragLen = 1;
+                isFirstRecordOfThePayload = false;
+            } else {
+                needMorePayload = false;
+
+                if (packetLeftSize > 0) {
+                    fragLen = writeCipher.calculateFragmentSize(
+                            packetLeftSize, macLen, headerSize);
+
+                    fragLen = Math.min(fragLen, Record.maxDataSize);
+                } else {
+                    fragLen = Record.maxDataSize;
+                }
+
+                if (fragmentSize > 0) {
+                    fragLen = Math.min(fragLen, fragmentSize);
+                }
+            }
+
+            int dstPos = destination.position();
+            int dstContent = dstPos + headerSize +
+                                writeCipher.getExplicitNonceSize();
+            destination.position(dstContent);
+
+            int remains = Math.min(fragLen, destination.remaining());
+            fragLen = 0;
+            int srcsLen = offset + length;
+            for (int i = offset; (i < srcsLen) && (remains > 0); i++) {
+                int amount = Math.min(sources[i].remaining(), remains);
+                int srcLimit = sources[i].limit();
+                sources[i].limit(sources[i].position() + amount);
+                destination.put(sources[i]);
+                sources[i].limit(srcLimit);         // restore the limit
+                remains -= amount;
+                fragLen += amount;
+
+                if (remains > 0) {
+                    offset++;
+                    length--;
+                }
+            }
+
+            destination.limit(destination.position());
+            destination.position(dstContent);
+
+            if ((debug != null) && Debug.isOn("record")) {
+                System.out.println(Thread.currentThread().getName() +
+                        ", WRITE: " + protocolVersion + " " +
+                        Record.contentName(Record.ct_application_data) +
+                        ", length = " + destination.remaining());
+            }
+
+            // Encrypt the fragment and wrap up a record.
+            recordSN = encrypt(writeAuthenticator, writeCipher,
+                    Record.ct_application_data, destination,
+                    dstPos, dstLim, headerSize,
+                    protocolVersion, false);
+
+            if ((debug != null) && Debug.isOn("packet")) {
+                ByteBuffer temporary = destination.duplicate();
+                temporary.limit(temporary.position());
+                temporary.position(dstPos);
+                Debug.printHex(
+                        "[Raw write]: length = " + temporary.remaining(),
+                        temporary);
+            }
+
+            packetLeftSize -= destination.position() - dstPos;
+
+            // remain the limit unchanged
+            destination.limit(dstLim);
+
+            if (isFirstAppOutputRecord) {
+                isFirstAppOutputRecord = false;
+            }
+        }
+
+        return new Ciphertext(RecordType.RECORD_APPLICATION_DATA, recordSN);
+    }
+
+    @Override
+    Ciphertext acquireCiphertext(ByteBuffer destination) throws IOException {
+        if (isTalkingToV2) {              // SSLv2Hello
+            // We don't support SSLv2.  Send an SSLv2 error message
+            // so that the connection can be closed gracefully.
+            //
+            // Please don't change the limit of the destination buffer.
+            destination.put(SSLRecord.v2NoCipher);
+            if (debug != null && Debug.isOn("packet")) {
+                Debug.printHex(
+                        "[Raw write]: length = " + SSLRecord.v2NoCipher.length,
+                        SSLRecord.v2NoCipher);
+            }
+
+            isTalkingToV2 = false;
+
+            return new Ciphertext(RecordType.RECORD_ALERT, -1L);
+        }
+
+        if (v2ClientHello != null) {
+            // deliver the SSLv2 format ClientHello message
+            //
+            // Please don't change the limit of the destination buffer.
+            if (debug != null) {
+                if (Debug.isOn("record")) {
+                     System.out.println(Thread.currentThread().getName() +
+                            ", WRITE: SSLv2 ClientHello message" +
+                            ", length = " + v2ClientHello.remaining());
+                }
+
+                if (Debug.isOn("packet")) {
+                    Debug.printHex(
+                        "[Raw write]: length = " + v2ClientHello.remaining(),
+                        v2ClientHello);
+                }
+            }
+
+            destination.put(v2ClientHello);
+            v2ClientHello = null;
+
+            return new Ciphertext(RecordType.RECORD_CLIENT_HELLO, -1L);
+        }
+
+        if (alertMemos != null && !alertMemos.isEmpty()) {
+            RecordMemo memo = alertMemos.pop();
+
+            int macLen = 0;
+            if (memo.encodeAuthenticator instanceof MAC) {
+                macLen = ((MAC)memo.encodeAuthenticator).MAClen();
+            }
+
+            int dstPos = destination.position();
+            int dstLim = destination.limit();
+            int dstContent = dstPos + headerSize +
+                                writeCipher.getExplicitNonceSize();
+            destination.position(dstContent);
+
+            destination.put(memo.fragment);
+
+            destination.limit(destination.position());
+            destination.position(dstContent);
+
+            if ((debug != null) && Debug.isOn("record")) {
+                System.out.println(Thread.currentThread().getName() +
+                        ", WRITE: " + protocolVersion + " " +
+                        Record.contentName(Record.ct_alert) +
+                        ", length = " + destination.remaining());
+            }
+
+            // Encrypt the fragment and wrap up a record.
+            long recordSN = encrypt(memo.encodeAuthenticator, memo.encodeCipher,
+                    Record.ct_alert, destination, dstPos, dstLim, headerSize,
+                    ProtocolVersion.valueOf(memo.majorVersion,
+                            memo.minorVersion), false);
+
+            if ((debug != null) && Debug.isOn("packet")) {
+                ByteBuffer temporary = destination.duplicate();
+                temporary.limit(temporary.position());
+                temporary.position(dstPos);
+                Debug.printHex(
+                        "[Raw write]: length = " + temporary.remaining(),
+                        temporary);
+            }
+
+            // remain the limit unchanged
+            destination.limit(dstLim);
+
+            if (isCloseWaiting && (memo.contentType == Record.ct_alert)) {
+                isCloseWaiting = true;
+                close();
+            }
+            return new Ciphertext(RecordType.RECORD_ALERT, recordSN);
+        }
+
+        if (fragmenter != null) {
+            return fragmenter.acquireCiphertext(destination);
+        }
+
+        return null;
+    }
+
+    @Override
+    boolean isEmpty() {
+        return (!isTalkingToV2) && (v2ClientHello == null) &&
+                ((fragmenter == null) || fragmenter.isEmpty()) &&
+                ((alertMemos == null) || alertMemos.isEmpty());
+    }
+
+    // buffered record fragment
+    private static class RecordMemo {
+        byte            contentType;
+        byte            majorVersion;
+        byte            minorVersion;
+        CipherBox       encodeCipher;
+        Authenticator   encodeAuthenticator;
+
+        byte[]          fragment;
+    }
+
+    private static class HandshakeMemo extends RecordMemo {
+        byte            handshakeType;
+        int             acquireOffset;
+    }
+
+    final class HandshakeFragment {
+        private LinkedList<RecordMemo> handshakeMemos = new LinkedList<>();
+
+        void queueUpFragment(byte[] source,
+                int offset, int length) throws IOException {
+
+            HandshakeMemo memo = new HandshakeMemo();
+
+            memo.contentType = Record.ct_handshake;
+            memo.majorVersion = protocolVersion.major;  // kick start version?
+            memo.minorVersion = protocolVersion.minor;
+            memo.encodeCipher = writeCipher;
+            memo.encodeAuthenticator = writeAuthenticator;
+
+            memo.handshakeType = source[offset];
+            memo.acquireOffset = 0;
+            memo.fragment = new byte[length - 4];       // 4: header size
+                                                        //    1: HandshakeType
+                                                        //    3: message length
+            System.arraycopy(source, offset + 4, memo.fragment, 0, length - 4);
+
+            handshakeMemos.add(memo);
+        }
+
+        void queueUpChangeCipherSpec() {
+            RecordMemo memo = new RecordMemo();
+
+            memo.contentType = Record.ct_change_cipher_spec;
+            memo.majorVersion = protocolVersion.major;
+            memo.minorVersion = protocolVersion.minor;
+            memo.encodeCipher = writeCipher;
+            memo.encodeAuthenticator = writeAuthenticator;
+
+            memo.fragment = new byte[1];
+            memo.fragment[0] = 1;
+
+            handshakeMemos.add(memo);
+        }
+
+        Ciphertext acquireCiphertext(ByteBuffer dstBuf) throws IOException {
+            if (isEmpty()) {
+                return null;
+            }
+
+            RecordMemo memo = handshakeMemos.getFirst();
+            HandshakeMemo hsMemo = null;
+            if (memo.contentType == Record.ct_handshake) {
+                hsMemo = (HandshakeMemo)memo;
+            }
+
+            int macLen = 0;
+            if (memo.encodeAuthenticator instanceof MAC) {
+                macLen = ((MAC)memo.encodeAuthenticator).MAClen();
+            }
+
+            // ChangeCipherSpec message is pretty small.  Don't worry about
+            // the fragmentation of ChangeCipherSpec record.
+            int fragLen;
+            if (packetSize > 0) {
+                fragLen = Math.min(maxRecordSize, packetSize);
+                fragLen = memo.encodeCipher.calculateFragmentSize(
+                        fragLen, macLen, headerSize);
+            } else {
+                fragLen = Record.maxDataSize;
+            }
+
+            if (fragmentSize > 0) {
+                fragLen = Math.min(fragLen, fragmentSize);
+            }
+
+            int dstPos = dstBuf.position();
+            int dstLim = dstBuf.limit();
+            int dstContent = dstPos + headerSize +
+                                    memo.encodeCipher.getExplicitNonceSize();
+            dstBuf.position(dstContent);
+
+            if (hsMemo != null) {
+                int remainingFragLen = fragLen;
+                while ((remainingFragLen > 0) && !handshakeMemos.isEmpty()) {
+                    int memoFragLen = hsMemo.fragment.length;
+                    if (hsMemo.acquireOffset == 0) {
+                        // Don't fragment handshake message header
+                        if (remainingFragLen <= 4) {
+                            break;
+                        }
+
+                        dstBuf.put(hsMemo.handshakeType);
+                        dstBuf.put((byte)((memoFragLen >> 16) & 0xFF));
+                        dstBuf.put((byte)((memoFragLen >> 8) & 0xFF));
+                        dstBuf.put((byte)(memoFragLen & 0xFF));
+
+                        remainingFragLen -= 4;
+                    } // Otherwise, handshake message is fragmented.
+
+                    int chipLen = Math.min(remainingFragLen,
+                            (memoFragLen - hsMemo.acquireOffset));
+                    dstBuf.put(hsMemo.fragment, hsMemo.acquireOffset, chipLen);
+
+                    hsMemo.acquireOffset += chipLen;
+                    if (hsMemo.acquireOffset == memoFragLen) {
+                        handshakeMemos.removeFirst();
+
+                        // still have space for more records?
+                        if ((remainingFragLen > chipLen) &&
+                                 !handshakeMemos.isEmpty()) {
+
+                            // look for the next buffered record fragment
+                            RecordMemo reMemo = handshakeMemos.getFirst();
+                            if (reMemo.contentType == Record.ct_handshake) {
+                                hsMemo = (HandshakeMemo)reMemo;
+                            } else {
+                                // not handshake message, break the loop
+                                break;
+                            }
+                        }
+                    }
+
+                    remainingFragLen -= chipLen;
+                }
+
+                fragLen -= remainingFragLen;
+            } else {
+                fragLen = Math.min(fragLen, memo.fragment.length);
+                dstBuf.put(memo.fragment, 0, fragLen);
+
+                handshakeMemos.removeFirst();
+            }
+
+            dstBuf.limit(dstBuf.position());
+            dstBuf.position(dstContent);
+
+            if ((debug != null) && Debug.isOn("record")) {
+                System.out.println(Thread.currentThread().getName() +
+                        ", WRITE: " + protocolVersion + " " +
+                        Record.contentName(memo.contentType) +
+                        ", length = " + dstBuf.remaining());
+            }
+
+            // Encrypt the fragment and wrap up a record.
+            long recordSN = encrypt(memo.encodeAuthenticator, memo.encodeCipher,
+                    memo.contentType, dstBuf,
+                    dstPos, dstLim, headerSize,
+                    ProtocolVersion.valueOf(memo.majorVersion,
+                            memo.minorVersion), false);
+
+            if ((debug != null) && Debug.isOn("packet")) {
+                ByteBuffer temporary = dstBuf.duplicate();
+                temporary.limit(temporary.position());
+                temporary.position(dstPos);
+                Debug.printHex(
+                        "[Raw write]: length = " + temporary.remaining(),
+                        temporary);
+            }
+
+            // remain the limit unchanged
+            dstBuf.limit(dstLim);
+
+            // Reset the fragmentation offset.
+            if (hsMemo != null) {
+                return new Ciphertext(RecordType.valueOf(
+                        hsMemo.contentType, hsMemo.handshakeType), recordSN);
+            } else {
+                return new Ciphertext(
+                        RecordType.RECORD_CHANGE_CIPHER_SPEC, recordSN);
+            }
+        }
+
+        boolean isEmpty() {
+            return handshakeMemos.isEmpty();
+        }
+    }
+
+    /*
+     * Need to split the payload except the following cases:
+     *
+     * 1. protocol version is TLS 1.1 or later;
+     * 2. bulk cipher does not use CBC mode, including null bulk cipher suites.
+     * 3. the payload is the first application record of a freshly
+     *    negotiated TLS session.
+     * 4. the CBC protection is disabled;
+     *
+     * By default, we counter chosen plaintext issues on CBC mode
+     * ciphersuites in SSLv3/TLS1.0 by sending one byte of application
+     * data in the first record of every payload, and the rest in
+     * subsequent record(s). Note that the issues have been solved in
+     * TLS 1.1 or later.
+     *
+     * It is not necessary to split the very first application record of
+     * a freshly negotiated TLS session, as there is no previous
+     * application data to guess.  To improve compatibility, we will not
+     * split such records.
+     *
+     * This avoids issues in the outbound direction.  For a full fix,
+     * the peer must have similar protections.
+     */
+    boolean needToSplitPayload() {
+        return (!protocolVersion.useTLS11PlusSpec()) &&
+                writeCipher.isCBCMode() && !isFirstAppOutputRecord &&
+                Record.enableCBCProtection;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/SSLRecord.java	Tue Jun 02 09:15:47 2015 -0700
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ssl;
+
+/**
+ * SSL/TLS record
+ *
+ * @author David Brownell
+ */
+interface SSLRecord extends Record {
+
+    static final int    headerSize = 5;         // SSLv3 record header
+
+    /*
+     * The size of the header plus the max IV length
+     */
+    static final int    headerPlusMaxIVSize =
+                                      headerSize        // header
+                                    + maxIVLength;      // iv
+
+    /*
+     * The maximum size that may be increased when translating plaintext to
+     * ciphertext fragment.
+     */
+    static final int    maxPlaintextPlusSize =
+                                      headerSize        // header
+                                    + maxIVLength       // iv
+                                    + maxMacSize        // MAC or AEAD tag
+                                    + maxPadding;       // block cipher padding
+
+    /*
+     * SSL has a maximum record size.  It's header, (compressed) data,
+     * padding, and a trailer for the message authentication information (MAC
+     * for block and stream ciphers, and message authentication tag for AEAD
+     * ciphers).
+     *
+     * Some compression algorithms have rare cases where they expand the data.
+     * As we don't support compression at this time, leave that out.
+     */
+    static final int    maxRecordSize =
+                                      headerPlusMaxIVSize   // header + iv
+                                    + maxDataSize           // data
+                                    + maxPadding            // padding
+                                    + maxMacSize;           // MAC or AEAD tag
+
+    /*
+     * For CBC protection in SSL3/TLS1, we break some plaintext into two
+     * packets.  Max application data size for the second packet.
+     */
+    static final int    maxDataSizeMinusOneByteRecord =
+                                  maxDataSize       // max data size
+                                - (                 // max one byte record size
+                                      headerPlusMaxIVSize   // header + iv
+                                    + 1             // one byte data
+                                    + maxPadding    // padding
+                                    + maxMacSize    // MAC
+                                  );
+
+    /*
+     * The maximum large record size.
+     *
+     * Some SSL/TLS implementations support large fragment upto 2^15 bytes,
+     * such as Microsoft. We support large incoming fragments.
+     *
+     * The maximum large record size is defined as maxRecordSize plus 2^14,
+     * this is the amount OpenSSL is using.
+     */
+    static final int    maxLargeRecordSize =
+                maxRecordSize   // Max size with a conforming implementation
+              + maxDataSize;    // extra 2^14 bytes for large data packets.
+
+
+    /*
+     * Maximum record size for alert and change cipher spec records.
+     * They only contain 2 and 1 bytes of data, respectively.
+     * Allocate a smaller array.
+     */
+    static final int    maxAlertRecordSize =
+                                      headerPlusMaxIVSize   // header + iv
+                                    + 2                     // alert
+                                    + maxPadding            // padding
+                                    + maxMacSize;           // MAC
+
+    /*
+     * We may need to send this SSL v2 "No Cipher" message back, if we
+     * are faced with an SSLv2 "hello" that's not saying "I talk v3".
+     * It's the only one documented in the V2 spec as a fatal error.
+     */
+    static final byte[] v2NoCipher = {
+        (byte)0x80, (byte)0x03, // unpadded 3 byte record
+        (byte)0x00,             // ... error message
+        (byte)0x00, (byte)0x01  // ... NO_CIPHER error
+    };
+}
--- a/jdk/src/java.base/share/classes/sun/security/ssl/SSLServerSocketImpl.java	Mon Jun 01 10:15:21 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/SSLServerSocketImpl.java	Tue Jun 02 09:15:47 2015 -0700
@@ -68,7 +68,7 @@
     private SSLContextImpl      sslContext;
 
     /* Do newly accepted connections require clients to authenticate? */
-    private byte                doClientAuth = SSLEngineImpl.clauth_none;
+    private ClientAuthType    clientAuthType = ClientAuthType.CLIENT_AUTH_NONE;
 
     /* Do new connections created here use the "server" mode of SSL? */
     private boolean             useServerMode = true;
@@ -230,13 +230,13 @@
      */
     @Override
     public void setNeedClientAuth(boolean flag) {
-        doClientAuth = (flag ?
-            SSLEngineImpl.clauth_required : SSLEngineImpl.clauth_none);
+        clientAuthType = (flag ? ClientAuthType.CLIENT_AUTH_REQUIRED :
+                ClientAuthType.CLIENT_AUTH_NONE);
     }
 
     @Override
     public boolean getNeedClientAuth() {
-        return (doClientAuth == SSLEngineImpl.clauth_required);
+        return (clientAuthType == ClientAuthType.CLIENT_AUTH_REQUIRED);
     }
 
     /**
@@ -245,13 +245,13 @@
      */
     @Override
     public void setWantClientAuth(boolean flag) {
-        doClientAuth = (flag ?
-            SSLEngineImpl.clauth_requested : SSLEngineImpl.clauth_none);
+        clientAuthType = (flag ? ClientAuthType.CLIENT_AUTH_REQUESTED :
+                ClientAuthType.CLIENT_AUTH_NONE);
     }
 
     @Override
     public boolean getWantClientAuth() {
-        return (doClientAuth == SSLEngineImpl.clauth_requested);
+        return (clientAuthType == ClientAuthType.CLIENT_AUTH_REQUESTED);
     }
 
     /**
@@ -341,7 +341,7 @@
     @Override
     public Socket accept() throws IOException {
         SSLSocketImpl s = new SSLSocketImpl(sslContext, useServerMode,
-            enabledCipherSuites, doClientAuth, enableSessionCreation,
+            enabledCipherSuites, clientAuthType, enableSessionCreation,
             enabledProtocols, identificationProtocol, algorithmConstraints,
             sniMatchers, preferLocalCipherSuites);
 
--- a/jdk/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java	Mon Jun 01 10:15:21 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java	Tue Jun 02 09:15:47 2015 -0700
@@ -109,6 +109,8 @@
     private String[]            peerSupportedSignAlgs;
     private List<SNIServerName>    requestedServerNames;
 
+    private int                 negotiatedMaxFragLen;
+    private int                 maximumPacketSize;
 
     // Principals for non-certificate based cipher suites
     private Principal peerPrincipal;
@@ -177,6 +179,7 @@
         sessionCount = ++counter;
         localSupportedSignAlgs =
             SignatureAndHashAlgorithm.getAlgorithmNames(algorithms);
+        negotiatedMaxFragLen = -1;
 
         if (debug != null && Debug.isOn("session")) {
             System.out.println("%% Initialized:  " + this);
@@ -422,10 +425,9 @@
         // change record of peer identity even by accident, much
         // less do it intentionally.
         //
-        if ((cipherSuite.keyExchange == K_KRB5) ||
-            (cipherSuite.keyExchange == K_KRB5_EXPORT)) {
+        if (ClientKeyExchangeService.find(cipherSuite.keyExchange.name) != null) {
             throw new SSLPeerUnverifiedException("no certificates expected"
-                        + " for Kerberos cipher suites");
+                        + " for " + cipherSuite.keyExchange + " cipher suites");
         }
         if (peerCerts == null) {
             throw new SSLPeerUnverifiedException("peer not authenticated");
@@ -478,10 +480,9 @@
         // change record of peer identity even by accident, much
         // less do it intentionally.
         //
-        if ((cipherSuite.keyExchange == K_KRB5) ||
-            (cipherSuite.keyExchange == K_KRB5_EXPORT)) {
+        if (ClientKeyExchangeService.find(cipherSuite.keyExchange.name) != null) {
             throw new SSLPeerUnverifiedException("no certificates expected"
-                        + " for Kerberos cipher suites");
+                        + " for " + cipherSuite.keyExchange + " cipher suites");
         }
         if (peerCerts == null) {
             throw new SSLPeerUnverifiedException("peer not authenticated");
@@ -519,10 +520,9 @@
          * change record of peer identity even by accident, much
          * less do it intentionally.
          */
-        if ((cipherSuite.keyExchange == K_KRB5) ||
-            (cipherSuite.keyExchange == K_KRB5_EXPORT)) {
+        if (ClientKeyExchangeService.find(cipherSuite.keyExchange.name) != null) {
             throw new SSLPeerUnverifiedException("no certificates expected"
-                        + " for Kerberos cipher suites");
+                        + " for " + cipherSuite.keyExchange + " cipher suites");
         }
         if (peerCerts != null) {
             return peerCerts.clone();
@@ -537,7 +537,7 @@
      *
      * @return the peer's principal. Returns an X500Principal of the
      * end-entity certificate for X509-based cipher suites, and
-     * Principal for Kerberos cipher suites.
+     * Principal for Kerberos cipher suites, etc.
      *
      * @throws SSLPeerUnverifiedException if the peer's identity has not
      *          been verified
@@ -546,12 +546,10 @@
     public Principal getPeerPrincipal()
                 throws SSLPeerUnverifiedException
     {
-        if ((cipherSuite.keyExchange == K_KRB5) ||
-            (cipherSuite.keyExchange == K_KRB5_EXPORT)) {
+        if (ClientKeyExchangeService.find(cipherSuite.keyExchange.name) != null) {
             if (peerPrincipal == null) {
                 throw new SSLPeerUnverifiedException("peer not authenticated");
             } else {
-                // Eliminate dependency on KerberosPrincipal
                 return peerPrincipal;
             }
         }
@@ -566,15 +564,13 @@
      *
      * @return the principal sent to the peer. Returns an X500Principal
      * of the end-entity certificate for X509-based cipher suites, and
-     * Principal for Kerberos cipher suites. If no principal was
+     * Principal for Kerberos cipher suites, etc. If no principal was
      * sent, then null is returned.
      */
     @Override
     public Principal getLocalPrincipal() {
 
-        if ((cipherSuite.keyExchange == K_KRB5) ||
-            (cipherSuite.keyExchange == K_KRB5_EXPORT)) {
-                // Eliminate dependency on KerberosPrincipal
+        if (ClientKeyExchangeService.find(cipherSuite.keyExchange.name) != null) {
                 return (localPrincipal == null ? null : localPrincipal);
         }
         return (localCerts == null ? null :
@@ -785,8 +781,30 @@
      */
     @Override
     public synchronized int getPacketBufferSize() {
-        return acceptLargeFragments ?
-                Record.maxLargeRecordSize : Record.maxRecordSize;
+        // Use the bigger packet size calculated from maximumPacketSize
+        // and negotiatedMaxFragLen.
+        int packetSize = 0;
+        if (negotiatedMaxFragLen > 0) {
+            packetSize = cipherSuite.calculatePacketSize(
+                    negotiatedMaxFragLen, protocolVersion,
+                    protocolVersion.isDTLSProtocol());
+        }
+
+        if (maximumPacketSize > 0) {
+            return (maximumPacketSize > packetSize) ?
+                    maximumPacketSize : packetSize;
+        }
+
+        if (packetSize != 0) {
+           return packetSize;
+        }
+
+        if (protocolVersion.isDTLSProtocol()) {
+            return DTLSRecord.maxRecordSize;
+        } else {
+            return acceptLargeFragments ?
+                    SSLRecord.maxLargeRecordSize : SSLRecord.maxRecordSize;
+        }
     }
 
     /**
@@ -795,7 +813,64 @@
      */
     @Override
     public synchronized int getApplicationBufferSize() {
-        return getPacketBufferSize() - Record.headerSize;
+        // Use the bigger fragment size calculated from maximumPacketSize
+        // and negotiatedMaxFragLen.
+        int fragmentSize = 0;
+        if (maximumPacketSize > 0) {
+            fragmentSize = cipherSuite.calculateFragSize(
+                    maximumPacketSize, protocolVersion,
+                    protocolVersion.isDTLSProtocol());
+        }
+
+        if (negotiatedMaxFragLen > 0) {
+            return (negotiatedMaxFragLen > fragmentSize) ?
+                    negotiatedMaxFragLen : fragmentSize;
+        }
+
+        if (fragmentSize != 0) {
+            return fragmentSize;
+        }
+
+        if (protocolVersion.isDTLSProtocol()) {
+            return Record.maxDataSize;
+        } else {
+            int maxPacketSize = acceptLargeFragments ?
+                        SSLRecord.maxLargeRecordSize : SSLRecord.maxRecordSize;
+            return (maxPacketSize - SSLRecord.headerSize);
+        }
+    }
+
+    /**
+     * Sets the negotiated maximum fragment length, as specified by the
+     * max_fragment_length ClientHello extension in RFC 6066.
+     *
+     * @param  negotiatedMaxFragLen
+     *         the negotiated maximum fragment length, or {@code -1} if
+     *         no such length has been negotiated.
+     */
+    synchronized void setNegotiatedMaxFragSize(
+            int negotiatedMaxFragLen) {
+
+        this.negotiatedMaxFragLen = negotiatedMaxFragLen;
+    }
+
+    /**
+     * Get the negotiated maximum fragment length, as specified by the
+     * max_fragment_length ClientHello extension in RFC 6066.
+     *
+     * @return the negotiated maximum fragment length, or {@code -1} if
+     *         no such length has been negotiated.
+     */
+    synchronized int getNegotiatedMaxFragSize() {
+        return negotiatedMaxFragLen;
+    }
+
+    synchronized void setMaximumPacketSize(int maximumPacketSize) {
+        this.maximumPacketSize = maximumPacketSize;
+    }
+
+    synchronized int getMaximumPacketSize() {
+        return maximumPacketSize;
     }
 
     /**
--- a/jdk/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java	Mon Jun 01 10:15:21 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java	Tue Jun 02 09:15:47 2015 -0700
@@ -27,6 +27,7 @@
 package sun.security.ssl;
 
 import java.io.*;
+import java.nio.*;
 import java.net.*;
 import java.security.GeneralSecurityException;
 import java.security.AccessController;
@@ -155,30 +156,16 @@
     private static final int    cs_DATA = 2;
     private static final int    cs_RENEGOTIATE = 3;
     private static final int    cs_ERROR = 4;
-    private static final int   cs_SENT_CLOSE = 5;
+    private static final int    cs_SENT_CLOSE = 5;
     private static final int    cs_CLOSED = 6;
     private static final int    cs_APP_CLOSED = 7;
 
-
-    /*
-     * Client authentication be off, requested, or required.
-     *
-     * Migrated to SSLEngineImpl:
-     *    clauth_none/cl_auth_requested/clauth_required
-     */
-
     /*
      * Drives the protocol state machine.
      */
     private volatile int        connectionState;
 
     /*
-     * Flag indicating that the engine's handshaker has done the necessary
-     * steps so the engine may process a ChangeCipherSpec message.
-     */
-    private boolean             receivedCCS;
-
-    /*
      * Flag indicating if the next record we receive MUST be a Finished
      * message. Temporarily set during the handshake to ensure that
      * a change cipher spec message is followed by a finished message.
@@ -197,7 +184,8 @@
      * Per-connection private state that doesn't change when the
      * session is changed.
      */
-    private byte                doClientAuth;
+    private ClientAuthType      doClientAuth =
+                                        ClientAuthType.CLIENT_AUTH_NONE;
     private boolean             roleIsServer;
     private boolean             enableSessionCreation = true;
     private String              host;
@@ -284,9 +272,9 @@
      * is created, at which time the new handshaker's state is set.
      *
      * The readLock is held during readRecord(), which is responsible
-     * for reading an InputRecord, decrypting it, and processing it.
+     * for reading an SSLInputRecord, decrypting it, and processing it.
      * The readLock ensures that these three steps are done atomically
-     * and that once started, no other thread can block on InputRecord.read.
+     * and that once started, no other thread can block on SSLInputRecord.read.
      * This is necessary so that processing of close_notify alerts
      * from the peer are handled properly.
      */
@@ -294,14 +282,8 @@
     final ReentrantLock         writeLock = new ReentrantLock();
     final private Object        readLock = new Object();
 
-    private InputRecord         inrec;
-
-    /*
-     * Crypto state that's reinitialized when the session changes.
-     */
-    private Authenticator       readAuthenticator, writeAuthenticator;
-    private CipherBox           readCipher, writeCipher;
-    // NOTE: compression state would be saved here
+    InputRecord                 inputRecord;
+    OutputRecord                outputRecord;
 
     /*
      * security parameters for secure renegotiation.
@@ -368,7 +350,7 @@
     /*
      * The SSL version associated with this connection.
      */
-    private ProtocolVersion     protocolVersion = ProtocolVersion.DEFAULT;
+    private ProtocolVersion     protocolVersion = ProtocolVersion.DEFAULT_TLS;
 
     /* Class and subclass dynamic debugging support */
     private static final Debug debug = Debug.getInstance("ssl");
@@ -390,6 +372,11 @@
      */
     private boolean preferLocalCipherSuites = false;
 
+    /*
+     * The maximum expected network packet size for SSL/TLS/DTLS records.
+     */
+    private int maximumPacketSize = 0;
+
     //
     // CONSTRUCTORS AND INITIALIZATION CODE
     //
@@ -491,7 +478,7 @@
      * giving control over the use of SSL client authentication.
      */
     SSLSocketImpl(SSLContextImpl context, boolean serverMode,
-            CipherSuiteList suites, byte clientAuth,
+            CipherSuiteList suites, ClientAuthType clientAuth,
             boolean sessionCreation, ProtocolList protocols,
             String identificationProtocol,
             AlgorithmConstraints algorithmConstraints,
@@ -594,17 +581,6 @@
          */
         roleIsServer = isServer;
         connectionState = cs_START;
-        receivedCCS = false;
-
-        /*
-         * default read and write side cipher and MAC support
-         *
-         * Note:  compression support would go here too
-         */
-        readCipher = CipherBox.NULL;
-        readAuthenticator = MAC.NULL;
-        writeCipher = CipherBox.NULL;
-        writeAuthenticator = MAC.NULL;
 
         // initial security parameters for secure renegotiation
         secureRenegotiation = false;
@@ -616,7 +592,10 @@
         enabledProtocols =
                 sslContext.getDefaultProtocolList(roleIsServer);
 
-        inrec = null;
+        inputRecord = new SSLSocketInputRecord();;
+        outputRecord = new SSLSocketOutputRecord();
+
+        maximumPacketSize = outputRecord.getMaxPacketSize();
 
         // save the acc
         acc = AccessController.getContext();
@@ -672,6 +651,9 @@
         sockInput = super.getInputStream();
         sockOutput = super.getOutputStream();
 
+        inputRecord.setDeliverStream(sockOutput);
+        outputRecord.setDeliverStream(sockOutput);
+
         /*
          * Move to handshaking state, with pending session initialized
          * to defaults and the appropriate kind of handshaker set up.
@@ -696,29 +678,18 @@
     //
 
     /*
-     * AppOutputStream calls may need to buffer multiple outbound
-     * application packets.
+     * Application data record output.
      *
-     * All other writeRecord() calls will not buffer, so do not hold
-     * these records.
+     * Application data can't be sent until the first handshake establishes
+     * a session.
      */
-    void writeRecord(OutputRecord r) throws IOException {
-        writeRecord(r, false);
-    }
-
-    /*
-     * Record Output. Application data can't be sent until the first
-     * handshake establishes a session.
-     *
-     * NOTE:  we let empty records be written as a hook to force some
-     * TCP-level activity, notably handshaking, to occur.
-     */
-    void writeRecord(OutputRecord r, boolean holdRecord) throws IOException {
+    void writeRecord(byte[] source, int offset, int length) throws IOException {
         /*
          * The loop is in case of HANDSHAKE --> ERROR transitions, etc
          */
-    loop:
-        while (r.contentType() == Record.ct_application_data) {
+        // Don't bother to check the emptiness of source applicatoin data
+        // before the security connection established.
+        for (boolean readyForApp = false; !readyForApp;) {
             /*
              * Not all states support passing application data.  We
              * synchronize access to the connection state, so that
@@ -726,41 +697,43 @@
              */
             switch (getConnectionState()) {
 
-            /*
-             * We've deferred the initial handshaking till just now,
-             * when presumably a thread's decided it's OK to block for
-             * longish periods of time for I/O purposes (as well as
-             * configured the cipher suites it wants to use).
-             */
-            case cs_HANDSHAKE:
-                performInitialHandshake();
-                break;
+                /*
+                 * We've deferred the initial handshaking till just now,
+                 * when presumably a thread's decided it's OK to block for
+                 * longish periods of time for I/O purposes (as well as
+                 * configured the cipher suites it wants to use).
+                 */
+                case cs_HANDSHAKE:
+                    performInitialHandshake();
+                    break;
 
-            case cs_DATA:
-            case cs_RENEGOTIATE:
-                break loop;
+                case cs_DATA:
+                case cs_RENEGOTIATE:
+                    readyForApp = true;
+                    break;
 
-            case cs_ERROR:
-                fatal(Alerts.alert_close_notify,
-                    "error while writing to socket");
-                break; // dummy
+                case cs_ERROR:
+                    fatal(Alerts.alert_close_notify,
+                            "error while writing to socket");
+                    break; // dummy
 
-            case cs_SENT_CLOSE:
-            case cs_CLOSED:
-            case cs_APP_CLOSED:
-                // we should never get here (check in AppOutputStream)
-                // this is just a fallback
-                if (closeReason != null) {
-                    throw closeReason;
-                } else {
-                    throw new SocketException("Socket closed");
-                }
+                case cs_SENT_CLOSE:
+                case cs_CLOSED:
+                case cs_APP_CLOSED:
+                    // we should never get here (check in AppOutputStream)
+                    // this is just a fallback
+                    if (closeReason != null) {
+                        throw closeReason;
+                    } else {
+                        throw new SocketException("Socket closed");
+                    }
 
-            /*
-             * Else something's goofy in this state machine's use.
-             */
-            default:
-                throw new SSLProtocolException("State error, send app data");
+                /*
+                 * Else something's goofy in this state machine's use.
+                 */
+                default:
+                    throw new SSLProtocolException(
+                            "State error, send app data");
             }
         }
 
@@ -772,97 +745,19 @@
         // implementations are fragile and don't like to see empty
         // records, so this also increases robustness.
         //
-        if (!r.isEmpty()) {
-
-            // If the record is a close notify alert, we need to honor
-            // socket option SO_LINGER. Note that we will try to send
-            // the close notify even if the SO_LINGER set to zero.
-            if (r.isAlert(Alerts.alert_close_notify) && getSoLinger() >= 0) {
-
-                // keep and clear the current thread interruption status.
-                boolean interrupted = Thread.interrupted();
-                try {
-                    if (writeLock.tryLock(getSoLinger(), TimeUnit.SECONDS)) {
-                        try {
-                            writeRecordInternal(r, holdRecord);
-                        } finally {
-                            writeLock.unlock();
-                        }
-                    } else {
-                        SSLException ssle = new SSLException(
-                                "SO_LINGER timeout," +
-                                " close_notify message cannot be sent.");
-
-
-                        // For layered, non-autoclose sockets, we are not
-                        // able to bring them into a usable state, so we
-                        // treat it as fatal error.
-                        if (isLayered() && !autoClose) {
-                            // Note that the alert description is
-                            // specified as -1, so no message will be send
-                            // to peer anymore.
-                            fatal((byte)(-1), ssle);
-                        } else if ((debug != null) && Debug.isOn("ssl")) {
-                            System.out.println(
-                                Thread.currentThread().getName() +
-                                ", received Exception: " + ssle);
-                        }
-
-                        // RFC2246 requires that the session becomes
-                        // unresumable if any connection is terminated
-                        // without proper close_notify messages with
-                        // level equal to warning.
-                        //
-                        // RFC4346 no longer requires that a session not be
-                        // resumed if failure to properly close a connection.
-                        //
-                        // We choose to make the session unresumable if
-                        // failed to send the close_notify message.
-                        //
-                        sess.invalidate();
-                    }
-                } catch (InterruptedException ie) {
-                    // keep interrupted status
-                    interrupted = true;
-                }
-
-                // restore the interrupted status
-                if (interrupted) {
-                    Thread.currentThread().interrupt();
-                }
-            } else {
-                writeLock.lock();
-                try {
-                    writeRecordInternal(r, holdRecord);
-                } finally {
-                    writeLock.unlock();
-                }
+        if (length > 0) {
+            writeLock.lock();
+            try {
+                outputRecord.deliver(source, offset, length);
+            } catch (SSLHandshakeException she) {
+                // may be record sequence number overflow
+                fatal(Alerts.alert_handshake_failure, she);
+            } catch (IOException e) {
+                fatal(Alerts.alert_unexpected_message, e);
+            } finally {
+                writeLock.unlock();
             }
         }
-    }
-
-    private void writeRecordInternal(OutputRecord r,
-            boolean holdRecord) throws IOException {
-
-        // r.compress(c);
-        r.encrypt(writeAuthenticator, writeCipher);
-
-        if (holdRecord) {
-            // If we were requested to delay the record due to possibility
-            // of Nagle's being active when finally got to writing, and
-            // it's actually not, we don't really need to delay it.
-            if (getTcpNoDelay()) {
-                holdRecord = false;
-            } else {
-                // We need to hold the record, so let's provide
-                // a per-socket place to do it.
-                if (heldRecordBuffer == null) {
-                    // Likely only need 37 bytes.
-                    heldRecordBuffer = new ByteArrayOutputStream(40);
-                }
-            }
-        }
-        r.write(sockOutput, holdRecord, heldRecordBuffer);
 
         /*
          * Check the sequence number state
@@ -874,51 +769,173 @@
          * when there is enough sequence number space left to
          * handle a few more records, so the sequence number
          * of the last record cannot be wrapped.
+         *
+         * Don't bother to kickstart the renegotiation when the
+         * local is asking for it.
          */
-        if (connectionState < cs_ERROR) {
-            checkSequenceNumber(writeAuthenticator, r.contentType());
-        }
+        if ((connectionState == cs_DATA) && outputRecord.seqNumIsHuge()) {
+            /*
+             * Ask for renegotiation when need to renew sequence number.
+             *
+             * Don't bother to kickstart the renegotiation when the local is
+             * asking for it.
+             */
+            if (debug != null && Debug.isOn("ssl")) {
+                System.out.println(Thread.currentThread().getName() +
+                        ", request renegotiation " +
+                        "to avoid sequence number overflow");
+            }
 
-        // turn off the flag of the first application record
-        if (isFirstAppOutputRecord &&
-                r.contentType() == Record.ct_application_data) {
-            isFirstAppOutputRecord = false;
+            startHandshake();
         }
     }
 
     /*
-     * Need to split the payload except the following cases:
-     *
-     * 1. protocol version is TLS 1.1 or later;
-     * 2. bulk cipher does not use CBC mode, including null bulk cipher suites.
-     * 3. the payload is the first application record of a freshly
-     *    negotiated TLS session.
-     * 4. the CBC protection is disabled;
-     *
-     * More details, please refer to AppOutputStream.write(byte[], int, int).
+     * Alert record output.
      */
-    boolean needToSplitPayload() {
-        writeLock.lock();
-        try {
-            return (protocolVersion.v <= ProtocolVersion.TLS10.v) &&
-                    writeCipher.isCBCMode() && !isFirstAppOutputRecord &&
-                    Record.enableCBCProtection;
-        } finally {
-            writeLock.unlock();
+    void writeAlert(byte level, byte description) throws IOException {
+
+        // If the record is a close notify alert, we need to honor
+        // socket option SO_LINGER. Note that we will try to send
+        // the close notify even if the SO_LINGER set to zero.
+        if ((description == Alerts.alert_close_notify) && getSoLinger() >= 0) {
+
+            // keep and clear the current thread interruption status.
+            boolean interrupted = Thread.interrupted();
+            try {
+                if (writeLock.tryLock(getSoLinger(), TimeUnit.SECONDS)) {
+                    try {
+                        outputRecord.encodeAlert(level, description);
+                    } finally {
+                        writeLock.unlock();
+                    }
+                } else {
+                    SSLException ssle = new SSLException(
+                            "SO_LINGER timeout," +
+                            " close_notify message cannot be sent.");
+
+
+                    // For layered, non-autoclose sockets, we are not
+                    // able to bring them into a usable state, so we
+                    // treat it as fatal error.
+                    if (isLayered() && !autoClose) {
+                        // Note that the alert description is
+                        // specified as -1, so no message will be send
+                        // to peer anymore.
+                        fatal((byte)(-1), ssle);
+                    } else if ((debug != null) && Debug.isOn("ssl")) {
+                        System.out.println(
+                            Thread.currentThread().getName() +
+                            ", received Exception: " + ssle);
+                    }
+
+                    // RFC2246 requires that the session becomes
+                    // unresumable if any connection is terminated
+                    // without proper close_notify messages with
+                    // level equal to warning.
+                    //
+                    // RFC4346 no longer requires that a session not be
+                    // resumed if failure to properly close a connection.
+                    //
+                    // We choose to make the session unresumable if
+                    // failed to send the close_notify message.
+                    //
+                    sess.invalidate();
+                }
+            } catch (InterruptedException ie) {
+                // keep interrupted status
+                interrupted = true;
+            }
+
+            // restore the interrupted status
+            if (interrupted) {
+                Thread.currentThread().interrupt();
+            }
+        } else {
+            writeLock.lock();
+            try {
+                outputRecord.encodeAlert(level, description);
+            } finally {
+                writeLock.unlock();
+            }
+        }
+
+        // Don't bother to check sequence number overlap here.  If sequence
+        // number is huge, there should be enough sequence number space to
+        // request renegotiation in next application data read and write.
+    }
+
+
+    int bytesInCompletePacket() throws IOException {
+        if (getConnectionState() == cs_HANDSHAKE) {
+            performInitialHandshake();
+        }
+
+        synchronized (readLock) {
+            int state = getConnectionState();
+            if ((state == cs_CLOSED) ||
+                    (state == cs_ERROR) || (state == cs_APP_CLOSED)) {
+                return -1;
+            }
+
+            try {
+                return inputRecord.bytesInCompletePacket(sockInput);
+            } catch (EOFException eofe) {
+                boolean handshaking = (connectionState <= cs_HANDSHAKE);
+                boolean rethrow = requireCloseNotify || handshaking;
+                if ((debug != null) && Debug.isOn("ssl")) {
+                    System.out.println(Thread.currentThread().getName() +
+                        ", received EOFException: "
+                        + (rethrow ? "error" : "ignored"));
+                }
+
+                if (!rethrow) {
+                    // treat as if we had received a close_notify
+                    closeInternal(false);
+                } else {
+                    SSLException e;
+                    if (handshaking) {
+                        e = new SSLHandshakeException(
+                            "Remote host terminated the handshake");
+                    } else {
+                        e = new SSLProtocolException(
+                            "Remote host terminated the handshake");
+                    }
+                    e.initCause(eofe);
+                    throw e;
+                }
+            }
+
+            return -1;
         }
     }
 
+    // the caller have synchronized readLock
+    void expectingFinishFlight() {
+        inputRecord.expectingFinishFlight();
+    }
+
     /*
-     * Read an application data record.  Alerts and handshake
-     * messages are handled directly.
+     * Read an application data record.
+     *
+     * Alerts and handshake messages are internally handled directly.
      */
-    void readDataRecord(InputRecord r) throws IOException {
+    int readRecord(ByteBuffer buffer) throws IOException {
         if (getConnectionState() == cs_HANDSHAKE) {
             performInitialHandshake();
         }
-        readRecord(r, true);
+
+        return readRecord(buffer, true);
     }
 
+    /*
+     * Read a record, no application data input required.
+     *
+     * Alerts and handshake messages are internally handled directly.
+     */
+    int readRecord(boolean needAppData) throws IOException {
+        return readRecord(null, needAppData);
+    }
 
     /*
      * Clear the pipeline of records from the peer, optionally returning
@@ -929,11 +946,11 @@
      * Don't synchronize (this) during a blocking read() since it
      * protects data which is accessed on the write side as well.
      */
-    private void readRecord(InputRecord r, boolean needAppData)
+    private int readRecord(ByteBuffer buffer, boolean needAppData)
             throws IOException {
         int state;
 
-        // readLock protects reading and processing of an InputRecord.
+        // readLock protects reading and processing of an SSLInputRecord.
         // It keeps the reading from sockInput and processing of the record
         // atomic so that no two threads can be blocked on the
         // read from the same input stream at the same time.
@@ -944,306 +961,282 @@
         //
         // Use readLock instead of 'this' for locking because
         // 'this' also protects data accessed during writing.
-      synchronized (readLock) {
-        /*
-         * Read and handle records ... return application data
-         * ONLY if it's needed.
-         */
-
-        while (((state = getConnectionState()) != cs_CLOSED) &&
-                (state != cs_ERROR) && (state != cs_APP_CLOSED)) {
+        synchronized (readLock) {
             /*
-             * Read a record ... maybe emitting an alert if we get a
-             * comprehensible but unsupported "hello" message during
-             * format checking (e.g. V2).
-             */
-            try {
-                r.setAppDataValid(false);
-                r.read(sockInput, sockOutput);
-            } catch (SSLProtocolException e) {
-                try {
-                    fatal(Alerts.alert_unexpected_message, e);
-                } catch (IOException x) {
-                    // discard this exception
-                }
-                throw e;
-            } catch (EOFException eof) {
-                boolean handshaking = (getConnectionState() <= cs_HANDSHAKE);
-                boolean rethrow = requireCloseNotify || handshaking;
-                if ((debug != null) && Debug.isOn("ssl")) {
-                    System.out.println(Thread.currentThread().getName() +
-                        ", received EOFException: "
-                        + (rethrow ? "error" : "ignored"));
-                }
-                if (rethrow) {
-                    SSLException e;
-                    if (handshaking) {
-                        e = new SSLHandshakeException
-                            ("Remote host closed connection during handshake");
-                    } else {
-                        e = new SSLProtocolException
-                            ("Remote host closed connection incorrectly");
-                    }
-                    e.initCause(eof);
-                    throw e;
-                } else {
-                    // treat as if we had received a close_notify
-                    closeInternal(false);
-                    continue;
-                }
-            }
-
-
-            /*
-             * The basic SSLv3 record protection involves (optional)
-             * encryption for privacy, and an integrity check ensuring
-             * data origin authentication.  We do them both here, and
-             * throw a fatal alert if the integrity check fails.
-             */
-            try {
-                r.decrypt(readAuthenticator, readCipher);
-            } catch (BadPaddingException e) {
-                byte alertType = (r.contentType() == Record.ct_handshake)
-                                        ? Alerts.alert_handshake_failure
-                                        : Alerts.alert_bad_record_mac;
-                fatal(alertType, e.getMessage(), e);
-            }
-
-            // if (!r.decompress(c))
-            //     fatal(Alerts.alert_decompression_failure,
-            //         "decompression failure");
-
-            /*
-             * Process the record.
+             * Read and handle records ... return application data
+             * ONLY if it's needed.
              */
-            synchronized (this) {
-              switch (r.contentType()) {
-                case Record.ct_handshake:
-                    /*
-                     * Handshake messages always go to a pending session
-                     * handshaker ... if there isn't one, create one.  This
-                     * must work asynchronously, for renegotiation.
-                     *
-                     * NOTE that handshaking will either resume a session
-                     * which was in the cache (and which might have other
-                     * connections in it already), or else will start a new
-                     * session (new keys exchanged) with just this connection
-                     * in it.
-                     */
-                    initHandshaker();
-                    if (!handshaker.activated()) {
-                        // prior to handshaking, activate the handshake
-                        if (connectionState == cs_RENEGOTIATE) {
-                            // don't use SSLv2Hello when renegotiating
-                            handshaker.activate(protocolVersion);
-                        } else {
-                            handshaker.activate(null);
-                        }
-                    }
-
-                    /*
-                     * process the handshake record ... may contain just
-                     * a partial handshake message or multiple messages.
-                     *
-                     * The handshaker state machine will ensure that it's
-                     * a finished message.
-                     */
-                    handshaker.process_record(r, expectingFinished);
-                    expectingFinished = false;
+            Plaintext plainText = null;
+            while (((state = getConnectionState()) != cs_CLOSED) &&
+                    (state != cs_ERROR) && (state != cs_APP_CLOSED)) {
+                // clean the buffer
+                if (buffer != null) {
+                    buffer.clear();
+                }
 
-                    if (handshaker.invalidated) {
-                        handshaker = null;
-                        receivedCCS = false;
-                        // if state is cs_RENEGOTIATE, revert it to cs_DATA
-                        if (connectionState == cs_RENEGOTIATE) {
-                            connectionState = cs_DATA;
+                /*
+                 * Read a record ... maybe emitting an alert if we get a
+                 * comprehensible but unsupported "hello" message during
+                 * format checking (e.g. V2).
+                 */
+                try {
+                    plainText = inputRecord.decode(sockInput, buffer);
+                } catch (BadPaddingException bpe) {
+                    byte alertType = (state != cs_DATA) ?
+                            Alerts.alert_handshake_failure :
+                            Alerts.alert_bad_record_mac;
+                    fatal(alertType, bpe.getMessage(), bpe);
+                } catch (SSLProtocolException spe) {
+                    try {
+                        fatal(Alerts.alert_unexpected_message, spe);
+                    } catch (IOException x) {
+                        // discard this exception, throw the original exception
+                    }
+                    throw spe;
+                } catch (SSLHandshakeException she) {
+                    // may be record sequence number overflow
+                    fatal(Alerts.alert_handshake_failure, she);
+                } catch (EOFException eof) {
+                    boolean handshaking = (connectionState <= cs_HANDSHAKE);
+                    boolean rethrow = requireCloseNotify || handshaking;
+                    if ((debug != null) && Debug.isOn("ssl")) {
+                        System.out.println(Thread.currentThread().getName() +
+                            ", received EOFException: "
+                            + (rethrow ? "error" : "ignored"));
+                    }
+                    if (rethrow) {
+                        SSLException e;
+                        if (handshaking) {
+                            e = new SSLHandshakeException(
+                                    "Remote host terminated the handshake");
+                        } else {
+                            e = new SSLProtocolException(
+                                    "Remote host terminated the connection");
                         }
-                    } else if (handshaker.isDone()) {
-                        // reset the parameters for secure renegotiation.
-                        secureRenegotiation =
-                                        handshaker.isSecureRenegotiation();
-                        clientVerifyData = handshaker.getClientVerifyData();
-                        serverVerifyData = handshaker.getServerVerifyData();
-
-                        sess = handshaker.getSession();
-                        handshakeSession = null;
-                        handshaker = null;
-                        connectionState = cs_DATA;
-                        receivedCCS = false;
-
-                        //
-                        // Tell folk about handshake completion, but do
-                        // it in a separate thread.
-                        //
-                        if (handshakeListeners != null) {
-                            HandshakeCompletedEvent event =
-                                new HandshakeCompletedEvent(this, sess);
-
-                            Thread t = new ManagedLocalsThread(
-                                new NotifyHandshake(
-                                    handshakeListeners.entrySet(), event),
-                                "HandshakeCompletedNotify-Thread");
-                            t.start();
-                        }
-                    }
-
-                    if (needAppData || connectionState != cs_DATA) {
+                        e.initCause(eof);
+                        throw e;
+                    } else {
+                        // treat as if we had received a close_notify
+                        closeInternal(false);
                         continue;
                     }
-                    break;
+                }
+
+                // PlainText should never be null. Process input record.
+                int volume = processInputRecord(plainText, needAppData);
+
+                if (plainText.contentType == Record.ct_application_data) {
+                    return volume;
+                }
+
+                if (plainText.contentType == Record.ct_handshake) {
+                    if (!needAppData && connectionState == cs_DATA) {
+                        return volume;
+                    }   // otherwise, need to read more for app data.
+                }
 
-                case Record.ct_application_data:
-                    // Pass this right back up to the application.
-                    if (connectionState != cs_DATA
-                            && connectionState != cs_RENEGOTIATE
-                            && connectionState != cs_SENT_CLOSE) {
-                        throw new SSLProtocolException(
-                            "Data received in non-data state: " +
-                            connectionState);
-                    }
-                    if (expectingFinished) {
-                        throw new SSLProtocolException
-                                ("Expecting finished message, received data");
-                    }
-                    if (!needAppData) {
-                        throw new SSLException("Discarding app data");
-                    }
+                // continue to read more net data
+            }   // while
 
-                    r.setAppDataValid(true);
-                    break;
+            //
+            // couldn't read, due to some kind of error
+            //
+            return -1;
+        }  // readLock synchronization
+    }
+
+    /*
+     * Process the plainText input record.
+     */
+    private synchronized int processInputRecord(
+            Plaintext plainText, boolean needAppData) throws IOException {
 
-                case Record.ct_alert:
-                    recvAlert(r);
-                    continue;
+        /*
+         * Process the record.
+         */
+        int volume = 0;    // no application data
+        switch (plainText.contentType) {
+            case Record.ct_handshake:
+                /*
+                 * Handshake messages always go to a pending session
+                 * handshaker ... if there isn't one, create one.  This
+                 * must work asynchronously, for renegotiation.
+                 *
+                 * NOTE that handshaking will either resume a session
+                 * which was in the cache (and which might have other
+                 * connections in it already), or else will start a new
+                 * session (new keys exchanged) with just this connection
+                 * in it.
+                 */
+                initHandshaker();
+                if (!handshaker.activated()) {
+                    // prior to handshaking, activate the handshake
+                    if (connectionState == cs_RENEGOTIATE) {
+                        // don't use SSLv2Hello when renegotiating
+                        handshaker.activate(protocolVersion);
+                    } else {
+                        handshaker.activate(null);
+                    }
+                }
 
-                case Record.ct_change_cipher_spec:
-                    if ((connectionState != cs_HANDSHAKE
-                                && connectionState != cs_RENEGOTIATE)
-                            || !handshaker.sessionKeysCalculated()
-                            || receivedCCS) {
-                        // For the CCS message arriving in the wrong state
-                        fatal(Alerts.alert_unexpected_message,
-                                "illegal change cipher spec msg, conn state = "
-                                + connectionState + ", handshake state = "
-                                + handshaker.state);
-                    } else if (r.available() != 1 || r.read() != 1) {
-                        // For structural/content issues with the CCS
-                        fatal(Alerts.alert_unexpected_message,
-                                "Malformed change cipher spec msg");
+                /*
+                 * process the handshake record ... may contain just
+                 * a partial handshake message or multiple messages.
+                 *
+                 * The handshaker state machine will ensure that it's
+                 * a finished message.
+                 */
+                handshaker.processRecord(plainText.fragment, expectingFinished);
+                expectingFinished = false;
+
+                if (handshaker.invalidated) {
+                    handshaker = null;
+                    inputRecord.setHandshakeHash(null);
+                    outputRecord.setHandshakeHash(null);
+
+                    // if state is cs_RENEGOTIATE, revert it to cs_DATA
+                    if (connectionState == cs_RENEGOTIATE) {
+                        connectionState = cs_DATA;
                     }
+                } else if (handshaker.isDone()) {
+                    // reset the parameters for secure renegotiation.
+                    secureRenegotiation =
+                                    handshaker.isSecureRenegotiation();
+                    clientVerifyData = handshaker.getClientVerifyData();
+                    serverVerifyData = handshaker.getServerVerifyData();
 
-                    // Once we've received CCS, update the flag.
-                    // If the remote endpoint sends it again in this handshake
-                    // we won't process it.
-                    receivedCCS = true;
+                    sess = handshaker.getSession();
+                    handshakeSession = null;
+                    handshaker = null;
+                    inputRecord.setHandshakeHash(null);
+                    outputRecord.setHandshakeHash(null);
+                    connectionState = cs_DATA;
 
                     //
-                    // The first message after a change_cipher_spec
-                    // record MUST be a "Finished" handshake record,
-                    // else it's a protocol violation.  We force this
-                    // to be checked by a minor tweak to the state
-                    // machine.
+                    // Tell folk about handshake completion, but do
+                    // it in a separate thread.
                     //
-                    changeReadCiphers();
-                    // next message MUST be a finished message
-                    expectingFinished = true;
-                    continue;
+                    if (handshakeListeners != null) {
+                        HandshakeCompletedEvent event =
+                            new HandshakeCompletedEvent(this, sess);
+
+                        Thread thread = new ManagedLocalsThread(
+                            new NotifyHandshake(
+                                handshakeListeners.entrySet(), event),
+                            "HandshakeCompletedNotify-Thread");
+                        thread.start();
+                    }
+                }
+
+                break;
 
-                default:
-                    //
-                    // TLS requires that unrecognized records be ignored.
-                    //
-                    if (debug != null && Debug.isOn("ssl")) {
-                        System.out.println(Thread.currentThread().getName() +
-                            ", Received record type: "
-                            + r.contentType());
-                    }
-                    continue;
-              } // switch
+            case Record.ct_application_data:
+                if (connectionState != cs_DATA
+                        && connectionState != cs_RENEGOTIATE
+                        && connectionState != cs_SENT_CLOSE) {
+                    throw new SSLProtocolException(
+                        "Data received in non-data state: " +
+                        connectionState);
+                }
+                if (expectingFinished) {
+                    throw new SSLProtocolException
+                            ("Expecting finished message, received data");
+                }
+                if (!needAppData) {
+                    throw new SSLException("Discarding app data");
+                }
+
+                volume = plainText.fragment.remaining();
+                break;
+
+            case Record.ct_alert:
+                recvAlert(plainText.fragment);
+                break;
 
-              /*
-               * Check the sequence number state
-               *
-               * Note that in order to maintain the connection I/O
-               * properly, we check the sequence number after the last
-               * record reading process. As we request renegotiation
-               * or close the connection for wrapped sequence number
-               * when there is enough sequence number space left to
-               * handle a few more records, so the sequence number
-               * of the last record cannot be wrapped.
-               */
-              if (connectionState < cs_ERROR) {
-                  checkSequenceNumber(readAuthenticator, r.contentType());
-              }
+            case Record.ct_change_cipher_spec:
+                if ((connectionState != cs_HANDSHAKE
+                        && connectionState != cs_RENEGOTIATE)) {
+                    // For the CCS message arriving in the wrong state
+                    fatal(Alerts.alert_unexpected_message,
+                            "illegal change cipher spec msg, conn state = "
+                            + connectionState);
+                } else if (plainText.fragment.remaining() != 1
+                        || plainText.fragment.get() != 1) {
+                    // For structural/content issues with the CCS
+                    fatal(Alerts.alert_unexpected_message,
+                            "Malformed change cipher spec msg");
+                }
 
-              return;
-            } // synchronized (this)
-        }
+                //
+                // The first message after a change_cipher_spec
+                // record MUST be a "Finished" handshake record,
+                // else it's a protocol violation.  We force this
+                // to be checked by a minor tweak to the state
+                // machine.
+                //
+                handshaker.receiveChangeCipherSpec();
 
-        //
-        // couldn't read, due to some kind of error
-        //
-        r.close();
-        return;
-      }  // synchronized (readLock)
-    }
+                CipherBox readCipher;
+                Authenticator readAuthenticator;
+                try {
+                    readCipher = handshaker.newReadCipher();
+                    readAuthenticator = handshaker.newReadAuthenticator();
+                } catch (GeneralSecurityException e) {
+                    // can't happen
+                    throw new SSLException("Algorithm missing:  ", e);
+                }
+                inputRecord.changeReadCiphers(readAuthenticator, readCipher);
 
-    /**
-     * Check the sequence number state
-     *
-     * RFC 4346 states that, "Sequence numbers are of type uint64 and
-     * may not exceed 2^64-1.  Sequence numbers do not wrap. If a TLS
-     * implementation would need to wrap a sequence number, it must
-     * renegotiate instead."
-     */
-    private void checkSequenceNumber(Authenticator authenticator, byte type)
-            throws IOException {
+                // next message MUST be a finished message
+                expectingFinished = true;
+
+                break;
 
-        /*
-         * Don't bother to check the sequence number for error or
-         * closed connections, or NULL MAC.
-         */
-        if (connectionState >= cs_ERROR || authenticator == MAC.NULL) {
-            return;
+            default:
+                //
+                // TLS requires that unrecognized records be ignored.
+                //
+                if (debug != null && Debug.isOn("ssl")) {
+                    System.out.println(Thread.currentThread().getName() +
+                        ", Received record type: " + plainText.contentType);
+                }
+                break;
         }
 
         /*
-         * Conservatively, close the connection immediately when the
-         * sequence number is close to overflow
+         * Check the sequence number state
+         *
+         * Note that in order to maintain the connection I/O
+         * properly, we check the sequence number after the last
+         * record reading process. As we request renegotiation
+         * or close the connection for wrapped sequence number
+         * when there is enough sequence number space left to
+         * handle a few more records, so the sequence number
+         * of the last record cannot be wrapped.
+         *
+         * Don't bother to kickstart the renegotiation when the
+         * local is asking for it.
          */
-        if (authenticator.seqNumOverflow()) {
+        if ((connectionState == cs_DATA) && inputRecord.seqNumIsHuge()) {
             /*
-             * TLS protocols do not define a error alert for sequence
-             * number overflow. We use handshake_failure error alert
-             * for handshaking and bad_record_mac for other records.
+             * Ask for renegotiation when need to renew sequence number.
+             *
+             * Don't bother to kickstart the renegotiation when the local is
+             * asking for it.
              */
             if (debug != null && Debug.isOn("ssl")) {
                 System.out.println(Thread.currentThread().getName() +
-                    ", sequence number extremely close to overflow " +
-                    "(2^64-1 packets). Closing connection.");
-
-            }
-
-            fatal(Alerts.alert_handshake_failure, "sequence number overflow");
-        }
-
-        /*
-         * Ask for renegotiation when need to renew sequence number.
-         *
-         * Don't bother to kickstart the renegotiation when the local is
-         * asking for it.
-         */
-        if ((type != Record.ct_handshake) && authenticator.seqNumIsHuge()) {
-            if (debug != null && Debug.isOn("ssl")) {
-                System.out.println(Thread.currentThread().getName() +
                         ", request renegotiation " +
                         "to avoid sequence number overflow");
             }
 
             startHandshake();
         }
+
+        return volume;
     }
 
+
     //
     // HANDSHAKE RELATED CODE
     //
@@ -1323,6 +1316,7 @@
                     secureRenegotiation, clientVerifyData, serverVerifyData);
             handshaker.setSNIServerNames(serverNames);
         }
+        handshaker.setMaximumPacketSize(maximumPacketSize);
         handshaker.setEnabledCipherSuites(enabledCipherSuites);
         handshaker.setEnableSessionCreation(enableSessionCreation);
     }
@@ -1342,29 +1336,12 @@
                 kickstartHandshake();
 
                 /*
-                 * All initial handshaking goes through this
-                 * InputRecord until we have a valid SSL connection.
-                 * Once initial handshaking is finished, AppInputStream's
-                 * InputRecord can handle any future renegotiation.
+                 * All initial handshaking goes through this operation
+                 * until we have a valid SSL connection.
                  *
-                 * Keep this local so that it goes out of scope and is
-                 * eventually GC'd.
+                 * Handle handshake messages only, need no application data.
                  */
-                if (inrec == null) {
-                    inrec = new InputRecord();
-
-                    /*
-                     * Grab the characteristics already assigned to
-                     * AppInputStream's InputRecord.  Enable checking for
-                     * SSLv2 hellos on this first handshake.
-                     */
-                    inrec.setHandshakeHash(input.r.getHandshakeHash());
-                    inrec.setHelloVersion(input.r.getHelloVersion());
-                    inrec.enableFormatChecks();
-                }
-
-                readRecord(inrec, false);
-                inrec = null;
+                readRecord(false);
             }
         }
     }
@@ -1482,9 +1459,6 @@
                 } else {
                     // we want to renegotiate, send hello request
                     handshaker.kickstart();
-                    // hello request is not included in the handshake
-                    // hashes, reset them
-                    handshaker.handshakeHash.reset();
                 }
             }
         }
@@ -1547,7 +1521,7 @@
         }
     }
 
-    protected void closeSocket() throws IOException {
+    private void closeSocket() throws IOException {
 
         if ((debug != null) && Debug.isOn("ssl")) {
             System.out.println(Thread.currentThread().getName() +
@@ -1591,6 +1565,23 @@
                                                     ", called close()");
         }
         closeInternal(true);  // caller is initiating close
+
+        // Clearup the resources.
+        try {
+            synchronized (readLock) {
+                inputRecord.close();
+            }
+
+            writeLock.lock();
+            try {
+                outputRecord.close();
+            } finally {
+                writeLock.unlock();
+            }
+        } catch (IOException ioe) {
+           // ignore
+        }
+
         setConnectionState(cs_APP_CLOSED);
     }
 
@@ -1714,19 +1705,17 @@
                 // notify any threads waiting for the closing to finish
                 this.notifyAll();
             }
-            if (closeSocketCalled) {
-                // Dispose of ciphers since we've closed socket
-                disposeCiphers();
-            }
+
             if (cachedThrowable != null) {
                /*
                 * Rethrow the error to the calling method
                 * The Throwable caught can only be an Error or RuntimeException
                 */
-                if (cachedThrowable instanceof Error)
-                    throw (Error) cachedThrowable;
-                if (cachedThrowable instanceof RuntimeException)
-                    throw (RuntimeException) cachedThrowable;
+                if (cachedThrowable instanceof Error) {
+                    throw (Error)cachedThrowable;
+                } else if (cachedThrowable instanceof RuntimeException) {
+                    throw (RuntimeException)cachedThrowable;
+                }   // Otherwise, unlikely
             }
         }
     }
@@ -1750,19 +1739,14 @@
 
             while (((state = getConnectionState()) != cs_CLOSED) &&
                    (state != cs_ERROR) && (state != cs_APP_CLOSED)) {
-                // create the InputRecord if it isn't initialized.
-                if (inrec == null) {
-                    inrec = new InputRecord();
-                }
 
                 // Ask for app data and then throw it away
                 try {
-                    readRecord(inrec, true);
+                    readRecord(true);
                 } catch (SocketTimeoutException e) {
                     // if time out, ignore the exception and continue
                 }
             }
-            inrec = null;
         } catch (IOException e) {
             if (debug != null && Debug.isOn("ssl")) {
                 System.out.println(Thread.currentThread().getName() +
@@ -1774,24 +1758,6 @@
         }
     }
 
-    /**
-     * Called by closeInternal() only. Be sure to consider the
-     * synchronization locks carefully before calling it elsewhere.
-     */
-    private void disposeCiphers() {
-        // See comment in changeReadCiphers()
-        synchronized (readLock) {
-            readCipher.dispose();
-        }
-        // See comment in changeReadCiphers()
-        writeLock.lock();
-        try {
-            writeCipher.dispose();
-        } finally {
-            writeLock.unlock();
-        }
-    }
-
     //
     // EXCEPTION AND ALERT HANDLING
     //
@@ -1853,7 +1819,7 @@
 
         // need to perform error shutdown
         boolean isSSLException = (e instanceof SSLException);
-        if ((isSSLException == false) && (e instanceof IOException)) {
+        if ((!isSSLException) && (e instanceof IOException)) {
             // IOException from the socket
             // this means the TCP connection is already dead
             // we call fatal just to set the error status
@@ -1903,9 +1869,14 @@
      */
     synchronized void fatal(byte description, String diagnostic,
             Throwable cause) throws IOException {
-        if ((input != null) && (input.r != null)) {
-            input.r.close();
+
+        // Be care of deadlock. Please don't synchronize readLock.
+        try {
+            inputRecord.close();
+        } catch (IOException ioe) {
+            // ignore
         }
+
         sess.invalidate();
         if (handshakeSession != null) {
             handshakeSession.invalidate();
@@ -1945,15 +1916,12 @@
          * Clean up our side.
          */
         closeSocket();
-        // Another thread may have disposed the ciphers during closing
-        if (connectionState < cs_CLOSED) {
-            connectionState = (oldState == cs_APP_CLOSED) ? cs_APP_CLOSED
-                                                              : cs_CLOSED;
 
-            // We should lock readLock and writeLock if no deadlock risks.
-            // See comment in changeReadCiphers()
-            readCipher.dispose();
-            writeCipher.dispose();
+        // Be care of deadlock. Please don't synchronize writeLock.
+        try {
+            outputRecord.close();
+        } catch (IOException ioe) {
+            // ignore
         }
 
         throw closeReason;
@@ -1964,9 +1932,10 @@
      * Process an incoming alert ... caller must already have synchronized
      * access to "this".
      */
-    private void recvAlert(InputRecord r) throws IOException {
-        byte level = (byte)r.read();
-        byte description = (byte)r.read();
+    private void recvAlert(ByteBuffer fragment) throws IOException {
+        byte level = fragment.get();
+        byte description = fragment.get();
+
         if (description == -1) { // check for short message
             fatal(Alerts.alert_illegal_parameter, "Short alert message");
         }
@@ -2029,14 +1998,14 @@
 
         // For initial handshaking, don't send alert message to peer if
         // handshaker has not started.
-        if (connectionState == cs_HANDSHAKE &&
-            (handshaker == null || !handshaker.started())) {
+        //
+        // Shall we send an fatal alter to terminate the connection gracefully?
+        if (connectionState <= cs_HANDSHAKE &&
+                (handshaker == null || !handshaker.started() ||
+                        !handshaker.activated())) {
             return;
         }
 
-        OutputRecord r = new OutputRecord(Record.ct_alert);
-        r.setVersion(protocolVersion);
-
         boolean useDebug = debug != null && Debug.isOn("ssl");
         if (useDebug) {
             synchronized (System.out) {
@@ -2054,10 +2023,8 @@
             }
         }
 
-        r.write(level);
-        r.write(description);
         try {
-            writeRecord(r);
+            writeAlert(level, description);
         } catch (IOException e) {
             if (useDebug) {
                 System.out.println(Thread.currentThread().getName() +
@@ -2070,61 +2037,10 @@
     // VARIOUS OTHER METHODS
     //
 
-    /*
-     * When a connection finishes handshaking by enabling use of a newly
-     * negotiated session, each end learns about it in two halves (read,
-     * and write).  When both read and write ciphers have changed, and the
-     * last handshake message has been read, the connection has joined
-     * (rejoined) the new session.
-     *
-     * NOTE:  The SSLv3 spec is rather unclear on the concepts here.
-     * Sessions don't change once they're established (including cipher
-     * suite and master secret) but connections can join them (and leave
-     * them).  They're created by handshaking, though sometime handshaking
-     * causes connections to join up with pre-established sessions.
-     */
-    private void changeReadCiphers() throws SSLException {
-        if (connectionState != cs_HANDSHAKE
-                && connectionState != cs_RENEGOTIATE) {
-            throw new SSLProtocolException(
-                "State error, change cipher specs");
-        }
-
-        // ... create decompressor
-
-        CipherBox oldCipher = readCipher;
-
-        try {
-            readCipher = handshaker.newReadCipher();
-            readAuthenticator = handshaker.newReadAuthenticator();
-        } catch (GeneralSecurityException e) {
-            // "can't happen"
-            throw new SSLException("Algorithm missing:  ", e);
-        }
-
-        /*
-         * Dispose of any intermediate state in the underlying cipher.
-         * For PKCS11 ciphers, this will release any attached sessions,
-         * and thus make finalization faster.
-         *
-         * Since MAC's doFinal() is called for every SSL/TLS packet, it's
-         * not necessary to do the same with MAC's.
-         */
-        oldCipher.dispose();
-    }
-
     // used by Handshaker
-    void changeWriteCiphers() throws SSLException {
-        if (connectionState != cs_HANDSHAKE
-                && connectionState != cs_RENEGOTIATE) {
-            throw new SSLProtocolException(
-                "State error, change cipher specs");
-        }
-
-        // ... create compressor
-
-        CipherBox oldCipher = writeCipher;
-
+    void changeWriteCiphers() throws IOException {
+        Authenticator writeAuthenticator;
+        CipherBox writeCipher;
         try {
             writeCipher = handshaker.newWriteCipher();
             writeAuthenticator = handshaker.newWriteAuthenticator();
@@ -2132,12 +2048,7 @@
             // "can't happen"
             throw new SSLException("Algorithm missing:  ", e);
         }
-
-        // See comment above.
-        oldCipher.dispose();
-
-        // reset the flag of the first application record
-        isFirstAppOutputRecord = true;
+        outputRecord.changeWriteCiphers(writeAuthenticator, writeCipher);
     }
 
     /*
@@ -2146,7 +2057,7 @@
      */
     synchronized void setVersion(ProtocolVersion protocolVersion) {
         this.protocolVersion = protocolVersion;
-        output.r.setVersion(protocolVersion);
+        outputRecord.setVersion(protocolVersion);
     }
 
     synchronized String getHost() {
@@ -2245,6 +2156,10 @@
     }
 
     synchronized void setHandshakeSession(SSLSessionImpl session) {
+        // update the fragment size, which may be negotiated during handshaking
+        inputRecord.changeFragmentSize(session.getNegotiatedMaxFragSize());
+        outputRecord.changeFragmentSize(session.getNegotiatedMaxFragSize());
+
         handshakeSession = session;
     }
 
@@ -2285,8 +2200,8 @@
      */
     @Override
     synchronized public void setNeedClientAuth(boolean flag) {
-        doClientAuth = (flag ?
-            SSLEngineImpl.clauth_required : SSLEngineImpl.clauth_none);
+        doClientAuth = (flag ? ClientAuthType.CLIENT_AUTH_REQUIRED :
+                ClientAuthType.CLIENT_AUTH_NONE);
 
         if ((handshaker != null) &&
                 (handshaker instanceof ServerHandshaker) &&
@@ -2297,7 +2212,7 @@
 
     @Override
     synchronized public boolean getNeedClientAuth() {
-        return (doClientAuth == SSLEngineImpl.clauth_required);
+        return (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUIRED);
     }
 
     /**
@@ -2310,8 +2225,8 @@
      */
     @Override
     synchronized public void setWantClientAuth(boolean flag) {
-        doClientAuth = (flag ?
-            SSLEngineImpl.clauth_requested : SSLEngineImpl.clauth_none);
+        doClientAuth = (flag ? ClientAuthType.CLIENT_AUTH_REQUESTED :
+                ClientAuthType.CLIENT_AUTH_NONE);
 
         if ((handshaker != null) &&
                 (handshaker instanceof ServerHandshaker) &&
@@ -2322,7 +2237,7 @@
 
     @Override
     synchronized public boolean getWantClientAuth() {
-        return (doClientAuth == SSLEngineImpl.clauth_requested);
+        return (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUESTED);
     }
 
 
@@ -2339,13 +2254,22 @@
         case cs_START:
             /*
              * If we need to change the socket mode and the enabled
-             * protocols haven't specifically been set by the user,
-             * change them to the corresponding default ones.
+             * protocols and cipher suites haven't specifically been
+             * set by the user, change them to the corresponding
+             * default ones.
              */
-            if (roleIsServer != (!flag) &&
-                    sslContext.isDefaultProtocolList(enabledProtocols)) {
-                enabledProtocols = sslContext.getDefaultProtocolList(!flag);
+            if (roleIsServer != (!flag)) {
+                if (sslContext.isDefaultProtocolList(enabledProtocols)) {
+                    enabledProtocols =
+                            sslContext.getDefaultProtocolList(!flag);
+                }
+
+                if (sslContext.isDefaultCipherSuiteList(enabledCipherSuites)) {
+                    enabledCipherSuites =
+                            sslContext.getDefaultCipherSuiteList(!flag);
+                }
             }
+
             roleIsServer = !flag;
             break;
 
@@ -2361,13 +2285,23 @@
             if (!handshaker.activated()) {
                 /*
                  * If we need to change the socket mode and the enabled
-                 * protocols haven't specifically been set by the user,
-                 * change them to the corresponding default ones.
+                 * protocols and cipher suites haven't specifically been
+                 * set by the user, change them to the corresponding
+                 * default ones.
                  */
-                if (roleIsServer != (!flag) &&
-                        sslContext.isDefaultProtocolList(enabledProtocols)) {
-                    enabledProtocols = sslContext.getDefaultProtocolList(!flag);
+                if (roleIsServer != (!flag)) {
+                    if (sslContext.isDefaultProtocolList(enabledProtocols)) {
+                        enabledProtocols =
+                                sslContext.getDefaultProtocolList(!flag);
+                    }
+
+                    if (sslContext.isDefaultCipherSuiteList(
+                                                    enabledCipherSuites)) {
+                        enabledCipherSuites =
+                            sslContext.getDefaultCipherSuiteList(!flag);
+                    }
                 }
+
                 roleIsServer = !flag;
                 connectionState = cs_START;
                 initHandshaker();
@@ -2535,6 +2469,9 @@
         params.setSNIMatchers(sniMatchers);
         params.setServerNames(serverNames);
         params.setUseCipherSuitesOrder(preferLocalCipherSuites);
+        params.setMaximumPacketSize(maximumPacketSize);
+
+        // DTLS handshake retransmissions parameter does not apply here.
 
         return params;
     }
@@ -2550,6 +2487,16 @@
         identificationProtocol = params.getEndpointIdentificationAlgorithm();
         algorithmConstraints = params.getAlgorithmConstraints();
         preferLocalCipherSuites = params.getUseCipherSuitesOrder();
+        maximumPacketSize = params.getMaximumPacketSize();
+
+        // DTLS handshake retransmissions parameter does not apply here.
+
+        if (maximumPacketSize != 0) {
+            outputRecord.changePacketSize(maximumPacketSize);
+        } else {
+            // use the implicit maximum packet size.
+            maximumPacketSize = outputRecord.getMaxPacketSize();
+        }
 
         List<SNIServerName> sniNames = params.getServerNames();
         if (sniNames != null) {
@@ -2564,6 +2511,7 @@
         if ((handshaker != null) && !handshaker.started()) {
             handshaker.setIdentificationProtocol(identificationProtocol);
             handshaker.setAlgorithmConstraints(algorithmConstraints);
+            handshaker.setMaximumPacketSize(maximumPacketSize);
             if (roleIsServer) {
                 handshaker.setSNIMatchers(sniMatchers);
                 handshaker.setUseCipherSuitesOrder(preferLocalCipherSuites);
@@ -2612,14 +2560,6 @@
     }
 
     /**
-     * Returns a boolean indicating whether the ChangeCipherSpec message
-     * has been received for this handshake.
-     */
-    boolean receivedChangeCipherSpec() {
-        return receivedCCS;
-    }
-
-    /**
      * Returns a printable representation of this end of the connection.
      */
     @Override
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/SSLSocketInputRecord.java	Tue Jun 02 09:15:47 2015 -0700
@@ -0,0 +1,441 @@
+/*
+ * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ssl;
+
+import java.io.*;
+import java.nio.*;
+
+import javax.crypto.BadPaddingException;
+
+import javax.net.ssl.*;
+
+import sun.misc.HexDumpEncoder;
+
+
+/**
+ * {@code InputRecord} implementation for {@code SSLSocket}.
+ *
+ * @author David Brownell
+ */
+final class SSLSocketInputRecord extends InputRecord implements SSLRecord {
+    private OutputStream deliverStream = null;
+    private byte[] temporary = new byte[1024];
+
+    // used by handshake hash computation for handshake fragment
+    private byte prevType = -1;
+    private int hsMsgOff = 0;
+    private int hsMsgLen = 0;
+
+    private boolean formatVerified = false;     // SSLv2 ruled out?
+
+    private boolean hasHeader = false;          // Had read the record header
+
+    SSLSocketInputRecord() {
+        this.readAuthenticator = MAC.TLS_NULL;
+    }
+
+    @Override
+    int bytesInCompletePacket(InputStream is) throws IOException {
+
+        if (!hasHeader) {
+            // read exactly one record
+            int really = read(is, temporary, 0, headerSize);
+            if (really < 0) {
+                throw new EOFException("SSL peer shut down incorrectly");
+            }
+            hasHeader = true;
+        }
+
+        byte byteZero = temporary[0];
+        int len = 0;
+
+        /*
+         * If we have already verified previous packets, we can
+         * ignore the verifications steps, and jump right to the
+         * determination.  Otherwise, try one last hueristic to
+         * see if it's SSL/TLS.
+         */
+        if (formatVerified ||
+                (byteZero == ct_handshake) || (byteZero == ct_alert)) {
+            /*
+             * Last sanity check that it's not a wild record
+             */
+            ProtocolVersion recordVersion =
+                    ProtocolVersion.valueOf(temporary[1], temporary[2]);
+
+            // check the record version
+            checkRecordVersion(recordVersion, false);
+
+            /*
+             * Reasonably sure this is a V3, disable further checks.
+             * We can't do the same in the v2 check below, because
+             * read still needs to parse/handle the v2 clientHello.
+             */
+            formatVerified = true;
+
+            /*
+             * One of the SSLv3/TLS message types.
+             */
+            len = ((temporary[3] & 0xFF) << 8) +
+                   (temporary[4] & 0xFF) + headerSize;
+        } else {
+            /*
+             * Must be SSLv2 or something unknown.
+             * Check if it's short (2 bytes) or
+             * long (3) header.
+             *
+             * Internals can warn about unsupported SSLv2
+             */
+            boolean isShort = ((byteZero & 0x80) != 0);
+
+            if (isShort && ((temporary[2] == 1) || (temporary[2] == 4))) {
+                ProtocolVersion recordVersion =
+                        ProtocolVersion.valueOf(temporary[3], temporary[4]);
+
+                // check the record version
+                checkRecordVersion(recordVersion, true);
+
+                /*
+                 * Client or Server Hello
+                 */
+                //
+                // Short header is using here.  We reverse the code here
+                // in case it it used in the future.
+                //
+                // int mask = (isShort ? 0x7F : 0x3F);
+                // len = ((byteZero & mask) << 8) +
+                //        (temporary[1] & 0xFF) + (isShort ? 2 : 3);
+                //
+                len = ((byteZero & 0x7F) << 8) + (temporary[1] & 0xFF) + 2;
+            } else {
+                // Gobblygook!
+                throw new SSLException(
+                        "Unrecognized SSL message, plaintext connection?");
+            }
+        }
+
+        return len;
+    }
+
+    // destination.position() is zero.
+    @Override
+    Plaintext decode(InputStream is, ByteBuffer destination)
+            throws IOException, BadPaddingException {
+
+        if (isClosed) {
+            return null;
+        }
+
+        if (!hasHeader) {
+            // read exactly one record
+            int really = read(is, temporary, 0, headerSize);
+            if (really < 0) {
+                throw new EOFException("SSL peer shut down incorrectly");
+            }
+            hasHeader = true;
+        }
+
+        Plaintext plaintext = null;
+        if (!formatVerified) {
+            formatVerified = true;
+
+            /*
+             * The first record must either be a handshake record or an
+             * alert message. If it's not, it is either invalid or an
+             * SSLv2 message.
+             */
+            if ((temporary[0] != ct_handshake) &&
+                (temporary[0] != ct_alert)) {
+
+                plaintext = handleUnknownRecord(is, temporary, destination);
+            }
+        }
+
+        if (plaintext == null) {
+            plaintext = decodeInputRecord(is, temporary, destination);
+        }
+
+        // The record header should has comsumed.
+        hasHeader = false;
+
+        return plaintext;
+    }
+
+    @Override
+    void setDeliverStream(OutputStream outputStream) {
+        this.deliverStream = outputStream;
+    }
+
+    // Note that destination may be null
+    private Plaintext decodeInputRecord(InputStream is, byte[] header,
+            ByteBuffer destination) throws IOException, BadPaddingException {
+
+        byte contentType = header[0];
+        byte majorVersion = header[1];
+        byte minorVersion = header[2];
+        int contentLen = ((header[3] & 0xFF) << 8) + (header[4] & 0xFF);
+
+        //
+        // Check for upper bound.
+        //
+        // Note: May check packetSize limit in the future.
+        if (contentLen < 0 || contentLen > maxLargeRecordSize - headerSize) {
+            throw new SSLProtocolException(
+                "Bad input record size, TLSCiphertext.length = " + contentLen);
+        }
+
+        //
+        // Read a complete record.
+        //
+        if (destination == null) {
+            destination = ByteBuffer.allocate(headerSize + contentLen);
+        }  // Otherwise, the destination buffer should have enough room.
+
+        int dstPos = destination.position();
+        destination.put(temporary, 0, headerSize);
+        while (contentLen > 0) {
+            int howmuch = Math.min(temporary.length, contentLen);
+            int really = read(is, temporary, 0, howmuch);
+            if (really < 0) {
+                throw new EOFException("SSL peer shut down incorrectly");
+            }
+
+            destination.put(temporary, 0, howmuch);
+            contentLen -= howmuch;
+        }
+        destination.flip();
+        destination.position(dstPos + headerSize);
+
+        if (debug != null && Debug.isOn("record")) {
+             System.out.println(Thread.currentThread().getName() +
+                    ", READ: " +
+                    ProtocolVersion.valueOf(majorVersion, minorVersion) +
+                    " " + Record.contentName(contentType) + ", length = " +
+                    destination.remaining());
+        }
+
+        //
+        // Decrypt the fragment
+        //
+        ByteBuffer plaintext =
+            decrypt(readAuthenticator, readCipher, contentType, destination);
+
+        if ((contentType != ct_handshake) && (hsMsgOff != hsMsgLen)) {
+            throw new SSLProtocolException(
+                    "Expected to get a handshake fragment");
+        }
+
+        //
+        // handshake hashing
+        //
+        if (contentType == ct_handshake) {
+            int pltPos = plaintext.position();
+            int pltLim = plaintext.limit();
+            int frgPos = pltPos;
+            for (int remains = plaintext.remaining(); remains > 0;) {
+                int howmuch;
+                byte handshakeType;
+                if (hsMsgOff < hsMsgLen) {
+                    // a fragment of the handshake message
+                    howmuch = Math.min((hsMsgLen - hsMsgOff), remains);
+                    handshakeType = prevType;
+
+                    hsMsgOff += howmuch;
+                    if (hsMsgOff == hsMsgLen) {
+                        // Now is a complete handshake message.
+                        hsMsgOff = 0;
+                        hsMsgLen = 0;
+                    }
+                } else {    // hsMsgOff == hsMsgLen, a new handshake message
+                    handshakeType = plaintext.get();
+                    int handshakeLen = ((plaintext.get() & 0xFF) << 16) |
+                                       ((plaintext.get() & 0xFF) << 8) |
+                                        (plaintext.get() & 0xFF);
+                    plaintext.position(frgPos);
+                    if (remains < (handshakeLen + 1)) { // 1: handshake type
+                        // This handshake message is fragmented.
+                        prevType = handshakeType;
+                        hsMsgOff = remains - 4;         // 4: handshake header
+                        hsMsgLen = handshakeLen;
+                    }
+
+                    howmuch = Math.min(handshakeLen + 4, remains);
+                }
+
+                plaintext.limit(frgPos + howmuch);
+
+                if (handshakeType == HandshakeMessage.ht_hello_request) {
+                    // omitted from handshake hash computation
+                } else if ((handshakeType != HandshakeMessage.ht_finished) &&
+                    (handshakeType != HandshakeMessage.ht_certificate_verify)) {
+
+                    if (handshakeHash == null) {
+                        // used for cache only
+                        handshakeHash = new HandshakeHash(false);
+                    }
+                    handshakeHash.update(plaintext);
+                } else {
+                    // Reserve until this handshake message has been processed.
+                    if (handshakeHash == null) {
+                        // used for cache only
+                        handshakeHash = new HandshakeHash(false);
+                    }
+                    handshakeHash.reserve(plaintext);
+                }
+
+                plaintext.position(frgPos + howmuch);
+                plaintext.limit(pltLim);
+
+                frgPos += howmuch;
+                remains -= howmuch;
+            }
+            plaintext.position(pltPos);
+        }
+
+        return new Plaintext(contentType,
+                majorVersion, minorVersion, -1, -1L, plaintext);
+                // recordEpoch, recordSeq, plaintext);
+    }
+
+    private Plaintext handleUnknownRecord(InputStream is, byte[] header,
+            ByteBuffer destination) throws IOException, BadPaddingException {
+
+        byte firstByte = header[0];
+        byte thirdByte = header[2];
+
+        // Does it look like a Version 2 client hello (V2ClientHello)?
+        if (((firstByte & 0x80) != 0) && (thirdByte == 1)) {
+            /*
+             * If SSLv2Hello is not enabled, throw an exception.
+             */
+            if (helloVersion != ProtocolVersion.SSL20Hello) {
+                throw new SSLHandshakeException("SSLv2Hello is not enabled");
+            }
+
+            byte majorVersion = header[3];
+            byte minorVersion = header[4];
+
+            if ((majorVersion == ProtocolVersion.SSL20Hello.major) &&
+                (minorVersion == ProtocolVersion.SSL20Hello.minor)) {
+
+                /*
+                 * Looks like a V2 client hello, but not one saying
+                 * "let's talk SSLv3".  So we need to send an SSLv2
+                 * error message, one that's treated as fatal by
+                 * clients (Otherwise we'll hang.)
+                 */
+                deliverStream.write(SSLRecord.v2NoCipher);      // SSLv2Hello
+
+                if (debug != null) {
+                    if (Debug.isOn("record")) {
+                         System.out.println(Thread.currentThread().getName() +
+                                "Requested to negotiate unsupported SSLv2!");
+                    }
+
+                    if (Debug.isOn("packet")) {
+                        Debug.printHex(
+                                "[Raw write]: length = " +
+                                SSLRecord.v2NoCipher.length,
+                                SSLRecord.v2NoCipher);
+                    }
+                }
+
+                throw new SSLException("Unsupported SSL v2.0 ClientHello");
+            }
+
+            int msgLen = ((header[0] & 0x7F) << 8) | (header[1] & 0xFF);
+
+            if (destination == null) {
+                destination = ByteBuffer.allocate(headerSize + msgLen);
+            }
+            destination.put(temporary, 0, headerSize);
+            msgLen -= 3;            // had read 3 bytes of content as header
+            while (msgLen > 0) {
+                int howmuch = Math.min(temporary.length, msgLen);
+                int really = read(is, temporary, 0, howmuch);
+                if (really < 0) {
+                    throw new EOFException("SSL peer shut down incorrectly");
+                }
+
+                destination.put(temporary, 0, howmuch);
+                msgLen -= howmuch;
+            }
+            destination.flip();
+
+            /*
+             * If we can map this into a V3 ClientHello, read and
+             * hash the rest of the V2 handshake, turn it into a
+             * V3 ClientHello message, and pass it up.
+             */
+            destination.position(2);     // exclude the header
+
+            if (handshakeHash == null) {
+                // used for cache only
+                handshakeHash = new HandshakeHash(false);
+            }
+            handshakeHash.update(destination);
+            destination.position(0);
+
+            ByteBuffer converted = convertToClientHello(destination);
+
+            if (debug != null && Debug.isOn("packet")) {
+                 Debug.printHex(
+                        "[Converted] ClientHello", converted);
+            }
+
+            return new Plaintext(ct_handshake,
+                majorVersion, minorVersion, -1, -1L, converted);
+        } else {
+            if (((firstByte & 0x80) != 0) && (thirdByte == 4)) {
+                throw new SSLException("SSL V2.0 servers are not supported.");
+            }
+
+            throw new SSLException("Unsupported or unrecognized SSL message");
+        }
+    }
+
+    // Read the exact bytes of data, otherwise, return -1.
+    private static int read(InputStream is,
+            byte[] buffer, int offset, int len) throws IOException {
+        int n = 0;
+        while (n < len) {
+            int readLen = is.read(buffer, offset + n, len - n);
+            if (readLen < 0) {
+                return -1;
+            }
+
+            if (debug != null && Debug.isOn("packet")) {
+                 Debug.printHex(
+                        "[Raw read]: length = " + readLen,
+                        buffer, offset + n, readLen);
+            }
+
+            n += readLen;
+        }
+
+        return n;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/SSLSocketOutputRecord.java	Tue Jun 02 09:15:47 2015 -0700
@@ -0,0 +1,383 @@
+/*
+ * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ssl;
+
+import java.io.*;
+import java.nio.*;
+import java.util.Arrays;
+
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLHandshakeException;
+import sun.misc.HexDumpEncoder;
+
+
+/**
+ * {@code OutputRecord} implementation for {@code SSLSocket}.
+ */
+final class SSLSocketOutputRecord extends OutputRecord implements SSLRecord {
+    private OutputStream deliverStream = null;
+
+    SSLSocketOutputRecord() {
+        this.writeAuthenticator = MAC.TLS_NULL;
+
+        this.packetSize = SSLRecord.maxRecordSize;
+        this.protocolVersion = ProtocolVersion.DEFAULT_TLS;
+    }
+
+    @Override
+    void encodeAlert(byte level, byte description) throws IOException {
+        // use the buf of ByteArrayOutputStream
+        int position = headerSize + writeCipher.getExplicitNonceSize();
+        count = position;
+
+        write(level);
+        write(description);
+
+        if (debug != null && Debug.isOn("record")) {
+            System.out.println(Thread.currentThread().getName() +
+                    ", WRITE: " + protocolVersion +
+                    " " + Record.contentName(Record.ct_alert) +
+                    ", length = " + (count - headerSize));
+        }
+
+        // Encrypt the fragment and wrap up a record.
+        encrypt(writeAuthenticator, writeCipher,
+                Record.ct_alert, headerSize);
+
+        // deliver this message
+        deliverStream.write(buf, 0, count);    // may throw IOException
+        deliverStream.flush();                 // may throw IOException
+
+        if (debug != null && Debug.isOn("packet")) {
+             Debug.printHex(
+                    "[Raw write]: length = " + count, buf, 0, count);
+        }
+
+        // reset the internal buffer
+        count = 0;
+    }
+
+    @Override
+    void encodeHandshake(byte[] source,
+            int offset, int length) throws IOException {
+
+        if (firstMessage) {
+            firstMessage = false;
+
+            if ((helloVersion == ProtocolVersion.SSL20Hello) &&
+                (source[offset] == HandshakeMessage.ht_client_hello) &&
+                                            //  5: recode header size
+                (source[offset + 4 + 2 + 32] == 0)) {
+                                            // V3 session ID is empty
+                                            //  4: handshake header size
+                                            //  2: client_version in ClientHello
+                                            // 32: random in ClientHello
+
+                ByteBuffer v2ClientHello = encodeV2ClientHello(
+                        source, (offset + 4), (length - 4));
+
+                byte[] record = v2ClientHello.array();  // array offset is zero
+                int limit = v2ClientHello.limit();
+                handshakeHash.update(record, 2, (limit - 2));
+
+                if (debug != null && Debug.isOn("record")) {
+                     System.out.println(Thread.currentThread().getName() +
+                        ", WRITE: SSLv2 ClientHello message" +
+                        ", length = " + limit);
+                }
+
+                // deliver this message
+                //
+                // Version 2 ClientHello message should be plaintext.
+                //
+                // No max fragment length negotiation.
+                deliverStream.write(record, 0, limit);
+                deliverStream.flush();
+
+                if (debug != null && Debug.isOn("packet")) {
+                     Debug.printHex(
+                            "[Raw write]: length = " + count, record, 0, limit);
+                }
+
+                return;
+            }
+        }
+
+        byte handshakeType = source[0];
+        if (handshakeType != HandshakeMessage.ht_hello_request) {
+            handshakeHash.update(source, offset, length);
+        }
+
+        int fragLimit = getFragLimit();
+        int position = headerSize + writeCipher.getExplicitNonceSize();
+        if (count == 0) {
+            count = position;
+        }
+
+        if ((count - position) < (fragLimit - length)) {
+            write(source, offset, length);
+            return;
+        }
+
+        for (int limit = (offset + length); offset < limit;) {
+
+            int remains = (limit - offset) + (count - position);
+            int fragLen = Math.min(fragLimit, remains);
+
+            // use the buf of ByteArrayOutputStream
+            write(source, offset, fragLen);
+            if (remains < fragLimit) {
+                return;
+            }
+
+            if (debug != null && Debug.isOn("record")) {
+                System.out.println(Thread.currentThread().getName() +
+                        ", WRITE: " + protocolVersion +
+                        " " + Record.contentName(Record.ct_handshake) +
+                        ", length = " + (count - headerSize));
+            }
+
+            // Encrypt the fragment and wrap up a record.
+            encrypt(writeAuthenticator, writeCipher,
+                    Record.ct_handshake, headerSize);
+
+            // deliver this message
+            deliverStream.write(buf, 0, count);    // may throw IOException
+            deliverStream.flush();                 // may throw IOException
+
+            if (debug != null && Debug.isOn("packet")) {
+                 Debug.printHex(
+                        "[Raw write]: length = " + count, buf, 0, count);
+            }
+
+            // reset the offset
+            offset += fragLen;
+
+            // reset the internal buffer
+            count = position;
+        }
+    }
+
+    @Override
+    void encodeChangeCipherSpec() throws IOException {
+
+        // use the buf of ByteArrayOutputStream
+        int position = headerSize + writeCipher.getExplicitNonceSize();
+        count = position;
+
+        write((byte)1);         // byte 1: change_cipher_spec(
+
+        if (debug != null && Debug.isOn("record")) {
+            System.out.println(Thread.currentThread().getName() +
+                    ", WRITE: " + protocolVersion +
+                    " " + Record.contentName(Record.ct_change_cipher_spec) +
+                    ", length = " + (count - headerSize));
+        }
+
+        // Encrypt the fragment and wrap up a record.
+        encrypt(writeAuthenticator, writeCipher,
+                Record.ct_change_cipher_spec, headerSize);
+
+        // deliver this message
+        deliverStream.write(buf, 0, count);        // may throw IOException
+        // deliverStream.flush();                  // flush in Finished
+
+        if (debug != null && Debug.isOn("packet")) {
+             Debug.printHex(
+                    "[Raw write]: length = " + count, buf, 0, count);
+        }
+
+        // reset the internal buffer
+        count = 0;
+    }
+
+    @Override
+    public void flush() throws IOException {
+        int position = headerSize + writeCipher.getExplicitNonceSize();
+        if (count <= position) {
+            return;
+        }
+
+        if (debug != null && Debug.isOn("record")) {
+            System.out.println(Thread.currentThread().getName() +
+                    ", WRITE: " + protocolVersion +
+                    " " + Record.contentName(Record.ct_handshake) +
+                    ", length = " + (count - headerSize));
+        }
+
+        // Encrypt the fragment and wrap up a record.
+        encrypt(writeAuthenticator, writeCipher,
+                    Record.ct_handshake, headerSize);
+
+        // deliver this message
+        deliverStream.write(buf, 0, count);    // may throw IOException
+        deliverStream.flush();                 // may throw IOException
+
+        if (debug != null && Debug.isOn("packet")) {
+             Debug.printHex(
+                    "[Raw write]: length = " + count, buf, 0, count);
+        }
+
+        // reset the internal buffer
+        count = 0;      // DON'T use position
+    }
+
+    @Override
+    void deliver(byte[] source, int offset, int length) throws IOException {
+
+        if (writeAuthenticator.seqNumOverflow()) {
+            if (debug != null && Debug.isOn("ssl")) {
+                System.out.println(Thread.currentThread().getName() +
+                    ", sequence number extremely close to overflow " +
+                    "(2^64-1 packets). Closing connection.");
+            }
+
+            throw new SSLHandshakeException("sequence number overflow");
+        }
+
+        boolean isFirstRecordOfThePayload = true;
+        for (int limit = (offset + length); offset < limit;) {
+            int macLen = 0;
+            if (writeAuthenticator instanceof MAC) {
+                macLen = ((MAC)writeAuthenticator).MAClen();
+            }
+
+            int fragLen;
+            if (packetSize > 0) {
+                fragLen = Math.min(maxRecordSize, packetSize);
+                fragLen = writeCipher.calculateFragmentSize(
+                        fragLen, macLen, headerSize);
+
+                fragLen = Math.min(fragLen, Record.maxDataSize);
+            } else {
+                fragLen = Record.maxDataSize;
+            }
+
+            if (fragmentSize > 0) {
+                fragLen = Math.min(fragLen, fragmentSize);
+            }
+
+            if (isFirstRecordOfThePayload && needToSplitPayload()) {
+                fragLen = 1;
+                isFirstRecordOfThePayload = false;
+            } else {
+                fragLen = Math.min(fragLen, (limit - offset));
+            }
+
+            // use the buf of ByteArrayOutputStream
+            int position = headerSize + writeCipher.getExplicitNonceSize();
+            count = position;
+            write(source, offset, fragLen);
+
+            if (debug != null && Debug.isOn("record")) {
+                System.out.println(Thread.currentThread().getName() +
+                        ", WRITE: " + protocolVersion +
+                        " " + Record.contentName(Record.ct_application_data) +
+                        ", length = " + (count - headerSize));
+            }
+
+            // Encrypt the fragment and wrap up a record.
+            encrypt(writeAuthenticator, writeCipher,
+                    Record.ct_application_data, headerSize);
+
+            // deliver this message
+            deliverStream.write(buf, 0, count);    // may throw IOException
+            deliverStream.flush();                 // may throw IOException
+
+            if (debug != null && Debug.isOn("packet")) {
+                 Debug.printHex(
+                        "[Raw write]: length = " + count, buf, 0, count);
+            }
+
+            // reset the internal buffer
+            count = 0;
+
+            if (isFirstAppOutputRecord) {
+                isFirstAppOutputRecord = false;
+            }
+
+            offset += fragLen;
+        }
+    }
+
+    @Override
+    void setDeliverStream(OutputStream outputStream) {
+        this.deliverStream = outputStream;
+    }
+
+    /*
+     * Need to split the payload except the following cases:
+     *
+     * 1. protocol version is TLS 1.1 or later;
+     * 2. bulk cipher does not use CBC mode, including null bulk cipher suites.
+     * 3. the payload is the first application record of a freshly
+     *    negotiated TLS session.
+     * 4. the CBC protection is disabled;
+     *
+     * By default, we counter chosen plaintext issues on CBC mode
+     * ciphersuites in SSLv3/TLS1.0 by sending one byte of application
+     * data in the first record of every payload, and the rest in
+     * subsequent record(s). Note that the issues have been solved in
+     * TLS 1.1 or later.
+     *
+     * It is not necessary to split the very first application record of
+     * a freshly negotiated TLS session, as there is no previous
+     * application data to guess.  To improve compatibility, we will not
+     * split such records.
+     *
+     * This avoids issues in the outbound direction.  For a full fix,
+     * the peer must have similar protections.
+     */
+    boolean needToSplitPayload() {
+        return (!protocolVersion.useTLS11PlusSpec()) &&
+                writeCipher.isCBCMode() && !isFirstAppOutputRecord &&
+                Record.enableCBCProtection;
+    }
+
+    private int getFragLimit() {
+        int macLen = 0;
+        if (writeAuthenticator instanceof MAC) {
+            macLen = ((MAC)writeAuthenticator).MAClen();
+        }
+
+        int fragLimit;
+        if (packetSize > 0) {
+            fragLimit = Math.min(maxRecordSize, packetSize);
+            fragLimit = writeCipher.calculateFragmentSize(
+                    fragLimit, macLen, headerSize);
+
+            fragLimit = Math.min(fragLimit, Record.maxDataSize);
+        } else {
+            fragLimit = Record.maxDataSize;
+        }
+
+        if (fragmentSize > 0) {
+            fragLimit = Math.min(fragLimit, fragmentSize);
+        }
+
+        return fragLimit;
+    }
+}
--- a/jdk/src/java.base/share/classes/sun/security/ssl/ServerHandshaker.java	Mon Jun 01 10:15:21 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/ServerHandshaker.java	Tue Jun 02 09:15:47 2015 -0700
@@ -38,8 +38,6 @@
 
 import javax.net.ssl.*;
 
-import javax.security.auth.Subject;
-
 import sun.security.util.KeyUtil;
 import sun.security.action.GetPropertyAction;
 import sun.security.ssl.HandshakeMessage.*;
@@ -58,7 +56,7 @@
 final class ServerHandshaker extends Handshaker {
 
     // is the server going to require the client to authenticate?
-    private byte                doClientAuth;
+    private ClientAuthType      doClientAuth;
 
     // our authentication info
     private X509Certificate[]   certs;
@@ -143,13 +141,13 @@
      * Constructor ... use the keys found in the auth context.
      */
     ServerHandshaker(SSLSocketImpl socket, SSLContextImpl context,
-            ProtocolList enabledProtocols, byte clientAuth,
+            ProtocolList enabledProtocols, ClientAuthType clientAuth,
             ProtocolVersion activeProtocolVersion, boolean isInitialHandshake,
             boolean secureRenegotiation,
             byte[] clientVerifyData, byte[] serverVerifyData) {
 
         super(socket, context, enabledProtocols,
-                (clientAuth != SSLEngineImpl.clauth_none), false,
+                (clientAuth != ClientAuthType.CLIENT_AUTH_NONE), false,
                 activeProtocolVersion, isInitialHandshake, secureRenegotiation,
                 clientVerifyData, serverVerifyData);
         doClientAuth = clientAuth;
@@ -159,15 +157,16 @@
      * Constructor ... use the keys found in the auth context.
      */
     ServerHandshaker(SSLEngineImpl engine, SSLContextImpl context,
-            ProtocolList enabledProtocols, byte clientAuth,
+            ProtocolList enabledProtocols, ClientAuthType clientAuth,
             ProtocolVersion activeProtocolVersion,
             boolean isInitialHandshake, boolean secureRenegotiation,
-            byte[] clientVerifyData, byte[] serverVerifyData) {
+            byte[] clientVerifyData, byte[] serverVerifyData,
+            boolean isDTLS) {
 
         super(engine, context, enabledProtocols,
-                (clientAuth != SSLEngineImpl.clauth_none), false,
+                (clientAuth != ClientAuthType.CLIENT_AUTH_NONE), false,
                 activeProtocolVersion, isInitialHandshake, secureRenegotiation,
-                clientVerifyData, serverVerifyData);
+                clientVerifyData, serverVerifyData, isDTLS);
         doClientAuth = clientAuth;
     }
 
@@ -176,7 +175,7 @@
      * whether client authentication is required.  Otherwise,
      * we will need to wait for the next handshake.
      */
-    void setClientAuth(byte clientAuth) {
+    void setClientAuth(ClientAuthType clientAuth) {
         doClientAuth = clientAuth;
     }
 
@@ -192,21 +191,15 @@
     @Override
     void processMessage(byte type, int message_len)
             throws IOException {
-        //
-        // In SSLv3 and TLS, messages follow strictly increasing
-        // numerical order _except_ for one annoying special case.
-        //
-        if ((state >= type)
-                && (state != HandshakeMessage.ht_client_key_exchange
-                    && type != HandshakeMessage.ht_certificate_verify)) {
-            throw new SSLProtocolException(
-                    "Handshake message sequence violation, state = " + state
-                    + ", type = " + type);
-        }
+
+        // check the handshake state
+        handshakeState.check(type);
 
         switch (type) {
             case HandshakeMessage.ht_client_hello:
-                ClientHello ch = new ClientHello(input, message_len);
+                ClientHello ch = new ClientHello(input, message_len, isDTLS);
+                handshakeState.update(ch, resumingSession);
+
                 /*
                  * send it off for processing.
                  */
@@ -214,12 +207,14 @@
                 break;
 
             case HandshakeMessage.ht_certificate:
-                if (doClientAuth == SSLEngineImpl.clauth_none) {
+                if (doClientAuth == ClientAuthType.CLIENT_AUTH_NONE) {
                     fatalSE(Alerts.alert_unexpected_message,
                                 "client sent unsolicited cert chain");
                     // NOTREACHED
                 }
-                this.clientCertificate(new CertificateMsg(input));
+                CertificateMsg certificateMsg = new CertificateMsg(input);
+                handshakeState.update(certificateMsg, resumingSession);
+                this.clientCertificate(certificateMsg);
                 break;
 
             case HandshakeMessage.ht_client_key_exchange:
@@ -237,18 +232,9 @@
                             protocolVersion, clientRequestedVersion,
                             sslContext.getSecureRandom(), input,
                             message_len, privateKey);
+                    handshakeState.update(pms, resumingSession);
                     preMasterSecret = this.clientKeyExchange(pms);
                     break;
-                case K_KRB5:
-                case K_KRB5_EXPORT:
-                    preMasterSecret = this.clientKeyExchange(
-                        new KerberosClientKeyExchange(protocolVersion,
-                            clientRequestedVersion,
-                            sslContext.getSecureRandom(),
-                            input,
-                            this.getAccSE(),
-                            serviceCreds));
-                    break;
                 case K_DHE_RSA:
                 case K_DHE_DSS:
                 case K_DH_ANON:
@@ -258,20 +244,39 @@
                      * protocol difference in these five flavors is in how
                      * the ServerKeyExchange message was constructed!
                      */
-                    preMasterSecret = this.clientKeyExchange(
-                            new DHClientKeyExchange(input));
+                    DHClientKeyExchange dhcke = new DHClientKeyExchange(input);
+                    handshakeState.update(dhcke, resumingSession);
+                    preMasterSecret = this.clientKeyExchange(dhcke);
                     break;
                 case K_ECDH_RSA:
                 case K_ECDH_ECDSA:
                 case K_ECDHE_RSA:
                 case K_ECDHE_ECDSA:
                 case K_ECDH_ANON:
-                    preMasterSecret = this.clientKeyExchange
-                                            (new ECDHClientKeyExchange(input));
+                    ECDHClientKeyExchange ecdhcke =
+                                    new ECDHClientKeyExchange(input);
+                    handshakeState.update(ecdhcke, resumingSession);
+                    preMasterSecret = this.clientKeyExchange(ecdhcke);
                     break;
                 default:
-                    throw new SSLProtocolException
-                        ("Unrecognized key exchange: " + keyExchange);
+                    ClientKeyExchangeService p =
+                            ClientKeyExchangeService.find(keyExchange.name);
+                    if (p == null) {
+                        throw new SSLProtocolException
+                                ("Unrecognized key exchange: " + keyExchange);
+                    }
+                    byte[] encodedTicket = input.getBytes16();
+                    input.getBytes16();
+                    byte[] secret = input.getBytes16();
+                    ClientKeyExchange cke = p.createServerExchange(protocolVersion,
+                            clientRequestedVersion,
+                            sslContext.getSecureRandom(),
+                            encodedTicket,
+                            secret,
+                            this.getAccSE(), serviceCreds);
+                    handshakeState.update(cke, resumingSession);
+                    preMasterSecret = this.clientKeyExchange(cke);
+                    break;
                 }
 
                 //
@@ -282,20 +287,20 @@
                 break;
 
             case HandshakeMessage.ht_certificate_verify:
-                this.clientCertificateVerify(new CertificateVerify(input,
-                            localSupportedSignAlgs, protocolVersion));
+                CertificateVerify cvm =
+                        new CertificateVerify(input,
+                            localSupportedSignAlgs, protocolVersion);
+                handshakeState.update(cvm, resumingSession);
+                this.clientCertificateVerify(cvm);
+
                 break;
 
             case HandshakeMessage.ht_finished:
-                // A ChangeCipherSpec record must have been received prior to
-                // reception of the Finished message (RFC 5246, 7.4.9).
-                if (!receivedChangeCipherSpec()) {
-                    fatalSE(Alerts.alert_handshake_failure,
-                        "Received Finished message before ChangeCipherSpec");
-                }
+                Finished cfm =
+                    new Finished(protocolVersion, input, cipherSuite);
+                handshakeState.update(cfm, resumingSession);
+                this.clientFinished(cfm);
 
-                this.clientFinished(
-                    new Finished(protocolVersion, input, cipherSuite));
                 break;
 
             default:
@@ -303,17 +308,6 @@
                         "Illegal server handshake msg, " + type);
         }
 
-        //
-        // Move state machine forward if the message handling
-        // code didn't already do so
-        //
-        if (state < type) {
-            if(type == HandshakeMessage.ht_certificate_verify) {
-                state = type + 2;    // an annoying special case
-            } else {
-                state = type;
-            }
-        }
     }
 
 
@@ -344,7 +338,7 @@
         //
         // This will not have any impact on server initiated renegotiation.
         if (rejectClientInitiatedRenego && !isInitialHandshake &&
-                state != HandshakeMessage.ht_hello_request) {
+                !serverHelloRequested) {
             fatalSE(Alerts.alert_handshake_failure,
                 "Client initiated renegotiation is not allowed");
         }
@@ -438,7 +432,7 @@
                 }
             } else if (!allowUnsafeRenegotiation) {
                 // abort the handshake
-                if (activeProtocolVersion.v >= ProtocolVersion.TLS10.v) {
+                if (activeProtocolVersion.useTLS10PlusSpec()) {
                     // respond with a no_renegotiation warning
                     warningSE(Alerts.alert_no_renegotiation);
 
@@ -480,11 +474,52 @@
             }
         }
 
-        /*
-         * Always make sure this entire record has been digested before we
-         * start emitting output, to ensure correct digesting order.
-         */
-        input.digestNow();
+        // check the "max_fragment_length" extension
+        MaxFragmentLengthExtension maxFragLenExt = (MaxFragmentLengthExtension)
+                    mesg.extensions.get(ExtensionType.EXT_MAX_FRAGMENT_LENGTH);
+        if ((maxFragLenExt != null) && (maximumPacketSize != 0)) {
+            // Not yet consider the impact of IV/MAC/padding.
+            int estimatedMaxFragSize = maximumPacketSize;
+            if (isDTLS) {
+                estimatedMaxFragSize -= DTLSRecord.headerSize;
+            } else {
+                estimatedMaxFragSize -= SSLRecord.headerSize;
+            }
+
+            if (maxFragLenExt.getMaxFragLen() > estimatedMaxFragSize) {
+                // For better interoperability, abort the maximum fragment
+                // length negotiation, rather than terminate the connection
+                // with a fatal alert.
+                maxFragLenExt = null;
+
+                // fatalSE(Alerts.alert_illegal_parameter,
+                //         "Not an allowed max_fragment_length value");
+            }
+        }
+
+        // cookie exchange
+        if (isDTLS) {
+             HelloCookieManager hcMgr = sslContext.getHelloCookieManager();
+             if ((mesg.cookie == null) || (mesg.cookie.length == 0) ||
+                    (!hcMgr.isValid(mesg))) {
+
+                //
+                // Perform cookie exchange for DTLS handshaking if no cookie
+                // or the cookie is invalid in the ClientHello message.
+                //
+                HelloVerifyRequest m0 = new HelloVerifyRequest(hcMgr, mesg);
+
+                if (debug != null && Debug.isOn("handshake")) {
+                    m0.print(System.out);
+                }
+
+                m0.write(output);
+                handshakeState.update(m0, resumingSession);
+                output.flush();
+
+                return;
+            }
+        }
 
         /*
          * FIRST, construct the ServerHello using the options and priorities
@@ -580,7 +615,7 @@
                 }
 
                 if (resumingSession &&
-                        (doClientAuth == SSLEngineImpl.clauth_required)) {
+                        (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUIRED)) {
                     try {
                         previous.getPeerPrincipal();
                     } catch (SSLPeerUnverifiedException e) {
@@ -591,47 +626,21 @@
                 // validate subject identity
                 if (resumingSession) {
                     CipherSuite suite = previous.getSuite();
-                    if (suite.keyExchange == K_KRB5 ||
-                        suite.keyExchange == K_KRB5_EXPORT) {
+                    ClientKeyExchangeService p =
+                            ClientKeyExchangeService.find(suite.keyExchange.name);
+                    if (p != null) {
                         Principal localPrincipal = previous.getLocalPrincipal();
 
-                        Subject subject = null;
-                        try {
-                            subject = AccessController.doPrivileged(
-                                new PrivilegedExceptionAction<Subject>() {
-                                @Override
-                                public Subject run() throws Exception {
-                                    return
-                                        Krb5Helper.getServerSubject(getAccSE());
-                            }});
-                        } catch (PrivilegedActionException e) {
-                            subject = null;
-                            if (debug != null && Debug.isOn("session")) {
-                                System.out.println("Attempt to obtain" +
-                                                " subject failed!");
-                            }
-                        }
-
-                        if (subject != null) {
-                            // Eliminate dependency on KerberosPrincipal
-                            if (Krb5Helper.isRelated(subject, localPrincipal)) {
-                                if (debug != null && Debug.isOn("session"))
-                                    System.out.println("Subject can" +
-                                            " provide creds for princ");
-                            } else {
-                                resumingSession = false;
-                                if (debug != null && Debug.isOn("session"))
-                                    System.out.println("Subject cannot" +
-                                            " provide creds for princ");
-                            }
+                        if (p.isRelated(
+                                false, getAccSE(), localPrincipal)) {
+                            if (debug != null && Debug.isOn("session"))
+                                System.out.println("Subject can" +
+                                        " provide creds for princ");
                         } else {
                             resumingSession = false;
                             if (debug != null && Debug.isOn("session"))
-                                System.out.println("Kerberos credentials are" +
-                                    " not present in the current Subject;" +
-                                    " check if " +
-                                    " javax.security.auth.useSubjectAsCreds" +
-                                    " system property has been set to false");
+                                System.out.println("Subject cannot" +
+                                        " provide creds for princ");
                         }
                     }
                 }
@@ -660,7 +669,7 @@
                     }
                 }
             }
-        } // else client did not try to resume
+        }   // else client did not try to resume
 
         //
         // If client hasn't specified a session we can resume, start a
@@ -677,7 +686,7 @@
 
             // We only need to handle the "signature_algorithm" extension
             // for full handshakes and TLS 1.2 or later.
-            if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+            if (protocolVersion.useTLS12PlusSpec()) {
                 SignatureAlgorithmsExtension signAlgs =
                     (SignatureAlgorithmsExtension)mesg.extensions.get(
                                     ExtensionType.EXT_SIGNATURE_ALGORITHMS);
@@ -708,7 +717,7 @@
                         sslContext.getSecureRandom(),
                         getHostAddressSE(), getPortSE());
 
-            if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+            if (protocolVersion.useTLS12PlusSpec()) {
                 if (peerSupportedSignAlgs != null) {
                     session.setPeerSupportedSignatureAlgorithms(
                             peerSupportedSignAlgs);
@@ -734,12 +743,41 @@
             session.setLocalPrivateKey(privateKey);
 
             // chooseCompression(mesg);
+
+            // set the negotiated maximum fragment in the session
+            //
+            // The protocol version and cipher suite have been negotiated
+            // in previous processes.
+            if (maxFragLenExt != null) {
+                int maxFragLen = maxFragLenExt.getMaxFragLen();
+
+                // More check of the requested "max_fragment_length" extension.
+                if (maximumPacketSize != 0) {
+                    int estimatedMaxFragSize = cipherSuite.calculatePacketSize(
+                            maxFragLen, protocolVersion, isDTLS);
+                    if (estimatedMaxFragSize > maximumPacketSize) {
+                        // For better interoperability, abort the maximum
+                        // fragment length negotiation, rather than terminate
+                        // the connection with a fatal alert.
+                        maxFragLenExt = null;
+
+                        // fatalSE(Alerts.alert_illegal_parameter,
+                        //         "Not an allowed max_fragment_length value");
+                    }
+                }
+
+                if (maxFragLenExt != null) {
+                    session.setNegotiatedMaxFragSize(maxFragLen);
+                }
+            }
+
+            session.setMaximumPacketSize(maximumPacketSize);
         } else {
             // set the handshake session
             setHandshakeSessionSE(session);
         }
 
-        if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+        if (protocolVersion.useTLS12PlusSpec()) {
             handshakeHash.setFinishedAlg(cipherSuite.prfAlg.getPRFHashAlg());
         }
 
@@ -771,11 +809,20 @@
             }
         }
 
+        if ((maxFragLenExt != null) && !resumingSession) {
+            // When resuming a session, the server MUST NOT include a
+            // max_fragment_length extension in the server hello.
+            //
+            // Otherwise, use the same value as the requested extension.
+            m1.extensions.add(maxFragLenExt);
+        }
+
         if (debug != null && Debug.isOn("handshake")) {
             m1.print(System.out);
             System.out.println("Cipher suite:  " + session.getSuite());
         }
         m1.write(output);
+        handshakeState.update(m1, resumingSession);
 
         //
         // If we are resuming a session, we finish writing handshake
@@ -784,6 +831,10 @@
         if (resumingSession) {
             calculateConnectionKeys(session.getMasterSecret());
             sendChangeCipherAndFinish(false);
+
+            // expecting the final ChangeCipherSpec and Finished messages
+            expectingFinishFlightSE();
+
             return;
         }
 
@@ -796,9 +847,8 @@
          * defined in the protocol spec are explicitly stated to require
          * using RSA certificates.
          */
-        if (keyExchange == K_KRB5 || keyExchange == K_KRB5_EXPORT) {
-            // Server certificates are omitted for Kerberos ciphers
-
+        if (ClientKeyExchangeService.find(cipherSuite.keyExchange.name) != null) {
+            // No external key exchange provider needs a cert now.
         } else if ((keyExchange != K_DH_ANON) && (keyExchange != K_ECDH_ANON)) {
             if (certs == null) {
                 throw new RuntimeException("no certificates");
@@ -815,6 +865,7 @@
                 m2.print(System.out);
             }
             m2.write(output);
+            handshakeState.update(m2, resumingSession);
 
             // XXX has some side effects with OS TCP buffering,
             // leave it out for now
@@ -839,9 +890,7 @@
         ServerKeyExchange m3;
         switch (keyExchange) {
         case K_RSA:
-        case K_KRB5:
-        case K_KRB5_EXPORT:
-            // no server key exchange for RSA or KRB5 ciphersuites
+            // no server key exchange for RSA ciphersuites
             m3 = null;
             break;
         case K_RSA_EXPORT:
@@ -853,9 +902,9 @@
                         sslContext.getSecureRandom());
                     privateKey = tempPrivateKey;
                 } catch (GeneralSecurityException e) {
-                    throwSSLException
-                        ("Error generating RSA server key exchange", e);
                     m3 = null; // make compiler happy
+                    throw new SSLException(
+                            "Error generating RSA server key exchange", e);
                 }
             } else {
                 // RSA_EXPORT with short key, don't need ServerKeyExchange
@@ -873,8 +922,9 @@
                     preferableSignatureAlgorithm,
                     protocolVersion);
             } catch (GeneralSecurityException e) {
-                throwSSLException("Error generating DH server key exchange", e);
                 m3 = null; // make compiler happy
+                throw new SSLException(
+                        "Error generating DH server key exchange", e);
             }
             break;
         case K_DH_ANON:
@@ -892,9 +942,9 @@
                     preferableSignatureAlgorithm,
                     protocolVersion);
             } catch (GeneralSecurityException e) {
-                throwSSLException(
-                    "Error generating ECDH server key exchange", e);
                 m3 = null; // make compiler happy
+                throw new SSLException(
+                        "Error generating ECDH server key exchange", e);
             }
             break;
         case K_ECDH_RSA:
@@ -903,6 +953,13 @@
             m3 = null;
             break;
         default:
+            ClientKeyExchangeService p =
+                    ClientKeyExchangeService.find(keyExchange.name);
+            if (p != null) {
+                // No external key exchange provider needs a cert now.
+                m3 = null;
+                break;
+            }
             throw new RuntimeException("internal error: " + keyExchange);
         }
         if (m3 != null) {
@@ -910,6 +967,7 @@
                 m3.print(System.out);
             }
             m3.write(output);
+            handshakeState.update(m3, resumingSession);
         }
 
         //
@@ -922,16 +980,16 @@
         // Needed only if server requires client to authenticate self.
         // Illegal for anonymous flavors, so we need to check that.
         //
-        // CertificateRequest is omitted for Kerberos ciphers
-        if (doClientAuth != SSLEngineImpl.clauth_none &&
+        // No external key exchange provider needs a cert now.
+        if (doClientAuth != ClientAuthType.CLIENT_AUTH_NONE &&
                 keyExchange != K_DH_ANON && keyExchange != K_ECDH_ANON &&
-                keyExchange != K_KRB5 && keyExchange != K_KRB5_EXPORT) {
+                ClientKeyExchangeService.find(keyExchange.name) == null) {
 
             CertificateRequest m4;
             X509Certificate caCerts[];
 
             Collection<SignatureAndHashAlgorithm> localSignAlgs = null;
-            if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+            if (protocolVersion.useTLS12PlusSpec()) {
                 // We currently use all local upported signature and hash
                 // algorithms. However, to minimize the computation cost
                 // of requested hash algorithms, we may use a restricted
@@ -959,6 +1017,7 @@
                 m4.print(System.out);
             }
             m4.write(output);
+            handshakeState.update(m4, resumingSession);
         }
 
         /*
@@ -970,6 +1029,7 @@
             m5.print(System.out);
         }
         m5.write(output);
+        handshakeState.update(m5, resumingSession);
 
         /*
          * Flush any buffered messages so the client will see them.
@@ -1000,7 +1060,7 @@
                 continue;
             }
 
-            if (doClientAuth == SSLEngineImpl.clauth_required) {
+            if (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUIRED) {
                 if ((suite.keyExchange == K_DH_ANON) ||
                     (suite.keyExchange == K_ECDH_ANON)) {
                     continue;
@@ -1043,12 +1103,12 @@
         }
 
         // must not negotiate the obsoleted weak cipher suites.
-        if (protocolVersion.v >= suite.obsoleted) {
+        if (protocolVersion.obsoletes(suite)) {
             return false;
         }
 
         // must not negotiate unsupported cipher suites.
-        if (protocolVersion.v < suite.supported) {
+        if (!protocolVersion.supports(suite)) {
             return false;
         }
 
@@ -1062,7 +1122,7 @@
         tempPublicKey = null;
 
         Collection<SignatureAndHashAlgorithm> supportedSignAlgs = null;
-        if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+        if (protocolVersion.useTLS12PlusSpec()) {
             if (peerSupportedSignAlgs != null) {
                 supportedSignAlgs = peerSupportedSignAlgs;
             } else {
@@ -1151,7 +1211,7 @@
             }
 
             // get preferable peer signature algorithm for server key exchange
-            if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+            if (protocolVersion.useTLS12PlusSpec()) {
                 preferableSignatureAlgorithm =
                     SignatureAndHashAlgorithm.getPreferableAlgorithm(
                                         supportedSignAlgs, "RSA", privateKey);
@@ -1169,7 +1229,7 @@
             }
 
             // get preferable peer signature algorithm for server key exchange
-            if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+            if (protocolVersion.useTLS12PlusSpec()) {
                 preferableSignatureAlgorithm =
                     SignatureAndHashAlgorithm.getPreferableAlgorithm(
                                         supportedSignAlgs, "RSA", privateKey);
@@ -1184,7 +1244,7 @@
             break;
         case K_DHE_DSS:
             // get preferable peer signature algorithm for server key exchange
-            if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+            if (protocolVersion.useTLS12PlusSpec()) {
                 preferableSignatureAlgorithm =
                     SignatureAndHashAlgorithm.getPreferableAlgorithm(
                                                 supportedSignAlgs, "DSA");
@@ -1202,7 +1262,7 @@
             break;
         case K_ECDHE_ECDSA:
             // get preferable peer signature algorithm for server key exchange
-            if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+            if (protocolVersion.useTLS12PlusSpec()) {
                 preferableSignatureAlgorithm =
                     SignatureAndHashAlgorithm.getPreferableAlgorithm(
                                             supportedSignAlgs, "ECDSA");
@@ -1233,13 +1293,6 @@
             }
             setupStaticECDHKeys();
             break;
-        case K_KRB5:
-        case K_KRB5_EXPORT:
-            // need Kerberos Key
-            if (!setupKerberosKeys()) {
-                return false;
-            }
-            break;
         case K_DH_ANON:
             // no certs needed for anonymous
             setupEphemeralDHKeys(suite.exportable, null);
@@ -1251,13 +1304,31 @@
             }
             break;
         default:
-            // internal error, unknown key exchange
-            throw new RuntimeException("Unrecognized cipherSuite: " + suite);
+            ClientKeyExchangeService p =
+                    ClientKeyExchangeService.find(keyExchange.name);
+            if (p == null) {
+                // internal error, unknown key exchange
+                throw new RuntimeException("Unrecognized cipherSuite: " + suite);
+            }
+            // need service creds
+            if (serviceCreds == null) {
+                AccessControlContext acc = getAccSE();
+                serviceCreds = p.getServiceCreds(acc);
+                if (serviceCreds != null) {
+                    if (debug != null && Debug.isOn("handshake")) {
+                        System.out.println("Using serviceCreds");
+                    }
+                }
+                if (serviceCreds == null) {
+                    return false;
+                }
+            }
+            break;
         }
         setCipherSuite(suite);
 
         // set the peer implicit supported signature algorithms
-        if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+        if (protocolVersion.useTLS12PlusSpec()) {
             if (peerSupportedSignAlgs == null) {
                 setPeerSupportedSignAlgs(supportedSignAlgs);
                 // we had alreay update the session
@@ -1442,73 +1513,10 @@
         return true;
     }
 
-    /**
-     * Retrieve the Kerberos key for the specified server principal
-     * from the JAAS configuration file.
-     *
-     * @return true if successful, false if not available or invalid
+    /*
+     * Returns premaster secret for external key exchange services.
      */
-    private boolean setupKerberosKeys() {
-        if (serviceCreds != null) {
-            return true;
-        }
-        try {
-            final AccessControlContext acc = getAccSE();
-            serviceCreds = AccessController.doPrivileged(
-                // Eliminate dependency on KerberosKey
-                new PrivilegedExceptionAction<Object>() {
-                @Override
-                public Object run() throws Exception {
-                    // get kerberos key for the default principal
-                    return Krb5Helper.getServiceCreds(acc);
-                        }});
-
-            // check permission to access and use the secret key of the
-            // Kerberized "host" service
-            if (serviceCreds != null) {
-                if (debug != null && Debug.isOn("handshake")) {
-                    System.out.println("Using Kerberos creds");
-                }
-                String serverPrincipal =
-                        Krb5Helper.getServerPrincipalName(serviceCreds);
-                if (serverPrincipal != null) {
-                    // When service is bound, we check ASAP. Otherwise,
-                    // will check after client request is received
-                    // in Kerberos ClientKeyExchange
-                    SecurityManager sm = System.getSecurityManager();
-                    try {
-                        if (sm != null) {
-                            // Eliminate dependency on ServicePermission
-                            sm.checkPermission(Krb5Helper.getServicePermission(
-                                    serverPrincipal, "accept"), acc);
-                        }
-                    } catch (SecurityException se) {
-                        serviceCreds = null;
-                        // Do not destroy keys. Will affect Subject
-                        if (debug != null && Debug.isOn("handshake")) {
-                            System.out.println("Permission to access Kerberos"
-                                    + " secret key denied");
-                        }
-                        return false;
-                    }
-                }
-            }
-            return serviceCreds != null;
-        } catch (PrivilegedActionException e) {
-            // Likely exception here is LoginExceptin
-            if (debug != null && Debug.isOn("handshake")) {
-                System.out.println("Attempt to obtain Kerberos key failed: "
-                                + e.toString());
-            }
-            return false;
-        }
-    }
-
-    /*
-     * For Kerberos ciphers, the premaster secret is encrypted using
-     * the session key. See RFC 2712.
-     */
-    private SecretKey clientKeyExchange(KerberosClientKeyExchange mesg)
+    private SecretKey clientKeyExchange(ClientKeyExchange mesg)
         throws IOException {
 
         if (debug != null && Debug.isOn("handshake")) {
@@ -1519,8 +1527,7 @@
         session.setPeerPrincipal(mesg.getPeerPrincipal());
         session.setLocalPrincipal(mesg.getLocalPrincipal());
 
-        byte[] b = mesg.getUnencryptedPreMasterSecret();
-        return new SecretKeySpec(b, "TlsPremasterSecret");
+        return mesg.clientKeyExchange();
     }
 
     /*
@@ -1571,7 +1578,7 @@
             mesg.print(System.out);
         }
 
-        if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+        if (protocolVersion.useTLS12PlusSpec()) {
             SignatureAndHashAlgorithm signAlg =
                 mesg.getPreferableSignatureAlgorithm();
             if (signAlg == null) {
@@ -1623,9 +1630,9 @@
          * Verify if client did send the certificate when client
          * authentication was required, otherwise server should not proceed
          */
-        if (doClientAuth == SSLEngineImpl.clauth_required) {
+        if (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUIRED) {
            // get X500Principal of the end-entity certificate for X509-based
-           // ciphersuites, or Kerberos principal for Kerberos ciphersuites
+           // ciphersuites, or Kerberos principal for Kerberos ciphersuites, etc
            session.getPeerPrincipal();
         }
 
@@ -1664,8 +1671,9 @@
          * the change_cipher_spec and Finished message.
          */
         if (!resumingSession) {
-            input.digestNow();
             sendChangeCipherAndFinish(true);
+        } else {
+            handshakeFinished = true;
         }
 
         /*
@@ -1695,7 +1703,8 @@
     private void sendChangeCipherAndFinish(boolean finishedTag)
             throws IOException {
 
-        output.flush();
+        // Reload if this message has been reserved.
+        handshakeHash.reload();
 
         Finished mesg = new Finished(protocolVersion, handshakeHash,
             Finished.SERVER, session.getMasterSecret(), cipherSuite);
@@ -1713,16 +1722,6 @@
         if (secureRenegotiation) {
             serverVerifyData = mesg.getVerifyData();
         }
-
-        /*
-         * Update state machine so client MUST send 'finished' next
-         * The update should only take place if it is not in the fast
-         * handshake mode since the server has to wait for a finished
-         * message from the client.
-         */
-        if (finishedTag) {
-            state = HandshakeMessage.ht_finished;
-        }
     }
 
 
@@ -1757,7 +1756,7 @@
          * session will get an SSLPeerUnverifiedException.
          */
         if ((description == Alerts.alert_no_certificate) &&
-                (doClientAuth == SSLEngineImpl.clauth_requested)) {
+                (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUESTED)) {
             return;
         }
 
@@ -1798,7 +1797,7 @@
              * If the client authentication is only *REQUESTED* (e.g.
              * not *REQUIRED*, this is an acceptable condition.)
              */
-            if (doClientAuth == SSLEngineImpl.clauth_requested) {
+            if (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUESTED) {
                 return;
             } else {
                 fatalSE(Alerts.alert_bad_certificate,
--- a/jdk/src/java.base/share/classes/sun/security/ssl/SunJSSE.java	Mon Jun 01 10:15:21 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/SunJSSE.java	Tue Jun 02 09:15:47 2015 -0700
@@ -61,7 +61,7 @@
 
     private static String info = "Sun JSSE provider" +
         "(PKCS12, SunX509/PKIX key/trust factories, " +
-        "SSLv3/TLSv1/TLSv1.1/TLSv1.2)";
+        "SSLv3/TLSv1/TLSv1.1/TLSv1.2/DTLSv1.0/DTLSv1.2)";
 
     private static String fipsInfo =
         "Sun JSSE provider (FIPS mode, crypto provider ";
@@ -220,6 +220,13 @@
             put("Alg.Alias.SSLContext.SSLv3", "TLSv1");
         }
 
+        put("SSLContext.DTLSv1.0",
+            "sun.security.ssl.SSLContextImpl$DTLS10Context");
+        put("SSLContext.DTLSv1.2",
+            "sun.security.ssl.SSLContextImpl$DTLS12Context");
+        put("SSLContext.DTLS",
+            "sun.security.ssl.SSLContextImpl$DTLSContext");
+
         put("SSLContext.Default",
             "sun.security.ssl.SSLContextImpl$DefaultSSLContext");
 
--- a/jdk/src/java.base/share/classes/sun/security/ssl/X509KeyManagerImpl.java	Mon Jun 01 10:15:21 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/X509KeyManagerImpl.java	Tue Jun 02 09:15:47 2015 -0700
@@ -190,7 +190,7 @@
             if (session != null) {
                 ProtocolVersion protocolVersion =
                     ProtocolVersion.valueOf(session.getProtocol());
-                if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+                if (protocolVersion.useTLS12PlusSpec()) {
                     String[] peerSupportedSignAlgs = null;
 
                     if (session instanceof ExtendedSSLSession) {
@@ -218,7 +218,7 @@
             if (session != null) {
                 ProtocolVersion protocolVersion =
                     ProtocolVersion.valueOf(session.getProtocol());
-                if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+                if (protocolVersion.useTLS12PlusSpec()) {
                     String[] peerSupportedSignAlgs = null;
 
                     if (session instanceof ExtendedSSLSession) {
--- a/jdk/src/java.base/share/classes/sun/security/ssl/X509TrustManagerImpl.java	Mon Jun 01 10:15:21 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/X509TrustManagerImpl.java	Tue Jun 02 09:15:47 2015 -0700
@@ -204,7 +204,7 @@
             // create the algorithm constraints
             ProtocolVersion protocolVersion =
                 ProtocolVersion.valueOf(session.getProtocol());
-            if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+            if (protocolVersion.useTLS12PlusSpec()) {
                 if (session instanceof ExtendedSSLSession) {
                     ExtendedSSLSession extSession =
                                     (ExtendedSSLSession)session;
@@ -256,7 +256,7 @@
             // create the algorithm constraints
             ProtocolVersion protocolVersion =
                 ProtocolVersion.valueOf(session.getProtocol());
-            if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+            if (protocolVersion.useTLS12PlusSpec()) {
                 if (session instanceof ExtendedSSLSession) {
                     ExtendedSSLSession extSession =
                                     (ExtendedSSLSession)session;
--- a/jdk/src/java.base/share/classes/sun/security/util/HostnameChecker.java	Mon Jun 01 10:15:21 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/util/HostnameChecker.java	Tue Jun 02 09:15:47 2015 -0700
@@ -35,7 +35,7 @@
 
 import javax.security.auth.x500.X500Principal;
 
-import sun.security.ssl.Krb5Helper;
+import sun.security.ssl.ClientKeyExchangeService;
 import sun.security.x509.X500Name;
 
 import sun.net.util.IPAddressUtil;
@@ -108,7 +108,12 @@
      * Return the Server name from Kerberos principal.
      */
     public static String getServerName(Principal principal) {
-        return Krb5Helper.getPrincipalHostName(principal);
+        ClientKeyExchangeService p =
+                ClientKeyExchangeService.find("KRB5");
+        if (p == null) {
+            throw new AssertionError("Kerberos should have been available");
+        }
+        return p.getServiceHostName(principal);
     }
 
     /**
--- a/jdk/src/java.base/share/conf/security/java.security	Mon Jun 01 10:15:21 2015 -0700
+++ b/jdk/src/java.base/share/conf/security/java.security	Tue Jun 02 09:15:47 2015 -0700
@@ -511,11 +511,11 @@
 jdk.certpath.disabledAlgorithms=MD2, MD5, RSA keySize < 1024
 
 # Algorithm restrictions for Secure Socket Layer/Transport Layer Security
-# (SSL/TLS) processing
+# (SSL/TLS/DTLS) processing
 #
 # In some environments, certain algorithms or key lengths may be undesirable
-# when using SSL/TLS.  This section describes the mechanism for disabling
-# algorithms during SSL/TLS security parameters negotiation, including
+# when using SSL/TLS/DTLS.  This section describes the mechanism for disabling
+# algorithms during SSL/TLS/DTLS security parameters negotiation, including
 # protocol version negotiation, cipher suites selection, peer authentication
 # and key exchange mechanisms.
 #
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.security.jgss/share/classes/META-INF/services/sun.security.ssl.ClientKeyExchangeService	Tue Jun 02 09:15:47 2015 -0700
@@ -0,0 +1,2 @@
+sun.security.krb5.internal.ssl.Krb5KeyExchangeService
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.security.jgss/share/classes/sun/security/krb5/internal/ssl/KerberosPreMasterSecret.java	Tue Jun 02 09:15:47 2015 -0700
@@ -0,0 +1,269 @@
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.krb5.internal.ssl;
+
+import java.io.*;
+import java.security.*;
+import java.util.Arrays;
+
+import javax.net.ssl.*;
+
+import sun.security.krb5.EncryptionKey;
+import sun.security.krb5.EncryptedData;
+import sun.security.krb5.KrbException;
+import sun.security.krb5.internal.crypto.KeyUsage;
+
+import sun.security.ssl.Debug;
+import sun.security.ssl.HandshakeInStream;
+import sun.security.ssl.HandshakeMessage;
+import sun.security.ssl.ProtocolVersion;
+
+/**
+ * This is the Kerberos premaster secret in the Kerberos client key
+ * exchange message (CLIENT --> SERVER); it holds the
+ * Kerberos-encrypted pre-master secret. The secret is encrypted using the
+ * Kerberos session key.  The padding and size of the resulting message
+ * depends on the session key type, but the pre-master secret is
+ * always exactly 48 bytes.
+ *
+ */
+final class KerberosPreMasterSecret {
+
+    private ProtocolVersion protocolVersion; // preMaster [0,1]
+    private byte preMaster[];           // 48 bytes
+    private byte encrypted[];
+
+    /**
+     * Constructor used by client to generate premaster secret.
+     *
+     * Client randomly creates a pre-master secret and encrypts it
+     * using the Kerberos session key; only the server can decrypt
+     * it, using the session key available in the service ticket.
+     *
+     * @param protocolVersion used to set preMaster[0,1]
+     * @param generator random number generator for generating premaster secret
+     * @param sessionKey Kerberos session key for encrypting premaster secret
+     */
+    KerberosPreMasterSecret(ProtocolVersion protocolVersion,
+            SecureRandom generator, EncryptionKey sessionKey) throws IOException {
+
+        if (sessionKey.getEType() ==
+                EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD) {
+            throw new IOException(
+                    "session keys with des3-cbc-hmac-sha1-kd encryption type " +
+                            "are not supported for TLS Kerberos cipher suites");
+        }
+
+        this.protocolVersion = protocolVersion;
+        preMaster = generatePreMaster(generator, protocolVersion);
+
+        // Encrypt premaster secret
+        try {
+            EncryptedData eData = new EncryptedData(sessionKey, preMaster,
+                    KeyUsage.KU_UNKNOWN);
+            encrypted = eData.getBytes();  // not ASN.1 encoded.
+
+        } catch (KrbException e) {
+            throw (SSLKeyException)new SSLKeyException
+                    ("Kerberos premaster secret error").initCause(e);
+        }
+    }
+
+    /*
+     * Constructor used by server to decrypt encrypted premaster secret.
+     * The protocol version in preMaster[0,1] must match either currentVersion
+     * or clientVersion, otherwise, the premaster secret is set to
+     * a random one to foil possible attack.
+     *
+     * @param currentVersion version of protocol being used
+     * @param clientVersion version requested by client
+     * @param generator random number generator used to generate
+     *        bogus premaster secret if premaster secret verification fails
+     * @param input input stream from which to read the encrypted
+     *        premaster secret
+     * @param sessionKey Kerberos session key to be used for decryption
+     */
+    KerberosPreMasterSecret(ProtocolVersion currentVersion,
+            ProtocolVersion clientVersion,
+            SecureRandom generator, byte[] encrypted,
+            EncryptionKey sessionKey) throws IOException {
+
+        if (HandshakeMessage.debug != null && Debug.isOn("handshake")) {
+            if (encrypted != null) {
+                Debug.println(System.out,
+                        "encrypted premaster secret", encrypted);
+            }
+        }
+
+        if (sessionKey.getEType() ==
+                EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD) {
+            throw new IOException(
+                    "session keys with des3-cbc-hmac-sha1-kd encryption type " +
+                            "are not supported for TLS Kerberos cipher suites");
+        }
+
+        // Decrypt premaster secret
+        try {
+            EncryptedData data = new EncryptedData(sessionKey.getEType(),
+                    null /* optional kvno */, encrypted);
+
+            byte[] temp = data.decrypt(sessionKey, KeyUsage.KU_UNKNOWN);
+            if (HandshakeMessage.debug != null && Debug.isOn("handshake")) {
+                if (encrypted != null) {
+                    Debug.println(System.out,
+                            "decrypted premaster secret", temp);
+                }
+            }
+
+            // Remove padding bytes after decryption. Only DES and DES3 have
+            // paddings and we don't support DES3 in TLS (see above)
+
+            if (temp.length == 52 &&
+                    data.getEType() == EncryptedData.ETYPE_DES_CBC_CRC) {
+                // For des-cbc-crc, 4 paddings. Value can be 0x04 or 0x00.
+                if (paddingByteIs(temp, 52, (byte)4) ||
+                        paddingByteIs(temp, 52, (byte)0)) {
+                    temp = Arrays.copyOf(temp, 48);
+                }
+            } else if (temp.length == 56 &&
+                    data.getEType() == EncryptedData.ETYPE_DES_CBC_MD5) {
+                // For des-cbc-md5, 8 paddings with 0x08, or no padding
+                if (paddingByteIs(temp, 56, (byte)8)) {
+                    temp = Arrays.copyOf(temp, 48);
+                }
+            }
+
+            preMaster = temp;
+
+            protocolVersion = ProtocolVersion.valueOf(preMaster[0],
+                    preMaster[1]);
+            if (HandshakeMessage.debug != null && Debug.isOn("handshake")) {
+                System.out.println("Kerberos PreMasterSecret version: "
+                        + protocolVersion);
+            }
+        } catch (Exception e) {
+            // catch exception & process below
+            preMaster = null;
+            protocolVersion = currentVersion;
+        }
+
+        // check if the premaster secret version is ok
+        // the specification says that it must be the maximum version supported
+        // by the client from its ClientHello message. However, many
+        // old implementations send the negotiated version, so accept both
+        // for SSL v3.0 and TLS v1.0.
+        // NOTE that we may be comparing two unsupported version numbers in
+        // the second case, which is why we cannot use object references
+        // equality in this special case
+        boolean versionMismatch = (protocolVersion.v != clientVersion.v);
+
+        /*
+         * we never checked the client_version in server side
+         * for TLS v1.0 and SSL v3.0. For compatibility, we
+         * maintain this behavior.
+         */
+        if (versionMismatch && (clientVersion.v <= 0x0301)) {
+            versionMismatch = (protocolVersion.v != currentVersion.v);
+        }
+
+        /*
+         * Bogus decrypted ClientKeyExchange? If so, conjure a
+         * a random preMaster secret that will fail later during
+         * Finished message processing. This is a countermeasure against
+         * the "interactive RSA PKCS#1 encryption envelop attack" reported
+         * in June 1998. Preserving the executation path will
+         * mitigate timing attacks and force consistent error handling
+         * that will prevent an attacking client from differentiating
+         * different kinds of decrypted ClientKeyExchange bogosities.
+         */
+        if ((preMaster == null) || (preMaster.length != 48)
+                || versionMismatch) {
+            if (HandshakeMessage.debug != null && Debug.isOn("handshake")) {
+                System.out.println("Kerberos PreMasterSecret error, "
+                        + "generating random secret");
+                if (preMaster != null) {
+                    Debug.println(System.out, "Invalid secret", preMaster);
+                }
+            }
+
+            /*
+             * Randomize the preMaster secret with the
+             * ClientHello.client_version, as will produce invalid master
+             * secret to prevent the attacks.
+             */
+            preMaster = generatePreMaster(generator, clientVersion);
+            protocolVersion = clientVersion;
+        }
+    }
+
+    /**
+     * Checks if all paddings of data are b
+     * @param data the block with padding
+     * @param len length of data, >= 48
+     * @param b expected padding byte
+     */
+    private static boolean paddingByteIs(byte[] data, int len, byte b) {
+        for (int i=48; i<len; i++) {
+            if (data[i] != b) return false;
+        }
+        return true;
+    }
+
+    /*
+     * Used by server to generate premaster secret in case of
+     * problem decoding ticket.
+     *
+     * @param protocolVersion used for preMaster[0,1]
+     * @param generator random number generator to use for generating secret.
+     */
+    KerberosPreMasterSecret(ProtocolVersion protocolVersion,
+            SecureRandom generator) {
+
+        this.protocolVersion = protocolVersion;
+        preMaster = generatePreMaster(generator, protocolVersion);
+    }
+
+    private static byte[] generatePreMaster(SecureRandom rand,
+            ProtocolVersion ver) {
+
+        byte[] pm = new byte[48];
+        rand.nextBytes(pm);
+        pm[0] = ver.major;
+        pm[1] = ver.minor;
+
+        return pm;
+    }
+
+    // Clone not needed; internal use only
+    byte[] getUnencrypted() {
+        return preMaster;
+    }
+
+    // Clone not needed; internal use only
+    byte[] getEncrypted() {
+        return encrypted;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.security.jgss/share/classes/sun/security/krb5/internal/ssl/Krb5KeyExchangeService.java	Tue Jun 02 09:15:47 2015 -0700
@@ -0,0 +1,557 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.krb5.internal.ssl;
+
+import sun.security.ssl.ClientKeyExchange;
+import sun.security.ssl.Debug;
+import sun.security.ssl.ClientKeyExchangeService;
+import sun.security.ssl.HandshakeOutStream;
+
+import sun.security.jgss.GSSCaller;
+import sun.security.jgss.krb5.Krb5Util;
+import sun.security.jgss.krb5.ServiceCreds;
+import sun.security.krb5.EncryptedData;
+import sun.security.krb5.EncryptionKey;
+import sun.security.krb5.KrbException;
+import sun.security.krb5.PrincipalName;
+import sun.security.krb5.internal.EncTicketPart;
+import sun.security.krb5.internal.Ticket;
+import sun.security.krb5.internal.crypto.KeyUsage;
+import sun.security.ssl.ProtocolVersion;
+
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+import javax.security.auth.Subject;
+import javax.security.auth.kerberos.KerberosKey;
+import javax.security.auth.kerberos.KerberosPrincipal;
+import javax.security.auth.kerberos.KerberosTicket;
+import javax.security.auth.kerberos.KeyTab;
+import javax.security.auth.kerberos.ServicePermission;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.net.InetAddress;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.Principal;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.security.SecureRandom;
+import java.util.Set;
+
+/**
+ * The provider for TLS_KRB_ cipher suites.
+ *
+ * @since 1.9
+ */
+public class Krb5KeyExchangeService implements ClientKeyExchangeService {
+
+    public static final Debug debug = Debug.getInstance("ssl");
+
+    @Override
+    public String[] supported() {
+        return new String[] { "KRB5", "KRB5_EXPORT" };
+    }
+
+    @Override
+    public Object getServiceCreds(AccessControlContext acc) {
+        try {
+            ServiceCreds serviceCreds = AccessController.doPrivileged(
+                    (PrivilegedExceptionAction<ServiceCreds>)
+                            () -> Krb5Util.getServiceCreds(
+                                    GSSCaller.CALLER_SSL_SERVER, null, acc));
+            if (debug != null && Debug.isOn("handshake")) {
+                System.out.println("Using Kerberos creds");
+            }
+            String serverPrincipal = serviceCreds.getName();
+            if (serverPrincipal != null) {
+                // When service is bound, we check ASAP. Otherwise,
+                // will check after client request is received
+                // in in Kerberos ClientKeyExchange
+                SecurityManager sm = System.getSecurityManager();
+                try {
+                    if (sm != null) {
+                        // Eliminate dependency on ServicePermission
+                        sm.checkPermission(new ServicePermission(
+                                serverPrincipal, "accept"), acc);
+                    }
+                } catch (SecurityException se) {
+                    if (debug != null && Debug.isOn("handshake")) {
+                        System.out.println("Permission to access Kerberos"
+                                + " secret key denied");
+                    }
+                    return null;
+                }
+            }
+            return serviceCreds;
+        } catch (PrivilegedActionException e) {
+            // Likely exception here is LoginException
+            if (debug != null && Debug.isOn("handshake")) {
+                System.out.println("Attempt to obtain Kerberos key failed: "
+                        + e.toString());
+            }
+            return null;
+        }
+    }
+
+    @Override
+    public String getServiceHostName(Principal principal) {
+        if (principal == null) {
+            return null;
+        }
+        String hostName = null;
+        try {
+            PrincipalName princName =
+                    new PrincipalName(principal.getName(),
+                            PrincipalName.KRB_NT_SRV_HST);
+            String[] nameParts = princName.getNameStrings();
+            if (nameParts.length >= 2) {
+                hostName = nameParts[1];
+            }
+        } catch (Exception e) {
+            // ignore
+        }
+        return hostName;
+    }
+
+
+    @Override
+    public boolean isRelated(boolean isClient,
+            AccessControlContext acc, Principal p) {
+
+        if (p == null) return false;
+        try {
+            Subject subject = AccessController.doPrivileged(
+                    (PrivilegedExceptionAction<Subject>)
+                            () -> Krb5Util.getSubject(
+                                    isClient ? GSSCaller.CALLER_SSL_CLIENT
+                                            : GSSCaller.CALLER_SSL_SERVER,
+                                    acc));
+            if (subject == null) {
+                if (debug != null && Debug.isOn("session")) {
+                    System.out.println("Kerberos credentials are" +
+                            " not present in the current Subject;" +
+                            " check if " +
+                            " javax.security.auth.useSubjectAsCreds" +
+                            " system property has been set to false");
+                }
+                return false;
+            }
+            Set<Principal> principals =
+                    subject.getPrincipals(Principal.class);
+            if (principals.contains(p)) {
+                // bound to this principal
+                return true;
+            } else {
+                if (isClient) {
+                    return false;
+                } else {
+                    for (KeyTab pc : subject.getPrivateCredentials(KeyTab.class)) {
+                        if (!pc.isBound()) {
+                            return true;
+                        }
+                    }
+                    return false;
+                }
+            }
+        } catch (PrivilegedActionException pae) {
+            if (debug != null && Debug.isOn("session")) {
+                System.out.println("Attempt to obtain" +
+                        " subject failed! " + pae);
+            }
+            return false;
+        }
+
+    }
+
+    public ClientKeyExchange createClientExchange(
+            String serverName, AccessControlContext acc,
+            ProtocolVersion protocolVerson, SecureRandom rand) throws IOException {
+        return new ExchangerImpl(serverName, acc, protocolVerson, rand);
+    }
+
+    public ClientKeyExchange createServerExchange(
+            ProtocolVersion protocolVersion, ProtocolVersion clientVersion,
+            SecureRandom rand, byte[] encodedTicket, byte[] encrypted,
+            AccessControlContext acc, Object serviceCreds) throws IOException {
+        return new ExchangerImpl(protocolVersion, clientVersion, rand,
+                encodedTicket, encrypted, acc, serviceCreds);
+    }
+
+    static class ExchangerImpl extends ClientKeyExchange {
+
+        final private KerberosPreMasterSecret preMaster;
+        final private byte[] encodedTicket;
+        final private KerberosPrincipal peerPrincipal;
+        final private KerberosPrincipal localPrincipal;
+
+        @Override
+        public int messageLength() {
+            return encodedTicket.length + preMaster.getEncrypted().length + 6;
+        }
+
+        @Override
+        public void send(HandshakeOutStream s) throws IOException {
+            s.putBytes16(encodedTicket);
+            s.putBytes16(null);
+            s.putBytes16(preMaster.getEncrypted());
+        }
+
+        @Override
+        public void print(PrintStream s) throws IOException {
+            s.println("*** ClientKeyExchange, Kerberos");
+
+            if (debug != null && Debug.isOn("verbose")) {
+                Debug.println(s, "Kerberos service ticket", encodedTicket);
+                Debug.println(s, "Random Secret", preMaster.getUnencrypted());
+                Debug.println(s, "Encrypted random Secret", preMaster.getEncrypted());
+            }
+        }
+
+        ExchangerImpl(String serverName, AccessControlContext acc,
+                ProtocolVersion protocolVersion, SecureRandom rand) throws IOException {
+
+            // Get service ticket
+            KerberosTicket ticket = getServiceTicket(serverName, acc);
+            encodedTicket = ticket.getEncoded();
+
+            // Record the Kerberos principals
+            peerPrincipal = ticket.getServer();
+            localPrincipal = ticket.getClient();
+
+            // Optional authenticator, encrypted using session key,
+            // currently ignored
+
+            // Generate premaster secret and encrypt it using session key
+            EncryptionKey sessionKey = new EncryptionKey(
+                    ticket.getSessionKeyType(),
+                    ticket.getSessionKey().getEncoded());
+
+            preMaster = new KerberosPreMasterSecret(protocolVersion,
+                    rand, sessionKey);
+        }
+
+        ExchangerImpl(
+                ProtocolVersion protocolVersion, ProtocolVersion clientVersion, SecureRandom rand,
+                byte[] encodedTicket, byte[] encrypted,
+                AccessControlContext acc, Object serviceCreds) throws IOException {
+
+            // Read ticket
+            this.encodedTicket = encodedTicket;
+
+            if (debug != null && Debug.isOn("verbose")) {
+                Debug.println(System.out,
+                        "encoded Kerberos service ticket", encodedTicket);
+            }
+
+            EncryptionKey sessionKey = null;
+            KerberosPrincipal tmpPeer = null;
+            KerberosPrincipal tmpLocal = null;
+
+            try {
+                Ticket t = new Ticket(encodedTicket);
+
+                EncryptedData encPart = t.encPart;
+                PrincipalName ticketSname = t.sname;
+
+                final ServiceCreds creds = (ServiceCreds)serviceCreds;
+                final KerberosPrincipal princ =
+                        new KerberosPrincipal(ticketSname.toString());
+
+                // For bound service, permission already checked at setup
+                if (creds.getName() == null) {
+                    SecurityManager sm = System.getSecurityManager();
+                    try {
+                        if (sm != null) {
+                            // Eliminate dependency on ServicePermission
+                            sm.checkPermission(new ServicePermission(
+                                    ticketSname.toString(), "accept"), acc);
+                        }
+                    } catch (SecurityException se) {
+                        serviceCreds = null;
+                        // Do not destroy keys. Will affect Subject
+                        if (debug != null && Debug.isOn("handshake")) {
+                            System.out.println("Permission to access Kerberos"
+                                    + " secret key denied");
+                            se.printStackTrace(System.out);
+                        }
+                        throw new IOException("Kerberos service not allowedy");
+                    }
+                }
+                KerberosKey[] serverKeys = AccessController.doPrivileged(
+                        new PrivilegedAction<KerberosKey[]>() {
+                            @Override
+                            public KerberosKey[] run() {
+                                return creds.getKKeys(princ);
+                            }
+                        });
+                if (serverKeys.length == 0) {
+                    throw new IOException("Found no key for " + princ +
+                            (creds.getName() == null ? "" :
+                                    (", this keytab is for " + creds.getName() + " only")));
+                }
+
+                /*
+                 * permission to access and use the secret key of the Kerberized
+                 * "host" service is done in ServerHandshaker.getKerberosKeys()
+                 * to ensure server has the permission to use the secret key
+                 * before promising the client
+                 */
+
+                // See if we have the right key to decrypt the ticket to get
+                // the session key.
+                int encPartKeyType = encPart.getEType();
+                Integer encPartKeyVersion = encPart.getKeyVersionNumber();
+                KerberosKey dkey = null;
+                try {
+                    dkey = findKey(encPartKeyType, encPartKeyVersion, serverKeys);
+                } catch (KrbException ke) { // a kvno mismatch
+                    throw new IOException(
+                            "Cannot find key matching version number", ke);
+                }
+                if (dkey == null) {
+                    // %%% Should print string repr of etype
+                    throw new IOException("Cannot find key of appropriate type" +
+                            " to decrypt ticket - need etype " + encPartKeyType);
+                }
+
+                EncryptionKey secretKey = new EncryptionKey(
+                        encPartKeyType,
+                        dkey.getEncoded());
+
+                // Decrypt encPart using server's secret key
+                byte[] bytes = encPart.decrypt(secretKey, KeyUsage.KU_TICKET);
+
+                // Reset data stream after decryption, remove redundant bytes
+                byte[] temp = encPart.reset(bytes);
+                EncTicketPart encTicketPart = new EncTicketPart(temp);
+
+                // Record the Kerberos Principals
+                tmpPeer = new KerberosPrincipal(encTicketPart.cname.getName());
+                tmpLocal = new KerberosPrincipal(ticketSname.getName());
+
+                sessionKey = encTicketPart.key;
+
+                if (debug != null && Debug.isOn("handshake")) {
+                    System.out.println("server principal: " + ticketSname);
+                    System.out.println("cname: " + encTicketPart.cname.toString());
+                }
+            } catch (IOException e) {
+                throw e;
+            } catch (Exception e) {
+                if (debug != null && Debug.isOn("handshake")) {
+                    System.out.println("KerberosWrapper error getting session key,"
+                            + " generating random secret (" + e.getMessage() + ")");
+                }
+                sessionKey = null;
+            }
+
+            //input.getBytes16();   // XXX Read and ignore authenticator
+
+            if (sessionKey != null) {
+                preMaster = new KerberosPreMasterSecret(protocolVersion,
+                        clientVersion, rand, encrypted, sessionKey);
+            } else {
+                // Generate bogus premaster secret
+                preMaster = new KerberosPreMasterSecret(clientVersion, rand);
+            }
+
+            peerPrincipal = tmpPeer;
+            localPrincipal = tmpLocal;
+        }
+
+        // Similar to sun.security.jgss.krb5.Krb5InitCredenetial/Krb5Context
+        private static KerberosTicket getServiceTicket(String serverName,
+                final AccessControlContext acc) throws IOException {
+
+            if ("localhost".equals(serverName) ||
+                    "localhost.localdomain".equals(serverName)) {
+
+                if (debug != null && Debug.isOn("handshake")) {
+                    System.out.println("Get the local hostname");
+                }
+                String localHost = java.security.AccessController.doPrivileged(
+                        new java.security.PrivilegedAction<String>() {
+                            public String run() {
+                                try {
+                                    return InetAddress.getLocalHost().getHostName();
+                                } catch (java.net.UnknownHostException e) {
+                                    if (debug != null && Debug.isOn("handshake")) {
+                                        System.out.println("Warning,"
+                                                + " cannot get the local hostname: "
+                                                + e.getMessage());
+                                    }
+                                    return null;
+                                }
+                            }
+                        });
+                if (localHost != null) {
+                    serverName = localHost;
+                }
+            }
+
+            // Resolve serverName (possibly in IP addr form) to Kerberos principal
+            // name for service with hostname
+            String serviceName = "host/" + serverName;
+            PrincipalName principal;
+            try {
+                principal = new PrincipalName(serviceName,
+                        PrincipalName.KRB_NT_SRV_HST);
+            } catch (SecurityException se) {
+                throw se;
+            } catch (Exception e) {
+                IOException ioe = new IOException("Invalid service principal" +
+                        " name: " + serviceName);
+                ioe.initCause(e);
+                throw ioe;
+            }
+            String realm = principal.getRealmAsString();
+
+            final String serverPrincipal = principal.toString();
+            final String tgsPrincipal = "krbtgt/" + realm + "@" + realm;
+            final String clientPrincipal = null;  // use default
+
+
+            // check permission to obtain a service ticket to initiate a
+            // context with the "host" service
+            SecurityManager sm = System.getSecurityManager();
+            if (sm != null) {
+                sm.checkPermission(new ServicePermission(serverPrincipal,
+                        "initiate"), acc);
+            }
+
+            try {
+                KerberosTicket ticket = AccessController.doPrivileged(
+                        new PrivilegedExceptionAction<KerberosTicket>() {
+                            public KerberosTicket run() throws Exception {
+                                return Krb5Util.getTicketFromSubjectAndTgs(
+                                        GSSCaller.CALLER_SSL_CLIENT,
+                                        clientPrincipal, serverPrincipal,
+                                        tgsPrincipal, acc);
+                            }});
+
+                if (ticket == null) {
+                    throw new IOException("Failed to find any kerberos service" +
+                            " ticket for " + serverPrincipal);
+                }
+                return ticket;
+            } catch (PrivilegedActionException e) {
+                IOException ioe = new IOException(
+                        "Attempt to obtain kerberos service ticket for " +
+                                serverPrincipal + " failed!");
+                ioe.initCause(e);
+                throw ioe;
+            }
+        }
+
+        @Override
+        public SecretKey clientKeyExchange() {
+            byte[] secretBytes = preMaster.getUnencrypted();
+            return new SecretKeySpec(secretBytes, "TlsPremasterSecret");
+        }
+
+        @Override
+        public Principal getPeerPrincipal() {
+            return peerPrincipal;
+        }
+
+        @Override
+        public Principal getLocalPrincipal() {
+            return localPrincipal;
+        }
+
+        /**
+         * Determines if a kvno matches another kvno. Used in the method
+         * findKey(etype, version, keys). Always returns true if either input
+         * is null or zero, in case any side does not have kvno info available.
+         *
+         * Note: zero is included because N/A is not a legal value for kvno
+         * in javax.security.auth.kerberos.KerberosKey. Therefore, the info
+         * that the kvno is N/A might be lost when converting between
+         * EncryptionKey and KerberosKey.
+         */
+        private static boolean versionMatches(Integer v1, int v2) {
+            if (v1 == null || v1 == 0 || v2 == 0) {
+                return true;
+            }
+            return v1.equals(v2);
+        }
+
+        private static KerberosKey findKey(int etype, Integer version,
+                KerberosKey[] keys) throws KrbException {
+            int ktype;
+            boolean etypeFound = false;
+
+            // When no matched kvno is found, returns tke key of the same
+            // etype with the highest kvno
+            int kvno_found = 0;
+            KerberosKey key_found = null;
+
+            for (int i = 0; i < keys.length; i++) {
+                ktype = keys[i].getKeyType();
+                if (etype == ktype) {
+                    int kv = keys[i].getVersionNumber();
+                    etypeFound = true;
+                    if (versionMatches(version, kv)) {
+                        return keys[i];
+                    } else if (kv > kvno_found) {
+                        key_found = keys[i];
+                        kvno_found = kv;
+                    }
+                }
+            }
+            // Key not found.
+            // %%% kludge to allow DES keys to be used for diff etypes
+            if ((etype == EncryptedData.ETYPE_DES_CBC_CRC ||
+                    etype == EncryptedData.ETYPE_DES_CBC_MD5)) {
+                for (int i = 0; i < keys.length; i++) {
+                    ktype = keys[i].getKeyType();
+                    if (ktype == EncryptedData.ETYPE_DES_CBC_CRC ||
+                            ktype == EncryptedData.ETYPE_DES_CBC_MD5) {
+                        int kv = keys[i].getVersionNumber();
+                        etypeFound = true;
+                        if (versionMatches(version, kv)) {
+                            return new KerberosKey(keys[i].getPrincipal(),
+                                    keys[i].getEncoded(),
+                                    etype,
+                                    kv);
+                        } else if (kv > kvno_found) {
+                            key_found = new KerberosKey(keys[i].getPrincipal(),
+                                    keys[i].getEncoded(),
+                                    etype,
+                                    kv);
+                            kvno_found = kv;
+                        }
+                    }
+                }
+            }
+            if (etypeFound) {
+                return key_found;
+            }
+            return null;
+        }
+    }
+}
--- a/jdk/src/java.security.jgss/share/classes/sun/security/ssl/krb5/KerberosClientKeyExchangeImpl.java	Mon Jun 01 10:15:21 2015 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,463 +0,0 @@
-/*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package sun.security.ssl.krb5;
-
-import java.io.IOException;
-import java.io.PrintStream;
-import java.security.AccessController;
-import java.security.AccessControlContext;
-import java.security.PrivilegedExceptionAction;
-import java.security.PrivilegedActionException;
-import java.security.SecureRandom;
-import java.net.InetAddress;
-import java.security.PrivilegedAction;
-
-import javax.security.auth.kerberos.KerberosTicket;
-import javax.security.auth.kerberos.KerberosKey;
-import javax.security.auth.kerberos.KerberosPrincipal;
-import javax.security.auth.kerberos.ServicePermission;
-import sun.security.jgss.GSSCaller;
-
-import sun.security.krb5.EncryptionKey;
-import sun.security.krb5.EncryptedData;
-import sun.security.krb5.PrincipalName;
-import sun.security.krb5.internal.Ticket;
-import sun.security.krb5.internal.EncTicketPart;
-import sun.security.krb5.internal.crypto.KeyUsage;
-
-import sun.security.jgss.krb5.Krb5Util;
-import sun.security.jgss.krb5.ServiceCreds;
-import sun.security.krb5.KrbException;
-import sun.security.krb5.internal.Krb5;
-
-import sun.security.ssl.Debug;
-import sun.security.ssl.HandshakeInStream;
-import sun.security.ssl.HandshakeOutStream;
-import sun.security.ssl.Krb5Helper;
-import sun.security.ssl.ProtocolVersion;
-
-/**
- * This is Kerberos option in the client key exchange message
- * (CLIENT -> SERVER). It holds the Kerberos ticket and the encrypted
- * premaster secret encrypted with the session key sealed in the ticket.
- * From RFC 2712:
- *  struct
- *  {
- *    opaque Ticket;
- *    opaque authenticator;            // optional
- *    opaque EncryptedPreMasterSecret; // encrypted with the session key
- *                                     // which is sealed in the ticket
- *  } KerberosWrapper;
- *
- *
- * Ticket and authenticator are encrypted as per RFC 1510 (in ASN.1)
- * Encrypted pre-master secret has the same structure as it does for RSA
- * except for Kerberos, the encryption key is the session key instead of
- * the RSA public key.
- *
- * XXX authenticator currently ignored
- *
- */
-public final class KerberosClientKeyExchangeImpl
-    extends sun.security.ssl.KerberosClientKeyExchange {
-
-    private KerberosPreMasterSecret preMaster;
-    private byte[] encodedTicket;
-    private KerberosPrincipal peerPrincipal;
-    private KerberosPrincipal localPrincipal;
-
-    public KerberosClientKeyExchangeImpl() {
-    }
-
-    /**
-     * Creates an instance of KerberosClientKeyExchange consisting of the
-     * Kerberos service ticket, authenticator and encrypted premaster secret.
-     * Called by client handshaker.
-     *
-     * @param serverName name of server with which to do handshake;
-     *             this is used to get the Kerberos service ticket
-     * @param protocolVersion Maximum version supported by client (i.e,
-     *          version it requested in client hello)
-     * @param rand random number generator to use for generating pre-master
-     *          secret
-     */
-    @Override
-    public void init(String serverName,
-        AccessControlContext acc, ProtocolVersion protocolVersion,
-        SecureRandom rand) throws IOException {
-
-         // Get service ticket
-         KerberosTicket ticket = getServiceTicket(serverName, acc);
-         encodedTicket = ticket.getEncoded();
-
-         // Record the Kerberos principals
-         peerPrincipal = ticket.getServer();
-         localPrincipal = ticket.getClient();
-
-         // Optional authenticator, encrypted using session key,
-         // currently ignored
-
-         // Generate premaster secret and encrypt it using session key
-         EncryptionKey sessionKey = new EncryptionKey(
-                                        ticket.getSessionKeyType(),
-                                        ticket.getSessionKey().getEncoded());
-
-         preMaster = new KerberosPreMasterSecret(protocolVersion,
-             rand, sessionKey);
-    }
-
-    /**
-     * Creates an instance of KerberosClientKeyExchange from its ASN.1 encoding.
-     * Used by ServerHandshaker to verify and obtain premaster secret.
-     *
-     * @param protocolVersion current protocol version
-     * @param clientVersion version requested by client in its ClientHello;
-     *          used by premaster secret version check
-     * @param rand random number generator used for generating random
-     *          premaster secret if ticket and/or premaster verification fails
-     * @param input inputstream from which to get ASN.1-encoded KerberosWrapper
-     * @param acc the AccessControlContext of the handshaker
-     * @param serviceCreds server's creds
-     */
-    @Override
-    public void init(ProtocolVersion protocolVersion,
-        ProtocolVersion clientVersion,
-        SecureRandom rand, HandshakeInStream input, AccessControlContext acc, Object serviceCreds)
-        throws IOException {
-
-        // Read ticket
-        encodedTicket = input.getBytes16();
-
-        if (debug != null && Debug.isOn("verbose")) {
-            Debug.println(System.out,
-                "encoded Kerberos service ticket", encodedTicket);
-        }
-
-        EncryptionKey sessionKey = null;
-
-        try {
-            Ticket t = new Ticket(encodedTicket);
-
-            EncryptedData encPart = t.encPart;
-            PrincipalName ticketSname = t.sname;
-
-            final ServiceCreds creds = (ServiceCreds)serviceCreds;
-            final KerberosPrincipal princ =
-                    new KerberosPrincipal(ticketSname.toString());
-
-            // For bound service, permission already checked at setup
-            if (creds.getName() == null) {
-                SecurityManager sm = System.getSecurityManager();
-                try {
-                    if (sm != null) {
-                        // Eliminate dependency on ServicePermission
-                        sm.checkPermission(Krb5Helper.getServicePermission(
-                                ticketSname.toString(), "accept"), acc);
-                    }
-                } catch (SecurityException se) {
-                    serviceCreds = null;
-                    // Do not destroy keys. Will affect Subject
-                    if (debug != null && Debug.isOn("handshake")) {
-                        System.out.println("Permission to access Kerberos"
-                                + " secret key denied");
-                    }
-                    throw new IOException("Kerberos service not allowedy");
-                }
-            }
-            KerberosKey[] serverKeys = AccessController.doPrivileged(
-                    new PrivilegedAction<KerberosKey[]>() {
-                        @Override
-                        public KerberosKey[] run() {
-                            return creds.getKKeys(princ);
-                        }
-                    });
-            if (serverKeys.length == 0) {
-                throw new IOException("Found no key for " + princ +
-                        (creds.getName() == null ? "" :
-                        (", this keytab is for " + creds.getName() + " only")));
-            }
-
-            /*
-             * permission to access and use the secret key of the Kerberized
-             * "host" service is done in ServerHandshaker.getKerberosKeys()
-             * to ensure server has the permission to use the secret key
-             * before promising the client
-             */
-
-            // See if we have the right key to decrypt the ticket to get
-            // the session key.
-            int encPartKeyType = encPart.getEType();
-            Integer encPartKeyVersion = encPart.getKeyVersionNumber();
-            KerberosKey dkey = null;
-            try {
-                dkey = findKey(encPartKeyType, encPartKeyVersion, serverKeys);
-            } catch (KrbException ke) { // a kvno mismatch
-                throw new IOException(
-                        "Cannot find key matching version number", ke);
-            }
-            if (dkey == null) {
-                // %%% Should print string repr of etype
-                throw new IOException("Cannot find key of appropriate type" +
-                        " to decrypt ticket - need etype " + encPartKeyType);
-            }
-
-            EncryptionKey secretKey = new EncryptionKey(
-                encPartKeyType,
-                dkey.getEncoded());
-
-            // Decrypt encPart using server's secret key
-            byte[] bytes = encPart.decrypt(secretKey, KeyUsage.KU_TICKET);
-
-            // Reset data stream after decryption, remove redundant bytes
-            byte[] temp = encPart.reset(bytes);
-            EncTicketPart encTicketPart = new EncTicketPart(temp);
-
-            // Record the Kerberos Principals
-            peerPrincipal =
-                new KerberosPrincipal(encTicketPart.cname.getName());
-            localPrincipal = new KerberosPrincipal(ticketSname.getName());
-
-            sessionKey = encTicketPart.key;
-
-            if (debug != null && Debug.isOn("handshake")) {
-                System.out.println("server principal: " + ticketSname);
-                System.out.println("cname: " + encTicketPart.cname.toString());
-            }
-        } catch (IOException e) {
-            throw e;
-        } catch (Exception e) {
-            if (debug != null && Debug.isOn("handshake")) {
-                System.out.println("KerberosWrapper error getting session key,"
-                        + " generating random secret (" + e.getMessage() + ")");
-            }
-            sessionKey = null;
-        }
-
-        input.getBytes16();   // XXX Read and ignore authenticator
-
-        if (sessionKey != null) {
-            preMaster = new KerberosPreMasterSecret(protocolVersion,
-                clientVersion, rand, input, sessionKey);
-        } else {
-            // Generate bogus premaster secret
-            preMaster = new KerberosPreMasterSecret(clientVersion, rand);
-        }
-    }
-
-    @Override
-    public int messageLength() {
-        return (6 + encodedTicket.length + preMaster.getEncrypted().length);
-    }
-
-    @Override
-    public void send(HandshakeOutStream s) throws IOException {
-        s.putBytes16(encodedTicket);
-        s.putBytes16(null); // XXX no authenticator
-        s.putBytes16(preMaster.getEncrypted());
-    }
-
-    @Override
-    public void print(PrintStream s) throws IOException {
-        s.println("*** ClientKeyExchange, Kerberos");
-
-        if (debug != null && Debug.isOn("verbose")) {
-            Debug.println(s, "Kerberos service ticket", encodedTicket);
-            Debug.println(s, "Random Secret", preMaster.getUnencrypted());
-            Debug.println(s, "Encrypted random Secret",
-                preMaster.getEncrypted());
-        }
-    }
-
-    // Similar to sun.security.jgss.krb5.Krb5InitCredenetial/Krb5Context
-    private static KerberosTicket getServiceTicket(String serverName,
-        final AccessControlContext acc) throws IOException {
-
-        if ("localhost".equals(serverName) ||
-                "localhost.localdomain".equals(serverName)) {
-
-            if (debug != null && Debug.isOn("handshake")) {
-                System.out.println("Get the local hostname");
-            }
-            String localHost = java.security.AccessController.doPrivileged(
-                new java.security.PrivilegedAction<String>() {
-                public String run() {
-                    try {
-                        return InetAddress.getLocalHost().getHostName();
-                    } catch (java.net.UnknownHostException e) {
-                        if (debug != null && Debug.isOn("handshake")) {
-                            System.out.println("Warning,"
-                                + " cannot get the local hostname: "
-                                + e.getMessage());
-                        }
-                        return null;
-                    }
-                }
-            });
-            if (localHost != null) {
-                serverName = localHost;
-            }
-        }
-
-        // Resolve serverName (possibly in IP addr form) to Kerberos principal
-        // name for service with hostname
-        String serviceName = "host/" + serverName;
-        PrincipalName principal;
-        try {
-            principal = new PrincipalName(serviceName,
-                                PrincipalName.KRB_NT_SRV_HST);
-        } catch (SecurityException se) {
-            throw se;
-        } catch (Exception e) {
-            IOException ioe = new IOException("Invalid service principal" +
-                                " name: " + serviceName);
-            ioe.initCause(e);
-            throw ioe;
-        }
-        String realm = principal.getRealmAsString();
-
-        final String serverPrincipal = principal.toString();
-        final String tgsPrincipal = "krbtgt/" + realm + "@" + realm;
-        final String clientPrincipal = null;  // use default
-
-
-        // check permission to obtain a service ticket to initiate a
-        // context with the "host" service
-        SecurityManager sm = System.getSecurityManager();
-        if (sm != null) {
-           sm.checkPermission(new ServicePermission(serverPrincipal,
-                                "initiate"), acc);
-        }
-
-        try {
-            KerberosTicket ticket = AccessController.doPrivileged(
-                new PrivilegedExceptionAction<KerberosTicket>() {
-                public KerberosTicket run() throws Exception {
-                    return Krb5Util.getTicketFromSubjectAndTgs(
-                        GSSCaller.CALLER_SSL_CLIENT,
-                        clientPrincipal, serverPrincipal,
-                        tgsPrincipal, acc);
-                        }});
-
-            if (ticket == null) {
-                throw new IOException("Failed to find any kerberos service" +
-                        " ticket for " + serverPrincipal);
-            }
-            return ticket;
-        } catch (PrivilegedActionException e) {
-            IOException ioe = new IOException(
-                "Attempt to obtain kerberos service ticket for " +
-                        serverPrincipal + " failed!");
-            ioe.initCause(e);
-            throw ioe;
-        }
-    }
-
-    @Override
-    public byte[] getUnencryptedPreMasterSecret() {
-        return preMaster.getUnencrypted();
-    }
-
-    @Override
-    public KerberosPrincipal getPeerPrincipal() {
-        return peerPrincipal;
-    }
-
-    @Override
-    public KerberosPrincipal getLocalPrincipal() {
-        return localPrincipal;
-    }
-
-    /**
-     * Determines if a kvno matches another kvno. Used in the method
-     * findKey(etype, version, keys). Always returns true if either input
-     * is null or zero, in case any side does not have kvno info available.
-     *
-     * Note: zero is included because N/A is not a legal value for kvno
-     * in javax.security.auth.kerberos.KerberosKey. Therefore, the info
-     * that the kvno is N/A might be lost when converting between
-     * EncryptionKey and KerberosKey.
-     */
-    private static boolean versionMatches(Integer v1, int v2) {
-        if (v1 == null || v1 == 0 || v2 == 0) {
-            return true;
-        }
-        return v1.equals(v2);
-    }
-
-    private static KerberosKey findKey(int etype, Integer version,
-            KerberosKey[] keys) throws KrbException {
-        int ktype;
-        boolean etypeFound = false;
-
-        // When no matched kvno is found, returns tke key of the same
-        // etype with the highest kvno
-        int kvno_found = 0;
-        KerberosKey key_found = null;
-
-        for (int i = 0; i < keys.length; i++) {
-            ktype = keys[i].getKeyType();
-            if (etype == ktype) {
-                int kv = keys[i].getVersionNumber();
-                etypeFound = true;
-                if (versionMatches(version, kv)) {
-                    return keys[i];
-                } else if (kv > kvno_found) {
-                    key_found = keys[i];
-                    kvno_found = kv;
-                }
-            }
-        }
-        // Key not found.
-        // %%% kludge to allow DES keys to be used for diff etypes
-        if ((etype == EncryptedData.ETYPE_DES_CBC_CRC ||
-            etype == EncryptedData.ETYPE_DES_CBC_MD5)) {
-            for (int i = 0; i < keys.length; i++) {
-                ktype = keys[i].getKeyType();
-                if (ktype == EncryptedData.ETYPE_DES_CBC_CRC ||
-                        ktype == EncryptedData.ETYPE_DES_CBC_MD5) {
-                    int kv = keys[i].getVersionNumber();
-                    etypeFound = true;
-                    if (versionMatches(version, kv)) {
-                        return new KerberosKey(keys[i].getPrincipal(),
-                            keys[i].getEncoded(),
-                            etype,
-                            kv);
-                    } else if (kv > kvno_found) {
-                        key_found = new KerberosKey(keys[i].getPrincipal(),
-                                keys[i].getEncoded(),
-                                etype,
-                                kv);
-                        kvno_found = kv;
-                    }
-                }
-            }
-        }
-        if (etypeFound) {
-            return key_found;
-        }
-        return null;
-    }
-}
--- a/jdk/src/java.security.jgss/share/classes/sun/security/ssl/krb5/KerberosPreMasterSecret.java	Mon Jun 01 10:15:21 2015 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,272 +0,0 @@
-/*
- * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package sun.security.ssl.krb5;
-
-import java.io.*;
-import java.security.*;
-import java.util.Arrays;
-
-import javax.net.ssl.*;
-
-import sun.security.krb5.EncryptionKey;
-import sun.security.krb5.EncryptedData;
-import sun.security.krb5.KrbException;
-import sun.security.krb5.internal.crypto.KeyUsage;
-
-import sun.security.ssl.Debug;
-import sun.security.ssl.HandshakeInStream;
-import sun.security.ssl.HandshakeMessage;
-import sun.security.ssl.ProtocolVersion;
-
-/**
- * This is the Kerberos premaster secret in the Kerberos client key
- * exchange message (CLIENT --> SERVER); it holds the
- * Kerberos-encrypted pre-master secret. The secret is encrypted using the
- * Kerberos session key.  The padding and size of the resulting message
- * depends on the session key type, but the pre-master secret is
- * always exactly 48 bytes.
- *
- */
-final class KerberosPreMasterSecret {
-
-    private ProtocolVersion protocolVersion; // preMaster [0,1]
-    private byte preMaster[];           // 48 bytes
-    private byte encrypted[];
-
-    /**
-     * Constructor used by client to generate premaster secret.
-     *
-     * Client randomly creates a pre-master secret and encrypts it
-     * using the Kerberos session key; only the server can decrypt
-     * it, using the session key available in the service ticket.
-     *
-     * @param protocolVersion used to set preMaster[0,1]
-     * @param generator random number generator for generating premaster secret
-     * @param sessionKey Kerberos session key for encrypting premaster secret
-     */
-    KerberosPreMasterSecret(ProtocolVersion protocolVersion,
-        SecureRandom generator, EncryptionKey sessionKey) throws IOException {
-
-        if (sessionKey.getEType() ==
-            EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD) {
-            throw new IOException(
-               "session keys with des3-cbc-hmac-sha1-kd encryption type " +
-               "are not supported for TLS Kerberos cipher suites");
-        }
-
-        this.protocolVersion = protocolVersion;
-        preMaster = generatePreMaster(generator, protocolVersion);
-
-        // Encrypt premaster secret
-        try {
-            EncryptedData eData = new EncryptedData(sessionKey, preMaster,
-                KeyUsage.KU_UNKNOWN);
-            encrypted = eData.getBytes();  // not ASN.1 encoded.
-
-        } catch (KrbException e) {
-            throw (SSLKeyException)new SSLKeyException
-                ("Kerberos premaster secret error").initCause(e);
-        }
-    }
-
-    /*
-     * Constructor used by server to decrypt encrypted premaster secret.
-     * The protocol version in preMaster[0,1] must match either currentVersion
-     * or clientVersion, otherwise, the premaster secret is set to
-     * a random one to foil possible attack.
-     *
-     * @param currentVersion version of protocol being used
-     * @param clientVersion version requested by client
-     * @param generator random number generator used to generate
-     *        bogus premaster secret if premaster secret verification fails
-     * @param input input stream from which to read the encrypted
-     *        premaster secret
-     * @param sessionKey Kerberos session key to be used for decryption
-     */
-    KerberosPreMasterSecret(ProtocolVersion currentVersion,
-        ProtocolVersion clientVersion,
-        SecureRandom generator, HandshakeInStream input,
-        EncryptionKey sessionKey) throws IOException {
-
-         // Extract encrypted premaster secret from message
-         encrypted = input.getBytes16();
-
-         if (HandshakeMessage.debug != null && Debug.isOn("handshake")) {
-            if (encrypted != null) {
-                Debug.println(System.out,
-                     "encrypted premaster secret", encrypted);
-            }
-         }
-
-        if (sessionKey.getEType() ==
-            EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD) {
-            throw new IOException(
-               "session keys with des3-cbc-hmac-sha1-kd encryption type " +
-               "are not supported for TLS Kerberos cipher suites");
-        }
-
-        // Decrypt premaster secret
-        try {
-            EncryptedData data = new EncryptedData(sessionKey.getEType(),
-                        null /* optional kvno */, encrypted);
-
-            byte[] temp = data.decrypt(sessionKey, KeyUsage.KU_UNKNOWN);
-            if (HandshakeMessage.debug != null && Debug.isOn("handshake")) {
-                 if (encrypted != null) {
-                     Debug.println(System.out,
-                         "decrypted premaster secret", temp);
-                 }
-            }
-
-            // Remove padding bytes after decryption. Only DES and DES3 have
-            // paddings and we don't support DES3 in TLS (see above)
-
-            if (temp.length == 52 &&
-                    data.getEType() == EncryptedData.ETYPE_DES_CBC_CRC) {
-                // For des-cbc-crc, 4 paddings. Value can be 0x04 or 0x00.
-                if (paddingByteIs(temp, 52, (byte)4) ||
-                        paddingByteIs(temp, 52, (byte)0)) {
-                    temp = Arrays.copyOf(temp, 48);
-                }
-            } else if (temp.length == 56 &&
-                    data.getEType() == EncryptedData.ETYPE_DES_CBC_MD5) {
-                // For des-cbc-md5, 8 paddings with 0x08, or no padding
-                if (paddingByteIs(temp, 56, (byte)8)) {
-                    temp = Arrays.copyOf(temp, 48);
-                }
-            }
-
-            preMaster = temp;
-
-            protocolVersion = ProtocolVersion.valueOf(preMaster[0],
-                 preMaster[1]);
-            if (HandshakeMessage.debug != null && Debug.isOn("handshake")) {
-                 System.out.println("Kerberos PreMasterSecret version: "
-                        + protocolVersion);
-            }
-        } catch (Exception e) {
-            // catch exception & process below
-            preMaster = null;
-            protocolVersion = currentVersion;
-        }
-
-        // check if the premaster secret version is ok
-        // the specification says that it must be the maximum version supported
-        // by the client from its ClientHello message. However, many
-        // old implementations send the negotiated version, so accept both
-        // for SSL v3.0 and TLS v1.0.
-        // NOTE that we may be comparing two unsupported version numbers in
-        // the second case, which is why we cannot use object references
-        // equality in this special case
-        boolean versionMismatch = (protocolVersion.v != clientVersion.v);
-
-        /*
-         * we never checked the client_version in server side
-         * for TLS v1.0 and SSL v3.0. For compatibility, we
-         * maintain this behavior.
-         */
-        if (versionMismatch && (clientVersion.v <= 0x0301)) {
-            versionMismatch = (protocolVersion.v != currentVersion.v);
-        }
-
-        /*
-         * Bogus decrypted ClientKeyExchange? If so, conjure a
-         * a random preMaster secret that will fail later during
-         * Finished message processing. This is a countermeasure against
-         * the "interactive RSA PKCS#1 encryption envelop attack" reported
-         * in June 1998. Preserving the executation path will
-         * mitigate timing attacks and force consistent error handling
-         * that will prevent an attacking client from differentiating
-         * different kinds of decrypted ClientKeyExchange bogosities.
-         */
-         if ((preMaster == null) || (preMaster.length != 48)
-                || versionMismatch) {
-            if (HandshakeMessage.debug != null && Debug.isOn("handshake")) {
-                System.out.println("Kerberos PreMasterSecret error, "
-                                   + "generating random secret");
-                if (preMaster != null) {
-                    Debug.println(System.out, "Invalid secret", preMaster);
-                }
-            }
-
-            /*
-             * Randomize the preMaster secret with the
-             * ClientHello.client_version, as will produce invalid master
-             * secret to prevent the attacks.
-             */
-            preMaster = generatePreMaster(generator, clientVersion);
-            protocolVersion = clientVersion;
-        }
-    }
-
-    /**
-     * Checks if all paddings of data are b
-     * @param data the block with padding
-     * @param len length of data, >= 48
-     * @param b expected padding byte
-     */
-    private static boolean paddingByteIs(byte[] data, int len, byte b) {
-        for (int i=48; i<len; i++) {
-            if (data[i] != b) return false;
-        }
-        return true;
-    }
-
-    /*
-     * Used by server to generate premaster secret in case of
-     * problem decoding ticket.
-     *
-     * @param protocolVersion used for preMaster[0,1]
-     * @param generator random number generator to use for generating secret.
-     */
-    KerberosPreMasterSecret(ProtocolVersion protocolVersion,
-        SecureRandom generator) {
-
-        this.protocolVersion = protocolVersion;
-        preMaster = generatePreMaster(generator, protocolVersion);
-    }
-
-    private static byte[] generatePreMaster(SecureRandom rand,
-        ProtocolVersion ver) {
-
-        byte[] pm = new byte[48];
-        rand.nextBytes(pm);
-        pm[0] = ver.major;
-        pm[1] = ver.minor;
-
-        return pm;
-    }
-
-    // Clone not needed; internal use only
-    byte[] getUnencrypted() {
-        return preMaster;
-    }
-
-    // Clone not needed; internal use only
-    byte[] getEncrypted() {
-        return encrypted;
-    }
-}
--- a/jdk/src/java.security.jgss/share/classes/sun/security/ssl/krb5/Krb5ProxyImpl.java	Mon Jun 01 10:15:21 2015 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,121 +0,0 @@
-/*
- * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package sun.security.ssl.krb5;
-
-import java.security.AccessControlContext;
-import java.security.Permission;
-import java.security.Principal;
-import java.util.Set;
-import javax.crypto.SecretKey;
-import javax.security.auth.Subject;
-import javax.security.auth.kerberos.KerberosKey;
-import javax.security.auth.kerberos.KeyTab;
-import javax.security.auth.kerberos.ServicePermission;
-import javax.security.auth.login.LoginException;
-
-import sun.security.jgss.GSSCaller;
-import sun.security.jgss.krb5.Krb5Util;
-import sun.security.jgss.krb5.ServiceCreds;
-import sun.security.krb5.PrincipalName;
-import sun.security.ssl.Krb5Proxy;
-
-/**
- * An implementation of Krb5Proxy that simply delegates to the appropriate
- * Kerberos APIs.
- */
-public class Krb5ProxyImpl implements Krb5Proxy {
-
-    public Krb5ProxyImpl() { }
-
-    @Override
-    public Subject getClientSubject(AccessControlContext acc)
-            throws LoginException {
-        return Krb5Util.getSubject(GSSCaller.CALLER_SSL_CLIENT, acc);
-    }
-
-    @Override
-    public Subject getServerSubject(AccessControlContext acc)
-            throws LoginException {
-        return Krb5Util.getSubject(GSSCaller.CALLER_SSL_SERVER, acc);
-    }
-
-    @Override
-    public Object getServiceCreds(AccessControlContext acc)
-            throws LoginException {
-        ServiceCreds serviceCreds =
-            Krb5Util.getServiceCreds(GSSCaller.CALLER_SSL_SERVER, null, acc);
-        return serviceCreds;
-    }
-
-    @Override
-    public String getServerPrincipalName(Object serviceCreds) {
-        return ((ServiceCreds)serviceCreds).getName();
-    }
-
-    @Override
-    public String getPrincipalHostName(Principal principal) {
-        if (principal == null) {
-           return null;
-        }
-        String hostName = null;
-        try {
-            PrincipalName princName =
-                new PrincipalName(principal.getName(),
-                        PrincipalName.KRB_NT_SRV_HST);
-            String[] nameParts = princName.getNameStrings();
-            if (nameParts.length >= 2) {
-                hostName = nameParts[1];
-            }
-        } catch (Exception e) {
-            // ignore
-        }
-        return hostName;
-    }
-
-
-    @Override
-    public Permission getServicePermission(String principalName,
-            String action) {
-        return new ServicePermission(principalName, action);
-    }
-
-    @Override
-    public boolean isRelated(Subject subject, Principal princ) {
-        if (princ == null) return false;
-        Set<Principal> principals =
-                subject.getPrincipals(Principal.class);
-        if (principals.contains(princ)) {
-            // bound to this principal
-            return true;
-        }
-        for (KeyTab pc: subject.getPrivateCredentials(KeyTab.class)) {
-            if (!pc.isBound()) {
-                return true;
-            }
-        }
-        return false;
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/net/ssl/DTLS/CipherSuite.java	Tue Jun 02 09:15:47 2015 -0700
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+
+/*
+ * @test
+ * @bug 8043758
+ * @summary Datagram Transport Layer Security (DTLS)
+ * @compile DTLSOverDatagram.java
+ * @run main/othervm CipherSuite TLS_RSA_WITH_AES_128_CBC_SHA
+ * @run main/othervm CipherSuite TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
+ * @run main/othervm CipherSuite TLS_RSA_WITH_AES_128_CBC_SHA256
+ * @run main/othervm CipherSuite TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
+ * @run main/othervm CipherSuite TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
+ * @run main/othervm CipherSuite TLS_DHE_RSA_WITH_AES_128_CBC_SHA
+ * @run main/othervm CipherSuite TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA
+ * @run main/othervm CipherSuite TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
+ * @run main/othervm CipherSuite TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
+ * @run main/othervm CipherSuite TLS_RSA_WITH_AES_128_GCM_SHA256
+ * @run main/othervm CipherSuite TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256
+ * @run main/othervm CipherSuite TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
+ * @run main/othervm CipherSuite TLS_DHE_DSS_WITH_AES_128_GCM_SHA256
+ * @run main/othervm CipherSuite TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256
+ */
+
+import javax.net.ssl.SSLEngine;
+
+/**
+ * Test common DTLS cipher suites.
+ */
+public class CipherSuite extends DTLSOverDatagram {
+
+    // use the specific cipher suite
+    volatile static String cipherSuite;
+
+    public static void main(String[] args) throws Exception {
+        cipherSuite = args[0];
+
+        CipherSuite testCase = new CipherSuite();
+        testCase.runTest(testCase);
+    }
+
+    @Override
+    SSLEngine createSSLEngine(boolean isClient) throws Exception {
+        SSLEngine engine = super.createSSLEngine(isClient);
+
+        if (isClient) {
+            engine.setEnabledCipherSuites(new String[]{cipherSuite});
+        }
+
+        return engine;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/net/ssl/DTLS/ClientAuth.java	Tue Jun 02 09:15:47 2015 -0700
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+
+/*
+ * @test
+ * @bug 8043758
+ * @summary Datagram Transport Layer Security (DTLS)
+ * @compile DTLSOverDatagram.java
+ * @run main/othervm ClientAuth
+ */
+
+import javax.net.ssl.SSLEngine;
+
+/**
+ * Test DTLS client authentication.
+ */
+public class ClientAuth extends DTLSOverDatagram {
+
+    public static void main(String[] args) throws Exception {
+        ClientAuth testCase = new ClientAuth();
+        testCase.runTest(testCase);
+    }
+
+    @Override
+    SSLEngine createSSLEngine(boolean isClient) throws Exception {
+        SSLEngine engine = super.createSSLEngine(isClient);
+
+        if (!isClient) {
+            engine.setNeedClientAuth(true);
+        }
+
+        return engine;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/net/ssl/DTLS/DTLSOverDatagram.java	Tue Jun 02 09:15:47 2015 -0700
@@ -0,0 +1,602 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+
+/*
+ * @test
+ * @bug 8043758
+ * @summary Datagram Transport Layer Security (DTLS)
+ * @run main/othervm DTLSOverDatagram
+ */
+
+import java.io.*;
+import java.nio.*;
+import java.net.*;
+import java.util.*;
+import java.security.*;
+import java.security.cert.*;
+import javax.net.ssl.*;
+import java.util.concurrent.*;
+
+import sun.misc.HexDumpEncoder;
+
+/**
+ * An example to show the way to use SSLEngine in datagram connections.
+ */
+public class DTLSOverDatagram {
+    private static int MAX_HANDSHAKE_LOOPS = 60;
+    private static int MAX_APP_READ_LOOPS = 10;
+
+    /*
+     * The following is to set up the keystores.
+     */
+    private static String pathToStores = "../etc";
+    private static String keyStoreFile = "keystore";
+    private static String trustStoreFile = "truststore";
+    private static String passwd = "passphrase";
+
+    private static String keyFilename =
+            System.getProperty("test.src", ".") + "/" + pathToStores +
+                "/" + keyStoreFile;
+    private static String trustFilename =
+            System.getProperty("test.src", ".") + "/" + pathToStores +
+                "/" + trustStoreFile;
+    private static Exception clientException = null;
+    private static Exception serverException = null;
+
+    private static ByteBuffer serverApp =
+                ByteBuffer.wrap("Hi Client, I'm Server".getBytes());
+    private static ByteBuffer clientApp =
+                ByteBuffer.wrap("Hi Server, I'm Client".getBytes());
+
+    /*
+     * =============================================================
+     * The test case
+     */
+    public static void main(String[] args) throws Exception {
+        DTLSOverDatagram testCase = new DTLSOverDatagram();
+        testCase.runTest(testCase);
+    }
+
+    /*
+     * Define the server side of the test.
+     */
+    void doServerSide() throws Exception {
+        DatagramSocket socket = serverDatagramSocket;
+        socket.setSoTimeout(10000);   // 10 second
+
+        // create SSLEngine
+        SSLEngine engine = createSSLEngine(false);
+
+        // handshaking
+        handshake(engine, socket, clientSocketAddr);
+
+        // read client application data
+        receiveAppData(engine, socket, clientApp);
+
+        // write server application data
+        deliverAppData(engine, socket, serverApp, clientSocketAddr);
+
+        socket.close();
+    }
+
+    /*
+     * Define the client side of the test.
+     */
+    void doClientSide() throws Exception {
+        DatagramSocket socket = clientDatagramSocket;
+        socket.setSoTimeout(1000);     // 1 second read timeout
+
+        // create SSLEngine
+        SSLEngine engine = createSSLEngine(true);
+
+        // handshaking
+        handshake(engine, socket, serverSocketAddr);
+
+        // write client application data
+        deliverAppData(engine, socket, clientApp, serverSocketAddr);
+
+        // read server application data
+        receiveAppData(engine, socket, serverApp);
+    }
+
+    /*
+     * =============================================================
+     * The remainder is support stuff for DTLS operations.
+     */
+    SSLEngine createSSLEngine(boolean isClient) throws Exception {
+        SSLContext context = getDTLSContext();
+        SSLEngine engine = context.createSSLEngine();
+
+        SSLParameters paras = engine.getSSLParameters();
+        paras.setMaximumPacketSize(1024);
+
+        engine.setUseClientMode(isClient);
+        engine.setSSLParameters(paras);
+
+        return engine;
+    }
+
+    // handshake
+    void handshake(SSLEngine engine, DatagramSocket socket,
+            SocketAddress peerAddr) throws Exception {
+
+        boolean endLoops = false;
+        int loops = MAX_HANDSHAKE_LOOPS;
+        engine.beginHandshake();
+        while (!endLoops &&
+                (serverException == null) && (clientException == null)) {
+
+            if (--loops < 0) {
+                throw new RuntimeException(
+                        "Too much loops to produce handshake packets");
+            }
+
+            SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus();
+            if (hs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP ||
+                hs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP_AGAIN) {
+
+                ByteBuffer iNet;
+                ByteBuffer iApp;
+                if (hs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP) {
+                    // receive ClientHello request and other SSL/TLS records
+                    byte[] buf = new byte[1024];
+                    DatagramPacket packet = new DatagramPacket(buf, buf.length);
+                    try {
+                        socket.receive(packet);
+                    } catch (SocketTimeoutException ste) {
+                        List<DatagramPacket> packets =
+                                onReceiveTimeout(engine, peerAddr);
+                        for (DatagramPacket p : packets) {
+                            socket.send(p);
+                        }
+
+                        continue;
+                    }
+
+                    iNet = ByteBuffer.wrap(buf, 0, packet.getLength());
+                    iApp = ByteBuffer.allocate(1024);
+                } else {
+                    iNet = ByteBuffer.allocate(0);
+                    iApp = ByteBuffer.allocate(1024);
+                }
+
+                SSLEngineResult r = engine.unwrap(iNet, iApp);
+                SSLEngineResult.Status rs = r.getStatus();
+                hs = r.getHandshakeStatus();
+                if (rs == SSLEngineResult.Status.BUFFER_OVERFLOW) {
+                    // the client maximum fragment size config does not work?
+                    throw new Exception("Buffer overflow: " +
+                        "incorrect client maximum fragment size");
+                } else if (rs == SSLEngineResult.Status.BUFFER_UNDERFLOW) {
+                    // bad packet, or the client maximum fragment size
+                    // config does not work?
+                    if (hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
+                        throw new Exception("Buffer underflow: " +
+                            "incorrect client maximum fragment size");
+                    } // otherwise, ignore this packet
+                } else if (rs == SSLEngineResult.Status.CLOSED) {
+                    endLoops = true;
+                }   // otherwise, SSLEngineResult.Status.OK:
+
+                if (rs != SSLEngineResult.Status.OK) {
+                    continue;
+                }
+            } else if (hs == SSLEngineResult.HandshakeStatus.NEED_WRAP) {
+                List<DatagramPacket> packets =
+                        produceHandshakePackets(engine, peerAddr);
+                for (DatagramPacket p : packets) {
+                    socket.send(p);
+                }
+            } else if (hs == SSLEngineResult.HandshakeStatus.NEED_TASK) {
+                runDelegatedTasks(engine);
+            } else if (hs == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
+                // OK, time to do application data exchange.
+                endLoops = true;
+            } else if (hs == SSLEngineResult.HandshakeStatus.FINISHED) {
+                endLoops = true;
+            }
+        }
+
+        SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus();
+        if (hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
+            throw new Exception("Not ready for application data yet");
+        }
+    }
+
+    // deliver application data
+    void deliverAppData(SSLEngine engine, DatagramSocket socket,
+            ByteBuffer appData, SocketAddress peerAddr) throws Exception {
+
+        // Note: have not consider the packet loses
+        List<DatagramPacket> packets =
+                produceApplicationPackets(engine, appData, peerAddr);
+        appData.flip();
+        for (DatagramPacket p : packets) {
+            socket.send(p);
+        }
+    }
+
+    // receive application data
+    void receiveAppData(SSLEngine engine,
+            DatagramSocket socket, ByteBuffer expectedApp) throws Exception {
+
+        int loops = MAX_APP_READ_LOOPS;
+        while ((serverException == null) && (clientException == null)) {
+            if (--loops < 0) {
+                throw new RuntimeException(
+                        "Too much loops to receive application data");
+            }
+
+            byte[] buf = new byte[1024];
+            DatagramPacket packet = new DatagramPacket(buf, buf.length);
+            socket.receive(packet);
+            ByteBuffer netBuffer = ByteBuffer.wrap(buf, 0, packet.getLength());
+            ByteBuffer recBuffer = ByteBuffer.allocate(1024);
+            SSLEngineResult rs = engine.unwrap(netBuffer, recBuffer);
+            recBuffer.flip();
+            if (recBuffer.remaining() != 0) {
+                printHex("Received application data", recBuffer);
+                if (!recBuffer.equals(expectedApp)) {
+                    System.out.println("Engine status is " + rs);
+                    throw new Exception("Not the right application data");
+                }
+                break;
+            }
+        }
+    }
+
+    // produce handshake packets
+    List<DatagramPacket> produceHandshakePackets(
+            SSLEngine engine, SocketAddress socketAddr) throws Exception {
+
+        List<DatagramPacket> packets = new ArrayList<>();
+        boolean endLoops = false;
+        int loops = MAX_HANDSHAKE_LOOPS;
+        while (!endLoops &&
+                (serverException == null) && (clientException == null)) {
+
+            if (--loops < 0) {
+                throw new RuntimeException(
+                        "Too much loops to produce handshake packets");
+            }
+
+            ByteBuffer oNet = ByteBuffer.allocate(32768);
+            ByteBuffer oApp = ByteBuffer.allocate(0);
+            SSLEngineResult r = engine.wrap(oApp, oNet);
+            oNet.flip();
+
+            SSLEngineResult.Status rs = r.getStatus();
+            SSLEngineResult.HandshakeStatus hs = r.getHandshakeStatus();
+            if (rs == SSLEngineResult.Status.BUFFER_OVERFLOW) {
+                // the client maximum fragment size config does not work?
+                throw new Exception("Buffer overflow: " +
+                            "incorrect server maximum fragment size");
+            } else if (rs == SSLEngineResult.Status.BUFFER_UNDERFLOW) {
+                // bad packet, or the client maximum fragment size
+                // config does not work?
+                if (hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
+                    throw new Exception("Buffer underflow: " +
+                            "incorrect server maximum fragment size");
+                } // otherwise, ignore this packet
+            } else if (rs == SSLEngineResult.Status.CLOSED) {
+                throw new Exception("SSLEngine has closed");
+            }   // otherwise, SSLEngineResult.Status.OK
+
+            // SSLEngineResult.Status.OK:
+            if (oNet.hasRemaining()) {
+                byte[] ba = new byte[oNet.remaining()];
+                oNet.get(ba);
+                DatagramPacket packet = createHandshakePacket(ba, socketAddr);
+                packets.add(packet);
+            }
+            boolean endInnerLoop = false;
+            SSLEngineResult.HandshakeStatus nhs = hs;
+            while (!endInnerLoop) {
+                if (nhs == SSLEngineResult.HandshakeStatus.NEED_TASK) {
+                    runDelegatedTasks(engine);
+                    nhs = engine.getHandshakeStatus();
+                } else if ((nhs == SSLEngineResult.HandshakeStatus.FINISHED) ||
+                    (nhs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP) ||
+                    (nhs == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING)) {
+
+                    endInnerLoop = true;
+                    endLoops = true;
+                } else if (nhs == SSLEngineResult.HandshakeStatus.NEED_WRAP) {
+                    endInnerLoop = true;
+                }
+            }
+        }
+
+        return packets;
+    }
+
+    DatagramPacket createHandshakePacket(byte[] ba, SocketAddress socketAddr) {
+        return new DatagramPacket(ba, ba.length, socketAddr);
+    }
+
+    // produce application packets
+    List<DatagramPacket> produceApplicationPackets(
+            SSLEngine engine, ByteBuffer source,
+            SocketAddress socketAddr) throws Exception {
+
+        List<DatagramPacket> packets = new ArrayList<>();
+        ByteBuffer appNet = ByteBuffer.allocate(32768);
+        SSLEngineResult r = engine.wrap(source, appNet);
+        appNet.flip();
+
+        SSLEngineResult.Status rs = r.getStatus();
+        if (rs == SSLEngineResult.Status.BUFFER_OVERFLOW) {
+            // the client maximum fragment size config does not work?
+            throw new Exception("Buffer overflow: " +
+                        "incorrect server maximum fragment size");
+        } else if (rs == SSLEngineResult.Status.BUFFER_UNDERFLOW) {
+            // unlikely
+            throw new Exception("Buffer underflow during wraping");
+        } else if (rs == SSLEngineResult.Status.CLOSED) {
+                throw new Exception("SSLEngine has closed");
+        }   // otherwise, SSLEngineResult.Status.OK
+
+        // SSLEngineResult.Status.OK:
+        if (appNet.hasRemaining()) {
+            byte[] ba = new byte[appNet.remaining()];
+            appNet.get(ba);
+            DatagramPacket packet =
+                    new DatagramPacket(ba, ba.length, socketAddr);
+            packets.add(packet);
+        }
+
+        return packets;
+    }
+
+    // run delegated tasks
+    void runDelegatedTasks(SSLEngine engine) throws Exception {
+        Runnable runnable;
+        while ((runnable = engine.getDelegatedTask()) != null) {
+            runnable.run();
+        }
+
+        SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus();
+        if (hs == SSLEngineResult.HandshakeStatus.NEED_TASK) {
+            throw new Exception("handshake shouldn't need additional tasks");
+        }
+    }
+
+    // retransmission if timeout
+    List<DatagramPacket> onReceiveTimeout(
+            SSLEngine engine, SocketAddress socketAddr) throws Exception {
+
+        SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus();
+        if (hs == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
+            return new ArrayList<DatagramPacket>();
+        } else {
+            // retransmission of handshake messages
+            return produceHandshakePackets(engine, socketAddr);
+        }
+    }
+
+    // get DTSL context
+    SSLContext getDTLSContext() throws Exception {
+        KeyStore ks = KeyStore.getInstance("JKS");
+        KeyStore ts = KeyStore.getInstance("JKS");
+
+        char[] passphrase = "passphrase".toCharArray();
+
+        ks.load(new FileInputStream(keyFilename), passphrase);
+        ts.load(new FileInputStream(trustFilename), passphrase);
+
+        KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
+        kmf.init(ks, passphrase);
+
+        TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
+        tmf.init(ts);
+
+        SSLContext sslCtx = SSLContext.getInstance("DTLS");
+
+        sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
+
+        return sslCtx;
+    }
+
+
+    /*
+     * =============================================================
+     * The remainder is support stuff to kickstart the testing.
+     */
+
+    // the server side SocketAddress
+    volatile static SocketAddress serverSocketAddr = null;
+
+    // the client side SocketAddress
+    volatile static SocketAddress clientSocketAddr = null;
+
+    // the server side DatagramSocket instance
+    volatile static DatagramSocket serverDatagramSocket = null;
+
+    // the server side DatagramSocket instance
+    volatile static DatagramSocket clientDatagramSocket = null;
+
+    // get server side SocketAddress object
+    public SocketAddress getServerSocketAddress() {
+        return serverSocketAddr;
+    }
+
+    // get client side SocketAddress object
+    public SocketAddress getClientSocketAddress() {
+        return clientSocketAddr;
+    }
+
+    // get server side DatagramSocket object
+    public DatagramSocket getServerDatagramSocket() {
+        return serverDatagramSocket;
+    }
+
+    // get client side DatagramSocket object
+    public DatagramSocket getClientDatagramSocket() {
+        return clientDatagramSocket;
+    }
+
+    // Will the handshaking and application data exchange succeed?
+    public boolean isGoodJob() {
+        return true;
+    }
+
+    public final void runTest(DTLSOverDatagram testCase) throws Exception {
+        serverDatagramSocket = new DatagramSocket();
+        serverSocketAddr = new InetSocketAddress(
+            InetAddress.getLocalHost(), serverDatagramSocket.getLocalPort());
+
+        clientDatagramSocket = new DatagramSocket();
+        clientSocketAddr = new InetSocketAddress(
+            InetAddress.getLocalHost(), clientDatagramSocket.getLocalPort());
+
+        ExecutorService pool = Executors.newFixedThreadPool(2);
+        List<Future<String>> list = new ArrayList<Future<String>>();
+
+        try {
+            list.add(pool.submit(new ServerCallable(testCase)));  // server task
+            list.add(pool.submit(new ClientCallable(testCase)));  // client task
+        } finally {
+            pool.shutdown();
+        }
+
+        Exception reserved = null;
+        for (Future<String> fut : list) {
+            try {
+                System.out.println(fut.get());
+            } catch (CancellationException |
+                    InterruptedException | ExecutionException cie) {
+                if (reserved != null) {
+                    cie.addSuppressed(reserved);
+                    reserved = cie;
+                } else {
+                    reserved = cie;
+                }
+            }
+        }
+
+        if (reserved != null) {
+            throw reserved;
+        }
+    }
+
+    final static class ServerCallable implements Callable<String> {
+        DTLSOverDatagram testCase;
+
+        ServerCallable(DTLSOverDatagram testCase) {
+            this.testCase = testCase;
+        }
+
+        @Override
+        public String call() throws Exception {
+            try {
+                testCase.doServerSide();
+            } catch (Exception e) {
+                e.printStackTrace(System.out);
+                serverException = e;
+
+                if (testCase.isGoodJob()) {
+                    throw e;
+                } else {
+                    return "Well done, server!";
+                }
+            } finally {
+                if (serverDatagramSocket != null) {
+                    serverDatagramSocket.close();
+                }
+            }
+
+            if (testCase.isGoodJob()) {
+                return "Well done, server!";
+            } else {
+                throw new Exception("No expected exception");
+            }
+        }
+    }
+
+    final static class ClientCallable implements Callable<String> {
+        DTLSOverDatagram testCase;
+
+        ClientCallable(DTLSOverDatagram testCase) {
+            this.testCase = testCase;
+        }
+
+        @Override
+        public String call() throws Exception {
+            try {
+                testCase.doClientSide();
+            } catch (Exception e) {
+                e.printStackTrace(System.out);
+                clientException = e;
+                if (testCase.isGoodJob()) {
+                    throw e;
+                } else {
+                    return "Well done, client!";
+                }
+            } finally {
+                if (clientDatagramSocket != null) {
+                    clientDatagramSocket.close();
+                }
+            }
+
+            if (testCase.isGoodJob()) {
+                return "Well done, client!";
+            } else {
+                throw new Exception("No expected exception");
+            }
+        }
+    }
+
+    final static void printHex(String prefix, ByteBuffer bb) {
+        HexDumpEncoder  dump = new HexDumpEncoder();
+
+        synchronized (System.out) {
+            System.out.println(prefix);
+            try {
+                dump.encodeBuffer(bb.slice(), System.out);
+            } catch (Exception e) {
+                // ignore
+            }
+            System.out.flush();
+        }
+    }
+
+    final static void printHex(String prefix,
+            byte[] bytes, int offset, int length) {
+
+        HexDumpEncoder  dump = new HexDumpEncoder();
+
+        synchronized (System.out) {
+            System.out.println(prefix);
+            try {
+                ByteBuffer bb = ByteBuffer.wrap(bytes, offset, length);
+                dump.encodeBuffer(bb, System.out);
+            } catch (Exception e) {
+                // ignore
+            }
+            System.out.flush();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/net/ssl/DTLS/InvalidCookie.java	Tue Jun 02 09:15:47 2015 -0700
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+
+/*
+ * @test
+ * @bug 8043758
+ * @summary Datagram Transport Layer Security (DTLS)
+ * @compile DTLSOverDatagram.java
+ * @run main/othervm InvalidCookie
+ */
+
+import java.net.DatagramPacket;
+import java.net.SocketAddress;
+
+/**
+ * Test that if the handshake cookie in client side is incorrect, the handshake
+ * process can continue as if the client does not use cookie.
+ */
+public class InvalidCookie extends DTLSOverDatagram {
+    boolean needInvalidCookie = true;
+
+    public static void main(String[] args) throws Exception {
+        InvalidCookie testCase = new InvalidCookie();
+        testCase.runTest(testCase);
+    }
+
+    @Override
+    DatagramPacket createHandshakePacket(byte[] ba, SocketAddress socketAddr) {
+        if (needInvalidCookie && (ba.length >= 60) &&
+                (ba[0] == (byte)0x16) && (ba[13] == (byte)0x03)) {
+            // HelloVerifyRequest
+            needInvalidCookie = false;
+            System.out.println("invalidate handshake verify cookie");
+            if (ba[ba.length - 1] == (byte)0xFF) {
+                ba[ba.length - 1] = (byte)0xFE;
+            } else {
+                ba[ba.length - 1] = (byte)0xFF;
+            }
+        }
+
+        return super.createHandshakePacket(ba, socketAddr);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/net/ssl/DTLS/InvalidRecords.java	Tue Jun 02 09:15:47 2015 -0700
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+
+/*
+ * @test
+ * @bug 8043758
+ * @summary Datagram Transport Layer Security (DTLS)
+ * @compile DTLSOverDatagram.java
+ * @run main/othervm InvalidRecords
+ */
+
+import java.net.DatagramPacket;
+import java.net.SocketAddress;
+
+/**
+ * Test that if handshake messages are crasged, the handshake would fail
+ * because of handshaking hash verification.
+ */
+public class InvalidRecords extends DTLSOverDatagram {
+    boolean needInvalidRecords = true;
+
+    public static void main(String[] args) throws Exception {
+        InvalidRecords testCase = new InvalidRecords();
+        testCase.runTest(testCase);
+    }
+
+    @Override
+    public boolean isGoodJob() {
+        return false;
+    }
+
+    @Override
+    DatagramPacket createHandshakePacket(byte[] ba, SocketAddress socketAddr) {
+        if (needInvalidRecords && (ba.length >= 60) &&
+                (ba[0x00] == (byte)0x16) && (ba[0x0D] == (byte)0x01) &&
+                (ba[0x3B] == (byte)0x00) && (ba[0x3C] > 0)) {
+
+            // ba[0x00]: record type
+            // ba[0x0D]: handshake type
+            // ba[0x3B]: length of session ID
+            // ba[0x3C]: length of cookie
+
+            // ClientHello with cookie
+            needInvalidRecords = false;
+            System.out.println("invalidate ClientHello message");
+            if (ba[ba.length - 1] == (byte)0xFF) {
+                ba[ba.length - 1] = (byte)0xFE;
+            } else {
+                ba[ba.length - 1] = (byte)0xFF;
+            }
+        }
+
+        return super.createHandshakePacket(ba, socketAddr);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/net/ssl/DTLS/NoMacInitialClientHello.java	Tue Jun 02 09:15:47 2015 -0700
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+
+/*
+ * @test
+ * @bug 8043758
+ * @summary Datagram Transport Layer Security (DTLS)
+ * @compile DTLSOverDatagram.java
+ * @run main/othervm NoMacInitialClientHello
+ */
+
+import java.net.DatagramPacket;
+import java.net.SocketAddress;
+
+/**
+ * Test that a server is able to discard invalid initial ClientHello silently.
+ */
+public class NoMacInitialClientHello extends DTLSOverDatagram {
+    boolean needInvalidRecords = true;
+
+    public static void main(String[] args) throws Exception {
+        NoMacInitialClientHello testCase = new NoMacInitialClientHello();
+        testCase.runTest(testCase);
+    }
+
+    @Override
+    DatagramPacket createHandshakePacket(byte[] ba, SocketAddress socketAddr) {
+        if (needInvalidRecords && (ba.length >= 60) &&
+            (ba[0] == (byte)0x16) && (ba[13] == (byte)0x01)) {  // ClientHello
+
+            needInvalidRecords = false;
+            System.out.println("invalidate ClientHello message");
+            if (ba[ba.length - 1] == (byte)0xFF) {
+                ba[ba.length - 1] = (byte)0xFE;
+            } else {
+                ba[ba.length - 1] = (byte)0xFF;
+            }
+        }
+
+        return super.createHandshakePacket(ba, socketAddr);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/net/ssl/DTLS/Reordered.java	Tue Jun 02 09:15:47 2015 -0700
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+
+/*
+ * @test
+ * @bug 8043758
+ * @summary Datagram Transport Layer Security (DTLS)
+ * @compile DTLSOverDatagram.java
+ * @run main/othervm Reordered
+ */
+
+import java.util.List;
+import java.util.Collections;
+import java.net.DatagramPacket;
+import java.net.SocketAddress;
+import javax.net.ssl.SSLEngine;
+
+/**
+ * Test that DTLS implementation is able to reorder data traffic.
+ */
+public class Reordered extends DTLSOverDatagram {
+    boolean needPacketReorder = true;
+
+    public static void main(String[] args) throws Exception {
+        Reordered testCase = new Reordered();
+        testCase.runTest(testCase);
+    }
+
+    @Override
+    List<DatagramPacket> produceHandshakePackets(
+            SSLEngine engine, SocketAddress socketAddr) throws Exception {
+        List<DatagramPacket> packets =
+                super.produceHandshakePackets(engine, socketAddr);
+
+        if (needPacketReorder && (!engine.getUseClientMode())) {
+            needPacketReorder = false;
+            Collections.reverse(packets);
+        }
+
+        return packets;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/net/ssl/DTLS/Retransmission.java	Tue Jun 02 09:15:47 2015 -0700
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+
+/*
+ * @test
+ * @bug 8043758
+ * @summary Datagram Transport Layer Security (DTLS)
+ * @compile DTLSOverDatagram.java
+ * @run main/othervm Retransmission
+ */
+
+import java.util.List;
+import java.util.ArrayList;
+import java.net.DatagramPacket;
+import java.net.SocketAddress;
+import javax.net.ssl.SSLEngine;
+
+/**
+ * Test that DTLS implementation is able to do retransmission internally
+ * automatically if packet get lost.
+ */
+public class Retransmission extends DTLSOverDatagram {
+    boolean needPacketLoss = true;
+
+    public static void main(String[] args) throws Exception {
+        Retransmission testCase = new Retransmission();
+        testCase.runTest(testCase);
+    }
+
+    @Override
+    List<DatagramPacket> produceHandshakePackets(
+            SSLEngine engine, SocketAddress socketAddr) throws Exception {
+        List<DatagramPacket> packets =
+                super.produceHandshakePackets(engine, socketAddr);
+
+        if (!needPacketLoss || (!engine.getUseClientMode())) {
+            return packets;
+        }
+
+        List<DatagramPacket> parts = new ArrayList<>();
+        int lostSeq = 2;
+        for (DatagramPacket packet : packets) {
+            lostSeq--;
+            if (lostSeq == 0) {
+                needPacketLoss = false;
+                // loss this packet
+                System.out.println("Loss a packet");
+                continue;
+            }
+
+            parts.add(packet);
+        }
+
+        return parts;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/net/ssl/DTLS/WeakCipherSuite.java	Tue Jun 02 09:15:47 2015 -0700
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+
+/*
+ * @test
+ * @bug 8043758
+ * @summary Datagram Transport Layer Security (DTLS)
+ * @compile DTLSOverDatagram.java
+ * @run main/othervm WeakCipherSuite TLS_DH_anon_WITH_AES_128_GCM_SHA256
+ * @run main/othervm WeakCipherSuite SSL_DH_anon_WITH_DES_CBC_SHA
+ * @run main/othervm WeakCipherSuite SSL_RSA_WITH_DES_CBC_SHA
+ * @run main/othervm WeakCipherSuite SSL_DHE_RSA_WITH_DES_CBC_SHA
+ * @run main/othervm WeakCipherSuite SSL_DHE_DSS_WITH_DES_CBC_SHA
+ */
+
+import javax.net.ssl.SSLEngine;
+import java.security.Security;
+
+/**
+ * Test common DTLS weak cipher suites.
+ */
+public class WeakCipherSuite extends DTLSOverDatagram {
+
+    // use the specific cipher suite
+    volatile static String cipherSuite;
+
+    public static void main(String[] args) throws Exception {
+        // reset security properties to make sure that the algorithms
+        // and keys used in this test are not disabled.
+        Security.setProperty("jdk.tls.disabledAlgorithms", "");
+        Security.setProperty("jdk.certpath.disabledAlgorithms", "");
+
+        cipherSuite = args[0];
+
+        WeakCipherSuite testCase = new WeakCipherSuite();
+        testCase.runTest(testCase);
+    }
+
+    @Override
+    SSLEngine createSSLEngine(boolean isClient) throws Exception {
+        SSLEngine engine = super.createSSLEngine(isClient);
+        engine.setEnabledCipherSuites(new String[]{cipherSuite});
+
+        return engine;
+    }
+}
--- a/jdk/test/javax/net/ssl/SSLEngine/CheckStatus.java	Mon Jun 01 10:15:21 2015 -0700
+++ b/jdk/test/javax/net/ssl/SSLEngine/CheckStatus.java	Tue Jun 02 09:15:47 2015 -0700
@@ -391,7 +391,7 @@
         appOut2.rewind();
 
         log("======================================");
-        log("Client Cert");
+        log("Client Cert and Key Exchange");
         result1 = ssle1.wrap(appOut1, oneToTwo);
         checkResult(appOut1, oneToTwo, result1,
              Status.OK, HandshakeStatus.NEED_WRAP, 0, -1);
@@ -406,22 +406,6 @@
         oneToTwo.compact();
 
         log("======================================");
-        log("Key Exchange");
-        result1 = ssle1.wrap(appOut1, oneToTwo);
-        checkResult(appOut1, oneToTwo, result1,
-             Status.OK, HandshakeStatus.NEED_WRAP, 0, -1);
-
-        oneToTwo.flip();
-        result2 = ssle2.unwrap(oneToTwo, appIn2);
-
-        checkResult(oneToTwo, appIn2, result2,
-             Status.OK, HandshakeStatus.NEED_TASK,
-             result1.bytesProduced(), 0);
-        runDelegatedTasks(ssle2);
-
-        oneToTwo.compact();
-
-        log("======================================");
         log("CCS");
         result1 = ssle1.wrap(appOut1, oneToTwo);
         checkResult(appOut1, oneToTwo, result1,
--- a/jdk/test/javax/net/ssl/SSLEngine/LargeBufs.java	Mon Jun 01 10:15:21 2015 -0700
+++ b/jdk/test/javax/net/ssl/SSLEngine/LargeBufs.java	Tue Jun 02 09:15:47 2015 -0700
@@ -116,7 +116,7 @@
             if ((result2.bytesConsumed() != 0) &&
                 (result2.bytesConsumed() != appBufferMax) &&
                 (result2.bytesConsumed() != 2 * OFFSET)) {
-                throw new Exception("result1: " + result1);
+                throw new Exception("result2: " + result2);
             }
 
             log("wrap1:  " + result1);
--- a/jdk/test/javax/net/ssl/TLS/CipherTestUtils.java	Mon Jun 01 10:15:21 2015 -0700
+++ b/jdk/test/javax/net/ssl/TLS/CipherTestUtils.java	Tue Jun 02 09:15:47 2015 -0700
@@ -406,6 +406,7 @@
                 return params;
             }).forEach((params) -> {
                 try {
+                    System.out.println("Testing " + params);
                     runTest(params);
                     System.out.println("Passed " + params);
                 } catch (Exception e) {
--- a/jdk/test/javax/net/ssl/TLSv11/ExportableBlockCipher.java	Mon Jun 01 10:15:21 2015 -0700
+++ b/jdk/test/javax/net/ssl/TLSv11/ExportableBlockCipher.java	Tue Jun 02 09:15:47 2015 -0700
@@ -23,15 +23,16 @@
  * questions.
  */
 
+//
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+//
+
 /*
  * @test
  * @bug 4873188
  * @summary Support TLS 1.1
  * @run main/othervm ExportableBlockCipher
- *
- *     SunJSSE does not support dynamic system properties, no way to re-use
- *     system properties in samevm/agentvm mode.
- *
  * @author Xuelei Fan
  */
 
@@ -109,7 +110,7 @@
             sslIS.read();
             sslOS.write('A');
             sslOS.flush();
-        } catch (SSLException ssle) {
+        } catch (IOException ioe) {
             // get the expected exception
             interrupted = true;
         } finally {
--- a/jdk/test/javax/net/ssl/TLSv11/ExportableStreamCipher.java	Mon Jun 01 10:15:21 2015 -0700
+++ b/jdk/test/javax/net/ssl/TLSv11/ExportableStreamCipher.java	Tue Jun 02 09:15:47 2015 -0700
@@ -23,15 +23,16 @@
  * questions.
  */
 
+//
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+//
+
 /*
  * @test
  * @bug 4873188
  * @summary Support TLS 1.1
  * @run main/othervm ExportableStreamCipher
- *
- *     SunJSSE does not support dynamic system properties, no way to re-use
- *     system properties in samevm/agentvm mode.
- *
  * @author Xuelei Fan
  */
 
@@ -109,7 +110,7 @@
             sslIS.read();
             sslOS.write('A');
             sslOS.flush();
-        } catch (SSLException ssle) {
+        } catch (IOException ioe) {
             // get the expected exception
             interrupted = true;
         } finally {
--- a/jdk/test/javax/net/ssl/templates/SSLSocketSSLEngineTemplate.java	Mon Jun 01 10:15:21 2015 -0700
+++ b/jdk/test/javax/net/ssl/templates/SSLSocketSSLEngineTemplate.java	Tue Jun 02 09:15:47 2015 -0700
@@ -139,6 +139,11 @@
      * Main entry point for this test.
      */
     public static void main(String args[]) throws Exception {
+        // reset security properties to make sure that the algorithms
+        // and keys used in this test are not disabled.
+        Security.setProperty("jdk.tls.disabledAlgorithms", "");
+        Security.setProperty("jdk.certpath.disabledAlgorithms", "");
+
         if (debug) {
             System.setProperty("javax.net.debug", "all");
         }
@@ -153,7 +158,12 @@
              */
             SSLSocketSSLEngineTemplate test =
                 new SSLSocketSSLEngineTemplate(protocol);
+            log("-------------------------------------");
+            log("Testing " + protocol + " for direct buffers ...");
             test.runTest(true);
+
+            log("---------------------------------------");
+            log("Testing " + protocol + " for indirect buffers ...");
             test.runTest(false);
         }
 
@@ -329,6 +339,10 @@
                 thread.join();
             }
 
+            if (sslSocket != null) {
+                sslSocket.close();
+            }
+
             if (serverException != null) {
                 if (clientException != null) {
                     serverException.initCause(clientException);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/krb5/auto/SSLwithPerms.java	Tue Jun 02 09:15:47 2015 -0700
@@ -0,0 +1,229 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8038089
+ * @summary TLS optional support for Kerberos cipher suites needs to be re-examined
+ * @library ../../../../java/security/testlibrary/
+ * @run main/othervm SSLwithPerms
+ */
+import java.io.*;
+import javax.net.ssl.*;
+import javax.security.auth.AuthPermission;
+import javax.security.auth.kerberos.ServicePermission;
+import java.net.SocketPermission;
+import java.nio.ByteBuffer;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.security.Principal;
+import java.security.Security;
+import java.security.SecurityPermission;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Locale;
+import java.util.PropertyPermission;
+
+import sun.security.jgss.GSSUtil;
+
+public class SSLwithPerms {
+
+    static String KRB5_CONF = "krb5.conf";
+    static String JAAS_CONF = "jaas.conf";
+    static String REALM = "REALM";
+    static String KTAB = "ktab";
+    static String HOST = "host." + REALM.toLowerCase(Locale.US);
+    static String SERVER = "host/" + HOST;
+    static String USER = "user";
+    static char[] PASS = "password".toCharArray();
+
+    public static void main(String[] args) throws Exception {
+
+        Security.setProperty("jdk.tls.disabledAlgorithms", "");
+        if (args.length == 0) {
+            KDC kdc = KDC.create(REALM, HOST, 0, true);
+
+            kdc.addPrincipal(USER, PASS);
+            kdc.addPrincipalRandKey("krbtgt/" + REALM);
+            kdc.addPrincipalRandKey(SERVER);
+            KDC.saveConfig(KRB5_CONF, kdc);
+            kdc.writeKtab(KTAB);
+
+            File f = new File(JAAS_CONF);
+            FileOutputStream fos = new FileOutputStream(f);
+            fos.write((
+                    "ssl {\n" +
+                            "    com.sun.security.auth.module.Krb5LoginModule required\n" +
+                            "    principal=\"" + SERVER + "\"\n" +
+                            "    useKeyTab=true\n" +
+                            "    keyTab=" + KTAB + "\n" +
+                            "    isInitiator=false\n" +
+                            "    storeKey=true;\n};\n"
+            ).getBytes());
+            fos.close();
+
+            Proc pc = Proc.create("SSLwithPerms")
+                    .args("client")
+                    .inheritIO()
+                    .prop("java.security.manager", "")
+                    .prop("java.security.krb5.conf", KRB5_CONF)
+                    .prop("sun.net.spi.nameservice.provider.1", "ns,mock")
+                    .prop("javax.net.ssl", "handshake")
+                    .prop("sun.security.krb5.debug", "true")
+                    .perm(new SecurityPermission("setProperty.jdk.tls.disabledAlgorithms"))
+                    .perm(new PropertyPermission("sun.security.krb5.principal", "read"))
+                    .perm(new FilePermission("port", "read"))
+                    .perm(new FilePermission(KTAB, "read"))
+                    .perm(new RuntimePermission("accessClassInPackage.sun.net.spi.nameservice"))
+                    .perm(new AuthPermission("modifyPrincipals"))
+                    .perm(new AuthPermission("modifyPrivateCredentials"))
+                    .perm(new AuthPermission("doAs"))
+                    .perm(new SocketPermission("127.0.0.1", "connect"))
+                    .perm(new ServicePermission("host/host.realm@REALM", "initiate"))
+                    .start();
+
+            Proc ps = Proc.create("SSLwithPerms")
+                    .args("server")
+                    .inheritIO()
+                    .prop("java.security.manager", "")
+                    .prop("java.security.krb5.conf", KRB5_CONF)
+                    .prop("java.security.auth.login.config", JAAS_CONF)
+                    .prop("javax.net.ssl", "handshake")
+                    .prop("sun.security.krb5.debug", "true")
+                    .perm(new SecurityPermission("setProperty.jdk.tls.disabledAlgorithms"))
+                    .perm(new AuthPermission("createLoginContext.ssl"))
+                    .perm(new AuthPermission("doAs"))
+                    .perm(new FilePermission("port", "write"))
+                    .perm(new SocketPermission("127.0.0.1", "accept"))
+                    .perm(new ServicePermission("host/host.realm@REALM", "accept"))
+                    .start();
+
+            if (pc.waitFor() != 0) {
+                throw new Exception();
+            }
+            if (ps.waitFor() != 0) {
+                throw new Exception();
+            }
+        } else if (args[0].equals("client")) {
+            Context c;
+            c = Context.fromUserPass(USER, PASS, false);
+            c.doAs(new JsseClientAction(), null);
+        } else if (args[0].equals("server")) {
+            final Context s = Context.fromJAAS("ssl");
+            s.doAs(new JsseServerAction(), null);
+        }
+    }
+
+    private static class JsseClientAction implements Action {
+        public byte[] run(Context s, byte[] input) throws Exception {
+            SSLSocketFactory sslsf =
+                (SSLSocketFactory) SSLSocketFactory.getDefault();
+            while (!Files.exists(Paths.get("port"))) {
+                Thread.sleep(100);
+            }
+            int port = ByteBuffer.allocate(4)
+                    .put(Files.readAllBytes(Paths.get("port"))).getInt(0);
+            System.out.println("Connecting " + SERVER + ":" + port);
+            SSLSocket sslSocket = (SSLSocket) sslsf.createSocket(HOST, port);
+
+            // Enable only a KRB5 cipher suite.
+            String enabledSuites[] = {"TLS_KRB5_WITH_RC4_128_SHA"};
+            sslSocket.setEnabledCipherSuites(enabledSuites);
+
+            SSLParameters params = sslSocket.getSSLParameters();
+            params.setServerNames(Collections.singletonList(new SNIHostName(HOST)));
+            sslSocket.setSSLParameters(params);
+
+            BufferedReader in = new BufferedReader(new InputStreamReader(
+                sslSocket.getInputStream()));
+            BufferedWriter out = new BufferedWriter(new OutputStreamWriter(
+                sslSocket.getOutputStream()));
+
+            String outStr = "Hello There!\n";
+            out.write(outStr);
+            out.flush();
+            System.out.print("Sending " + outStr);
+
+            String inStr = in.readLine();
+            System.out.println("Received " + inStr);
+
+            String cipherSuiteChosen = sslSocket.getSession().getCipherSuite();
+            System.out.println("Cipher suite in use: " + cipherSuiteChosen);
+            Principal self = sslSocket.getSession().getLocalPrincipal();
+            System.out.println("I am: " + self.toString());
+            Principal peer = sslSocket.getSession().getPeerPrincipal();
+            System.out.println("Server is: " + peer.toString());
+
+            sslSocket.close();
+            return null;
+        }
+    }
+
+    private static class JsseServerAction implements Action {
+        public byte[] run(Context s, byte[] input) throws Exception {
+            SSLServerSocketFactory sslssf =
+                (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
+            SSLServerSocket sslServerSocket =
+                (SSLServerSocket) sslssf.createServerSocket(0); // any port
+            int port = sslServerSocket.getLocalPort();
+            System.out.println("Listening on " + port);
+
+            String enabledSuites[] = {"TLS_KRB5_WITH_RC4_128_SHA"};
+            sslServerSocket.setEnabledCipherSuites(enabledSuites);
+
+            Files.write(Paths.get("port"), ByteBuffer.allocate(4).putInt(port).array());
+            System.out.println("Waiting for incoming connection...");
+
+            SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
+
+            System.out.println("Got connection from client "
+                + sslSocket.getInetAddress());
+
+            BufferedReader in = new BufferedReader(new InputStreamReader(
+                sslSocket.getInputStream()));
+            BufferedWriter out = new BufferedWriter(new OutputStreamWriter(
+                sslSocket.getOutputStream()));
+
+            String inStr = in.readLine();
+            System.out.println("Received " + inStr);
+
+            String outStr = inStr + " " + new Date().toString() + "\n";
+            out.write(outStr);
+            System.out.println("Sending " + outStr);
+            out.flush();
+
+            String cipherSuiteChosen =
+                sslSocket.getSession().getCipherSuite();
+            System.out.println("Cipher suite in use: " + cipherSuiteChosen);
+            Principal self = sslSocket.getSession().getLocalPrincipal();
+            System.out.println("I am: " + self.toString());
+            Principal peer = sslSocket.getSession().getPeerPrincipal();
+            System.out.println("Client is: " + peer.toString());
+
+            sslSocket.close();
+            return null;
+        }
+    }
+}
--- a/jdk/test/sun/security/ssl/AppInputStream/ReadHandshake.java	Mon Jun 01 10:15:21 2015 -0700
+++ b/jdk/test/sun/security/ssl/AppInputStream/ReadHandshake.java	Tue Jun 02 09:15:47 2015 -0700
@@ -21,19 +21,22 @@
  * questions.
  */
 
+//
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+//
+
 /*
  * @test
  * @bug 4514971
  * @summary Verify applications do not read handshake data after failure
  * @run main/othervm ReadHandshake
- *
- *     SunJSSE does not support dynamic system properties, no way to re-use
- *     system properties in samevm/agentvm mode.
  */
 
 import java.io.*;
 import java.net.*;
 import javax.net.ssl.*;
+import java.security.Security;
 
 public class ReadHandshake {
 
@@ -219,6 +222,10 @@
     volatile Exception clientException = null;
 
     public static void main(String[] args) throws Exception {
+        // reset security properties to make sure that the algorithms
+        // and keys used in this test are not disabled.
+        Security.setProperty("jdk.tls.disabledAlgorithms", "");
+        Security.setProperty("jdk.certpath.disabledAlgorithms", "");
 
         if (debug)
             System.setProperty("javax.net.debug", "all");
--- a/jdk/test/sun/security/ssl/ClientHandshaker/LengthCheckTest.java	Mon Jun 01 10:15:21 2015 -0700
+++ b/jdk/test/sun/security/ssl/ClientHandshaker/LengthCheckTest.java	Tue Jun 02 09:15:47 2015 -0700
@@ -233,7 +233,7 @@
             // sent back to the server.
             if (gotException == false ||
                 !isTlsMessage(cTOs, TLS_RECTYPE_ALERT, TLS_ALERT_LVL_FATAL,
-                        TLS_ALERT_INTERNAL_ERROR)) {
+                        TLS_ALERT_UNEXPECTED_MSG)) {
                 throw new SSLException(
                     "Client failed to throw Alert:fatal:internal_error");
             }
@@ -285,7 +285,7 @@
             // sent back to the client.
             if (gotException == false ||
                 !isTlsMessage(sTOc, TLS_RECTYPE_ALERT, TLS_ALERT_LVL_FATAL,
-                        TLS_ALERT_INTERNAL_ERROR)) {
+                        TLS_ALERT_UNEXPECTED_MSG)) {
                 throw new SSLException(
                     "Server failed to throw Alert:fatal:internal_error");
             }