changeset 5988:3f62cfc4e83d

7068321: Support TLS Server Name Indication (SNI) Extension in JSSE Server Reviewed-by: mullan, weijun, wetmore
author xuelei
date Thu, 18 Oct 2012 01:14:00 -0700
parents 60994591be6b
children 27f854a1e5c5 46b24eb85b86
files src/share/classes/javax/net/ssl/ExtendedSSLSession.java src/share/classes/javax/net/ssl/HandshakeCompletedEvent.java src/share/classes/javax/net/ssl/SNIHostName.java src/share/classes/javax/net/ssl/SNIMatcher.java src/share/classes/javax/net/ssl/SNIServerName.java src/share/classes/javax/net/ssl/SSLEngine.java src/share/classes/javax/net/ssl/SSLParameters.java src/share/classes/javax/net/ssl/SSLServerSocket.java src/share/classes/javax/net/ssl/SSLSocket.java src/share/classes/javax/net/ssl/SSLSocketFactory.java src/share/classes/javax/net/ssl/StandardConstants.java src/share/classes/sun/security/ssl/BaseSSLSocketImpl.java src/share/classes/sun/security/ssl/ClientHandshaker.java src/share/classes/sun/security/ssl/HandshakeInStream.java src/share/classes/sun/security/ssl/HandshakeMessage.java src/share/classes/sun/security/ssl/Handshaker.java src/share/classes/sun/security/ssl/HelloExtensions.java src/share/classes/sun/security/ssl/ProtocolList.java src/share/classes/sun/security/ssl/SSLEngineImpl.java src/share/classes/sun/security/ssl/SSLServerSocketImpl.java src/share/classes/sun/security/ssl/SSLSessionImpl.java src/share/classes/sun/security/ssl/SSLSocketFactoryImpl.java src/share/classes/sun/security/ssl/SSLSocketImpl.java src/share/classes/sun/security/ssl/ServerHandshaker.java src/share/classes/sun/security/ssl/SunJSSE.java src/share/classes/sun/security/ssl/Utilities.java src/share/classes/sun/security/ssl/X509KeyManagerImpl.java src/share/classes/sun/security/ssl/X509TrustManagerImpl.java test/sun/security/ssl/javax/net/ssl/NewAPIs/SSLEngine/LargePacket.java test/sun/security/ssl/javax/net/ssl/NewAPIs/SSLEngine/SSLEngineService.java test/sun/security/ssl/javax/net/ssl/ServerName/SSLEngineExplorer.java test/sun/security/ssl/javax/net/ssl/ServerName/SSLEngineExplorerMatchedSNI.java test/sun/security/ssl/javax/net/ssl/ServerName/SSLEngineExplorerUnmatchedSNI.java test/sun/security/ssl/javax/net/ssl/ServerName/SSLEngineExplorerWithCli.java test/sun/security/ssl/javax/net/ssl/ServerName/SSLEngineExplorerWithSrv.java test/sun/security/ssl/javax/net/ssl/ServerName/SSLSocketConsistentSNI.java test/sun/security/ssl/javax/net/ssl/ServerName/SSLSocketExplorer.java test/sun/security/ssl/javax/net/ssl/ServerName/SSLSocketExplorerFailure.java test/sun/security/ssl/javax/net/ssl/ServerName/SSLSocketExplorerMatchedSNI.java test/sun/security/ssl/javax/net/ssl/ServerName/SSLSocketExplorerUnmatchedSNI.java test/sun/security/ssl/javax/net/ssl/ServerName/SSLSocketExplorerWithCliSNI.java test/sun/security/ssl/javax/net/ssl/ServerName/SSLSocketExplorerWithSrvSNI.java test/sun/security/ssl/javax/net/ssl/ServerName/SSLSocketInconsistentSNI.java test/sun/security/ssl/javax/net/ssl/ServerName/SSLSocketSNISensitive.java test/sun/security/ssl/templates/SSLCapabilities.java test/sun/security/ssl/templates/SSLExplorer.java
diffstat 46 files changed, 8169 insertions(+), 288 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/javax/net/ssl/ExtendedSSLSession.java	Wed Oct 17 13:22:39 2012 -0700
+++ b/src/share/classes/javax/net/ssl/ExtendedSSLSession.java	Thu Oct 18 01:14:00 2012 -0700
@@ -25,6 +25,8 @@
 
 package javax.net.ssl;
 
+import java.util.List;
+
 /**
  * Extends the <code>SSLSession</code> interface to support additional
  * session attributes.
@@ -83,4 +85,34 @@
      * @see X509ExtendedKeyManager
      */
     public abstract String[] getPeerSupportedSignatureAlgorithms();
+
+    /**
+     * Obtains a {@link List} containing all {@link SNIServerName}s
+     * of the requested Server Name Indication (SNI) extension.
+     * <P>
+     * In server mode, unless the return {@link List} is empty,
+     * the server should use the requested server names to guide its
+     * selection of an appropriate authentication certificate, and/or
+     * other aspects of security policy.
+     * <P>
+     * In client mode, unless the return {@link List} is empty,
+     * the client should use the requested server names to guide its
+     * endpoint identification of the peer's identity, and/or
+     * other aspects of security policy.
+     *
+     * @return a non-null immutable list of {@link SNIServerName}s of the
+     *         requested server name indications. The returned list may be
+     *         empty if no server name indications were requested.
+     * @throws UnsupportedOperationException if the underlying provider
+     *         does not implement the operation
+     *
+     * @see SNIServerName
+     * @see X509ExtendedTrustManager
+     * @see X509ExtendedKeyManager
+     *
+     * @since 1.8
+     */
+    public List<SNIServerName> getRequestedServerNames() {
+        throw new UnsupportedOperationException();
+    }
 }
--- a/src/share/classes/javax/net/ssl/HandshakeCompletedEvent.java	Wed Oct 17 13:22:39 2012 -0700
+++ b/src/share/classes/javax/net/ssl/HandshakeCompletedEvent.java	Thu Oct 18 01:14:00 2012 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2003, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 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
@@ -186,8 +186,7 @@
             // if the provider does not support it, fallback to peer certs.
             // return the X500Principal of the end-entity cert.
             Certificate[] certs = getPeerCertificates();
-            principal = (X500Principal)
-                ((X509Certificate)certs[0]).getSubjectX500Principal();
+            principal = ((X509Certificate)certs[0]).getSubjectX500Principal();
         }
         return principal;
     }
@@ -216,7 +215,7 @@
             // return the X500Principal of the end-entity cert.
             Certificate[] certs = getLocalCertificates();
             if (certs != null) {
-                principal = (X500Principal)
+                principal =
                         ((X509Certificate)certs[0]).getSubjectX500Principal();
             }
         }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/javax/net/ssl/SNIHostName.java	Thu Oct 18 01:14:00 2012 -0700
@@ -0,0 +1,394 @@
+/*
+ * Copyright (c) 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 javax.net.ssl;
+
+import java.net.IDN;
+import java.nio.ByteBuffer;
+import java.nio.charset.CodingErrorAction;
+import java.nio.charset.StandardCharsets;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CharacterCodingException;
+import java.util.Locale;
+import java.util.Objects;
+import java.util.regex.Pattern;
+
+/**
+ * Instances of this class represent a server name of type
+ * {@link StandardConstants#SNI_HOST_NAME host_name} in a Server Name
+ * Indication (SNI) extension.
+ * <P>
+ * As described in section 3, "Server Name Indication", of
+ * <A HREF="http://www.ietf.org/rfc/rfc6066.txt">TLS Extensions (RFC 6066)</A>,
+ * "HostName" contains the fully qualified DNS hostname of the server, as
+ * understood by the client.  The encoded server name value of a hostname is
+ * represented as a byte string using ASCII encoding without a trailing dot.
+ * This allows the support of Internationalized Domain Names (IDN) through
+ * the use of A-labels (the ASCII-Compatible Encoding (ACE) form of a valid
+ * string of Internationalized Domain Names for Applications (IDNA)) defined
+ * in <A HREF="http://www.ietf.org/rfc/rfc5890.txt">RFC 5890</A>.
+ * <P>
+ * Note that {@code SNIHostName} objects are immutable.
+ *
+ * @see SNIServerName
+ * @see StandardConstants#SNI_HOST_NAME
+ *
+ * @since 1.8
+ */
+public final class SNIHostName extends SNIServerName {
+
+    // the decoded string value of the server name
+    private final String hostname;
+
+    /**
+     * Creates an {@code SNIHostName} using the specified hostname.
+     * <P>
+     * Note that per <A HREF="http://www.ietf.org/rfc/rfc6066.txt">RFC 6066</A>,
+     * the encoded server name value of a hostname is
+     * {@link StandardCharsets#US_ASCII}-compliant.  In this method,
+     * {@code hostname} can be a user-friendly Internationalized Domain Name
+     * (IDN).  {@link IDN#toASCII(String, int)} is used to enforce the
+     * restrictions on ASCII characters in hostnames (see
+     * <A HREF="http://www.ietf.org/rfc/rfc3490.txt">RFC 3490</A>,
+     * <A HREF="http://www.ietf.org/rfc/rfc1122.txt">RFC 1122</A>,
+     * <A HREF="http://www.ietf.org/rfc/rfc1123.txt">RFC 1123</A>) and
+     * translate the {@code hostname} into ASCII Compatible Encoding (ACE), as:
+     * <pre>
+     *     IDN.toASCII(hostname, IDN.USE_STD3_ASCII_RULES);
+     * </pre>
+     * <P>
+     * The {@code hostname} argument is illegal if it:
+     * <ul>
+     * <li> {@code hostname} is empty,</li>
+     * <li> {@code hostname} ends with a trailing dot,</li>
+     * <li> {@code hostname} is not a valid Internationalized
+     *      Domain Name (IDN) compliant with the RFC 3490 specification.</li>
+     * </ul>
+     * @param  hostname
+     *         the hostname of this server name
+     *
+     * @throws NullPointerException if {@code hostname} is {@code null}
+     * @throws IllegalArgumentException if {@code hostname} is illegal
+     */
+    public SNIHostName(String hostname) {
+        // IllegalArgumentException will be thrown if {@code hostname} is
+        // not a valid IDN.
+        super(StandardConstants.SNI_HOST_NAME,
+                (hostname = IDN.toASCII(
+                    Objects.requireNonNull(hostname,
+                        "Server name value of host_name cannot be null"),
+                    IDN.USE_STD3_ASCII_RULES))
+                .getBytes(StandardCharsets.US_ASCII));
+
+        this.hostname = hostname;
+
+        // check the validity of the string hostname
+        checkHostName();
+    }
+
+    /**
+     * Creates an {@code SNIHostName} using the specified encoded value.
+     * <P>
+     * This method is normally used to parse the encoded name value in a
+     * requested SNI extension.
+     * <P>
+     * Per <A HREF="http://www.ietf.org/rfc/rfc6066.txt">RFC 6066</A>,
+     * the encoded name value of a hostname is
+     * {@link StandardCharsets#US_ASCII}-compliant.  However, in the previous
+     * version of the SNI extension (
+     * <A HREF="http://www.ietf.org/rfc/rfc4366.txt">RFC 4366</A>),
+     * the encoded hostname is represented as a byte string using UTF-8
+     * encoding.  For the purpose of version tolerance, this method allows
+     * that the charset of {@code encoded} argument can be
+     * {@link StandardCharsets#UTF_8}, as well as
+     * {@link StandardCharsets#US_ASCII}.  {@link IDN#toASCII(String)} is used
+     * to translate the {@code encoded} argument into ASCII Compatible
+     * Encoding (ACE) hostname.
+     * <P>
+     * It is strongly recommended that this constructor is only used to parse
+     * the encoded name value in a requested SNI extension.  Otherwise, to
+     * comply with <A HREF="http://www.ietf.org/rfc/rfc6066.txt">RFC 6066</A>,
+     * please always use {@link StandardCharsets#US_ASCII}-compliant charset
+     * and enforce the restrictions on ASCII characters in hostnames (see
+     * <A HREF="http://www.ietf.org/rfc/rfc3490.txt">RFC 3490</A>,
+     * <A HREF="http://www.ietf.org/rfc/rfc1122.txt">RFC 1122</A>,
+     * <A HREF="http://www.ietf.org/rfc/rfc1123.txt">RFC 1123</A>)
+     * for {@code encoded} argument, or use {@link SNIHostName(String)} instead.
+     * <P>
+     * The {@code encoded} argument is illegal if it:
+     * <ul>
+     * <li> {@code encoded} is empty,</li>
+     * <li> {@code encoded} ends with a trailing dot,</li>
+     * <li> {@code encoded} is not encoded in
+     *      {@link StandardCharsets#US_ASCII} or
+     *      {@link StandardCharsets#UTF_8}-compliant charset,</li>
+     * <li> {@code encoded} is not a valid Internationalized
+     *      Domain Name (IDN) compliant with the RFC 3490 specification.</li>
+     * </ul>
+     *
+     * <P>
+     * Note that the {@code encoded} byte array is cloned
+     * to protect against subsequent modification.
+     *
+     * @param  encoded
+     *         the encoded hostname of this server name
+     *
+     * @throws NullPointerException if {@code encoded} is {@code null}
+     * @throws IllegalArgumentException if {@code encoded} is illegal
+     */
+    public SNIHostName(byte[] encoded) {
+        // NullPointerException will be thrown if {@code encoded} is null
+        super(StandardConstants.SNI_HOST_NAME, encoded);
+
+        // Compliance: RFC 4366 requires that the hostname is represented
+        // as a byte string using UTF_8 encoding [UTF8]
+        try {
+            // Please don't use {@link String} constructors because they
+            // do not report coding errors.
+            CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder()
+                    .onMalformedInput(CodingErrorAction.REPORT)
+                    .onUnmappableCharacter(CodingErrorAction.REPORT);
+
+            this.hostname = IDN.toASCII(
+                    decoder.decode(ByteBuffer.wrap(encoded)).toString());
+        } catch (RuntimeException | CharacterCodingException e) {
+            throw new IllegalArgumentException(
+                        "The encoded server name value is invalid", e);
+        }
+
+        // check the validity of the string hostname
+        checkHostName();
+    }
+
+    /**
+     * Returns the {@link StandardCharsets#US_ASCII}-compliant hostname of
+     * this {@code SNIHostName} object.
+     * <P>
+     * Note that, per
+     * <A HREF="http://www.ietf.org/rfc/rfc6066.txt">RFC 6066</A>, the
+     * returned hostname may be an internationalized domain name that
+     * contains A-labels. See
+     * <A HREF="http://www.ietf.org/rfc/rfc5890.txt">RFC 5890</A>
+     * for more information about the detailed A-label specification.
+     *
+     * @return the {@link StandardCharsets#US_ASCII}-compliant hostname
+     *         of this {@code SNIHostName} object
+     */
+    public String getAsciiName() {
+        return hostname;
+    }
+
+    /**
+     * Compares this server name to the specified object.
+     * <P>
+     * Per <A HREF="http://www.ietf.org/rfc/rfc6066.txt">RFC 6066</A>, DNS
+     * hostnames are case-insensitive.  Two server hostnames are equal if,
+     * and only if, they have the same name type, and the hostnames are
+     * equal in a case-independent comparison.
+     *
+     * @param  other
+     *         the other server name object to compare with.
+     * @return true if, and only if, the {@code other} is considered
+     *         equal to this instance
+     */
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        }
+
+        if (other instanceof SNIHostName) {
+            return hostname.equalsIgnoreCase(((SNIHostName)other).hostname);
+        }
+
+        return false;
+    }
+
+    /**
+     * Returns a hash code value for this {@code SNIHostName}.
+     * <P>
+     * The hash code value is generated using the case-insensitive hostname
+     * of this {@code SNIHostName}.
+     *
+     * @return a hash code value for this {@code SNIHostName}.
+     */
+    @Override
+    public int hashCode() {
+        int result = 17;        // 17/31: prime number to decrease collisions
+        result = 31 * result + hostname.toUpperCase(Locale.ENGLISH).hashCode();
+
+        return result;
+    }
+
+    /**
+     * Returns a string representation of the object, including the DNS
+     * hostname in this {@code SNIHostName} object.
+     * <P>
+     * The exact details of the representation are unspecified and subject
+     * to change, but the following may be regarded as typical:
+     * <pre>
+     *     "type=host_name (0), value={@literal <hostname>}"
+     * </pre>
+     * The "{@literal <hostname>}" is an ASCII representation of the hostname,
+     * which may contains A-labels.  For example, a returned value of an pseudo
+     * hostname may look like:
+     * <pre>
+     *     "type=host_name (0), value=www.example.com"
+     * </pre>
+     * or
+     * <pre>
+     *     "type=host_name (0), value=xn--fsqu00a.xn--0zwm56d"
+     * </pre>
+     * <P>
+     * Please NOTE that the exact details of the representation are unspecified
+     * and subject to change.
+     *
+     * @return a string representation of the object.
+     */
+    @Override
+    public String toString() {
+        return "type=host_name (0), value=" + hostname;
+    }
+
+    /**
+     * Creates an {@link SNIMatcher} object for {@code SNIHostName}s.
+     * <P>
+     * This method can be used by a server to verify the acceptable
+     * {@code SNIHostName}s.  For example,
+     * <pre>
+     *     SNIMatcher matcher =
+     *         SNIHostName.createSNIMatcher("www\\.example\\.com");
+     * </pre>
+     * will accept the hostname "www.example.com".
+     * <pre>
+     *     SNIMatcher matcher =
+     *         SNIHostName.createSNIMatcher("www\\.example\\.(com|org)");
+     * </pre>
+     * will accept hostnames "www.example.com" and "www.example.org".
+     *
+     * @param  regex
+     *         the <a href="{@docRoot}/java/util/regex/Pattern.html#sum">
+     *         regular expression pattern</a>
+     *         representing the hostname(s) to match
+     * @throws NullPointerException if {@code regex} is
+     *         {@code null}
+     * @throws PatternSyntaxException if the regular expression's syntax
+     *         is invalid
+     */
+    public static SNIMatcher createSNIMatcher(String regex) {
+        if (regex == null) {
+            throw new NullPointerException(
+                "The regular expression cannot be null");
+        }
+
+        return new SNIHostNameMatcher(regex);
+    }
+
+    // check the validity of the string hostname
+    private void checkHostName() {
+        if (hostname.isEmpty()) {
+            throw new IllegalArgumentException(
+                "Server name value of host_name cannot be empty");
+        }
+
+        if (hostname.endsWith(".")) {
+            throw new IllegalArgumentException(
+                "Server name value of host_name cannot have the trailing dot");
+        }
+    }
+
+    private final static class SNIHostNameMatcher extends SNIMatcher {
+
+        // the compiled representation of a regular expression.
+        private final Pattern pattern;
+
+        /**
+         * Creates an SNIHostNameMatcher object.
+         *
+         * @param  regex
+         *         the <a href="{@docRoot}/java/util/regex/Pattern.html#sum">
+         *         regular expression pattern</a>
+         *         representing the hostname(s) to match
+         * @throws NullPointerException if {@code regex} is
+         *         {@code null}
+         * @throws PatternSyntaxException if the regular expression's syntax
+         *         is invalid
+         */
+        SNIHostNameMatcher(String regex) {
+            super(StandardConstants.SNI_HOST_NAME);
+            pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
+        }
+
+        /**
+         * Attempts to match the given {@link SNIServerName}.
+         *
+         * @param  serverName
+         *         the {@link SNIServerName} instance on which this matcher
+         *         performs match operations
+         *
+         * @return {@code true} if, and only if, the matcher matches the
+         *         given {@code serverName}
+         *
+         * @throws NullPointerException if {@code serverName} is {@code null}
+         * @throws IllegalArgumentException if {@code serverName} is
+         *         not of {@code StandardConstants#SNI_HOST_NAME} type
+         *
+         * @see SNIServerName
+         */
+        @Override
+        public boolean matches(SNIServerName serverName) {
+            if (serverName == null) {
+                throw new NullPointerException(
+                    "The SNIServerName argument cannot be null");
+            }
+
+            SNIHostName hostname;
+            if (!(serverName instanceof SNIHostName)) {
+                if (serverName.getType() != StandardConstants.SNI_HOST_NAME) {
+                    throw new IllegalArgumentException(
+                        "The server name type is not host_name");
+                }
+
+                try {
+                    hostname = new SNIHostName(serverName.getEncoded());
+                } catch (NullPointerException | IllegalArgumentException e) {
+                    return false;
+                }
+            } else {
+                hostname = (SNIHostName)serverName;
+            }
+
+            // Let's first try the ascii name matching
+            String asciiName = hostname.getAsciiName();
+            if (pattern.matcher(asciiName).matches()) {
+                return true;
+            }
+
+            // May be an internationalized domain name, check the Unicode
+            // representations.
+            return pattern.matcher(IDN.toUnicode(asciiName)).matches();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/javax/net/ssl/SNIMatcher.java	Thu Oct 18 01:14:00 2012 -0700
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 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 javax.net.ssl;
+
+/**
+ * Instances of this class represent a matcher that performs match
+ * operations on an {@link SNIServerName} instance.
+ * <P>
+ * Servers can use Server Name Indication (SNI) information to decide if
+ * specific {@link SSLSocket} or {@link SSLEngine} instances should accept
+ * a connection.  For example, when multiple "virtual" or "name-based"
+ * servers are hosted on a single underlying network address, the server
+ * application can use SNI information to determine whether this server is
+ * the exact server that the client wants to access.  Instances of this
+ * class can be used by a server to verify the acceptable server names of
+ * a particular type, such as host names.
+ * <P>
+ * {@code SNIMatcher} objects are immutable.  Subclasses should not provide
+ * methods that can change the state of an instance once it has been created.
+ *
+ * @see SNIServerName
+ * @see SNIHostName
+ * @see SSLParameters#getSNIMatchers()
+ * @see SSLParameters#setSNIMatchers(Collection<SNIMatcher>)
+ *
+ * @since 1.8
+ */
+public abstract class SNIMatcher {
+
+    // the type of the server name that this matcher performs on
+    private final int type;
+
+    /**
+     * Creates an {@code SNIMatcher} using the specified server name type.
+     *
+     * @param  type
+     *         the type of the server name that this matcher performs on
+     *
+     * @throws IllegalArgumentException if {@code type} is not in the range
+     *         of 0 to 255, inclusive.
+     */
+    protected SNIMatcher(int type) {
+        if (type < 0) {
+            throw new IllegalArgumentException(
+                "Server name type cannot be less than zero");
+        } else if (type > 255) {
+            throw new IllegalArgumentException(
+                "Server name type cannot be greater than 255");
+        }
+
+        this.type = type;
+    }
+
+    /**
+     * Returns the server name type of this {@code SNIMatcher} object.
+     *
+     * @return the server name type of this {@code SNIMatcher} object.
+     *
+     * @see SNIServerName
+     */
+    public final int getType() {
+        return type;
+    }
+
+    /**
+     * Attempts to match the given {@link SNIServerName}.
+     *
+     * @param  serverName
+     *         the {@link SNIServerName} instance on which this matcher
+     *         performs match operations
+     *
+     * @return {@code true} if, and only if, the matcher matches the
+     *         given {@code serverName}
+     *
+     * @throws NullPointerException if {@code serverName} is {@code null}
+     * @throws IllegalArgumentException if {@code serverName} is
+     *         not of the given server name type of this matcher
+     *
+     * @see SNIServerName
+     */
+    public abstract boolean matches(SNIServerName serverName);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/javax/net/ssl/SNIServerName.java	Thu Oct 18 01:14:00 2012 -0700
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 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 javax.net.ssl;
+
+import java.util.Arrays;
+
+/**
+ * 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
+ * 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>.
+ * <P>
+ * {@code SNIServerName} objects are immutable.  Subclasses should not provide
+ * methods that can change the state of an instance once it has been created.
+ *
+ * @see SSLParameters#getServerNames()
+ * @see SSLParameters#setServerNames(List<SNIServerName>)
+ *
+ * @since 1.8
+ */
+public abstract class SNIServerName {
+
+    // the type of the server name
+    private final int type;
+
+    // the encoded value of the server name
+    private final byte[] encoded;
+
+    // the hex digitals
+    private static final char[] HEXES = "0123456789ABCDEF".toCharArray();
+
+    /**
+     * Creates an {@code SNIServerName} using the specified name type and
+     * encoded value.
+     * <P>
+     * Note that the {@code encoded} byte array is cloned to protect against
+     * subsequent modification.
+     *
+     * @param  type
+     *         the type of the server name
+     * @param  encoded
+     *         the encoded value of the server name
+     *
+     * @throws IllegalArgumentException if {@code type} is not in the range
+     *         of 0 to 255, inclusive.
+     * @throws NullPointerException if {@code encoded} is null
+     */
+    protected SNIServerName(int type, byte[] encoded) {
+        if (type < 0) {
+            throw new IllegalArgumentException(
+                "Server name type cannot be less than zero");
+        } else if (type > 255) {
+            throw new IllegalArgumentException(
+                "Server name type cannot be greater than 255");
+        }
+        this.type = type;
+
+        if (encoded == null) {
+            throw new NullPointerException(
+                "Server name encoded value cannot be null");
+        }
+        this.encoded = encoded.clone();
+    }
+
+
+    /**
+     * Returns the name type of this server name.
+     *
+     * @return the name type of this server name
+     */
+    public final int getType() {
+        return type;
+    }
+
+    /**
+     * Returns a copy of the encoded server name value of this server name.
+     *
+     * @return a copy of the encoded server name value of this server name
+     */
+    public final byte[] getEncoded() {
+        return encoded.clone();
+    }
+
+    /**
+     * Indicates whether some other object is "equal to" this server name.
+     *
+     * @return true if, and only if, {@code other} is of the same class
+     *         of this object, and has the same name type and
+     *         encoded value as this server name.
+     */
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        }
+
+        if (this.getClass() != other.getClass()) {
+            return false;
+        }
+
+        SNIServerName that = (SNIServerName)other;
+        return (this.type == that.type) &&
+                    Arrays.equals(this.encoded, that.encoded);
+    }
+
+    /**
+     * Returns a hash code value for this server name.
+     * <P>
+     * The hash code value is generated using the name type and encoded
+     * value of this server name.
+     *
+     * @return a hash code value for this server name.
+     */
+    @Override
+    public int hashCode() {
+        int result = 17;    // 17/31: prime number to decrease collisions
+        result = 31 * result + type;
+        result = 31 * result + Arrays.hashCode(encoded);
+
+        return result;
+    }
+
+    /**
+     * Returns a string representation of this server name, including the server
+     * name type and the encoded server name value in this
+     * {@code SNIServerName} object.
+     * <P>
+     * The exact details of the representation are unspecified and subject
+     * to change, but the following may be regarded as typical:
+     * <pre>
+     *     "type={@literal <name type>}, value={@literal <name value>}"
+     * </pre>
+     * <P>
+     * In this class, the format of "{@literal <name type>}" is
+     * "[LITERAL] (INTEGER)", where the optional "LITERAL" is the literal
+     * name, and INTEGER is the integer value of the name type.  The format
+     * of "{@literal <name value>}" is "XX:...:XX", where "XX" is the
+     * hexadecimal digit representation of a byte value. For example, a
+     * returned value of an pseudo server name may look like:
+     * <pre>
+     *     "type=(31), value=77:77:77:2E:65:78:61:6D:70:6C:65:2E:63:6E"
+     * </pre>
+     * or
+     * <pre>
+     *     "type=host_name (0), value=77:77:77:2E:65:78:61:6D:70:6C:65:2E:63:6E"
+     * </pre>
+     *
+     * <P>
+     * Please NOTE that the exact details of the representation are unspecified
+     * and subject to change, and subclasses may override the method with
+     * their own formats.
+     *
+     * @return a string representation of this server name
+     */
+    @Override
+    public String toString() {
+        if (type == StandardConstants.SNI_HOST_NAME) {
+            return "type=host_name (0), value=" + toHexString(encoded);
+        } else {
+            return "type=(" + type + "), value=" + toHexString(encoded);
+        }
+    }
+
+    // convert byte array to hex string
+    private static String toHexString(byte[] bytes) {
+        if (bytes.length == 0) {
+            return "(empty)";
+        }
+
+        StringBuilder sb = new StringBuilder(bytes.length * 3 - 1);
+        boolean isInitial = true;
+        for (byte b : bytes) {
+            if (isInitial) {
+                isInitial = false;
+            } else {
+                sb.append(':');
+            }
+
+            int k = b & 0xFF;
+            sb.append(HEXES[k >>> 4]);
+            sb.append(HEXES[k & 0xF]);
+        }
+
+        return sb.toString();
+    }
+}
+
--- a/src/share/classes/javax/net/ssl/SSLEngine.java	Wed Oct 17 13:22:39 2012 -0700
+++ b/src/share/classes/javax/net/ssl/SSLEngine.java	Thu Oct 18 01:14:00 2012 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 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
@@ -1214,15 +1214,19 @@
      *
      * <p>This means:
      * <ul>
-     * <li>if <code>params.getCipherSuites()</code> is non-null,
-     *   <code>setEnabledCipherSuites()</code> is called with that value
-     * <li>if <code>params.getProtocols()</code> is non-null,
-     *   <code>setEnabledProtocols()</code> is called with that value
-     * <li>if <code>params.getNeedClientAuth()</code> or
-     *   <code>params.getWantClientAuth()</code> return <code>true</code>,
-     *   <code>setNeedClientAuth(true)</code> and
-     *   <code>setWantClientAuth(true)</code> are called, respectively;
-     *   otherwise <code>setWantClientAuth(false)</code> is called.
+     * <li>If {@code params.getCipherSuites()} is non-null,
+     *   {@code setEnabledCipherSuites()} is called with that value.</li>
+     * <li>If {@code params.getProtocols()} is non-null,
+     *   {@code setEnabledProtocols()} is called with that value.</li>
+     * <li>If {@code params.getNeedClientAuth()} or
+     *   {@code params.getWantClientAuth()} return {@code true},
+     *   {@code setNeedClientAuth(true)} and
+     *   {@code setWantClientAuth(true)} are called, respectively;
+     *   otherwise {@code setWantClientAuth(false)} is called.</li>
+     * <li>If {@code params.getServerNames()} is non-null, the engine will
+     *   configure its server names with that value.</li>
+     * <li>If {@code params.getSNIMatchers()} is non-null, the engine will
+     *   configure its SNI matchers with that value.</li>
      * </ul>
      *
      * @param params the parameters
--- a/src/share/classes/javax/net/ssl/SSLParameters.java	Wed Oct 17 13:22:39 2012 -0700
+++ b/src/share/classes/javax/net/ssl/SSLParameters.java	Thu Oct 18 01:14:00 2012 -0700
@@ -26,13 +26,23 @@
 package javax.net.ssl;
 
 import java.security.AlgorithmConstraints;
+import java.util.Map;
+import java.util.List;
+import java.util.HashSet;
+import java.util.HashMap;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.regex.Pattern;
 
 /**
  * Encapsulates parameters for an SSL/TLS connection. The parameters
  * are the list of ciphersuites to be accepted in an SSL/TLS handshake,
  * the list of protocols to be allowed, the endpoint identification
- * algorithm during SSL/TLS handshaking, the algorithm constraints and
- * whether SSL/TLS servers should request or require client authentication.
+ * algorithm during SSL/TLS handshaking, the Server Name Indication (SNI),
+ * the algorithm constraints and whether SSL/TLS servers should request
+ * or require client authentication.
  * <p>
  * SSLParameters can be created via the constructors in this class.
  * Objects can also be obtained using the <code>getSSLParameters()</code>
@@ -47,7 +57,7 @@
  * SSLParameters can be applied to a connection via the methods
  * {@link SSLSocket#setSSLParameters SSLSocket.setSSLParameters()} and
  * {@link SSLServerSocket#setSSLParameters SSLServerSocket.setSSLParameters()}
- * and {@link SSLEngine#setSSLParameters SSLEngine.getSSLParameters()}.
+ * and {@link SSLEngine#setSSLParameters SSLEngine.setSSLParameters()}.
  *
  * @see SSLSocket
  * @see SSLEngine
@@ -63,11 +73,15 @@
     private boolean needClientAuth;
     private String identificationAlgorithm;
     private AlgorithmConstraints algorithmConstraints;
+    private Map<Integer, SNIServerName> sniNames = null;
+    private Map<Integer, SNIMatcher> sniMatchers = null;
 
     /**
      * Constructs SSLParameters.
      * <p>
-     * The cipherSuites and protocols values are set to <code>null</code>,
+     * The values of cipherSuites, protocols, cryptographic algorithm
+     * constraints, endpoint identification algorithm, server names and
+     * server name matchers are set to <code>null</code>,
      * wantClientAuth and needClientAuth are set to <code>false</code>.
      */
     public SSLParameters() {
@@ -254,4 +268,173 @@
         this.identificationAlgorithm = algorithm;
     }
 
+    /**
+     * Sets the desired {@link SNIServerName}s of the Server Name
+     * Indication (SNI) parameter.
+     * <P>
+     * This method is only useful to {@link SSLSocket}s or {@link SSLEngine}s
+     * operating in client mode.
+     * <P>
+     * Note that the {@code serverNames} list is cloned
+     * to protect against subsequent modification.
+     *
+     * @param  serverNames
+     *         the list of desired {@link SNIServerName}s (or null)
+     *
+     * @throws NullPointerException if the {@code serverNames}
+     *         contains {@code null} element
+     * @throws IllegalArgumentException if the {@code serverNames}
+     *         contains more than one name of the same name type
+     *
+     * @see SNIServerName
+     * @see #getServerNames()
+     *
+     * @since 1.8
+     */
+    public void setServerNames(List<SNIServerName> serverNames) {
+        if (serverNames != null) {
+            if (!serverNames.isEmpty()) {
+                sniNames = new LinkedHashMap<>(serverNames.size());
+                for (SNIServerName serverName : serverNames) {
+                    if (sniNames.put(serverName.getType(),
+                                                serverName) != null) {
+                        throw new IllegalArgumentException(
+                                    "Duplicated server name of type " +
+                                    serverName.getType());
+                    }
+                }
+            } else {
+                sniNames = Collections.<Integer, SNIServerName>emptyMap();
+            }
+        } else {
+            sniNames = null;
+        }
+    }
+
+    /**
+     * Returns a {@link List} containing all {@link SNIServerName}s of the
+     * Server Name Indication (SNI) parameter, or null if none has been set.
+     * <P>
+     * 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
+     * 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
+     * by a supported server name type.
+     * <P>
+     * It is recommended that providers initialize default Server Name
+     * Indications when creating {@code SSLSocket}/{@code SSLEngine}s.
+     * In the following examples, the server name could be represented by an
+     * instance of {@link SNIHostName} which has been initialized with the
+     * hostname "www.example.com" and type
+     * {@link StandardConstants#SNI_HOST_NAME}.
+     *
+     * <pre>
+     *     Socket socket =
+     *         sslSocketFactory.createSocket("www.example.com", 443);
+     * </pre>
+     * or
+     * <pre>
+     *     SSLEngine engine =
+     *         sslContext.createSSLEngine("www.example.com", 443);
+     * </pre>
+     * <P>
+     *
+     * @return null or an immutable list of non-null {@link SNIServerName}s
+     *
+     * @see List
+     * @see #setServerNames(List<SNIServerName>)
+     *
+     * @since 1.8
+     */
+    public List<SNIServerName> getServerNames() {
+        if (sniNames != null) {
+            if (!sniNames.isEmpty()) {
+                return Collections.<SNIServerName>unmodifiableList(
+                                        new ArrayList<>(sniNames.values()));
+            } else {
+                return Collections.<SNIServerName>emptyList();
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Sets the {@link SNIMatcher}s of the Server Name Indication (SNI)
+     * parameter.
+     * <P>
+     * This method is only useful to {@link SSLSocket}s or {@link SSLEngine}s
+     * operating in server mode.
+     * <P>
+     * Note that the {@code matchers} collection is cloned to protect
+     * against subsequent modification.
+     *
+     * @param  matchers
+     *         the collection of {@link SNIMatcher}s (or null)
+     *
+     * @throws NullPointerException if the {@code matchers}
+     *         contains {@code null} element
+     * @throws IllegalArgumentException if the {@code matchers}
+     *         contains more than one name of the same name type
+     *
+     * @see Collection
+     * @see SNIMatcher
+     * @see #getSNIMatchers()
+     *
+     * @since 1.8
+     */
+    public void setSNIMatchers(Collection<SNIMatcher> matchers) {
+        if (matchers != null) {
+            if (!matchers.isEmpty()) {
+                sniMatchers = new HashMap<>(matchers.size());
+                for (SNIMatcher matcher : matchers) {
+                    if (sniMatchers.put(matcher.getType(),
+                                                matcher) != null) {
+                        throw new IllegalArgumentException(
+                                    "Duplicated server name of type " +
+                                    matcher.getType());
+                    }
+                }
+            } else {
+                sniMatchers = Collections.<Integer, SNIMatcher>emptyMap();
+            }
+        } else {
+            sniMatchers = null;
+        }
+    }
+
+    /**
+     * Returns a {@link Collection} containing all {@link SNIMatcher}s of the
+     * Server Name Indication (SNI) parameter, or null if none has been set.
+     * <P>
+     * This method is only useful to {@link SSLSocket}s or {@link SSLEngine}s
+     * operating in server mode.
+     * <P>
+     * For better interoperability, providers generally will not define
+     * default matchers so that by default servers will ignore the SNI
+     * extension and continue the handshake.
+     *
+     * @return null or an immutable collection of non-null {@link SNIMatcher}s
+     *
+     * @see SNIMatcher
+     * @see #setSNIMatchers(Collection<SNIMatcher>)
+     *
+     * @since 1.8
+     */
+    public Collection<SNIMatcher> getSNIMatchers() {
+        if (sniMatchers != null) {
+            if (!sniMatchers.isEmpty()) {
+                return Collections.<SNIMatcher>unmodifiableList(
+                                        new ArrayList<>(sniMatchers.values()));
+            } else {
+                return Collections.<SNIMatcher>emptyList();
+            }
+        }
+
+        return null;
+    }
 }
+
--- a/src/share/classes/javax/net/ssl/SSLServerSocket.java	Wed Oct 17 13:22:39 2012 -0700
+++ b/src/share/classes/javax/net/ssl/SSLServerSocket.java	Thu Oct 18 01:14:00 2012 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 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
@@ -484,15 +484,19 @@
      *
      * <p>This means:
      * <ul>
-     * <li>if <code>params.getCipherSuites()</code> is non-null,
-     *   <code>setEnabledCipherSuites()</code> is called with that value
-     * <li>if <code>params.getProtocols()</code> is non-null,
-     *   <code>setEnabledProtocols()</code> is called with that value
-     * <li>if <code>params.getNeedClientAuth()</code> or
-     *   <code>params.getWantClientAuth()</code> return <code>true</code>,
-     *   <code>setNeedClientAuth(true)</code> and
-     *   <code>setWantClientAuth(true)</code> are called, respectively;
-     *   otherwise <code>setWantClientAuth(false)</code> is called.
+     * <li>If {@code params.getCipherSuites()} is non-null,
+     *   {@code setEnabledCipherSuites()} is called with that value.</li>
+     * <li>If {@code params.getProtocols()} is non-null,
+     *   {@code setEnabledProtocols()} is called with that value.</li>
+     * <li>If {@code params.getNeedClientAuth()} or
+     *   {@code params.getWantClientAuth()} return {@code true},
+     *   {@code setNeedClientAuth(true)} and
+     *   {@code setWantClientAuth(true)} are called, respectively;
+     *   otherwise {@code setWantClientAuth(false)} is called.</li>
+     * <li>If {@code params.getServerNames()} is non-null, the socket will
+     *   configure its server names with that value.</li>
+     * <li>If {@code params.getSNIMatchers()} is non-null, the socket will
+     *   configure its SNI matchers with that value.</li>
      * </ul>
      *
      * @param params the parameters
--- a/src/share/classes/javax/net/ssl/SSLSocket.java	Wed Oct 17 13:22:39 2012 -0700
+++ b/src/share/classes/javax/net/ssl/SSLSocket.java	Thu Oct 18 01:14:00 2012 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 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
@@ -626,15 +626,19 @@
      *
      * <p>This means:
      * <ul>
-     * <li>if <code>params.getCipherSuites()</code> is non-null,
-     *   <code>setEnabledCipherSuites()</code> is called with that value
-     * <li>if <code>params.getProtocols()</code> is non-null,
-     *   <code>setEnabledProtocols()</code> is called with that value
-     * <li>if <code>params.getNeedClientAuth()</code> or
-     *   <code>params.getWantClientAuth()</code> return <code>true</code>,
-     *   <code>setNeedClientAuth(true)</code> and
-     *   <code>setWantClientAuth(true)</code> are called, respectively;
-     *   otherwise <code>setWantClientAuth(false)</code> is called.
+     * <li>If {@code params.getCipherSuites()} is non-null,
+     *   {@code setEnabledCipherSuites()} is called with that value.</li>
+     * <li>If {@code params.getProtocols()} is non-null,
+     *   {@code setEnabledProtocols()} is called with that value.</li>
+     * <li>If {@code params.getNeedClientAuth()} or
+     *   {@code params.getWantClientAuth()} return {@code true},
+     *   {@code setNeedClientAuth(true)} and
+     *   {@code setWantClientAuth(true)} are called, respectively;
+     *   otherwise {@code setWantClientAuth(false)} is called.</li>
+     * <li>If {@code params.getServerNames()} is non-null, the socket will
+     *   configure its server names with that value.</li>
+     * <li>If {@code params.getSNIMatchers()} is non-null, the socket will
+     *   configure its SNI matchers with that value.</li>
      * </ul>
      *
      * @param params the parameters
--- a/src/share/classes/javax/net/ssl/SSLSocketFactory.java	Wed Oct 17 13:22:39 2012 -0700
+++ b/src/share/classes/javax/net/ssl/SSLSocketFactory.java	Thu Oct 18 01:14:00 2012 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 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
@@ -29,6 +29,7 @@
 import java.net.*;
 import javax.net.SocketFactory;
 import java.io.IOException;
+import java.io.InputStream;
 import java.security.*;
 import java.util.Locale;
 
@@ -180,8 +181,55 @@
      * @throws NullPointerException if the parameter s is null
      */
     public abstract Socket createSocket(Socket s, String host,
-                                        int port, boolean autoClose)
-    throws IOException;
+            int port, boolean autoClose) throws IOException;
+
+    /**
+     * Creates a server mode {@link Socket} layered over an
+     * existing connected socket, and is able to read data which has
+     * already been consumed/removed from the {@link Socket}'s
+     * underlying {@link InputStream}.
+     * <p>
+     * This method can be used by a server application that needs to
+     * observe the inbound data but still create valid SSL/TLS
+     * connections: for example, inspection of Server Name Indication
+     * (SNI) extensions (See section 3 of <A
+     * HREF="http://www.ietf.org/rfc/rfc6066.txt">TLS Extensions
+     * (RFC6066)</A>).  Data that has been already removed from the
+     * underlying {@link InputStream} should be loaded into the
+     * {@code consumed} stream before this method is called, perhaps
+     * using a {@link ByteArrayInputStream}.  When this {@link Socket}
+     * begins handshaking, it will read all of the data in
+     * {@code consumed} until it reaches {@code EOF}, then all further
+     * data is read from the underlying {@link InputStream} as
+     * usual.
+     * <p>
+     * The returned socket is configured using the socket options
+     * established for this factory, and is set to use server mode when
+     * handshaking (see {@link SSLSocket#setUseClientMode(boolean)}).
+     *
+     * @param  s
+     *         the existing socket
+     * @param  consumed
+     *         the consumed inbound network data that has already been
+     *         removed from the existing {@link Socket}
+     *         {@link InputStream}.  This parameter may be
+     *         {@code null} if no data has been removed.
+     * @param  autoClose close the underlying socket when this socket is closed.
+     *
+     * @return the {@link Socket} compliant with the socket options
+     *         established for this factory
+     *
+     * @throws IOException if an I/O error occurs when creating the socket
+     * @throws UnsupportedOperationException if the underlying provider
+     *         does not implement the operation
+     * @throws NullPointerException if {@code s} is {@code null}
+     *
+     * @since 1.8
+     */
+    public Socket createSocket(Socket s, InputStream consumed,
+            boolean autoClose) throws IOException {
+        throw new UnsupportedOperationException();
+    }
 }
 
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/javax/net/ssl/StandardConstants.java	Thu Oct 18 01:14:00 2012 -0700
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 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 javax.net.ssl;
+
+/**
+ * Standard constants definitions
+ *
+ * @since 1.8
+ */
+public final class StandardConstants {
+
+    // Suppress default constructor for noninstantiability
+    private StandardConstants() {
+        throw new AssertionError(
+            "No javax.net.ssl.StandardConstants instances for you!");
+    }
+
+    /**
+     * The "host_name" type representing of a DNS hostname
+     * (see {@link SNIHostName}) in a Server Name Indication (SNI) extension.
+     * <P>
+     * The SNI extension is a feature that extends the SSL/TLS 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>.
+     * <P>
+     * The value of this constant is {@value}.
+     *
+     * @see SNIServerName
+     * @see SNIHostName
+     */
+    public static final int SNI_HOST_NAME = 0x00;
+}
--- a/src/share/classes/sun/security/ssl/BaseSSLSocketImpl.java	Wed Oct 17 13:22:39 2012 -0700
+++ b/src/share/classes/sun/security/ssl/BaseSSLSocketImpl.java	Thu Oct 18 01:14:00 2012 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2008, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 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
@@ -23,7 +23,6 @@
  * questions.
  */
 
-
 package sun.security.ssl;
 
 import java.io.*;
@@ -55,16 +54,25 @@
      * recurse infinitely ... e.g. close() calling itself, or doing
      * I/O in terms of our own streams.
      */
-    final Socket self;
+    final private Socket self;
+    final private InputStream consumedInput;
 
     BaseSSLSocketImpl() {
         super();
         this.self = this;
+        this.consumedInput = null;
     }
 
     BaseSSLSocketImpl(Socket socket) {
         super();
         this.self = socket;
+        this.consumedInput = null;
+    }
+
+    BaseSSLSocketImpl(Socket socket, InputStream consumed) {
+        super();
+        this.self = socket;
+        this.consumedInput = consumed;
     }
 
     //
@@ -541,4 +549,57 @@
         }
     }
 
+    @Override
+    public String toString() {
+        if (self == this) {
+            return super.toString();
+        }
+
+        return self.toString();
+    }
+
+    @Override
+    public InputStream getInputStream() throws IOException {
+        if (self == this) {
+            return super.getInputStream();
+        }
+
+        if (consumedInput != null) {
+            return new SequenceInputStream(consumedInput,
+                                                self.getInputStream());
+        }
+
+        return self.getInputStream();
+    }
+
+    @Override
+    public OutputStream getOutputStream() throws IOException {
+        if (self == this) {
+            return super.getOutputStream();
+        }
+
+        return self.getOutputStream();
+    }
+
+    @Override
+    public synchronized void close() throws IOException {
+        if (self == this) {
+            super.close();
+        } else {
+            self.close();
+        }
+    }
+
+    @Override
+    public synchronized void setSoTimeout(int timeout) throws SocketException {
+        if (self == this) {
+            super.setSoTimeout(timeout);
+        } else {
+            self.setSoTimeout(timeout);
+        }
+    }
+
+    boolean isLayered() {
+        return (self != this);
+    }
 }
--- a/src/share/classes/sun/security/ssl/ClientHandshaker.java	Wed Oct 17 13:22:39 2012 -0700
+++ b/src/share/classes/sun/security/ssl/ClientHandshaker.java	Thu Oct 18 01:14:00 2012 -0700
@@ -48,8 +48,6 @@
 import sun.security.ssl.CipherSuite.*;
 import static sun.security.ssl.CipherSuite.KeyExchange.*;
 
-import sun.net.util.IPAddressUtil;
-
 /**
  * ClientHandshaker does the protocol handshaking from the point
  * of view of a client.  It is driven asychronously by handshake messages
@@ -92,6 +90,9 @@
     private final static boolean enableSNIExtension =
             Debug.getBooleanProperty("jsse.enableSNIExtension", true);
 
+    private List<SNIServerName> requestedServerNames =
+            Collections.<SNIServerName>emptyList();
+
     /*
      * Constructors
      */
@@ -579,6 +580,7 @@
         session = new SSLSessionImpl(protocolVersion, cipherSuite,
                             getLocalSupportedSignAlgs(),
                             mesg.sessionId, getHostSE(), getPortSE());
+        session.setRequestedServerNames(requestedServerNames);
         setHandshakeSessionSE(session);
         if (debug != null && Debug.isOn("handshake")) {
             System.out.println("** " + cipherSuite);
@@ -1246,17 +1248,14 @@
 
         // add server_name extension
         if (enableSNIExtension) {
-            // We cannot use the hostname resolved from name services.  For
-            // virtual hosting, multiple hostnames may be bound to the same IP
-            // address, so the hostname resolved from name services is not
-            // reliable.
-            String hostname = getRawHostnameSE();
+            if (session != null) {
+                requestedServerNames = session.getRequestedServerNames();
+            } else {
+                requestedServerNames = serverNames;
+            }
 
-            // we only allow FQDN
-            if (hostname != null && hostname.indexOf('.') > 0 &&
-                    !IPAddressUtil.isIPv4LiteralAddress(hostname) &&
-                    !IPAddressUtil.isIPv6LiteralAddress(hostname)) {
-                clientHelloMessage.addServerNameIndicationExtension(hostname);
+            if (!requestedServerNames.isEmpty()) {
+                clientHelloMessage.addSNIExtension(requestedServerNames);
             }
         }
 
--- a/src/share/classes/sun/security/ssl/HandshakeInStream.java	Wed Oct 17 13:22:39 2012 -0700
+++ b/src/share/classes/sun/security/ssl/HandshakeInStream.java	Thu Oct 18 01:14:00 2012 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2009, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 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
@@ -121,7 +121,8 @@
         r.mark(readlimit);
     }
 
-    public void reset() {
+    @Override
+    public void reset() throws IOException {
         r.reset();
     }
 
--- a/src/share/classes/sun/security/ssl/HandshakeMessage.java	Wed Oct 17 13:22:39 2012 -0700
+++ b/src/share/classes/sun/security/ssl/HandshakeMessage.java	Thu Oct 18 01:14:00 2012 -0700
@@ -256,13 +256,9 @@
     }
 
     // add server_name extension
-    void addServerNameIndicationExtension(String hostname) {
-        // We would have checked that the hostname ia a FQDN.
-        ArrayList<String> hostnames = new ArrayList<>(1);
-        hostnames.add(hostname);
-
+    void addSNIExtension(List<SNIServerName> serverNames) {
         try {
-            extensions.add(new ServerNameExtension(hostnames));
+            extensions.add(new ServerNameExtension(serverNames));
         } catch (IOException ioe) {
             // ignore the exception and return
         }
--- a/src/share/classes/sun/security/ssl/Handshaker.java	Wed Oct 17 13:22:39 2012 -0700
+++ b/src/share/classes/sun/security/ssl/Handshaker.java	Thu Oct 18 01:14:00 2012 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 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
@@ -112,6 +112,12 @@
      */
     private CipherSuiteList    activeCipherSuites;
 
+    // The server name indication and matchers
+    List<SNIServerName>         serverNames =
+                                    Collections.<SNIServerName>emptyList();
+    Collection<SNIMatcher>      sniMatchers =
+                                    Collections.<SNIMatcher>emptyList();
+
     private boolean             isClient;
     private boolean             needCertVerify;
 
@@ -287,14 +293,7 @@
         }
     }
 
-    String getRawHostnameSE() {
-        if (conn != null) {
-            return conn.getRawHostname();
-        } else {
-            return engine.getPeerHost();
-        }
-    }
-
+    // ONLY used by ClientHandshaker to setup the peer host in SSLSession.
     String getHostSE() {
         if (conn != null) {
             return conn.getHost();
@@ -303,6 +302,7 @@
         }
     }
 
+    // ONLY used by ServerHandshaker to setup the peer host in SSLSession.
     String getHostAddressSE() {
         if (conn != null) {
             return conn.getInetAddress().getHostAddress();
@@ -436,6 +436,22 @@
     }
 
     /**
+     * Sets the server name indication of the handshake.
+     */
+    void setSNIServerNames(List<SNIServerName> serverNames) {
+        // The serverNames parameter is unmodifiable.
+        this.serverNames = serverNames;
+    }
+
+    /**
+     * Sets the server name matchers of the handshaking.
+     */
+    void setSNIMatchers(Collection<SNIMatcher> sniMatchers) {
+        // The sniMatchers parameter is unmodifiable.
+        this.sniMatchers = sniMatchers;
+    }
+
+    /**
      * Prior to handshaking, activate the handshake and initialize the version,
      * input stream and output stream.
      */
--- a/src/share/classes/sun/security/ssl/HelloExtensions.java	Wed Oct 17 13:22:39 2012 -0700
+++ b/src/share/classes/sun/security/ssl/HelloExtensions.java	Thu Oct 18 01:14:00 2012 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 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
@@ -28,11 +28,10 @@
 import java.io.IOException;
 import java.io.PrintStream;
 import java.util.*;
-
+import javax.net.ssl.*;
+import java.nio.charset.StandardCharsets;
 import java.security.spec.ECParameterSpec;
 
-import javax.net.ssl.SSLProtocolException;
-
 /**
  * This file contains all the classes relevant to TLS Extensions for the
  * ClientHello and ServerHello messages. The extension mechanism and
@@ -274,11 +273,11 @@
 }
 
 /*
- * [RFC4366] To facilitate secure connections to servers that host multiple
- * 'virtual' servers at a single underlying network address, clients MAY
- * include an extension of type "server_name" in the (extended) client hello.
- * The "extension_data" field of this extension SHALL contain "ServerNameList"
- * where:
+ * [RFC 4366/6066] To facilitate secure connections to servers that host
+ * multiple 'virtual' servers at a single underlying network address, clients
+ * MAY include an extension of type "server_name" in the (extended) client
+ * hello.  The "extension_data" field of this extension SHALL contain
+ * "ServerNameList" where:
  *
  *     struct {
  *         NameType name_type;
@@ -299,44 +298,47 @@
  */
 final class ServerNameExtension extends HelloExtension {
 
-    final static int NAME_HOST_NAME = 0;
-
-    private List<ServerName> names;
+    // For backward compatibility, all future data structures associated with
+    // new NameTypes MUST begin with a 16-bit length field.
+    final static int NAME_HEADER_LENGTH = 3;    // NameType: 1 byte
+                                                // Name length: 2 bytes
+    private Map<Integer, SNIServerName> sniMap;
     private int listLength;     // ServerNameList length
 
-    ServerNameExtension(List<String> hostnames) throws IOException {
+    // constructor for ServerHello
+    ServerNameExtension() throws IOException {
         super(ExtensionType.EXT_SERVER_NAME);
 
         listLength = 0;
-        names = new ArrayList<ServerName>(hostnames.size());
-        for (String hostname : hostnames) {
-            if (hostname != null && hostname.length() != 0) {
-                // we only support DNS hostname now.
-                ServerName serverName =
-                        new ServerName(NAME_HOST_NAME, hostname);
-                names.add(serverName);
-                listLength += serverName.length;
+        sniMap = Collections.<Integer, SNIServerName>emptyMap();
+    }
+
+    // constructor for ClientHello
+    ServerNameExtension(List<SNIServerName> serverNames)
+            throws IOException {
+        super(ExtensionType.EXT_SERVER_NAME);
+
+        listLength = 0;
+        sniMap = new LinkedHashMap<>();
+        for (SNIServerName serverName : serverNames) {
+            // check for duplicated server name type
+            if (sniMap.put(serverName.getType(), serverName) != null) {
+                // unlikely to happen, but in case ...
+                throw new RuntimeException(
+                    "Duplicated server name of type " + serverName.getType());
             }
+
+            listLength += serverName.getEncoded().length + NAME_HEADER_LENGTH;
         }
 
-        // As we only support DNS hostname now, the hostname list must
-        // not contain more than one hostname
-        if (names.size() > 1) {
-            throw new SSLProtocolException(
-                    "The ServerNameList MUST NOT contain more than " +
-                    "one name of the same name_type");
-        }
-
-        // We only need to add "server_name" extension in ClientHello unless
-        // we support SNI in server side in the future. It is possible that
-        // the SNI is empty in ServerHello. As we don't support SNI in
-        // ServerHello now, we will throw exception for empty list for now.
+        // This constructor is used for ClientHello only.  Empty list is
+        // not allowed in client mode.
         if (listLength == 0) {
-            throw new SSLProtocolException(
-                    "The ServerNameList cannot be empty");
+            throw new RuntimeException("The ServerNameList cannot be empty");
         }
     }
 
+    // constructor for ServerHello for parsing SNI extension
     ServerNameExtension(HandshakeInStream s, int len)
             throws IOException {
         super(ExtensionType.EXT_SERVER_NAME);
@@ -350,17 +352,54 @@
             }
 
             remains -= 2;
-            names = new ArrayList<ServerName>();
+            sniMap = new LinkedHashMap<>();
             while (remains > 0) {
-                ServerName name = new ServerName(s);
-                names.add(name);
-                remains -= name.length;
+                int code = s.getInt8();       // NameType
 
-                // we may need to check the duplicated ServerName type
+                // HostName (length read in getBytes16);
+                byte[] encoded = s.getBytes16();
+                SNIServerName serverName;
+                switch (code) {
+                    case StandardConstants.SNI_HOST_NAME:
+                        if (encoded.length == 0) {
+                            throw new SSLProtocolException(
+                                "Empty HostName in server name indication");
+                        }
+                        try {
+                            serverName = new SNIHostName(encoded);
+                        } catch (IllegalArgumentException iae) {
+                            SSLProtocolException spe = new SSLProtocolException(
+                                "Illegal server name, type=host_name(" +
+                                code + "), name=" +
+                                (new String(encoded, StandardCharsets.UTF_8)) +
+                                ", value=" + Debug.toString(encoded));
+                            spe.initCause(iae);
+                            throw spe;
+                        }
+                        break;
+                    default:
+                        try {
+                            serverName = new UnknownServerName(code, encoded);
+                        } catch (IllegalArgumentException iae) {
+                            SSLProtocolException spe = new SSLProtocolException(
+                                "Illegal server name, type=(" + code +
+                                "), value=" + Debug.toString(encoded));
+                            spe.initCause(iae);
+                            throw spe;
+                        }
+                }
+                // check for duplicated server name type
+                if (sniMap.put(serverName.getType(), serverName) != null) {
+                    throw new SSLProtocolException(
+                            "Duplicated server name of type " +
+                            serverName.getType());
+                }
+
+                remains -= encoded.length + NAME_HEADER_LENGTH;
             }
         } else if (len == 0) {     // "server_name" extension in ServerHello
             listLength = 0;
-            names = Collections.<ServerName>emptyList();
+            sniMap = Collections.<Integer, SNIServerName>emptyMap();
         }
 
         if (remains != 0) {
@@ -368,39 +407,72 @@
         }
     }
 
-    static class ServerName {
-        final int length;
-        final int type;
-        final byte[] data;
-        final String hostname;
-
-        ServerName(int type, String hostname) throws IOException {
-            this.type = type;                       // NameType
-            this.hostname = hostname;
-            this.data = hostname.getBytes("UTF8");  // HostName
-            this.length = data.length + 3;          // NameType: 1 byte
-                                                    // HostName length: 2 bytes
+    List<SNIServerName> getServerNames() {
+        if (sniMap != null && !sniMap.isEmpty()) {
+            return Collections.<SNIServerName>unmodifiableList(
+                                        new ArrayList<>(sniMap.values()));
         }
 
-        ServerName(HandshakeInStream s) throws IOException {
-            type = s.getInt8();         // NameType
-            data = s.getBytes16();      // HostName (length read in getBytes16)
-            length = data.length + 3;   // NameType: 1 byte
-                                        // HostName length: 2 bytes
-            if (type == NAME_HOST_NAME) {
-                hostname = new String(data, "UTF8");
-            } else {
-                hostname = null;
+        return Collections.<SNIServerName>emptyList();
+    }
+
+    /*
+     * Is the extension recognized by the corresponding matcher?
+     *
+     * This method is used to check whether the server name indication can
+     * be recognized by the server name matchers.
+     *
+     * Per RFC 6066, if the server understood the ClientHello extension but
+     * does not recognize the server name, the server SHOULD take one of two
+     * actions: either abort the handshake by sending a fatal-level
+     * unrecognized_name(112) alert or continue the handshake.
+     *
+     * If there is an instance of SNIMatcher defined for a particular name
+     * type, it must be used to perform match operations on the server name.
+     */
+    boolean isMatched(Collection<SNIMatcher> matchers) {
+        if (sniMap != null && !sniMap.isEmpty()) {
+            for (SNIMatcher matcher : matchers) {
+                SNIServerName sniName = sniMap.get(matcher.getType());
+                if (sniName != null && (!matcher.matches(sniName))) {
+                    return false;
+                }
             }
         }
 
-        public String toString() {
-            if (type == NAME_HOST_NAME) {
-                return "host_name: " + hostname;
-            } else {
-                return "unknown-" + type + ": " + Debug.toString(data);
+        return true;
+    }
+
+    /*
+     * Is the extension is identical to a server name list?
+     *
+     * This method is used to check the server name indication during session
+     * resumption.
+     *
+     * Per RFC 6066, when the server is deciding whether or not to accept a
+     * request to resume a session, the contents of a server_name extension
+     * MAY be used in the lookup of the session in the session cache.  The
+     * client SHOULD include the same server_name extension in the session
+     * resumption request as it did in the full handshake that established
+     * the session.  A server that implements this extension MUST NOT accept
+     * the request to resume the session if the server_name extension contains
+     * a different name.  Instead, it proceeds with a full handshake to
+     * establish a new session.  When resuming a session, the server MUST NOT
+     * include a server_name extension in the server hello.
+     */
+    boolean isIdentical(List<SNIServerName> other) {
+        if (other.size() == sniMap.size()) {
+            for(SNIServerName sniInOther : other) {
+                SNIServerName sniName = sniMap.get(sniInOther.getType());
+                if (sniName == null || !sniInOther.equals(sniName)) {
+                    return false;
+                }
             }
+
+            return true;
         }
+
+        return false;
     }
 
     int length() {
@@ -409,25 +481,34 @@
 
     void send(HandshakeOutStream s) throws IOException {
         s.putInt16(type.id);
-        s.putInt16(listLength + 2);
-        if (listLength != 0) {
-            s.putInt16(listLength);
+        if (listLength == 0) {
+            s.putInt16(listLength);     // in ServerHello, empty extension_data
+        } else {
+            s.putInt16(listLength + 2); // length of extension_data
+            s.putInt16(listLength);     // length of ServerNameList
 
-            for (ServerName name : names) {
-                s.putInt8(name.type);           // NameType
-                s.putBytes16(name.data);        // HostName
+            for (SNIServerName sniName : sniMap.values()) {
+                s.putInt8(sniName.getType());         // server name type
+                s.putBytes16(sniName.getEncoded());   // server name value
             }
         }
     }
 
     public String toString() {
         StringBuffer buffer = new StringBuffer();
-        for (ServerName name : names) {
-            buffer.append("[" + name + "]");
+        for (SNIServerName sniName : sniMap.values()) {
+            buffer.append("[" + sniName + "]");
         }
 
         return "Extension " + type + ", server_name: " + buffer;
     }
+
+    private static class UnknownServerName extends SNIServerName {
+        UnknownServerName(int code, byte[] encoded) {
+            super(code, encoded);
+        }
+    }
+
 }
 
 final class SupportedEllipticCurvesExtension extends HelloExtension {
--- a/src/share/classes/sun/security/ssl/ProtocolList.java	Wed Oct 17 13:22:39 2012 -0700
+++ b/src/share/classes/sun/security/ssl/ProtocolList.java	Thu Oct 18 01:14:00 2012 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 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
@@ -79,7 +79,7 @@
             throw new IllegalArgumentException("Protocols may not be null");
         }
 
-        ArrayList<ProtocolVersion> versions = new ArrayList<>(3);
+        ArrayList<ProtocolVersion> versions = new ArrayList<>(names.length);
         for (int i = 0; i < names.length; i++ ) {
             ProtocolVersion version = ProtocolVersion.valueOf(names[i]);
             if (versions.contains(version) == false) {
--- a/src/share/classes/sun/security/ssl/SSLEngineImpl.java	Wed Oct 17 13:22:39 2012 -0700
+++ b/src/share/classes/sun/security/ssl/SSLEngineImpl.java	Thu Oct 18 01:14:00 2012 -0700
@@ -27,6 +27,7 @@
 
 import java.io.*;
 import java.nio.*;
+import java.util.*;
 import java.security.*;
 
 import javax.crypto.BadPaddingException;
@@ -34,7 +35,6 @@
 import javax.net.ssl.*;
 import javax.net.ssl.SSLEngineResult.*;
 
-
 /**
  * Implementation of an non-blocking SSLEngine.
  *
@@ -253,6 +253,12 @@
     // The cryptographic algorithm constraints
     private AlgorithmConstraints        algorithmConstraints = null;
 
+    // The server name indication and matchers
+    List<SNIServerName>         serverNames =
+                                    Collections.<SNIServerName>emptyList();
+    Collection<SNIMatcher>      sniMatchers =
+                                    Collections.<SNIMatcher>emptyList();
+
     // Have we been told whether we're client or server?
     private boolean                     serverModeSet = false;
     private boolean                     roleIsServer;
@@ -361,6 +367,10 @@
         roleIsServer = true;
         connectionState = cs_START;
 
+        // default server name indication
+        serverNames =
+            Utilities.addToSNIServerNameList(serverNames, getPeerHost());
+
         /*
          * default read and write side cipher and MAC support
          *
@@ -459,11 +469,13 @@
                     enabledProtocols, doClientAuth,
                     protocolVersion, connectionState == cs_HANDSHAKE,
                     secureRenegotiation, clientVerifyData, serverVerifyData);
+            handshaker.setSNIMatchers(sniMatchers);
         } else {
             handshaker = new ClientHandshaker(this, sslContext,
                     enabledProtocols,
                     protocolVersion, connectionState == cs_HANDSHAKE,
                     secureRenegotiation, clientVerifyData, serverVerifyData);
+            handshaker.setSNIServerNames(serverNames);
         }
         handshaker.setEnabledCipherSuites(enabledCipherSuites);
         handshaker.setEnableSessionCreation(enableSessionCreation);
@@ -1100,7 +1112,7 @@
                     // TLS requires that unrecognized records be ignored.
                     //
                     if (debug != null && Debug.isOn("ssl")) {
-                        System.out.println(threadName() +
+                        System.out.println(Thread.currentThread().getName() +
                             ", Received record type: "
                             + inputRecord.contentType());
                     }
@@ -1384,7 +1396,7 @@
              * for handshaking and bad_record_mac for other records.
              */
             if (debug != null && Debug.isOn("ssl")) {
-                System.out.println(threadName() +
+                System.out.println(Thread.currentThread().getName() +
                     ", sequence number extremely close to overflow " +
                     "(2^64-1 packets). Closing connection.");
             }
@@ -1402,7 +1414,8 @@
          */
         if ((type != Record.ct_handshake) && mac.seqNumIsHuge()) {
             if (debug != null && Debug.isOn("ssl")) {
-                System.out.println(threadName() + ", request renegotiation " +
+                System.out.println(Thread.currentThread().getName() +
+                        ", request renegotiation " +
                         "to avoid sequence number overflow");
             }
 
@@ -1420,7 +1433,8 @@
     private void closeOutboundInternal() {
 
         if ((debug != null) && Debug.isOn("ssl")) {
-            System.out.println(threadName() + ", closeOutboundInternal()");
+            System.out.println(Thread.currentThread().getName() +
+                                    ", closeOutboundInternal()");
         }
 
         /*
@@ -1467,7 +1481,8 @@
          * Dump out a close_notify to the remote side
          */
         if ((debug != null) && Debug.isOn("ssl")) {
-            System.out.println(threadName() + ", called closeOutbound()");
+            System.out.println(Thread.currentThread().getName() +
+                                    ", called closeOutbound()");
         }
 
         closeOutboundInternal();
@@ -1487,7 +1502,8 @@
     private void closeInboundInternal() {
 
         if ((debug != null) && Debug.isOn("ssl")) {
-            System.out.println(threadName() + ", closeInboundInternal()");
+            System.out.println(Thread.currentThread().getName() +
+                                    ", closeInboundInternal()");
         }
 
         /*
@@ -1519,7 +1535,8 @@
          * someday in the future.
          */
         if ((debug != null) && Debug.isOn("ssl")) {
-            System.out.println(threadName() + ", called closeInbound()");
+            System.out.println(Thread.currentThread().getName() +
+                                    ", called closeInbound()");
         }
 
         /*
@@ -1642,7 +1659,7 @@
          */
         if (closeReason != null) {
             if ((debug != null) && Debug.isOn("ssl")) {
-                System.out.println(threadName() +
+                System.out.println(Thread.currentThread().getName() +
                     ", fatal: engine already closed.  Rethrowing " +
                     cause.toString());
             }
@@ -1656,7 +1673,7 @@
         }
 
         if ((debug != null) && Debug.isOn("ssl")) {
-            System.out.println(threadName()
+            System.out.println(Thread.currentThread().getName()
                         + ", fatal error: " + description +
                         ": " + diagnostic + "\n" + cause.toString());
         }
@@ -1723,7 +1740,7 @@
         if (debug != null && (Debug.isOn("record") ||
                 Debug.isOn("handshake"))) {
             synchronized (System.out) {
-                System.out.print(threadName());
+                System.out.print(Thread.currentThread().getName());
                 System.out.print(", RECV " + protocolVersion + " ALERT:  ");
                 if (level == Alerts.alert_fatal) {
                     System.out.print("fatal, ");
@@ -1790,7 +1807,7 @@
         boolean useDebug = debug != null && Debug.isOn("ssl");
         if (useDebug) {
             synchronized (System.out) {
-                System.out.print(threadName());
+                System.out.print(Thread.currentThread().getName());
                 System.out.print(", SEND " + protocolVersion + " ALERT:  ");
                 if (level == Alerts.alert_fatal) {
                     System.out.print("fatal, ");
@@ -1810,7 +1827,7 @@
             writeRecord(r);
         } catch (IOException e) {
             if (useDebug) {
-                System.out.println(threadName() +
+                System.out.println(Thread.currentThread().getName() +
                     ", Exception sending alert: " + e);
             }
         }
@@ -1948,7 +1965,7 @@
 
         default:
             if (debug != null && Debug.isOn("ssl")) {
-                System.out.println(threadName() +
+                System.out.println(Thread.currentThread().getName() +
                     ", setUseClientMode() invoked in state = " +
                     connectionState);
             }
@@ -2050,6 +2067,8 @@
         // the super implementation does not handle the following parameters
         params.setEndpointIdentificationAlgorithm(identificationProtocol);
         params.setAlgorithmConstraints(algorithmConstraints);
+        params.setSNIMatchers(sniMatchers);
+        params.setServerNames(serverNames);
 
         return params;
     }
@@ -2063,20 +2082,29 @@
         // the super implementation does not handle the following parameters
         identificationProtocol = params.getEndpointIdentificationAlgorithm();
         algorithmConstraints = params.getAlgorithmConstraints();
+
+        List<SNIServerName> sniNames = params.getServerNames();
+        if (sniNames != null) {
+            serverNames = sniNames;
+        }
+
+        Collection<SNIMatcher> matchers = params.getSNIMatchers();
+        if (matchers != null) {
+            sniMatchers = matchers;
+        }
+
         if ((handshaker != null) && !handshaker.started()) {
             handshaker.setIdentificationProtocol(identificationProtocol);
             handshaker.setAlgorithmConstraints(algorithmConstraints);
+            if (roleIsServer) {
+                handshaker.setSNIMatchers(sniMatchers);
+            } else {
+                handshaker.setSNIServerNames(serverNames);
+            }
         }
     }
 
     /**
-     * Return the name of the current thread. Utility method.
-     */
-    private static String threadName() {
-        return Thread.currentThread().getName();
-    }
-
-    /**
      * Returns a printable representation of this end of the connection.
      */
     public String toString() {
--- a/src/share/classes/sun/security/ssl/SSLServerSocketImpl.java	Wed Oct 17 13:22:39 2012 -0700
+++ b/src/share/classes/sun/security/ssl/SSLServerSocketImpl.java	Thu Oct 18 01:14:00 2012 -0700
@@ -39,6 +39,7 @@
 import javax.net.ssl.SSLException;
 import javax.net.ssl.SSLServerSocket;
 import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SNIMatcher;
 
 
 /**
@@ -92,6 +93,10 @@
     // The cryptographic algorithm constraints
     private AlgorithmConstraints    algorithmConstraints = null;
 
+    // The server name indication
+    Collection<SNIMatcher>      sniMatchers =
+                                    Collections.<SNIMatcher>emptyList();
+
     /**
      * Create an SSL server socket on a port, using a non-default
      * authentication context and a specified connection backlog.
@@ -289,6 +294,7 @@
         // the super implementation does not handle the following parameters
         params.setEndpointIdentificationAlgorithm(identificationProtocol);
         params.setAlgorithmConstraints(algorithmConstraints);
+        params.setSNIMatchers(sniMatchers);
 
         return params;
     }
@@ -302,6 +308,10 @@
         // the super implementation does not handle the following parameters
         identificationProtocol = params.getEndpointIdentificationAlgorithm();
         algorithmConstraints = params.getAlgorithmConstraints();
+        Collection<SNIMatcher> matchers = params.getSNIMatchers();
+        if (matchers != null) {
+            sniMatchers = params.getSNIMatchers();
+        }
     }
 
     /**
@@ -312,7 +322,8 @@
     public Socket accept() throws IOException {
         SSLSocketImpl s = new SSLSocketImpl(sslContext, useServerMode,
             enabledCipherSuites, doClientAuth, enableSessionCreation,
-            enabledProtocols, identificationProtocol, algorithmConstraints);
+            enabledProtocols, identificationProtocol, algorithmConstraints,
+            sniMatchers);
 
         implAccept(s);
         s.doneConnect();
--- a/src/share/classes/sun/security/ssl/SSLSessionImpl.java	Wed Oct 17 13:22:39 2012 -0700
+++ b/src/share/classes/sun/security/ssl/SSLSessionImpl.java	Thu Oct 18 01:14:00 2012 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 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
@@ -33,6 +33,9 @@
 import java.util.Vector;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.ArrayList;
 
 import java.security.Principal;
 import java.security.PrivateKey;
@@ -51,6 +54,7 @@
 import javax.net.ssl.SSLPermission;
 import javax.net.ssl.SSLException;
 import javax.net.ssl.ExtendedSSLSession;
+import javax.net.ssl.SNIServerName;
 
 import javax.security.auth.x500.X500Principal;
 
@@ -111,6 +115,8 @@
     private PrivateKey          localPrivateKey;
     private String[]            localSupportedSignAlgs;
     private String[]            peerSupportedSignAlgs;
+    private List<SNIServerName>    requestedServerNames;
+
 
     // Principals for non-certificate based cipher suites
     private Principal peerPrincipal;
@@ -212,6 +218,10 @@
             SignatureAndHashAlgorithm.getAlgorithmNames(algorithms);
     }
 
+    void setRequestedServerNames(List<SNIServerName> requestedServerNames) {
+        this.requestedServerNames = new ArrayList<>(requestedServerNames);
+    }
+
     /**
      * Set the peer principal.
      */
@@ -748,6 +758,7 @@
      * Gets an array of supported signature algorithms that the local side is
      * willing to verify.
      */
+    @Override
     public String[] getLocalSupportedSignatureAlgorithms() {
         if (localSupportedSignAlgs != null) {
             return localSupportedSignAlgs.clone();
@@ -760,6 +771,7 @@
      * Gets an array of supported signature algorithms that the peer is
      * able to verify.
      */
+    @Override
     public String[] getPeerSupportedSignatureAlgorithms() {
         if (peerSupportedSignAlgs != null) {
             return peerSupportedSignAlgs.clone();
@@ -768,6 +780,20 @@
         return new String[0];
     }
 
+    /**
+     * Obtains a <code>List</code> containing all {@link SNIServerName}s
+     * of the requested Server Name Indication (SNI) extension.
+     */
+    @Override
+    public List<SNIServerName> getRequestedServerNames() {
+        if (requestedServerNames != null && !requestedServerNames.isEmpty()) {
+            return Collections.<SNIServerName>unmodifiableList(
+                                                requestedServerNames);
+        }
+
+        return Collections.<SNIServerName>emptyList();
+    }
+
     /** Returns a string representation of this SSL session */
     public String toString() {
         return "[Session-" + sessionCount
--- a/src/share/classes/sun/security/ssl/SSLSocketFactoryImpl.java	Wed Oct 17 13:22:39 2012 -0700
+++ b/src/share/classes/sun/security/ssl/SSLSocketFactoryImpl.java	Thu Oct 18 01:14:00 2012 -0700
@@ -109,6 +109,16 @@
         return new SSLSocketImpl(context, s, host, port, autoClose);
     }
 
+    @Override
+    public Socket createSocket(Socket s, InputStream consumed,
+            boolean autoClose) throws IOException {
+        if (s == null) {
+            throw new NullPointerException(
+                    "the existing socket cannot be null");
+        }
+
+        return new SSLSocketImpl(context, s, consumed, autoClose);
+    }
 
     /**
      * Constructs an SSL connection to a server at a specified address
--- a/src/share/classes/sun/security/ssl/SSLSocketImpl.java	Wed Oct 17 13:22:39 2012 -0700
+++ b/src/share/classes/sun/security/ssl/SSLSocketImpl.java	Thu Oct 18 01:14:00 2012 -0700
@@ -36,9 +36,9 @@
 import java.util.*;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.locks.ReentrantLock;
+import java.nio.charset.StandardCharsets;
 
 import javax.crypto.BadPaddingException;
-
 import javax.net.ssl.*;
 
 /**
@@ -198,14 +198,6 @@
     private boolean             autoClose = true;
     private AccessControlContext acc;
 
-    /*
-     * We cannot use the hostname resolved from name services.  For
-     * virtual hosting, multiple hostnames may be bound to the same IP
-     * address, so the hostname resolved from name services is not
-     * reliable.
-     */
-    private String              rawHostname;
-
     // The cipher suites enabled for use on this connection.
     private CipherSuiteList     enabledCipherSuites;
 
@@ -215,6 +207,12 @@
     // The cryptographic algorithm constraints
     private AlgorithmConstraints    algorithmConstraints = null;
 
+    // The server name indication and matchers
+    List<SNIServerName>         serverNames =
+                                    Collections.<SNIServerName>emptyList();
+    Collection<SNIMatcher>      sniMatchers =
+                                    Collections.<SNIMatcher>emptyList();
+
     /*
      * READ ME * READ ME * READ ME * READ ME * READ ME * READ ME *
      * IMPORTANT STUFF TO UNDERSTANDING THE SYNCHRONIZATION ISSUES.
@@ -397,7 +395,8 @@
             throws IOException, UnknownHostException {
         super();
         this.host = host;
-        this.rawHostname = host;
+        this.serverNames =
+            Utilities.addToSNIServerNameList(this.serverNames, this.host);
         init(context, false);
         SocketAddress socketAddress =
                host != null ? new InetSocketAddress(host, port) :
@@ -440,7 +439,8 @@
             throws IOException, UnknownHostException {
         super();
         this.host = host;
-        this.rawHostname = host;
+        this.serverNames =
+            Utilities.addToSNIServerNameList(this.serverNames, this.host);
         init(context, false);
         bind(new InetSocketAddress(localAddr, localPort));
         SocketAddress socketAddress =
@@ -482,13 +482,15 @@
             CipherSuiteList suites, byte clientAuth,
             boolean sessionCreation, ProtocolList protocols,
             String identificationProtocol,
-            AlgorithmConstraints algorithmConstraints) throws IOException {
+            AlgorithmConstraints algorithmConstraints,
+            Collection<SNIMatcher> sniMatchers) throws IOException {
 
         super();
         doClientAuth = clientAuth;
         enableSessionCreation = sessionCreation;
         this.identificationProtocol = identificationProtocol;
         this.algorithmConstraints = algorithmConstraints;
+        this.sniMatchers = sniMatchers;
         init(context, serverMode);
 
         /*
@@ -535,13 +537,36 @@
             throw new SocketException("Underlying socket is not connected");
         }
         this.host = host;
-        this.rawHostname = host;
+        this.serverNames =
+            Utilities.addToSNIServerNameList(this.serverNames, this.host);
         init(context, false);
         this.autoClose = autoClose;
         doneConnect();
     }
 
     /**
+     * Creates a server mode {@link Socket} layered over an
+     * existing connected socket, and is able to read data which has
+     * already been consumed/removed from the {@link Socket}'s
+     * underlying {@link InputStream}.
+     */
+    SSLSocketImpl(SSLContextImpl context, Socket sock,
+            InputStream consumed, boolean autoClose) throws IOException {
+        super(sock, consumed);
+        // We always layer over a connected socket
+        if (!sock.isConnected()) {
+            throw new SocketException("Underlying socket is not connected");
+        }
+
+        // In server mode, it is not necessary to set host and serverNames.
+        // Otherwise, would require a reverse DNS lookup to get the hostname.
+
+        init(context, true);
+        this.autoClose = autoClose;
+        doneConnect();
+    }
+
+    /**
      * Initializes the client socket.
      */
     private void init(SSLContextImpl context, boolean isServer) {
@@ -604,7 +629,7 @@
     public void connect(SocketAddress endpoint, int timeout)
             throws IOException {
 
-        if (self != this) {
+        if (isLayered()) {
             throw new SocketException("Already connected");
         }
 
@@ -628,13 +653,8 @@
          * java.net actually connects using the socket "self", else
          * we get some pretty bizarre failure modes.
          */
-        if (self == this) {
-            sockInput = super.getInputStream();
-            sockOutput = super.getOutputStream();
-        } else {
-            sockInput = self.getInputStream();
-            sockOutput = self.getOutputStream();
-        }
+        sockInput = super.getInputStream();
+        sockOutput = super.getOutputStream();
 
         /*
          * Move to handshaking state, with pending session initialized
@@ -761,13 +781,14 @@
                         // For layered, non-autoclose sockets, we are not
                         // able to bring them into a usable state, so we
                         // treat it as fatal error.
-                        if (self != this && !autoClose) {
+                        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(threadName() +
+                            System.out.println(
+                                Thread.currentThread().getName() +
                                 ", received Exception: " + ssle);
                         }
 
@@ -935,7 +956,7 @@
                 boolean handshaking = (getConnectionState() <= cs_HANDSHAKE);
                 boolean rethrow = requireCloseNotify || handshaking;
                 if ((debug != null) && Debug.isOn("ssl")) {
-                    System.out.println(threadName() +
+                    System.out.println(Thread.currentThread().getName() +
                         ", received EOFException: "
                         + (rethrow ? "error" : "ignored"));
                 }
@@ -1119,7 +1140,7 @@
                     // TLS requires that unrecognized records be ignored.
                     //
                     if (debug != null && Debug.isOn("ssl")) {
-                        System.out.println(threadName() +
+                        System.out.println(Thread.currentThread().getName() +
                             ", Received record type: "
                             + r.contentType());
                     }
@@ -1183,7 +1204,7 @@
              * for handshaking and bad_record_mac for other records.
              */
             if (debug != null && Debug.isOn("ssl")) {
-                System.out.println(threadName() +
+                System.out.println(Thread.currentThread().getName() +
                     ", sequence number extremely close to overflow " +
                     "(2^64-1 packets). Closing connection.");
 
@@ -1200,7 +1221,8 @@
          */
         if ((type != Record.ct_handshake) && mac.seqNumIsHuge()) {
             if (debug != null && Debug.isOn("ssl")) {
-                System.out.println(threadName() + ", request renegotiation " +
+                System.out.println(Thread.currentThread().getName() +
+                        ", request renegotiation " +
                         "to avoid sequence number overflow");
             }
 
@@ -1278,11 +1300,13 @@
                     enabledProtocols, doClientAuth,
                     protocolVersion, connectionState == cs_HANDSHAKE,
                     secureRenegotiation, clientVerifyData, serverVerifyData);
+            handshaker.setSNIMatchers(sniMatchers);
         } else {
             handshaker = new ClientHandshaker(this, sslContext,
                     enabledProtocols,
                     protocolVersion, connectionState == cs_HANDSHAKE,
                     secureRenegotiation, clientVerifyData, serverVerifyData);
+            handshaker.setSNIServerNames(serverNames);
         }
         handshaker.setEnabledCipherSuites(enabledCipherSuites);
         handshaker.setEnableSessionCreation(enableSessionCreation);
@@ -1509,24 +1533,20 @@
     protected void closeSocket() throws IOException {
 
         if ((debug != null) && Debug.isOn("ssl")) {
-            System.out.println(threadName() + ", called closeSocket()");
+            System.out.println(Thread.currentThread().getName() +
+                                                ", called closeSocket()");
         }
-        if (self == this) {
-            super.close();
-        } else {
-            self.close();
-        }
+
+        super.close();
     }
 
     private void closeSocket(boolean selfInitiated) throws IOException {
         if ((debug != null) && Debug.isOn("ssl")) {
-            System.out.println(threadName() +
+            System.out.println(Thread.currentThread().getName() +
                 ", called closeSocket(" + selfInitiated + ")");
         }
-        if (self == this) {
+        if (!isLayered() || autoClose) {
             super.close();
-        } else if (autoClose) {
-            self.close();
         } else if (selfInitiated) {
             // layered && non-autoclose
             // read close_notify alert to clear input stream
@@ -1549,7 +1569,8 @@
      */
     public void close() throws IOException {
         if ((debug != null) && Debug.isOn("ssl")) {
-            System.out.println(threadName() + ", called close()");
+            System.out.println(Thread.currentThread().getName() +
+                                                    ", called close()");
         }
         closeInternal(true);  // caller is initiating close
         setConnectionState(cs_APP_CLOSED);
@@ -1567,8 +1588,8 @@
      */
     private void closeInternal(boolean selfInitiated) throws IOException {
         if ((debug != null) && Debug.isOn("ssl")) {
-            System.out.println(threadName() + ", called closeInternal("
-                + selfInitiated + ")");
+            System.out.println(Thread.currentThread().getName() +
+                        ", called closeInternal(" + selfInitiated + ")");
         }
 
         int state = getConnectionState();
@@ -1630,7 +1651,7 @@
                 // closing since it is already in progress.
                 if (state == cs_SENT_CLOSE) {
                     if (debug != null && Debug.isOn("ssl")) {
-                        System.out.println(threadName() +
+                        System.out.println(Thread.currentThread().getName() +
                             ", close invoked again; state = " +
                             getConnectionState());
                     }
@@ -1653,7 +1674,7 @@
                         }
                     }
                     if ((debug != null) && Debug.isOn("ssl")) {
-                        System.out.println(threadName() +
+                        System.out.println(Thread.currentThread().getName() +
                             ", after primary close; state = " +
                             getConnectionState());
                     }
@@ -1701,7 +1722,7 @@
      */
     void waitForClose(boolean rethrow) throws IOException {
         if (debug != null && Debug.isOn("ssl")) {
-            System.out.println(threadName() +
+            System.out.println(Thread.currentThread().getName() +
                 ", waiting for close_notify or alert: state "
                 + getConnectionState());
         }
@@ -1726,7 +1747,7 @@
             inrec = null;
         } catch (IOException e) {
             if (debug != null && Debug.isOn("ssl")) {
-                System.out.println(threadName() +
+                System.out.println(Thread.currentThread().getName() +
                     ", Exception while waiting for close " +e);
             }
             if (rethrow) {
@@ -1788,8 +1809,8 @@
     synchronized private void handleException(Exception e, boolean resumable)
         throws IOException {
         if ((debug != null) && Debug.isOn("ssl")) {
-            System.out.println(threadName()
-                        + ", handling exception: " + e.toString());
+            System.out.println(Thread.currentThread().getName() +
+                        ", handling exception: " + e.toString());
         }
 
         // don't close the Socket in case of timeouts or interrupts if
@@ -1935,7 +1956,7 @@
         if (debug != null && (Debug.isOn("record") ||
                 Debug.isOn("handshake"))) {
             synchronized (System.out) {
-                System.out.print(threadName());
+                System.out.print(Thread.currentThread().getName());
                 System.out.print(", RECV " + protocolVersion + " ALERT:  ");
                 if (level == Alerts.alert_fatal) {
                     System.out.print("fatal, ");
@@ -2001,7 +2022,7 @@
         boolean useDebug = debug != null && Debug.isOn("ssl");
         if (useDebug) {
             synchronized (System.out) {
-                System.out.print(threadName());
+                System.out.print(Thread.currentThread().getName());
                 System.out.print(", SEND " + protocolVersion + " ALERT:  ");
                 if (level == Alerts.alert_fatal) {
                     System.out.print("fatal, ");
@@ -2021,7 +2042,7 @@
             writeRecord(r);
         } catch (IOException e) {
             if (useDebug) {
-                System.out.println(threadName() +
+                System.out.println(Thread.currentThread().getName() +
                     ", Exception sending alert: " + e);
             }
         }
@@ -2118,14 +2139,15 @@
         return host;
     }
 
-    synchronized String getRawHostname() {
-        return rawHostname;
-    }
-
     // ONLY used by HttpsClient to setup the URI specified hostname
+    //
+    // Please NOTE that this method MUST be called before calling to
+    // SSLSocket.setSSLParameters(). Otherwise, the {@code host} parameter
+    // may override SNIHostName in the customized server name indication.
     synchronized public void setHost(String host) {
         this.host = host;
-        this.rawHostname = host;
+        this.serverNames =
+            Utilities.addToSNIServerNameList(this.serverNames, this.host);
     }
 
     /**
@@ -2186,7 +2208,7 @@
             } catch (IOException e) {
                 // handshake failed. log and return a nullSession
                 if (debug != null && Debug.isOn("handshake")) {
-                      System.out.println(threadName() +
+                      System.out.println(Thread.currentThread().getName() +
                           ", IOException in getSession():  " + e);
                 }
             }
@@ -2328,7 +2350,7 @@
 
         default:
             if (debug != null && Debug.isOn("ssl")) {
-                System.out.println(threadName() +
+                System.out.println(Thread.currentThread().getName() +
                     ", setUseClientMode() invoked in state = " +
                     connectionState);
             }
@@ -2422,14 +2444,11 @@
      */
     public void setSoTimeout(int timeout) throws SocketException {
         if ((debug != null) && Debug.isOn("ssl")) {
-            System.out.println(threadName() +
+            System.out.println(Thread.currentThread().getName() +
                 ", setSoTimeout(" + timeout + ") called");
         }
-        if (self == this) {
-            super.setSoTimeout(timeout);
-        } else {
-            self.setSoTimeout(timeout);
-        }
+
+        super.setSoTimeout(timeout);
     }
 
     /**
@@ -2474,6 +2493,8 @@
         // the super implementation does not handle the following parameters
         params.setEndpointIdentificationAlgorithm(identificationProtocol);
         params.setAlgorithmConstraints(algorithmConstraints);
+        params.setSNIMatchers(sniMatchers);
+        params.setServerNames(serverNames);
 
         return params;
     }
@@ -2487,9 +2508,25 @@
         // the super implementation does not handle the following parameters
         identificationProtocol = params.getEndpointIdentificationAlgorithm();
         algorithmConstraints = params.getAlgorithmConstraints();
+
+        List<SNIServerName> sniNames = params.getServerNames();
+        if (sniNames != null) {
+            serverNames = sniNames;
+        }
+
+        Collection<SNIMatcher> matchers = params.getSNIMatchers();
+        if (matchers != null) {
+            sniMatchers = matchers;
+        }
+
         if ((handshaker != null) && !handshaker.started()) {
             handshaker.setIdentificationProtocol(identificationProtocol);
             handshaker.setAlgorithmConstraints(algorithmConstraints);
+            if (roleIsServer) {
+                handshaker.setSNIMatchers(sniMatchers);
+            } else {
+                handshaker.setSNIServerNames(serverNames);
+            }
         }
     }
 
@@ -2531,13 +2568,6 @@
     }
 
     /**
-     * Return the name of the current thread. Utility method.
-     */
-    private static String threadName() {
-        return Thread.currentThread().getName();
-    }
-
-    /**
      * Returns a printable representation of this end of the connection.
      */
     public String toString() {
@@ -2548,11 +2578,7 @@
         retval.append(sess.getCipherSuite());
         retval.append(": ");
 
-        if (self == this) {
-            retval.append(super.toString());
-        } else {
-            retval.append(self.toString());
-        }
+        retval.append(super.toString());
         retval.append("]");
 
         return retval.toString();
--- a/src/share/classes/sun/security/ssl/ServerHandshaker.java	Wed Oct 17 13:22:39 2012 -0700
+++ b/src/share/classes/sun/security/ssl/ServerHandshaker.java	Thu Oct 18 01:14:00 2012 -0700
@@ -276,6 +276,18 @@
             mesg.print(System.out);
         }
 
+        // check the server name indication if required
+        ServerNameExtension clientHelloSNIExt = (ServerNameExtension)
+                    mesg.extensions.get(ExtensionType.EXT_SERVER_NAME);
+        if (!sniMatchers.isEmpty()) {
+            // we do not reject client without SNI extension
+            if (clientHelloSNIExt != null &&
+                        !clientHelloSNIExt.isMatched(sniMatchers)) {
+                fatalSE(Alerts.alert_unrecognized_name,
+                    "Unrecognized server name indication");
+            }
+        }
+
         // Does the message include security renegotiation indication?
         boolean renegotiationIndicated = false;
 
@@ -474,6 +486,26 @@
                     }
                 }
 
+                // cannot resume session with different server name indication
+                if (resumingSession) {
+                    List<SNIServerName> oldServerNames =
+                            previous.getRequestedServerNames();
+                    if (clientHelloSNIExt != null) {
+                        if (!clientHelloSNIExt.isIdentical(oldServerNames)) {
+                            resumingSession = false;
+                        }
+                    } else if (!oldServerNames.isEmpty()) {
+                        resumingSession = false;
+                    }
+
+                    if (!resumingSession &&
+                            debug != null && Debug.isOn("handshake")) {
+                        System.out.println(
+                            "The requested server name indication " +
+                            "is not identical to the previous one");
+                    }
+                }
+
                 if (resumingSession &&
                         (doClientAuth == SSLEngineImpl.clauth_required)) {
                     try {
@@ -613,6 +645,14 @@
                     // algorithms in chooseCipherSuite()
             }
 
+            // set the server name indication in the session
+            List<SNIServerName> clientHelloSNI =
+                    Collections.<SNIServerName>emptyList();
+            if (clientHelloSNIExt != null) {
+                clientHelloSNI = clientHelloSNIExt.getServerNames();
+            }
+            session.setRequestedServerNames(clientHelloSNI);
+
             // set the handshake session
             setHandshakeSessionSE(session);
 
@@ -654,6 +694,15 @@
             m1.extensions.add(serverHelloRI);
         }
 
+        if (!sniMatchers.isEmpty() && clientHelloSNIExt != null) {
+            // When resuming a session, the server MUST NOT include a
+            // server_name extension in the server hello.
+            if (!resumingSession) {
+                ServerNameExtension serverHelloSNI = new ServerNameExtension();
+                m1.extensions.add(serverHelloSNI);
+            }
+        }
+
         if (debug != null && Debug.isOn("handshake")) {
             m1.print(System.out);
             System.out.println("Cipher suite:  " + session.getSuite());
--- a/src/share/classes/sun/security/ssl/SunJSSE.java	Wed Oct 17 13:22:39 2012 -0700
+++ b/src/share/classes/sun/security/ssl/SunJSSE.java	Thu Oct 18 01:14:00 2012 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 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
@@ -103,7 +103,7 @@
 
     // standard constructor
     protected SunJSSE() {
-        super("SunJSSE", 1.7d, info);
+        super("SunJSSE", 1.8d, info);
         subclassCheck();
         if (Boolean.TRUE.equals(fips)) {
             throw new ProviderException
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/sun/security/ssl/Utilities.java	Thu Oct 18 01:14:00 2012 -0700
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 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 javax.net.ssl.*;
+import java.util.*;
+import sun.net.util.IPAddressUtil;
+
+/**
+ * A utility class to share the static methods.
+ */
+final class Utilities {
+    /**
+     * Puts {@code hostname} into the {@code serverNames} list.
+     * <P>
+     * If the {@code serverNames} does not look like a legal FQDN, it will
+     * not be put into the returned list.
+     * <P>
+     * Note that the returned list does not allow duplicated name type.
+     *
+     * @return a list of {@link SNIServerName}
+     */
+    static List<SNIServerName> addToSNIServerNameList(
+            List<SNIServerName> serverNames, String hostname) {
+
+        SNIHostName sniHostName = rawToSNIHostName(hostname);
+        if (sniHostName == null) {
+            return serverNames;
+        }
+
+        int size = serverNames.size();
+        List<SNIServerName> sniList = (size != 0) ?
+                new ArrayList<SNIServerName>(serverNames) :
+                new ArrayList<SNIServerName>(1);
+
+        boolean reset = false;
+        for (int i = 0; i < size; i++) {
+            SNIServerName serverName = sniList.get(i);
+            if (serverName.getType() == StandardConstants.SNI_HOST_NAME) {
+                sniList.set(i, sniHostName);
+                if (Debug.isOn("ssl")) {
+                    System.out.println(Thread.currentThread().getName() +
+                        ", the previous server name in SNI (" + serverName +
+                        ") was replaced with (" + sniHostName + ")");
+                }
+                reset = true;
+                break;
+            }
+        }
+
+        if (!reset) {
+            sniList.add(sniHostName);
+        }
+
+        return Collections.<SNIServerName>unmodifiableList(sniList);
+    }
+
+    /**
+     * Converts string hostname to {@code SNIHostName}.
+     * <P>
+     * Note that to check whether a hostname is a valid domain name, we cannot
+     * use the hostname resolved from name services.  For virtual hosting,
+     * multiple hostnames may be bound to the same IP address, so the hostname
+     * resolved from name services is not always reliable.
+     *
+     * @param  hostname
+     *         the raw hostname
+     * @return an instance of {@link SNIHostName}, or null if the hostname does
+     *         not look like a FQDN
+     */
+    private static SNIHostName rawToSNIHostName(String hostname) {
+        SNIHostName sniHostName = null;
+        if (hostname != null && hostname.indexOf('.') > 0 &&
+                !hostname.endsWith(".") &&
+                !IPAddressUtil.isIPv4LiteralAddress(hostname) &&
+                !IPAddressUtil.isIPv6LiteralAddress(hostname)) {
+
+            try {
+                sniHostName = new SNIHostName(hostname);
+            } catch (IllegalArgumentException iae) {
+                // don't bother to handle illegal host_name
+                if (Debug.isOn("ssl")) {
+                    System.out.println(Thread.currentThread().getName() +
+                        ", \"" + hostname + "\" " +
+                        "is not a legal HostName for  server name indication");
+                }
+            }
+        }
+
+        return sniHostName;
+    }
+}
--- a/src/share/classes/sun/security/ssl/X509KeyManagerImpl.java	Wed Oct 17 13:22:39 2012 -0700
+++ b/src/share/classes/sun/security/ssl/X509KeyManagerImpl.java	Thu Oct 18 01:14:00 2012 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved.
+ * 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
@@ -128,13 +128,35 @@
     public String chooseServerAlias(String keyType,
             Principal[] issuers, Socket socket) {
         return chooseAlias(getKeyTypes(keyType), issuers, CheckType.SERVER,
-                        getAlgorithmConstraints(socket));
+            getAlgorithmConstraints(socket),
+            X509TrustManagerImpl.getRequestedServerNames(socket),
+            "HTTPS");    // The SNI HostName is a fully qualified domain name.
+                         // The certificate selection scheme for SNI HostName
+                         // is similar to HTTPS endpoint identification scheme
+                         // implemented in this provider.
+                         //
+                         // Using HTTPS endpoint identification scheme to guide
+                         // the selection of an appropriate authentication
+                         // certificate according to requested SNI extension.
+                         //
+                         // It is not a really HTTPS endpoint identification.
     }
 
     public String chooseEngineServerAlias(String keyType,
             Principal[] issuers, SSLEngine engine) {
         return chooseAlias(getKeyTypes(keyType), issuers, CheckType.SERVER,
-                        getAlgorithmConstraints(engine));
+            getAlgorithmConstraints(engine),
+            X509TrustManagerImpl.getRequestedServerNames(engine),
+            "HTTPS");    // The SNI HostName is a fully qualified domain name.
+                         // The certificate selection scheme for SNI HostName
+                         // is similar to HTTPS endpoint identification scheme
+                         // implemented in this provider.
+                         //
+                         // Using HTTPS endpoint identification scheme to guide
+                         // the selection of an appropriate authentication
+                         // certificate according to requested SNI extension.
+                         //
+                         // It is not a really HTTPS endpoint identification.
     }
 
     public String[] getClientAliases(String keyType, Principal[] issuers) {
@@ -321,8 +343,8 @@
      * The algorithm we use is:
      *   . scan through all the aliases in all builders in order
      *   . as soon as we find a perfect match, return
-     *     (i.e. a match with a cert that has appropriate key usage
-     *      and is not expired).
+     *     (i.e. a match with a cert that has appropriate key usage,
+     *      qualified endpoint identity, and is not expired).
      *   . if we do not find a perfect match, keep looping and remember
      *     the imperfect matches
      *   . at the end, sort the imperfect matches. we prefer expired certs
@@ -331,6 +353,15 @@
      */
     private String chooseAlias(List<KeyType> keyTypeList, Principal[] issuers,
             CheckType checkType, AlgorithmConstraints constraints) {
+
+        return chooseAlias(keyTypeList, issuers,
+                                    checkType, constraints, null, null);
+    }
+
+    private String chooseAlias(List<KeyType> keyTypeList, Principal[] issuers,
+            CheckType checkType, AlgorithmConstraints constraints,
+            List<SNIServerName> requestedServerNames, String idAlgorithm) {
+
         if (keyTypeList == null || keyTypeList.isEmpty()) {
             return null;
         }
@@ -340,7 +371,8 @@
         for (int i = 0, n = builders.size(); i < n; i++) {
             try {
                 List<EntryStatus> results = getAliases(i, keyTypeList,
-                                    issuerSet, false, checkType, constraints);
+                            issuerSet, false, checkType, constraints,
+                            requestedServerNames, idAlgorithm);
                 if (results != null) {
                     // the results will either be a single perfect match
                     // or 1 or more imperfect matches
@@ -394,7 +426,8 @@
         for (int i = 0, n = builders.size(); i < n; i++) {
             try {
                 List<EntryStatus> results = getAliases(i, keyTypeList,
-                                    issuerSet, true, checkType, constraints);
+                                    issuerSet, true, checkType, constraints,
+                                    null, null);
                 if (results != null) {
                     if (allResults == null) {
                         allResults = new ArrayList<EntryStatus>();
@@ -504,7 +537,9 @@
         // first check extensions, if they match, check expiration
         // note: we may want to move this code into the sun.security.validator
         // package
-        CheckResult check(X509Certificate cert, Date date) {
+        CheckResult check(X509Certificate cert, Date date,
+                List<SNIServerName> serverNames, String idAlgorithm) {
+
             if (this == NONE) {
                 return CheckResult.OK;
             }
@@ -553,11 +588,11 @@
                                 return CheckResult.EXTENSION_MISMATCH;
                             }
                             // For servers, also require key agreement.
-                            // This is not totally accurate as the keyAgreement bit
-                            // is only necessary for static ECDH key exchange and
-                            // not ephemeral ECDH. We leave it in for now until
-                            // there are signs that this check causes problems
-                            // for real world EC certificates.
+                            // This is not totally accurate as the keyAgreement
+                            // bit is only necessary for static ECDH key
+                            // exchange and not ephemeral ECDH. We leave it in
+                            // for now until there are signs that this check
+                            // causes problems for real world EC certificates.
                             if ((this == SERVER) && (getBit(ku, 4) == false)) {
                                 return CheckResult.EXTENSION_MISMATCH;
                             }
@@ -571,10 +606,50 @@
 
             try {
                 cert.checkValidity(date);
-                return CheckResult.OK;
             } catch (CertificateException e) {
                 return CheckResult.EXPIRED;
             }
+
+            if (serverNames != null && !serverNames.isEmpty()) {
+                for (SNIServerName serverName : serverNames) {
+                    if (serverName.getType() ==
+                                StandardConstants.SNI_HOST_NAME) {
+                        if (!(serverName instanceof SNIHostName)) {
+                            try {
+                                serverName =
+                                    new SNIHostName(serverName.getEncoded());
+                            } catch (IllegalArgumentException iae) {
+                                // unlikely to happen, just in case ...
+                                if (useDebug) {
+                                    debug.println(
+                                       "Illegal server name: " + serverName);
+                                }
+
+                                return CheckResult.INSENSITIVE;
+                            }
+                        }
+                        String hostname =
+                                ((SNIHostName)serverName).getAsciiName();
+
+                        try {
+                            X509TrustManagerImpl.checkIdentity(hostname,
+                                                        cert, idAlgorithm);
+                        } catch (CertificateException e) {
+                            if (useDebug) {
+                                debug.println(
+                                   "Certificate identity does not match " +
+                                   "Server Name Inidication (SNI): " +
+                                   hostname);
+                            }
+                            return CheckResult.INSENSITIVE;
+                        }
+
+                        break;
+                    }
+                }
+            }
+
+            return CheckResult.OK;
         }
     }
 
@@ -583,6 +658,7 @@
     // for sorting, i.e. OK is best, followed by EXPIRED and EXTENSION_MISMATCH
     private static enum CheckResult {
         OK,                     // ok or not checked
+        INSENSITIVE,            // server name indication insensitive
         EXPIRED,                // extensions valid but cert expired
         EXTENSION_MISMATCH,     // extensions invalid (expiration not checked)
     }
@@ -616,7 +692,10 @@
     private List<EntryStatus> getAliases(int builderIndex,
             List<KeyType> keyTypes, Set<Principal> issuerSet,
             boolean findAll, CheckType checkType,
-            AlgorithmConstraints constraints) throws Exception {
+            AlgorithmConstraints constraints,
+            List<SNIServerName> requestedServerNames,
+            String idAlgorithm) throws Exception {
+
         Builder builder = builders.get(builderIndex);
         KeyStore ks = builder.getKeyStore();
         List<EntryStatus> results = null;
@@ -699,7 +778,8 @@
                 date = new Date();
             }
             CheckResult checkResult =
-                    checkType.check((X509Certificate)chain[0], date);
+                    checkType.check((X509Certificate)chain[0], date,
+                                    requestedServerNames, idAlgorithm);
             EntryStatus status =
                     new EntryStatus(builderIndex, keyIndex,
                                         alias, chain, checkResult);
--- a/src/share/classes/sun/security/ssl/X509TrustManagerImpl.java	Wed Oct 17 13:22:39 2012 -0700
+++ b/src/share/classes/sun/security/ssl/X509TrustManagerImpl.java	Thu Oct 18 01:14:00 2012 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 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
@@ -28,15 +28,14 @@
 
 import java.net.Socket;
 import javax.net.ssl.SSLSession;
+import java.nio.charset.StandardCharsets;
 
 import java.util.*;
 import java.security.*;
 import java.security.cert.*;
-
 import javax.net.ssl.*;
 
 import sun.security.validator.*;
-
 import sun.security.util.HostnameChecker;
 
 /**
@@ -199,8 +198,8 @@
             String identityAlg = sslSocket.getSSLParameters().
                                         getEndpointIdentificationAlgorithm();
             if (identityAlg != null && identityAlg.length() != 0) {
-                String hostname = session.getPeerHost();
-                checkIdentity(hostname, chain[0], identityAlg);
+                checkIdentity(session, chain[0], identityAlg, isClient,
+                        getRequestedServerNames(socket));
             }
 
             // create the algorithm constraints
@@ -251,8 +250,8 @@
             String identityAlg = engine.getSSLParameters().
                                         getEndpointIdentificationAlgorithm();
             if (identityAlg != null && identityAlg.length() != 0) {
-                String hostname = session.getPeerHost();
-                checkIdentity(hostname, chain[0], identityAlg);
+                checkIdentity(session, chain[0], identityAlg, isClient,
+                        getRequestedServerNames(engine));
             }
 
             // create the algorithm constraints
@@ -329,6 +328,117 @@
         }
     }
 
+    // Get string representation of HostName from a list of server names.
+    //
+    // We are only accepting host_name name type in the list.
+    private static String getHostNameInSNI(List<SNIServerName> sniNames) {
+
+        SNIHostName hostname = null;
+        for (SNIServerName sniName : sniNames) {
+            if (sniName.getType() != StandardConstants.SNI_HOST_NAME) {
+                continue;
+            }
+
+            if (sniName instanceof SNIHostName) {
+                hostname = (SNIHostName)sniName;
+            } else {
+                try {
+                    hostname = new SNIHostName(sniName.getEncoded());
+                } catch (IllegalArgumentException iae) {
+                    // unlikely to happen, just in case ...
+                    if ((debug != null) && Debug.isOn("trustmanager")) {
+                        byte[] encoded = hostname.getEncoded();
+                        System.out.println("Illegal server name: " + sniName);
+                    }
+                }
+            }
+
+            // no more than server name of the same name type
+            break;
+        }
+
+        if (hostname != null) {
+            return hostname.getAsciiName();
+        }
+
+        return null;
+    }
+
+    // Also used by X509KeyManagerImpl
+    static List<SNIServerName> getRequestedServerNames(Socket socket) {
+        if (socket != null && socket.isConnected() &&
+                                        socket instanceof SSLSocket) {
+
+            SSLSocket sslSocket = (SSLSocket)socket;
+            SSLSession session = sslSocket.getHandshakeSession();
+
+            if (session != null && (session instanceof ExtendedSSLSession)) {
+                ExtendedSSLSession extSession = (ExtendedSSLSession)session;
+                return extSession.getRequestedServerNames();
+            }
+        }
+
+        return Collections.<SNIServerName>emptyList();
+    }
+
+    // Also used by X509KeyManagerImpl
+    static List<SNIServerName> getRequestedServerNames(SSLEngine engine) {
+        if (engine != null) {
+            SSLSession session = engine.getHandshakeSession();
+
+            if (session != null && (session instanceof ExtendedSSLSession)) {
+                ExtendedSSLSession extSession = (ExtendedSSLSession)session;
+                return extSession.getRequestedServerNames();
+            }
+        }
+
+        return Collections.<SNIServerName>emptyList();
+    }
+
+    /*
+     * Per RFC 6066, if an application negotiates a server name using an
+     * application protocol and then upgrades to TLS, and if a server_name
+     * extension is sent, then the extension SHOULD contain the same name
+     * that was negotiated in the application protocol.  If the server_name
+     * is established in the TLS session handshake, the client SHOULD NOT
+     * attempt to request a different server name at the application layer.
+     *
+     * According to the above spec, we only need to check either the identity
+     * in server_name extension or the peer host of the connection.  Peer host
+     * is not always a reliable fully qualified domain name. The HostName in
+     * server_name extension is more reliable than peer host. So we prefer
+     * the identity checking aginst the server_name extension if present, and
+     * may failove to peer host checking.
+     */
+    private static void checkIdentity(SSLSession session,
+            X509Certificate cert,
+            String algorithm,
+            boolean isClient,
+            List<SNIServerName> sniNames) throws CertificateException {
+
+        boolean identifiable = false;
+        String peerHost = session.getPeerHost();
+        if (isClient) {
+            String hostname = getHostNameInSNI(sniNames);
+            if (hostname != null) {
+                try {
+                    checkIdentity(hostname, cert, algorithm);
+                    identifiable = true;
+                } catch (CertificateException ce) {
+                    if (hostname.equalsIgnoreCase(peerHost)) {
+                        throw ce;
+                    }
+
+                    // otherwisw, failover to check peer host
+                }
+            }
+        }
+
+        if (!identifiable) {
+            checkIdentity(peerHost, cert, algorithm);
+        }
+    }
+
     /*
      * Identify the peer by its certificate and hostname.
      *
--- a/test/sun/security/ssl/javax/net/ssl/NewAPIs/SSLEngine/LargePacket.java	Wed Oct 17 13:22:39 2012 -0700
+++ b/test/sun/security/ssl/javax/net/ssl/NewAPIs/SSLEngine/LargePacket.java	Thu Oct 18 01:14:00 2012 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 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
@@ -21,13 +21,18 @@
  * questions.
  */
 
+//
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+//
+
 /*
  * @test
  *
  * @bug 6388456
  * @summary Need adjustable TLS max record size for interoperability
  *      with non-compliant
- * @run main/othervm -Djsse.enableCBCProtection=false LargePacket
+ * @run main/othervm LargePacket
  *
  * @author Xuelei Fan
  */
@@ -83,12 +88,12 @@
         SocketChannel sc = ssc.accept();
 
         // Complete connection.
-        while (!sc.finishConnect() ) {
+        while (!sc.finishConnect()) {
             // waiting for the connection completed.
         }
 
         // handshaking
-        handshaking(ssle, sc);
+        handshaking(ssle, sc, null);
 
         // receive application data
         receive(ssle, sc);
@@ -131,7 +136,7 @@
         }
 
         // handshaking
-        handshaking(ssle, sc);
+        handshaking(ssle, sc, null);
 
         // send out application data
         deliver(ssle, sc);
@@ -169,6 +174,8 @@
      * Fork off the other side, then do your work.
      */
     LargePacket() throws Exception {
+        super("../../../../../etc");
+
         if (separateServerThread) {
             startServer(true);
             startClient(false);
--- a/test/sun/security/ssl/javax/net/ssl/NewAPIs/SSLEngine/SSLEngineService.java	Wed Oct 17 13:22:39 2012 -0700
+++ b/test/sun/security/ssl/javax/net/ssl/NewAPIs/SSLEngine/SSLEngineService.java	Thu Oct 18 01:14:00 2012 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 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
@@ -22,8 +22,6 @@
  */
 
 /*
- *
- *
  * @bug 6388456
  * @summary Need adjustable TLS max record size for interoperability
  *      with non-compliant stacks
@@ -42,17 +40,31 @@
 
 public class SSLEngineService {
 
-    private static String pathToStores = "../../../../../etc";
     private static String keyStoreFile = "keystore";
     private static String trustStoreFile = "truststore";
     private static char[] passphrase = "passphrase".toCharArray();
 
-    private static String keyFilename =
+    private String pathToStores;
+    private String keyFilename;
+    private String trustFilename;
+
+    protected SSLEngineService() {
+        init("../../../../../etc");
+    }
+
+    protected SSLEngineService(String pathToStores) {
+        init(pathToStores);
+    }
+
+    private void init(String pathToStores) {
+        this.pathToStores = pathToStores;
+        this.keyFilename =
             System.getProperty("test.src", "./") + "/" + pathToStores +
                 "/" + keyStoreFile;
-    private static String trustFilename =
+        this.trustFilename =
             System.getProperty("test.src", "./") + "/" + pathToStores +
                 "/" + trustStoreFile;
+    }
 
     // deliver local application data.
     protected static void deliver(SSLEngine ssle, SocketChannel sc)
@@ -143,9 +155,12 @@
         ByteBuffer peerNetData = ByteBuffer.allocate(netBufferMax/2);
         int received = -1;
 
+        boolean needToReadMore = true;
         while (received != 0) {
-            if (ssle.isInboundDone() || sc.read(peerNetData) < 0) {
-                break;
+            if (needToReadMore) {
+                if (ssle.isInboundDone() || sc.read(peerNetData) < 0) {
+                    break;
+                }
             }
 
             peerNetData.flip();
@@ -186,6 +201,8 @@
                         " bytes large packet ");
                 }
 
+                needToReadMore = (peerNetData.position() > 0) ? false : true;
+
                 break;
 
             case BUFFER_OVERFLOW :
@@ -206,6 +223,8 @@
                         " bytes for BUFFER_UNDERFLOW");
                     peerNetData = enlargeBuffer(peerNetData, size);
                 }
+
+                needToReadMore = true;
                 break;
 
             default : // CLOSED :
@@ -215,8 +234,8 @@
         }
     }
 
-    protected static void handshaking(SSLEngine ssle, SocketChannel sc)
-        throws Exception {
+    protected static void handshaking(SSLEngine ssle, SocketChannel sc,
+            ByteBuffer additional) throws Exception {
 
         int appBufferMax = ssle.getSession().getApplicationBufferSize();
         int netBufferMax = ssle.getSession().getPacketBufferSize();
@@ -232,15 +251,39 @@
         SSLEngineResult.HandshakeStatus hs = ssle.getHandshakeStatus();
 
         // start handshaking from unwrap
+        byte[] buffer = new byte[0xFF];
+        boolean underflow = false;
         do {
             switch (hs) {
 
             case NEED_UNWRAP :
                 if (peerNetData.position() == 0) {
+                    if (additional != null && additional.hasRemaining()) {
+                        do {
+                            int len = Math.min(buffer.length,
+                                                peerNetData.remaining());
+                            len = Math.min(len, additional.remaining());
+                            if (len != 0) {
+                                additional.get(buffer, 0, len);
+                                peerNetData.put(buffer, 0, len);
+                            }
+                        } while (peerNetData.remaining() > 0 &&
+                                    additional.hasRemaining());
+                    } else {
+                        if (sc.read(peerNetData) < 0) {
+                            ssle.closeInbound();
+                            return;
+                        }
+                    }
+                }
+
+                if (underflow) {
                     if (sc.read(peerNetData) < 0) {
                         ssle.closeInbound();
                         return;
                     }
+
+                    underflow = false;
                 }
 
                 peerNetData.flip();
@@ -259,6 +302,8 @@
                                 size + " bytes for BUFFER_UNDERFLOW");
                         peerNetData = enlargeBuffer(peerNetData, size);
                     }
+
+                    underflow = true;
                     break;
                 case BUFFER_OVERFLOW :
                     // maybe need to enlarge the peer application data buffer.
@@ -339,7 +384,7 @@
     /*
      * Create an initialized SSLContext to use for this test.
      */
-    protected static SSLEngine createSSLEngine(boolean mode) throws Exception {
+    protected SSLEngine createSSLEngine(boolean mode) throws Exception {
 
         SSLEngine ssle;
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/sun/security/ssl/javax/net/ssl/ServerName/SSLEngineExplorer.java	Thu Oct 18 01:14:00 2012 -0700
@@ -0,0 +1,335 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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 7068321
+ * @summary Support TLS Server Name Indication (SNI) Extension in JSSE Server
+ * @library ../NewAPIs/SSLEngine ../../../../templates
+ * @build SSLEngineService SSLCapabilities SSLExplorer
+ * @run main/othervm SSLEngineExplorer SSLv2Hello,SSLv3
+ * @run main/othervm SSLEngineExplorer SSLv3
+ * @run main/othervm SSLEngineExplorer TLSv1
+ * @run main/othervm SSLEngineExplorer TLSv1.1
+ * @run main/othervm SSLEngineExplorer TLSv1.2
+ */
+
+import javax.net.ssl.*;
+import java.nio.*;
+import java.net.*;
+import java.util.*;
+import java.nio.channels.*;
+
+public class SSLEngineExplorer extends SSLEngineService {
+
+    /*
+     * =============================================================
+     * Set the various variables needed for the tests, then
+     * specify what tests to run on each side.
+     */
+
+    /*
+     * Should we run the client or server in a separate thread?
+     * Both sides can throw exceptions, but do you have a preference
+     * as to which side should be the main thread.
+     */
+    static boolean separateServerThread = true;
+
+    // Is the server ready to serve?
+    volatile static boolean serverReady = false;
+
+    /*
+     * Turn on SSL debugging?
+     */
+    static boolean debug = false;
+
+    /*
+     * Define the server side of the test.
+     *
+     * If the server prematurely exits, serverReady will be set to true
+     * to avoid infinite hangs.
+     */
+    void doServerSide() throws Exception {
+
+        // create SSLEngine.
+        SSLEngine ssle = createSSLEngine(false);
+
+        // Create a server socket channel.
+        InetSocketAddress isa =
+                new InetSocketAddress(InetAddress.getLocalHost(), serverPort);
+        ServerSocketChannel ssc = ServerSocketChannel.open();
+        ssc.socket().bind(isa);
+        serverPort = ssc.socket().getLocalPort();
+
+        // Signal Client, we're ready for his connect.
+        serverReady = true;
+
+        // Accept a socket channel.
+        SocketChannel sc = ssc.accept();
+        sc.configureBlocking(false);
+
+        // Complete connection.
+        while (!sc.finishConnect()) {
+            Thread.sleep(50);
+            // waiting for the connection completed.
+        }
+
+        ByteBuffer buffer = ByteBuffer.allocate(0xFF);
+        int position = 0;
+        SSLCapabilities capabilities = null;
+
+        // Read the header of TLS record
+        buffer.limit(SSLExplorer.RECORD_HEADER_SIZE);
+        while (position < SSLExplorer.RECORD_HEADER_SIZE) {
+            int n = sc.read(buffer);
+            if (n < 0) {
+                throw new Exception("unexpected end of stream!");
+            }
+            position += n;
+        }
+        buffer.flip();
+
+        int recordLength = SSLExplorer.getRequiredSize(buffer);
+        if (buffer.capacity() < recordLength) {
+            ByteBuffer oldBuffer = buffer;
+            buffer = ByteBuffer.allocate(recordLength);
+            buffer.put(oldBuffer);
+        }
+
+        buffer.position(SSLExplorer.RECORD_HEADER_SIZE);
+        buffer.limit(buffer.capacity());
+        while (position < recordLength) {
+            int n = sc.read(buffer);
+            if (n < 0) {
+                throw new Exception("unexpected end of stream!");
+            }
+            position += n;
+        }
+        buffer.flip();
+
+        capabilities = SSLExplorer.explore(buffer);
+        if (capabilities != null) {
+            System.out.println("Record version: " +
+                    capabilities.getRecordVersion());
+            System.out.println("Hello version: " +
+                    capabilities.getHelloVersion());
+        }
+
+        // handshaking
+        handshaking(ssle, sc, buffer);
+
+        // receive application data
+        receive(ssle, sc);
+
+        // send out application data
+        deliver(ssle, sc);
+
+        ExtendedSSLSession session = (ExtendedSSLSession)ssle.getSession();
+        checkCapabilities(capabilities, session);
+
+        // close the socket channel.
+        sc.close();
+        ssc.close();
+    }
+
+    /*
+     * Define the client side of the test.
+     *
+     * If the server prematurely exits, serverReady will be set to true
+     * to avoid infinite hangs.
+     */
+    void doClientSide() throws Exception {
+        // create SSLEngine.
+        SSLEngine ssle = createSSLEngine(true);
+
+        /*
+         * Wait for server to get started.
+         */
+        while (!serverReady) {
+            Thread.sleep(50);
+        }
+
+        // Create a non-blocking socket channel.
+        SocketChannel sc = SocketChannel.open();
+        sc.configureBlocking(false);
+        InetSocketAddress isa =
+                new InetSocketAddress(InetAddress.getLocalHost(), serverPort);
+        sc.connect(isa);
+
+        // Complete connection.
+        while (!sc.finishConnect() ) {
+            Thread.sleep(50);
+            // waiting for the connection completed.
+        }
+
+        // enable the specified TLS protocol
+        ssle.setEnabledProtocols(supportedProtocols);
+
+        // handshaking
+        handshaking(ssle, sc, null);
+
+        // send out application data
+        deliver(ssle, sc);
+
+        // receive application data
+        receive(ssle, sc);
+
+        // close the socket channel.
+        sc.close();
+    }
+
+    void checkCapabilities(SSLCapabilities capabilities,
+            ExtendedSSLSession session) throws Exception {
+
+        List<SNIServerName> sessionSNI = session.getRequestedServerNames();
+        if (!sessionSNI.equals(capabilities.getServerNames())) {
+            throw new Exception(
+                    "server name indication does not match capabilities");
+        }
+    }
+
+    private static String[] supportedProtocols;    // supported protocols
+
+    private static void parseArguments(String[] args) {
+        supportedProtocols = args[0].split(",");
+    }
+
+
+    /*
+     * =============================================================
+     * The remainder is just support stuff
+     */
+    volatile Exception serverException = null;
+    volatile Exception clientException = null;
+
+    // use any free port by default
+    volatile int serverPort = 0;
+
+    public static void main(String args[]) throws Exception {
+        if (debug)
+            System.setProperty("javax.net.debug", "all");
+
+        /*
+         * Get the customized arguments.
+         */
+        parseArguments(args);
+
+        new SSLEngineExplorer();
+    }
+
+    Thread clientThread = null;
+    Thread serverThread = null;
+
+    /*
+     * Primary constructor, used to drive remainder of the test.
+     *
+     * Fork off the other side, then do your work.
+     */
+    SSLEngineExplorer() throws Exception {
+        super("../../../../etc");
+
+        if (separateServerThread) {
+            startServer(true);
+            startClient(false);
+        } else {
+            startClient(true);
+            startServer(false);
+        }
+
+        /*
+         * Wait for other side to close down.
+         */
+        if (separateServerThread) {
+            serverThread.join();
+        } else {
+            clientThread.join();
+        }
+
+        /*
+         * When we get here, the test is pretty much over.
+         *
+         * If the main thread excepted, that propagates back
+         * immediately.  If the other thread threw an exception, we
+         * should report back.
+         */
+        if (serverException != null) {
+            System.out.print("Server Exception:");
+            throw serverException;
+        }
+        if (clientException != null) {
+            System.out.print("Client Exception:");
+            throw clientException;
+        }
+    }
+
+    void startServer(boolean newThread) throws Exception {
+        if (newThread) {
+            serverThread = new Thread() {
+                public void run() {
+                    try {
+                        doServerSide();
+                    } catch (Exception e) {
+                        /*
+                         * Our server thread just died.
+                         *
+                         * Release the client, if not active already...
+                         */
+                        System.err.println("Server died...");
+                        System.err.println(e);
+                        serverReady = true;
+                        serverException = e;
+                    }
+                }
+            };
+            serverThread.start();
+        } else {
+            doServerSide();
+        }
+    }
+
+    void startClient(boolean newThread) throws Exception {
+        if (newThread) {
+            clientThread = new Thread() {
+                public void run() {
+                    try {
+                        doClientSide();
+                    } catch (Exception e) {
+                        /*
+                         * Our client thread just died.
+                         */
+                        System.err.println("Client died...");
+                        clientException = e;
+                    }
+                }
+            };
+            clientThread.start();
+        } else {
+            doClientSide();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/sun/security/ssl/javax/net/ssl/ServerName/SSLEngineExplorerMatchedSNI.java	Thu Oct 18 01:14:00 2012 -0700
@@ -0,0 +1,393 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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 7068321
+ * @summary Support TLS Server Name Indication (SNI) Extension in JSSE Server
+ * @library ../NewAPIs/SSLEngine ../../../../templates
+ * @build SSLEngineService SSLCapabilities SSLExplorer
+ * @run main/othervm SSLEngineExplorerMatchedSNI www.example.com
+ *     www\.example\.com
+ * @run main/othervm SSLEngineExplorerMatchedSNI www.example.com
+ *     www\.example\.(com|org)
+ * @run main/othervm SSLEngineExplorerMatchedSNI example.com
+ *     (.*\.)*example\.(com|org)
+ * @run main/othervm SSLEngineExplorerMatchedSNI www.example.com
+ *     (.*\.)*example\.(com|org)
+ * @run main/othervm SSLEngineExplorerMatchedSNI www.us.example.com
+ *     (.*\.)*example\.(com|org)
+ */
+
+import javax.net.ssl.*;
+import java.nio.*;
+import java.net.*;
+import java.util.*;
+import java.nio.channels.*;
+
+public class SSLEngineExplorerMatchedSNI extends SSLEngineService {
+
+    /*
+     * =============================================================
+     * Set the various variables needed for the tests, then
+     * specify what tests to run on each side.
+     */
+
+    /*
+     * Should we run the client or server in a separate thread?
+     * Both sides can throw exceptions, but do you have a preference
+     * as to which side should be the main thread.
+     */
+    static boolean separateServerThread = false;
+
+    // Is the server ready to serve?
+    volatile static boolean serverReady = false;
+
+    /*
+     * Turn on SSL debugging?
+     */
+    static boolean debug = false;
+
+    /*
+     * Define the server side of the test.
+     *
+     * If the server prematurely exits, serverReady will be set to true
+     * to avoid infinite hangs.
+     */
+    void doServerSide() throws Exception {
+
+        // create SSLEngine.
+        SSLEngine ssle = createSSLEngine(false);
+
+        // Create a server socket channel.
+        InetSocketAddress isa =
+                new InetSocketAddress(InetAddress.getLocalHost(), serverPort);
+        ServerSocketChannel ssc = ServerSocketChannel.open();
+        ssc.socket().bind(isa);
+        serverPort = ssc.socket().getLocalPort();
+
+        // Signal Client, we're ready for his connect.
+        serverReady = true;
+
+        // Accept a socket channel.
+        SocketChannel sc = ssc.accept();
+
+        // Complete connection.
+        while (!sc.finishConnect()) {
+            Thread.sleep(50);
+            // waiting for the connection completed.
+        }
+
+        ByteBuffer buffer = ByteBuffer.allocate(0xFF);
+        int position = 0;
+        SSLCapabilities capabilities = null;
+
+        // Read the header of TLS record
+        buffer.limit(SSLExplorer.RECORD_HEADER_SIZE);
+        while (position < SSLExplorer.RECORD_HEADER_SIZE) {
+            int n = sc.read(buffer);
+            if (n < 0) {
+                throw new Exception("unexpected end of stream!");
+            }
+            position += n;
+        }
+        buffer.flip();
+
+        int recordLength = SSLExplorer.getRequiredSize(buffer);
+        if (buffer.capacity() < recordLength) {
+            ByteBuffer oldBuffer = buffer;
+            buffer = ByteBuffer.allocate(recordLength);
+            buffer.put(oldBuffer);
+        }
+
+        buffer.position(SSLExplorer.RECORD_HEADER_SIZE);
+        buffer.limit(buffer.capacity());
+        while (position < recordLength) {
+            int n = sc.read(buffer);
+            if (n < 0) {
+                throw new Exception("unexpected end of stream!");
+            }
+            position += n;
+        }
+        buffer.flip();
+
+        capabilities = SSLExplorer.explore(buffer);
+        if (capabilities != null) {
+            System.out.println("Record version: " +
+                    capabilities.getRecordVersion());
+            System.out.println("Hello version: " +
+                    capabilities.getHelloVersion());
+        }
+
+        // enable server name indication checking
+        SNIMatcher matcher = SNIHostName.createSNIMatcher(
+                                                serverAcceptableHostname);
+        Collection<SNIMatcher> matchers = new ArrayList<>(1);
+        matchers.add(matcher);
+        SSLParameters params = ssle.getSSLParameters();
+        params.setSNIMatchers(matchers);
+        ssle.setSSLParameters(params);
+
+        // handshaking
+        handshaking(ssle, sc, buffer);
+
+        // receive application data
+        receive(ssle, sc);
+
+        // send out application data
+        deliver(ssle, sc);
+
+        // check server name indication
+        ExtendedSSLSession session = (ExtendedSSLSession)ssle.getSession();
+        checkCapabilities(capabilities, session);
+
+        // close the socket channel.
+        sc.close();
+        ssc.close();
+    }
+
+    /*
+     * Define the client side of the test.
+     *
+     * If the server prematurely exits, serverReady will be set to true
+     * to avoid infinite hangs.
+     */
+    void doClientSide() throws Exception {
+        // create SSLEngine.
+        SSLEngine ssle = createSSLEngine(true);
+
+        /*
+         * Wait for server to get started.
+         */
+        while (!serverReady) {
+            Thread.sleep(50);
+        }
+
+        // Create a non-blocking socket channel.
+        SocketChannel sc = SocketChannel.open();
+        sc.configureBlocking(false);
+        InetSocketAddress isa =
+                new InetSocketAddress(InetAddress.getLocalHost(), serverPort);
+        sc.connect(isa);
+
+        // Complete connection.
+        while (!sc.finishConnect() ) {
+            Thread.sleep(50);
+            // waiting for the connection completed.
+        }
+
+        SNIHostName serverName = new SNIHostName(clientRequestedHostname);
+        List<SNIServerName> serverNames = new ArrayList<>(1);
+        serverNames.add(serverName);
+        SSLParameters params = ssle.getSSLParameters();
+        params.setServerNames(serverNames);
+        ssle.setSSLParameters(params);
+
+        // handshaking
+        handshaking(ssle, sc, null);
+
+        // send out application data
+        deliver(ssle, sc);
+
+        // receive application data
+        receive(ssle, sc);
+
+        // check server name indication
+        ExtendedSSLSession session = (ExtendedSSLSession)ssle.getSession();
+        checkSNIInSession(session);
+
+        // close the socket channel.
+        sc.close();
+    }
+
+    void checkCapabilities(SSLCapabilities capabilities,
+            ExtendedSSLSession session) throws Exception {
+        List<SNIServerName> sessionSNI = session.getRequestedServerNames();
+        if (!sessionSNI.equals(capabilities.getServerNames())) {
+            for (SNIServerName sni : sessionSNI) {
+                System.out.println("SNI in session is " + sni);
+            }
+
+            List<SNIServerName> capaSNI = capabilities.getServerNames();
+            for (SNIServerName sni : capaSNI) {
+                System.out.println("SNI in session is " + sni);
+            }
+
+            throw new Exception(
+                    "server name indication does not match capabilities");
+        }
+
+        checkSNIInSession(session);
+    }
+
+    void checkSNIInSession(ExtendedSSLSession session) throws Exception {
+        List<SNIServerName> sessionSNI = session.getRequestedServerNames();
+        if (sessionSNI.isEmpty()) {
+            throw new Exception(
+                    "unexpected empty request server name indication");
+        }
+
+        if (sessionSNI.size() != 1) {
+            throw new Exception(
+                    "unexpected request server name indication");
+        }
+
+        SNIServerName serverName = sessionSNI.get(0);
+        if (!(serverName instanceof SNIHostName)) {
+            throw new Exception(
+                    "unexpected instance of request server name indication");
+        }
+
+        String hostname = ((SNIHostName)serverName).getAsciiName();
+        if (!clientRequestedHostname.equalsIgnoreCase(hostname)) {
+            throw new Exception(
+                    "unexpected request server name indication value");
+        }
+    }
+
+    private static String clientRequestedHostname;
+    private static String serverAcceptableHostname;
+
+    private static void parseArguments(String[] args) {
+        clientRequestedHostname = args[0];
+        serverAcceptableHostname = args[1];
+    }
+
+    /*
+     * =============================================================
+     * The remainder is just support stuff
+     */
+    volatile Exception serverException = null;
+    volatile Exception clientException = null;
+
+    // use any free port by default
+    volatile int serverPort = 0;
+
+    public static void main(String args[]) throws Exception {
+        if (debug)
+            System.setProperty("javax.net.debug", "all");
+
+        /*
+         * Get the customized arguments.
+         */
+        parseArguments(args);
+
+        new SSLEngineExplorerMatchedSNI();
+    }
+
+    Thread clientThread = null;
+    Thread serverThread = null;
+
+    /*
+     * Primary constructor, used to drive remainder of the test.
+     *
+     * Fork off the other side, then do your work.
+     */
+    SSLEngineExplorerMatchedSNI() throws Exception {
+        super("../../../../etc");
+
+        if (separateServerThread) {
+            startServer(true);
+            startClient(false);
+        } else {
+            startClient(true);
+            startServer(false);
+        }
+
+        /*
+         * Wait for other side to close down.
+         */
+        if (separateServerThread) {
+            serverThread.join();
+        } else {
+            clientThread.join();
+        }
+
+        /*
+         * When we get here, the test is pretty much over.
+         *
+         * If the main thread excepted, that propagates back
+         * immediately.  If the other thread threw an exception, we
+         * should report back.
+         */
+        if (serverException != null) {
+            System.out.print("Server Exception:");
+            throw serverException;
+        }
+        if (clientException != null) {
+            System.out.print("Client Exception:");
+            throw clientException;
+        }
+    }
+
+    void startServer(boolean newThread) throws Exception {
+        if (newThread) {
+            serverThread = new Thread() {
+                public void run() {
+                    try {
+                        doServerSide();
+                    } catch (Exception e) {
+                        /*
+                         * Our server thread just died.
+                         *
+                         * Release the client, if not active already...
+                         */
+                        System.err.println("Server died...");
+                        System.err.println(e);
+                        serverReady = true;
+                        serverException = e;
+                    }
+                }
+            };
+            serverThread.start();
+        } else {
+            doServerSide();
+        }
+    }
+
+    void startClient(boolean newThread) throws Exception {
+        if (newThread) {
+            clientThread = new Thread() {
+                public void run() {
+                    try {
+                        doClientSide();
+                    } catch (Exception e) {
+                        /*
+                         * Our client thread just died.
+                         */
+                        System.err.println("Client died...");
+                        clientException = e;
+                    }
+                }
+            };
+            clientThread.start();
+        } else {
+            doClientSide();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/sun/security/ssl/javax/net/ssl/ServerName/SSLEngineExplorerUnmatchedSNI.java	Thu Oct 18 01:14:00 2012 -0700
@@ -0,0 +1,406 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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 7068321
+ * @summary Support TLS Server Name Indication (SNI) Extension in JSSE Server
+ * @library ../NewAPIs/SSLEngine ../../../../templates
+ * @build SSLEngineService SSLCapabilities SSLExplorer
+ * @run main/othervm SSLEngineExplorerUnmatchedSNI www.example.com
+ *                                                 www\.example\.org
+ */
+
+import javax.net.ssl.*;
+import java.io.*;
+import java.nio.*;
+import java.net.*;
+import java.util.*;
+import java.nio.channels.*;
+
+public class SSLEngineExplorerUnmatchedSNI extends SSLEngineService {
+
+    /*
+     * =============================================================
+     * Set the various variables needed for the tests, then
+     * specify what tests to run on each side.
+     */
+
+    /*
+     * Should we run the client or server in a separate thread?
+     * Both sides can throw exceptions, but do you have a preference
+     * as to which side should be the main thread.
+     */
+    static boolean separateServerThread = false;
+
+    // Is the server ready to serve?
+    volatile static boolean serverReady = false;
+
+    /*
+     * Turn on SSL debugging?
+     */
+    static boolean debug = false;
+
+    /*
+     * Define the server side of the test.
+     *
+     * If the server prematurely exits, serverReady will be set to true
+     * to avoid infinite hangs.
+     */
+    void doServerSide() throws Exception {
+
+        // create SSLEngine.
+        SSLEngine ssle = createSSLEngine(false);
+
+        // Create a server socket channel.
+        InetSocketAddress isa =
+                new InetSocketAddress(InetAddress.getLocalHost(), serverPort);
+        ServerSocketChannel ssc = ServerSocketChannel.open();
+        ssc.socket().bind(isa);
+        serverPort = ssc.socket().getLocalPort();
+
+        // Signal Client, we're ready for his connect.
+        serverReady = true;
+
+        // Accept a socket channel.
+        SocketChannel sc = ssc.accept();
+
+        // Complete connection.
+        while (!sc.finishConnect()) {
+            Thread.sleep(50);
+            // waiting for the connection completed.
+        }
+
+        ByteBuffer buffer = ByteBuffer.allocate(0xFF);
+        int position = 0;
+        SSLCapabilities capabilities = null;
+
+        // Read the header of TLS record
+        buffer.limit(SSLExplorer.RECORD_HEADER_SIZE);
+        while (position < SSLExplorer.RECORD_HEADER_SIZE) {
+            int n = sc.read(buffer);
+            if (n < 0) {
+                throw new Exception("unexpected end of stream!");
+            }
+            position += n;
+        }
+        buffer.flip();
+
+        int recordLength = SSLExplorer.getRequiredSize(buffer);
+        if (buffer.capacity() < recordLength) {
+            ByteBuffer oldBuffer = buffer;
+            buffer = ByteBuffer.allocate(recordLength);
+            buffer.put(oldBuffer);
+        }
+
+        buffer.position(SSLExplorer.RECORD_HEADER_SIZE);
+        buffer.limit(buffer.capacity());
+        while (position < recordLength) {
+            int n = sc.read(buffer);
+            if (n < 0) {
+                throw new Exception("unexpected end of stream!");
+            }
+            position += n;
+        }
+        buffer.flip();
+
+        capabilities = SSLExplorer.explore(buffer);
+        if (capabilities != null) {
+            System.out.println("Record version: " +
+                    capabilities.getRecordVersion());
+            System.out.println("Hello version: " +
+                    capabilities.getHelloVersion());
+        }
+
+        // enable server name indication checking
+        SNIMatcher matcher = SNIHostName.createSNIMatcher(
+                                                serverAcceptableHostname);
+        Collection<SNIMatcher> matchers = new ArrayList<>(1);
+        matchers.add(matcher);
+        SSLParameters params = ssle.getSSLParameters();
+        params.setSNIMatchers(matchers);
+        ssle.setSSLParameters(params);
+
+        try {
+            // handshaking
+            handshaking(ssle, sc, buffer);
+
+            // receive application data
+            receive(ssle, sc);
+
+            // send out application data
+            deliver(ssle, sc);
+
+            // check server name indication
+            ExtendedSSLSession session = (ExtendedSSLSession)ssle.getSession();
+            checkCapabilities(capabilities, session);
+
+            throw new Exception(
+                "Mismatched server name indication was accepted");
+        } catch (SSLHandshakeException sslhe) {
+            // the expected unrecognized server name indication exception
+        } catch (IOException ioe) {
+            // the peer may have closed the socket because of the unmatched
+            // server name indication.
+        } finally {
+            // close the socket channel.
+            sc.close();
+            ssc.close();
+        }
+    }
+
+    /*
+     * Define the client side of the test.
+     *
+     * If the server prematurely exits, serverReady will be set to true
+     * to avoid infinite hangs.
+     */
+    void doClientSide() throws Exception {
+        // create SSLEngine.
+        SSLEngine ssle = createSSLEngine(true);
+
+        /*
+         * Wait for server to get started.
+         */
+        while (!serverReady) {
+            Thread.sleep(50);
+        }
+
+        // Create a non-blocking socket channel.
+        SocketChannel sc = SocketChannel.open();
+        sc.configureBlocking(false);
+        InetSocketAddress isa =
+                new InetSocketAddress(InetAddress.getLocalHost(), serverPort);
+        sc.connect(isa);
+
+        // Complete connection.
+        while (!sc.finishConnect() ) {
+            Thread.sleep(50);
+            // waiting for the connection completed.
+        }
+
+        SNIHostName serverName = new SNIHostName(clientRequestedHostname);
+        List<SNIServerName> serverNames = new ArrayList<>(1);
+        serverNames.add(serverName);
+        SSLParameters params = ssle.getSSLParameters();
+        params.setServerNames(serverNames);
+        ssle.setSSLParameters(params);
+
+        try {
+            // handshaking
+            handshaking(ssle, sc, null);
+
+            // send out application data
+            deliver(ssle, sc);
+
+            // receive application data
+            receive(ssle, sc);
+
+            // check server name indication
+            ExtendedSSLSession session = (ExtendedSSLSession)ssle.getSession();
+            checkSNIInSession(session);
+
+            throw new Exception(
+                "Mismatched server name indication was accepted");
+        } catch (SSLHandshakeException sslhe) {
+            // the expected unrecognized server name indication exception
+        } catch (IOException ioe) {
+            // the peer may have closed the socket because of the unmatched
+            // server name indication.
+        } finally {
+            // close the socket channel.
+            sc.close();
+        }
+    }
+
+    void checkCapabilities(SSLCapabilities capabilities,
+            ExtendedSSLSession session) throws Exception {
+        List<SNIServerName> sessionSNI = session.getRequestedServerNames();
+        if (!sessionSNI.equals(capabilities.getServerNames())) {
+            for (SNIServerName sni : sessionSNI) {
+                System.out.println("SNI in session is " + sni);
+            }
+
+            List<SNIServerName> capaSNI = capabilities.getServerNames();
+            for (SNIServerName sni : capaSNI) {
+                System.out.println("SNI in session is " + sni);
+            }
+
+            throw new Exception(
+                    "server name indication does not match capabilities");
+        }
+
+        checkSNIInSession(session);
+    }
+
+    void checkSNIInSession(ExtendedSSLSession session) throws Exception {
+        List<SNIServerName> sessionSNI = session.getRequestedServerNames();
+        if (sessionSNI.isEmpty()) {
+            throw new Exception(
+                    "unexpected empty request server name indication");
+        }
+
+        if (sessionSNI.size() != 1) {
+            throw new Exception(
+                    "unexpected request server name indication");
+        }
+
+        SNIServerName serverName = sessionSNI.get(0);
+        if (!(serverName instanceof SNIHostName)) {
+            throw new Exception(
+                    "unexpected instance of request server name indication");
+        }
+
+        String hostname = ((SNIHostName)serverName).getAsciiName();
+        if (!clientRequestedHostname.equalsIgnoreCase(hostname)) {
+            throw new Exception(
+                    "unexpected request server name indication value");
+        }
+    }
+
+    private static String clientRequestedHostname;
+    private static String serverAcceptableHostname;
+
+    private static void parseArguments(String[] args) {
+        clientRequestedHostname = args[0];
+        serverAcceptableHostname = args[1];
+    }
+
+    /*
+     * =============================================================
+     * The remainder is just support stuff
+     */
+    volatile Exception serverException = null;
+    volatile Exception clientException = null;
+
+    // use any free port by default
+    volatile int serverPort = 0;
+
+    public static void main(String args[]) throws Exception {
+        if (debug)
+            System.setProperty("javax.net.debug", "all");
+
+        /*
+         * Get the customized arguments.
+         */
+        parseArguments(args);
+
+        new SSLEngineExplorerUnmatchedSNI();
+    }
+
+    Thread clientThread = null;
+    Thread serverThread = null;
+
+    /*
+     * Primary constructor, used to drive remainder of the test.
+     *
+     * Fork off the other side, then do your work.
+     */
+    SSLEngineExplorerUnmatchedSNI() throws Exception {
+        super("../../../../etc");
+
+        if (separateServerThread) {
+            startServer(true);
+            startClient(false);
+        } else {
+            startClient(true);
+            startServer(false);
+        }
+
+        /*
+         * Wait for other side to close down.
+         */
+        if (separateServerThread) {
+            serverThread.join();
+        } else {
+            clientThread.join();
+        }
+
+        /*
+         * When we get here, the test is pretty much over.
+         *
+         * If the main thread excepted, that propagates back
+         * immediately.  If the other thread threw an exception, we
+         * should report back.
+         */
+        if (serverException != null) {
+            System.out.print("Server Exception:");
+            throw serverException;
+        }
+        if (clientException != null) {
+            System.out.print("Client Exception:");
+            throw clientException;
+        }
+    }
+
+    void startServer(boolean newThread) throws Exception {
+        if (newThread) {
+            serverThread = new Thread() {
+                public void run() {
+                    try {
+                        doServerSide();
+                    } catch (Exception e) {
+                        /*
+                         * Our server thread just died.
+                         *
+                         * Release the client, if not active already...
+                         */
+                        System.err.println("Server died...");
+                        System.err.println(e);
+                        serverReady = true;
+                        serverException = e;
+                    }
+                }
+            };
+            serverThread.start();
+        } else {
+            doServerSide();
+        }
+    }
+
+    void startClient(boolean newThread) throws Exception {
+        if (newThread) {
+            clientThread = new Thread() {
+                public void run() {
+                    try {
+                        doClientSide();
+                    } catch (Exception e) {
+                        /*
+                         * Our client thread just died.
+                         */
+                        System.err.println("Client died...");
+                        clientException = e;
+                    }
+                }
+            };
+            clientThread.start();
+        } else {
+            doClientSide();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/sun/security/ssl/javax/net/ssl/ServerName/SSLEngineExplorerWithCli.java	Thu Oct 18 01:14:00 2012 -0700
@@ -0,0 +1,365 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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 7068321
+ * @summary Support TLS Server Name Indication (SNI) Extension in JSSE Server
+ * @library ../NewAPIs/SSLEngine ../../../../templates
+ * @build SSLEngineService SSLCapabilities SSLExplorer
+ * @run main/othervm SSLEngineExplorerWithCli
+ */
+
+import javax.net.ssl.*;
+import java.nio.*;
+import java.net.*;
+import java.util.*;
+import java.nio.channels.*;
+
+public class SSLEngineExplorerWithCli extends SSLEngineService {
+
+    /*
+     * =============================================================
+     * Set the various variables needed for the tests, then
+     * specify what tests to run on each side.
+     */
+
+    /*
+     * Should we run the client or server in a separate thread?
+     * Both sides can throw exceptions, but do you have a preference
+     * as to which side should be the main thread.
+     */
+    static boolean separateServerThread = true;
+
+    // Is the server ready to serve?
+    volatile static boolean serverReady = false;
+
+    /*
+     * Turn on SSL debugging?
+     */
+    static boolean debug = false;
+
+    /*
+     * Define the server side of the test.
+     *
+     * If the server prematurely exits, serverReady will be set to true
+     * to avoid infinite hangs.
+     */
+    void doServerSide() throws Exception {
+
+        // create SSLEngine.
+        SSLEngine ssle = createSSLEngine(false);
+
+        // Create a server socket channel.
+        InetSocketAddress isa =
+                new InetSocketAddress(InetAddress.getLocalHost(), serverPort);
+        ServerSocketChannel ssc = ServerSocketChannel.open();
+        ssc.socket().bind(isa);
+        serverPort = ssc.socket().getLocalPort();
+
+        // Signal Client, we're ready for his connect.
+        serverReady = true;
+
+        // Accept a socket channel.
+        SocketChannel sc = ssc.accept();
+
+        // Complete connection.
+        while (!sc.finishConnect()) {
+            Thread.sleep(50);
+            // waiting for the connection completed.
+        }
+
+        ByteBuffer buffer = ByteBuffer.allocate(0xFF);
+        int position = 0;
+        SSLCapabilities capabilities = null;
+
+        // Read the header of TLS record
+        buffer.limit(SSLExplorer.RECORD_HEADER_SIZE);
+        while (position < SSLExplorer.RECORD_HEADER_SIZE) {
+            int n = sc.read(buffer);
+            if (n < 0) {
+                throw new Exception("unexpected end of stream!");
+            }
+            position += n;
+        }
+        buffer.flip();
+
+        int recordLength = SSLExplorer.getRequiredSize(buffer);
+        if (buffer.capacity() < recordLength) {
+            ByteBuffer oldBuffer = buffer;
+            buffer = ByteBuffer.allocate(recordLength);
+            buffer.put(oldBuffer);
+        }
+
+        buffer.position(SSLExplorer.RECORD_HEADER_SIZE);
+        buffer.limit(buffer.capacity());
+        while (position < recordLength) {
+            int n = sc.read(buffer);
+            if (n < 0) {
+                throw new Exception("unexpected end of stream!");
+            }
+            position += n;
+        }
+        buffer.flip();
+
+        capabilities = SSLExplorer.explore(buffer);
+        if (capabilities != null) {
+            System.out.println("Record version: " +
+                    capabilities.getRecordVersion());
+            System.out.println("Hello version: " +
+                    capabilities.getHelloVersion());
+        }
+
+        // handshaking
+        handshaking(ssle, sc, buffer);
+
+        // receive application data
+        receive(ssle, sc);
+
+        // send out application data
+        deliver(ssle, sc);
+
+        ExtendedSSLSession session = (ExtendedSSLSession)ssle.getSession();
+        checkCapabilities(capabilities, session);
+
+        // close the socket channel.
+        sc.close();
+        ssc.close();
+    }
+
+    /*
+     * Define the client side of the test.
+     *
+     * If the server prematurely exits, serverReady will be set to true
+     * to avoid infinite hangs.
+     */
+    void doClientSide() throws Exception {
+        // create SSLEngine.
+        SSLEngine ssle = createSSLEngine(true);
+
+        /*
+         * Wait for server to get started.
+         */
+        while (!serverReady) {
+            Thread.sleep(50);
+        }
+
+        // Create a non-blocking socket channel.
+        SocketChannel sc = SocketChannel.open();
+        sc.configureBlocking(false);
+        InetSocketAddress isa =
+                new InetSocketAddress(InetAddress.getLocalHost(), serverPort);
+        sc.connect(isa);
+
+        // Complete connection.
+        while (!sc.finishConnect() ) {
+            Thread.sleep(50);
+            // waiting for the connection completed.
+        }
+
+        SNIHostName serverName = new SNIHostName(clientRequestedHostname);
+        List<SNIServerName> serverNames = new ArrayList<>(1);
+        serverNames.add(serverName);
+        SSLParameters params = ssle.getSSLParameters();
+        params.setServerNames(serverNames);
+        ssle.setSSLParameters(params);
+
+        // handshaking
+        handshaking(ssle, sc, null);
+
+        // send out application data
+        deliver(ssle, sc);
+
+        // receive application data
+        receive(ssle, sc);
+
+        // check server name indication
+        ExtendedSSLSession session = (ExtendedSSLSession)ssle.getSession();
+        checkSNIInSession(session);
+
+        // close the socket channel.
+        sc.close();
+    }
+
+    private static String clientRequestedHostname = "www.example.com";
+    private static String serverAcceptableHostname =
+                                                "www\\.example\\.(com|org)";
+
+    void checkCapabilities(SSLCapabilities capabilities,
+            ExtendedSSLSession session) throws Exception {
+        List<SNIServerName> sessionSNI = session.getRequestedServerNames();
+        if (!sessionSNI.equals(capabilities.getServerNames())) {
+            for (SNIServerName sni : sessionSNI) {
+                System.out.println("SNI in session is " + sni);
+            }
+
+            List<SNIServerName> capaSNI = capabilities.getServerNames();
+            for (SNIServerName sni : capaSNI) {
+                System.out.println("SNI in session is " + sni);
+            }
+
+            throw new Exception(
+                    "server name indication does not match capabilities");
+        }
+
+        checkSNIInSession(session);
+    }
+
+    void checkSNIInSession(ExtendedSSLSession session) throws Exception {
+        List<SNIServerName> sessionSNI = session.getRequestedServerNames();
+        if (sessionSNI.isEmpty()) {
+            throw new Exception(
+                    "unexpected empty request server name indication");
+        }
+
+        if (sessionSNI.size() != 1) {
+            throw new Exception(
+                    "unexpected request server name indication");
+        }
+
+        SNIServerName serverName = sessionSNI.get(0);
+        if (!(serverName instanceof SNIHostName)) {
+            throw new Exception(
+                    "unexpected instance of request server name indication");
+        }
+
+        String hostname = ((SNIHostName)serverName).getAsciiName();
+        if (!clientRequestedHostname.equalsIgnoreCase(hostname)) {
+            throw new Exception(
+                    "unexpected request server name indication value");
+        }
+    }
+
+    /*
+     * =============================================================
+     * The remainder is just support stuff
+     */
+    volatile Exception serverException = null;
+    volatile Exception clientException = null;
+
+    // use any free port by default
+    volatile int serverPort = 0;
+
+    public static void main(String args[]) throws Exception {
+        if (debug)
+            System.setProperty("javax.net.debug", "all");
+
+        new SSLEngineExplorerWithCli();
+    }
+
+    Thread clientThread = null;
+    Thread serverThread = null;
+
+    /*
+     * Primary constructor, used to drive remainder of the test.
+     *
+     * Fork off the other side, then do your work.
+     */
+    SSLEngineExplorerWithCli() throws Exception {
+        super("../../../../etc");
+
+        if (separateServerThread) {
+            startServer(true);
+            startClient(false);
+        } else {
+            startClient(true);
+            startServer(false);
+        }
+
+        /*
+         * Wait for other side to close down.
+         */
+        if (separateServerThread) {
+            serverThread.join();
+        } else {
+            clientThread.join();
+        }
+
+        /*
+         * When we get here, the test is pretty much over.
+         *
+         * If the main thread excepted, that propagates back
+         * immediately.  If the other thread threw an exception, we
+         * should report back.
+         */
+        if (serverException != null) {
+            System.out.print("Server Exception:");
+            throw serverException;
+        }
+        if (clientException != null) {
+            System.out.print("Client Exception:");
+            throw clientException;
+        }
+    }
+
+    void startServer(boolean newThread) throws Exception {
+        if (newThread) {
+            serverThread = new Thread() {
+                public void run() {
+                    try {
+                        doServerSide();
+                    } catch (Exception e) {
+                        /*
+                         * Our server thread just died.
+                         *
+                         * Release the client, if not active already...
+                         */
+                        System.err.println("Server died...");
+                        System.err.println(e);
+                        serverReady = true;
+                        serverException = e;
+                    }
+                }
+            };
+            serverThread.start();
+        } else {
+            doServerSide();
+        }
+    }
+
+    void startClient(boolean newThread) throws Exception {
+        if (newThread) {
+            clientThread = new Thread() {
+                public void run() {
+                    try {
+                        doClientSide();
+                    } catch (Exception e) {
+                        /*
+                         * Our client thread just died.
+                         */
+                        System.err.println("Client died...");
+                        clientException = e;
+                    }
+                }
+            };
+            clientThread.start();
+        } else {
+            doClientSide();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/sun/security/ssl/javax/net/ssl/ServerName/SSLEngineExplorerWithSrv.java	Thu Oct 18 01:14:00 2012 -0700
@@ -0,0 +1,351 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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 7068321
+ * @summary Support TLS Server Name Indication (SNI) Extension in JSSE Server
+ * @library ../NewAPIs/SSLEngine ../../../../templates
+ * @build SSLEngineService SSLCapabilities SSLExplorer
+ * @run main/othervm SSLEngineExplorerWithSrv
+ */
+
+import javax.net.ssl.*;
+import java.nio.*;
+import java.net.*;
+import java.util.*;
+import java.nio.channels.*;
+
+public class SSLEngineExplorerWithSrv extends SSLEngineService {
+
+    /*
+     * =============================================================
+     * Set the various variables needed for the tests, then
+     * specify what tests to run on each side.
+     */
+
+    /*
+     * Should we run the client or server in a separate thread?
+     * Both sides can throw exceptions, but do you have a preference
+     * as to which side should be the main thread.
+     */
+    static boolean separateServerThread = true;
+
+    // Is the server ready to serve?
+    volatile static boolean serverReady = false;
+
+    /*
+     * Turn on SSL debugging?
+     */
+    static boolean debug = false;
+
+    /*
+     * Define the server side of the test.
+     *
+     * If the server prematurely exits, serverReady will be set to true
+     * to avoid infinite hangs.
+     */
+    void doServerSide() throws Exception {
+
+        // create SSLEngine.
+        SSLEngine ssle = createSSLEngine(false);
+
+        // Create a server socket channel.
+        InetSocketAddress isa =
+                new InetSocketAddress(InetAddress.getLocalHost(), serverPort);
+        ServerSocketChannel ssc = ServerSocketChannel.open();
+        ssc.socket().bind(isa);
+        serverPort = ssc.socket().getLocalPort();
+
+        // Signal Client, we're ready for his connect.
+        serverReady = true;
+
+        // Accept a socket channel.
+        SocketChannel sc = ssc.accept();
+
+        // Complete connection.
+        while (!sc.finishConnect()) {
+            Thread.sleep(50);
+            // waiting for the connection completed.
+        }
+
+        ByteBuffer buffer = ByteBuffer.allocate(0xFF);
+        int position = 0;
+        SSLCapabilities capabilities = null;
+
+        // Read the header of TLS record
+        buffer.limit(SSLExplorer.RECORD_HEADER_SIZE);
+        while (position < SSLExplorer.RECORD_HEADER_SIZE) {
+            int n = sc.read(buffer);
+            if (n < 0) {
+                throw new Exception("unexpected end of stream!");
+            }
+            position += n;
+        }
+        buffer.flip();
+
+        int recordLength = SSLExplorer.getRequiredSize(buffer);
+        if (buffer.capacity() < recordLength) {
+            ByteBuffer oldBuffer = buffer;
+            buffer = ByteBuffer.allocate(recordLength);
+            buffer.put(oldBuffer);
+        }
+
+        buffer.position(SSLExplorer.RECORD_HEADER_SIZE);
+        buffer.limit(buffer.capacity());
+        while (position < recordLength) {
+            int n = sc.read(buffer);
+            if (n < 0) {
+                throw new Exception("unexpected end of stream!");
+            }
+            position += n;
+        }
+        buffer.flip();
+
+        capabilities = SSLExplorer.explore(buffer);
+        if (capabilities != null) {
+            System.out.println("Record version: " +
+                    capabilities.getRecordVersion());
+            System.out.println("Hello version: " +
+                    capabilities.getHelloVersion());
+        }
+
+        // enable server name indication checking
+        SNIMatcher matcher = SNIHostName.createSNIMatcher(
+                                                serverAcceptableHostname);
+        Collection<SNIMatcher> matchers = new ArrayList<>(1);
+        matchers.add(matcher);
+        SSLParameters params = ssle.getSSLParameters();
+        params.setSNIMatchers(matchers);
+        ssle.setSSLParameters(params);
+
+        // handshaking
+        handshaking(ssle, sc, buffer);
+
+        // receive application data
+        receive(ssle, sc);
+
+        // send out application data
+        deliver(ssle, sc);
+
+        // check server name indication
+        ExtendedSSLSession session = (ExtendedSSLSession)ssle.getSession();
+        checkCapabilities(capabilities, session);
+
+        // close the socket channel.
+        sc.close();
+        ssc.close();
+    }
+
+    /*
+     * Define the client side of the test.
+     *
+     * If the server prematurely exits, serverReady will be set to true
+     * to avoid infinite hangs.
+     */
+    void doClientSide() throws Exception {
+        // create SSLEngine.
+        SSLEngine ssle = createSSLEngine(true);
+
+        /*
+         * Wait for server to get started.
+         */
+        while (!serverReady) {
+            Thread.sleep(50);
+        }
+
+        // Create a non-blocking socket channel.
+        SocketChannel sc = SocketChannel.open();
+        sc.configureBlocking(false);
+        InetSocketAddress isa =
+                new InetSocketAddress(InetAddress.getLocalHost(), serverPort);
+        sc.connect(isa);
+
+        // Complete connection.
+        while (!sc.finishConnect() ) {
+            Thread.sleep(50);
+            // waiting for the connection completed.
+        }
+
+        // handshaking
+        handshaking(ssle, sc, null);
+
+        // send out application data
+        deliver(ssle, sc);
+
+        // receive application data
+        receive(ssle, sc);
+
+        // check server name indication
+        ExtendedSSLSession session = (ExtendedSSLSession)ssle.getSession();
+        checkSNIInSession(session);
+
+        // close the socket channel.
+        sc.close();
+    }
+
+    private static String clientRequestedHostname = "www.example.com";
+    private static String serverAcceptableHostname =
+                                                "www\\.example\\.(com|org)";
+
+    void checkCapabilities(SSLCapabilities capabilities,
+            ExtendedSSLSession session) throws Exception {
+        List<SNIServerName> sessionSNI = session.getRequestedServerNames();
+        if (!sessionSNI.equals(capabilities.getServerNames())) {
+            for (SNIServerName sni : sessionSNI) {
+                System.out.println("SNI in session is " + sni);
+            }
+
+            List<SNIServerName> capaSNI = capabilities.getServerNames();
+            for (SNIServerName sni : capaSNI) {
+                System.out.println("SNI in session is " + sni);
+            }
+
+            throw new Exception(
+                    "server name indication does not match capabilities");
+        }
+
+        checkSNIInSession(session);
+    }
+
+    void checkSNIInSession(ExtendedSSLSession session) throws Exception {
+        List<SNIServerName> sessionSNI = session.getRequestedServerNames();
+        if (!sessionSNI.isEmpty()) {
+            throw new Exception(
+                    "should be empty request server name indication");
+        }
+    }
+
+    /*
+     * =============================================================
+     * The remainder is just support stuff
+     */
+    volatile Exception serverException = null;
+    volatile Exception clientException = null;
+
+    // use any free port by default
+    volatile int serverPort = 0;
+
+    public static void main(String args[]) throws Exception {
+        if (debug)
+            System.setProperty("javax.net.debug", "all");
+
+        new SSLEngineExplorerWithSrv();
+    }
+
+    Thread clientThread = null;
+    Thread serverThread = null;
+
+    /*
+     * Primary constructor, used to drive remainder of the test.
+     *
+     * Fork off the other side, then do your work.
+     */
+    SSLEngineExplorerWithSrv() throws Exception {
+        super("../../../../etc");
+
+        if (separateServerThread) {
+            startServer(true);
+            startClient(false);
+        } else {
+            startClient(true);
+            startServer(false);
+        }
+
+        /*
+         * Wait for other side to close down.
+         */
+        if (separateServerThread) {
+            serverThread.join();
+        } else {
+            clientThread.join();
+        }
+
+        /*
+         * When we get here, the test is pretty much over.
+         *
+         * If the main thread excepted, that propagates back
+         * immediately.  If the other thread threw an exception, we
+         * should report back.
+         */
+        if (serverException != null) {
+            System.out.print("Server Exception:");
+            throw serverException;
+        }
+        if (clientException != null) {
+            System.out.print("Client Exception:");
+            throw clientException;
+        }
+    }
+
+    void startServer(boolean newThread) throws Exception {
+        if (newThread) {
+            serverThread = new Thread() {
+                public void run() {
+                    try {
+                        doServerSide();
+                    } catch (Exception e) {
+                        /*
+                         * Our server thread just died.
+                         *
+                         * Release the client, if not active already...
+                         */
+                        System.err.println("Server died...");
+                        System.err.println(e);
+                        serverReady = true;
+                        serverException = e;
+                    }
+                }
+            };
+            serverThread.start();
+        } else {
+            doServerSide();
+        }
+    }
+
+    void startClient(boolean newThread) throws Exception {
+        if (newThread) {
+            clientThread = new Thread() {
+                public void run() {
+                    try {
+                        doClientSide();
+                    } catch (Exception e) {
+                        /*
+                         * Our client thread just died.
+                         */
+                        System.err.println("Client died...");
+                        clientException = e;
+                    }
+                }
+            };
+            clientThread.start();
+        } else {
+            doClientSide();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/sun/security/ssl/javax/net/ssl/ServerName/SSLSocketConsistentSNI.java	Thu Oct 18 01:14:00 2012 -0700
@@ -0,0 +1,357 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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 7068321
+ * @summary Support TLS Server Name Indication (SNI) Extension in JSSE Server
+ * @run main/othervm SSLSocketConsistentSNI
+ */
+
+import java.io.*;
+import java.nio.*;
+import java.nio.channels.*;
+import java.util.*;
+import java.net.*;
+import javax.net.ssl.*;
+
+public class SSLSocketConsistentSNI {
+
+    /*
+     * =============================================================
+     * Set the various variables needed for the tests, then
+     * specify what tests to run on each side.
+     */
+
+    /*
+     * Should we run the client or server in a separate thread?
+     * Both sides can throw exceptions, but do you have a preference
+     * as to which side should be the main thread.
+     */
+    static boolean separateServerThread = true;
+
+    /*
+     * Where do we find the keystores?
+     */
+    static String pathToStores = "../../../../etc";
+    static String keyStoreFile = "keystore";
+    static String trustStoreFile = "truststore";
+    static String passwd = "passphrase";
+
+    /*
+     * Is the server ready to serve?
+     */
+    volatile static boolean serverReady = false;
+
+    /*
+     * Turn on SSL debugging?
+     */
+    static boolean debug = false;
+
+    /*
+     * If the client or server is doing some kind of object creation
+     * that the other side depends on, and that thread prematurely
+     * exits, you may experience a hang.  The test harness will
+     * terminate all hung threads after its timeout has expired,
+     * currently 3 minutes by default, but you might try to be
+     * smart about it....
+     */
+
+    /*
+     * Define the server side of the test.
+     *
+     * If the server prematurely exits, serverReady will be set to true
+     * to avoid infinite hangs.
+     */
+    void doServerSide() throws Exception {
+        SSLServerSocketFactory sslssf =
+            (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
+        SSLServerSocket sslServerSocket =
+            (SSLServerSocket) sslssf.createServerSocket(serverPort);
+
+        SNIMatcher matcher = SNIHostName.createSNIMatcher(
+                                                serverAcceptableHostname);
+        Collection<SNIMatcher> matchers = new ArrayList<>(1);
+        matchers.add(matcher);
+        SSLParameters params = sslServerSocket.getSSLParameters();
+        params.setSNIMatchers(matchers);
+        sslServerSocket.setSSLParameters(params);
+
+        serverPort = sslServerSocket.getLocalPort();
+
+        /*
+         * Signal Client, we're ready for his connect.
+         */
+        serverReady = true;
+
+        SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
+        try {
+            InputStream sslIS = sslSocket.getInputStream();
+            OutputStream sslOS = sslSocket.getOutputStream();
+
+            sslIS.read();
+            sslOS.write(85);
+            sslOS.flush();
+
+            ExtendedSSLSession session =
+                    (ExtendedSSLSession)sslSocket.getSession();
+            checkSNIInSession(session);
+        } finally {
+            sslSocket.close();
+            sslServerSocket.close();
+        }
+    }
+
+
+    /*
+     * Define the client side of the test.
+     *
+     * If the server prematurely exits, serverReady will be set to true
+     * to avoid infinite hangs.
+     */
+    void doClientSide() throws Exception {
+
+        /*
+         * Wait for server to get started.
+         */
+        while (!serverReady) {
+            Thread.sleep(50);
+        }
+
+        SSLSocketFactory sslsf =
+            (SSLSocketFactory) SSLSocketFactory.getDefault();
+        SSLSocket sslSocket = (SSLSocket)
+            sslsf.createSocket("localhost", serverPort);
+
+        SNIHostName serverName = new SNIHostName(clientRequestedHostname);
+        List<SNIServerName> serverNames = new ArrayList<>(1);
+        serverNames.add(serverName);
+        SSLParameters params = sslSocket.getSSLParameters();
+        params.setServerNames(serverNames);
+        sslSocket.setSSLParameters(params);
+
+        try {
+            InputStream sslIS = sslSocket.getInputStream();
+            OutputStream sslOS = sslSocket.getOutputStream();
+
+            sslOS.write(280);
+            sslOS.flush();
+            sslIS.read();
+
+            ExtendedSSLSession session =
+                    (ExtendedSSLSession)sslSocket.getSession();
+            checkSNIInSession(session);
+        } finally {
+            sslSocket.close();
+        }
+    }
+
+    private static String clientRequestedHostname = "www.example.com";
+    private static String serverAcceptableHostname = "www\\.example\\.com";
+
+    void checkSNIInSession(ExtendedSSLSession session) throws Exception {
+        List<SNIServerName> sessionSNI = session.getRequestedServerNames();
+        if (sessionSNI.isEmpty()) {
+            throw new Exception(
+                    "unexpected empty request server name indication");
+        }
+
+        if (sessionSNI.size() != 1) {
+            throw new Exception(
+                    "unexpected request server name indication");
+        }
+
+        SNIServerName serverName = sessionSNI.get(0);
+        if (!(serverName instanceof SNIHostName)) {
+            throw new Exception(
+                    "unexpected instance of request server name indication");
+        }
+
+        String hostname = ((SNIHostName)serverName).getAsciiName();
+        if (!clientRequestedHostname.equalsIgnoreCase(hostname)) {
+            throw new Exception(
+                    "unexpected request server name indication value");
+        }
+    }
+
+    /*
+     * =============================================================
+     * The remainder is just support stuff
+     */
+
+    // use any free port by default
+    volatile int serverPort = 0;
+
+    volatile Exception serverException = null;
+    volatile Exception clientException = null;
+
+    public static void main(String[] args) throws Exception {
+        String keyFilename =
+            System.getProperty("test.src", ".") + "/" + pathToStores +
+                "/" + keyStoreFile;
+        String trustFilename =
+            System.getProperty("test.src", ".") + "/" + pathToStores +
+                "/" + trustStoreFile;
+
+        System.setProperty("javax.net.ssl.keyStore", keyFilename);
+        System.setProperty("javax.net.ssl.keyStorePassword", passwd);
+        System.setProperty("javax.net.ssl.trustStore", trustFilename);
+        System.setProperty("javax.net.ssl.trustStorePassword", passwd);
+
+        if (debug)
+            System.setProperty("javax.net.debug", "all");
+
+        /*
+         * Start the tests.
+         */
+        new SSLSocketConsistentSNI();
+    }
+
+    Thread clientThread = null;
+    Thread serverThread = null;
+
+    /*
+     * Primary constructor, used to drive remainder of the test.
+     *
+     * Fork off the other side, then do your work.
+     */
+    SSLSocketConsistentSNI() throws Exception {
+        try {
+            if (separateServerThread) {
+                startServer(true);
+                startClient(false);
+            } else {
+                startClient(true);
+                startServer(false);
+            }
+        } catch (Exception e) {
+            // swallow for now.  Show later
+        }
+
+        /*
+         * Wait for other side to close down.
+         */
+        if (separateServerThread) {
+            serverThread.join();
+        } else {
+            clientThread.join();
+        }
+
+        /*
+         * When we get here, the test is pretty much over.
+         * Which side threw the error?
+         */
+        Exception local;
+        Exception remote;
+        String whichRemote;
+
+        if (separateServerThread) {
+            remote = serverException;
+            local = clientException;
+            whichRemote = "server";
+        } else {
+            remote = clientException;
+            local = serverException;
+            whichRemote = "client";
+        }
+
+        /*
+         * If both failed, return the curthread's exception, but also
+         * print the remote side Exception
+         */
+        if ((local != null) && (remote != null)) {
+            System.out.println(whichRemote + " also threw:");
+            remote.printStackTrace();
+            System.out.println();
+            throw local;
+        }
+
+        if (remote != null) {
+            throw remote;
+        }
+
+        if (local != null) {
+            throw local;
+        }
+    }
+
+    void startServer(boolean newThread) throws Exception {
+        if (newThread) {
+            serverThread = new Thread() {
+                public void run() {
+                    try {
+                        doServerSide();
+                    } catch (Exception e) {
+                        /*
+                         * Our server thread just died.
+                         *
+                         * Release the client, if not active already...
+                         */
+                        System.err.println("Server died...");
+                        serverReady = true;
+                        serverException = e;
+                    }
+                }
+            };
+            serverThread.start();
+        } else {
+            try {
+                doServerSide();
+            } catch (Exception e) {
+                serverException = e;
+            } finally {
+                serverReady = true;
+            }
+        }
+    }
+
+    void startClient(boolean newThread) throws Exception {
+        if (newThread) {
+            clientThread = new Thread() {
+                public void run() {
+                    try {
+                        doClientSide();
+                    } catch (Exception e) {
+                        /*
+                         * Our client thread just died.
+                         */
+                        System.err.println("Client died...");
+                        clientException = e;
+                    }
+                }
+            };
+            clientThread.start();
+        } else {
+            try {
+                doClientSide();
+            } catch (Exception e) {
+                clientException = e;
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/sun/security/ssl/javax/net/ssl/ServerName/SSLSocketExplorer.java	Thu Oct 18 01:14:00 2012 -0700
@@ -0,0 +1,375 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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 7068321
+ * @summary Support TLS Server Name Indication (SNI) Extension in JSSE Server
+ * @library ../../../../templates
+ * @build SSLCapabilities SSLExplorer
+ * @run main/othervm SSLSocketExplorer SSLv2Hello,SSLv3
+ * @run main/othervm SSLSocketExplorer SSLv3
+ * @run main/othervm SSLSocketExplorer TLSv1
+ * @run main/othervm SSLSocketExplorer TLSv1.1
+ * @run main/othervm SSLSocketExplorer TLSv1.2
+ */
+
+import java.io.*;
+import java.nio.*;
+import java.nio.channels.*;
+import java.util.*;
+import java.net.*;
+import javax.net.ssl.*;
+
+public class SSLSocketExplorer {
+
+    /*
+     * =============================================================
+     * Set the various variables needed for the tests, then
+     * specify what tests to run on each side.
+     */
+
+    /*
+     * Should we run the client or server in a separate thread?
+     * Both sides can throw exceptions, but do you have a preference
+     * as to which side should be the main thread.
+     */
+    static boolean separateServerThread = true;
+
+    /*
+     * Where do we find the keystores?
+     */
+    static String pathToStores = "../../../../etc";
+    static String keyStoreFile = "keystore";
+    static String trustStoreFile = "truststore";
+    static String passwd = "passphrase";
+
+    /*
+     * Is the server ready to serve?
+     */
+    volatile static boolean serverReady = false;
+
+    /*
+     * Turn on SSL debugging?
+     */
+    static boolean debug = false;
+
+    /*
+     * If the client or server is doing some kind of object creation
+     * that the other side depends on, and that thread prematurely
+     * exits, you may experience a hang.  The test harness will
+     * terminate all hung threads after its timeout has expired,
+     * currently 3 minutes by default, but you might try to be
+     * smart about it....
+     */
+
+    /*
+     * Define the server side of the test.
+     *
+     * If the server prematurely exits, serverReady will be set to true
+     * to avoid infinite hangs.
+     */
+    void doServerSide() throws Exception {
+
+        ServerSocket serverSocket = new ServerSocket(serverPort);
+
+        // Signal Client, we're ready for his connect.
+        serverPort = serverSocket.getLocalPort();
+        serverReady = true;
+
+        Socket socket = serverSocket.accept();
+        InputStream ins = socket.getInputStream();
+
+        byte[] buffer = new byte[0xFF];
+        int position = 0;
+        SSLCapabilities capabilities = null;
+
+        // Read the header of TLS record
+        while (position < SSLExplorer.RECORD_HEADER_SIZE) {
+            int count = SSLExplorer.RECORD_HEADER_SIZE - position;
+            int n = ins.read(buffer, position, count);
+            if (n < 0) {
+                throw new Exception("unexpected end of stream!");
+            }
+            position += n;
+        }
+
+        int recordLength = SSLExplorer.getRequiredSize(buffer, 0, position);
+        if (buffer.length < recordLength) {
+            buffer = Arrays.copyOf(buffer, recordLength);
+        }
+
+        while (position < recordLength) {
+            int count = recordLength - position;
+            int n = ins.read(buffer, position, count);
+            if (n < 0) {
+                throw new Exception("unexpected end of stream!");
+            }
+            position += n;
+        }
+
+        capabilities = SSLExplorer.explore(buffer, 0, recordLength);;
+        if (capabilities != null) {
+            System.out.println("Record version: " +
+                    capabilities.getRecordVersion());
+            System.out.println("Hello version: " +
+                    capabilities.getHelloVersion());
+        }
+
+        SSLSocketFactory sslsf =
+            (SSLSocketFactory) SSLSocketFactory.getDefault();
+        ByteArrayInputStream bais =
+            new ByteArrayInputStream(buffer, 0, position);
+        SSLSocket sslSocket = (SSLSocket)sslsf.createSocket(socket, bais, true);
+
+        InputStream sslIS = sslSocket.getInputStream();
+        OutputStream sslOS = sslSocket.getOutputStream();
+
+        sslIS.read();
+        sslOS.write(85);
+        sslOS.flush();
+
+        ExtendedSSLSession session = (ExtendedSSLSession)sslSocket.getSession();
+        checkCapabilities(capabilities, session);
+
+        sslSocket.close();
+        serverSocket.close();
+    }
+
+
+    /*
+     * Define the client side of the test.
+     *
+     * If the server prematurely exits, serverReady will be set to true
+     * to avoid infinite hangs.
+     */
+    void doClientSide() throws Exception {
+
+        /*
+         * Wait for server to get started.
+         */
+        while (!serverReady) {
+            Thread.sleep(50);
+        }
+
+        SSLSocketFactory sslsf =
+            (SSLSocketFactory) SSLSocketFactory.getDefault();
+        SSLSocket sslSocket = (SSLSocket)
+            sslsf.createSocket("localhost", serverPort);
+
+        // enable the specified TLS protocol
+        sslSocket.setEnabledProtocols(supportedProtocols);
+
+        InputStream sslIS = sslSocket.getInputStream();
+        OutputStream sslOS = sslSocket.getOutputStream();
+
+        sslOS.write(280);
+        sslOS.flush();
+        sslIS.read();
+
+        sslSocket.close();
+    }
+
+    void checkCapabilities(SSLCapabilities capabilities,
+            ExtendedSSLSession session) throws Exception {
+
+        List<SNIServerName> sessionSNI = session.getRequestedServerNames();
+        if (!sessionSNI.equals(capabilities.getServerNames())) {
+            throw new Exception(
+                    "server name indication does not match capabilities");
+        }
+    }
+
+    private static String[] supportedProtocols;    // supported protocols
+
+    private static void parseArguments(String[] args) {
+        supportedProtocols = args[0].split(",");
+    }
+
+
+    /*
+     * =============================================================
+     * The remainder is just support stuff
+     */
+
+    // use any free port by default
+    volatile int serverPort = 0;
+
+    volatile Exception serverException = null;
+    volatile Exception clientException = null;
+
+    public static void main(String[] args) throws Exception {
+        String keyFilename =
+            System.getProperty("test.src", ".") + "/" + pathToStores +
+                "/" + keyStoreFile;
+        String trustFilename =
+            System.getProperty("test.src", ".") + "/" + pathToStores +
+                "/" + trustStoreFile;
+
+        System.setProperty("javax.net.ssl.keyStore", keyFilename);
+        System.setProperty("javax.net.ssl.keyStorePassword", passwd);
+        System.setProperty("javax.net.ssl.trustStore", trustFilename);
+        System.setProperty("javax.net.ssl.trustStorePassword", passwd);
+
+        if (debug)
+            System.setProperty("javax.net.debug", "all");
+
+        /*
+         * Get the customized arguments.
+         */
+        parseArguments(args);
+
+        /*
+         * Start the tests.
+         */
+        new SSLSocketExplorer();
+    }
+
+    Thread clientThread = null;
+    Thread serverThread = null;
+
+    /*
+     * Primary constructor, used to drive remainder of the test.
+     *
+     * Fork off the other side, then do your work.
+     */
+    SSLSocketExplorer() throws Exception {
+        try {
+            if (separateServerThread) {
+                startServer(true);
+                startClient(false);
+            } else {
+                startClient(true);
+                startServer(false);
+            }
+        } catch (Exception e) {
+            // swallow for now.  Show later
+        }
+
+        /*
+         * Wait for other side to close down.
+         */
+        if (separateServerThread) {
+            serverThread.join();
+        } else {
+            clientThread.join();
+        }
+
+        /*
+         * When we get here, the test is pretty much over.
+         * Which side threw the error?
+         */
+        Exception local;
+        Exception remote;
+        String whichRemote;
+
+        if (separateServerThread) {
+            remote = serverException;
+            local = clientException;
+            whichRemote = "server";
+        } else {
+            remote = clientException;
+            local = serverException;
+            whichRemote = "client";
+        }
+
+        /*
+         * If both failed, return the curthread's exception, but also
+         * print the remote side Exception
+         */
+        if ((local != null) && (remote != null)) {
+            System.out.println(whichRemote + " also threw:");
+            remote.printStackTrace();
+            System.out.println();
+            throw local;
+        }
+
+        if (remote != null) {
+            throw remote;
+        }
+
+        if (local != null) {
+            throw local;
+        }
+    }
+
+    void startServer(boolean newThread) throws Exception {
+        if (newThread) {
+            serverThread = new Thread() {
+                public void run() {
+                    try {
+                        doServerSide();
+                    } catch (Exception e) {
+                        /*
+                         * Our server thread just died.
+                         *
+                         * Release the client, if not active already...
+                         */
+                        System.err.println("Server died...");
+                        serverReady = true;
+                        serverException = e;
+                    }
+                }
+            };
+            serverThread.start();
+        } else {
+            try {
+                doServerSide();
+            } catch (Exception e) {
+                serverException = e;
+            } finally {
+                serverReady = true;
+            }
+        }
+    }
+
+    void startClient(boolean newThread) throws Exception {
+        if (newThread) {
+            clientThread = new Thread() {
+                public void run() {
+                    try {
+                        doClientSide();
+                    } catch (Exception e) {
+                        /*
+                         * Our client thread just died.
+                         */
+                        System.err.println("Client died...");
+                        clientException = e;
+                    }
+                }
+            };
+            clientThread.start();
+        } else {
+            try {
+                doClientSide();
+            } catch (Exception e) {
+                clientException = e;
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/sun/security/ssl/javax/net/ssl/ServerName/SSLSocketExplorerFailure.java	Thu Oct 18 01:14:00 2012 -0700
@@ -0,0 +1,383 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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 7068321
+ * @summary Support TLS Server Name Indication (SNI) Extension in JSSE Server
+ * @library ../../../../templates
+ * @build SSLCapabilities SSLExplorer
+ * @run main/othervm SSLSocketExplorerFailure SSLv2Hello,SSLv3
+ * @run main/othervm SSLSocketExplorerFailure SSLv3
+ * @run main/othervm SSLSocketExplorerFailure TLSv1
+ * @run main/othervm SSLSocketExplorerFailure TLSv1.1
+ * @run main/othervm SSLSocketExplorerFailure TLSv1.2
+ */
+
+import java.io.*;
+import java.nio.*;
+import java.nio.channels.*;
+import java.util.*;
+import java.net.*;
+import javax.net.ssl.*;
+
+public class SSLSocketExplorerFailure {
+
+    /*
+     * =============================================================
+     * Set the various variables needed for the tests, then
+     * specify what tests to run on each side.
+     */
+
+    /*
+     * Should we run the client or server in a separate thread?
+     * Both sides can throw exceptions, but do you have a preference
+     * as to which side should be the main thread.
+     */
+    static boolean separateServerThread = true;
+
+    /*
+     * Where do we find the keystores?
+     */
+    static String pathToStores = "../../../../etc";
+    static String keyStoreFile = "keystore";
+    static String trustStoreFile = "truststore";
+    static String passwd = "passphrase";
+
+    /*
+     * Is the server ready to serve?
+     */
+    volatile static boolean serverReady = false;
+
+    /*
+     * Turn on SSL debugging?
+     */
+    static boolean debug = false;
+
+    /*
+     * If the client or server is doing some kind of object creation
+     * that the other side depends on, and that thread prematurely
+     * exits, you may experience a hang.  The test harness will
+     * terminate all hung threads after its timeout has expired,
+     * currently 3 minutes by default, but you might try to be
+     * smart about it....
+     */
+
+    /*
+     * Define the server side of the test.
+     *
+     * If the server prematurely exits, serverReady will be set to true
+     * to avoid infinite hangs.
+     */
+    void doServerSide() throws Exception {
+
+        ServerSocket serverSocket = new ServerSocket(serverPort);
+
+        // Signal Client, we're ready for his connect.
+        serverPort = serverSocket.getLocalPort();
+        serverReady = true;
+
+        Socket socket = serverSocket.accept();
+        InputStream ins = socket.getInputStream();
+
+        byte[] buffer = new byte[0xFF];
+        int position = 0;
+        SSLCapabilities capabilities = null;
+        boolean failed = false;
+        try {
+            // Read the header of TLS record
+            while (position < SSLExplorer.RECORD_HEADER_SIZE) {
+                int count = SSLExplorer.RECORD_HEADER_SIZE - position;
+                int n = ins.read(buffer, position, count);
+                if (n < 0) {
+                    throw new Exception("unexpected end of stream!");
+                }
+                position += n;
+            }
+
+            int recordLength = SSLExplorer.getRequiredSize(buffer, 0, position);
+            if (buffer.length < recordLength) {
+                buffer = Arrays.copyOf(buffer, recordLength);
+            }
+
+            while (position < recordLength) {
+                int count = recordLength - position;
+                int n = ins.read(buffer, position, count);
+                if (n < 0) {
+                    throw new Exception("unexpected end of stream!");
+                }
+                position += n;
+            }
+
+            capabilities = SSLExplorer.explore(buffer, 0, recordLength);;
+            if (capabilities != null) {
+                System.out.println("Record version: " +
+                        capabilities.getRecordVersion());
+                System.out.println("Hello version: " +
+                        capabilities.getHelloVersion());
+            }
+
+            // want an I/O exception
+            throw new IOException("We just want a I/O exception");
+        } catch (Exception e) {
+            failed =  true;
+        }
+
+        // off course, the above explore failed. Faile to failure handler
+        SSLContext context = SSLContext.getInstance("TLS");
+        context.init(null, null, null);
+        SSLSocketFactory sslsf = context.getSocketFactory();
+        ByteArrayInputStream bais =
+            new ByteArrayInputStream(buffer, 0, position);
+        SSLSocket sslSocket = (SSLSocket)sslsf.createSocket(socket, bais, true);
+
+        try {
+            InputStream sslIS = sslSocket.getInputStream();
+            OutputStream sslOS = sslSocket.getOutputStream();
+
+            sslIS.read();
+            if (!failed) {
+                sslOS.write(85);
+                sslOS.flush();
+            } else {
+                sslSocket.close();
+            }
+        } catch (Exception e) {
+            System.out.println("server exception " + e);
+        } finally {
+            sslSocket.close();
+            serverSocket.close();
+        }
+    }
+
+
+    /*
+     * Define the client side of the test.
+     *
+     * If the server prematurely exits, serverReady will be set to true
+     * to avoid infinite hangs.
+     */
+    void doClientSide() throws Exception {
+
+        /*
+         * Wait for server to get started.
+         */
+        while (!serverReady) {
+            Thread.sleep(50);
+        }
+
+        SSLSocketFactory sslsf =
+            (SSLSocketFactory) SSLSocketFactory.getDefault();
+        SSLSocket sslSocket = (SSLSocket)
+            sslsf.createSocket("localhost", serverPort);
+
+        // enable the specified TLS protocol
+        sslSocket.setEnabledProtocols(supportedProtocols);
+
+        try {
+            InputStream sslIS = sslSocket.getInputStream();
+            OutputStream sslOS = sslSocket.getOutputStream();
+
+            sslOS.write(280);
+            sslOS.flush();
+            sslIS.read();
+        } catch (Exception e) {
+            System.out.println("client exception " + e);
+        } finally {
+            sslSocket.close();
+        }
+    }
+
+    private static String[] supportedProtocols;    // supported protocols
+
+    private static void parseArguments(String[] args) {
+        supportedProtocols = args[0].split(",");
+    }
+
+
+    /*
+     * =============================================================
+     * The remainder is just support stuff
+     */
+
+    // use any free port by default
+    volatile int serverPort = 0;
+
+    volatile Exception serverException = null;
+    volatile Exception clientException = null;
+
+    public static void main(String[] args) throws Exception {
+        String keyFilename =
+            System.getProperty("test.src", ".") + "/" + pathToStores +
+                "/" + keyStoreFile;
+        String trustFilename =
+            System.getProperty("test.src", ".") + "/" + pathToStores +
+                "/" + trustStoreFile;
+
+        System.setProperty("javax.net.ssl.keyStore", keyFilename);
+        System.setProperty("javax.net.ssl.keyStorePassword", passwd);
+        System.setProperty("javax.net.ssl.trustStore", trustFilename);
+        System.setProperty("javax.net.ssl.trustStorePassword", passwd);
+
+        if (debug)
+            System.setProperty("javax.net.debug", "all");
+
+        /*
+         * Get the customized arguments.
+         */
+        parseArguments(args);
+
+        /*
+         * Start the tests.
+         */
+        new SSLSocketExplorerFailure();
+    }
+
+    Thread clientThread = null;
+    Thread serverThread = null;
+
+    /*
+     * Primary constructor, used to drive remainder of the test.
+     *
+     * Fork off the other side, then do your work.
+     */
+    SSLSocketExplorerFailure() throws Exception {
+        try {
+            if (separateServerThread) {
+                startServer(true);
+                startClient(false);
+            } else {
+                startClient(true);
+                startServer(false);
+            }
+        } catch (Exception e) {
+            // swallow for now.  Show later
+        }
+
+        /*
+         * Wait for other side to close down.
+         */
+        if (separateServerThread) {
+            serverThread.join();
+        } else {
+            clientThread.join();
+        }
+
+        /*
+         * When we get here, the test is pretty much over.
+         * Which side threw the error?
+         */
+        Exception local;
+        Exception remote;
+        String whichRemote;
+
+        if (separateServerThread) {
+            remote = serverException;
+            local = clientException;
+            whichRemote = "server";
+        } else {
+            remote = clientException;
+            local = serverException;
+            whichRemote = "client";
+        }
+
+        /*
+         * If both failed, return the curthread's exception, but also
+         * print the remote side Exception
+         */
+        if ((local != null) && (remote != null)) {
+            System.out.println(whichRemote + " also threw:");
+            remote.printStackTrace();
+            System.out.println();
+            throw local;
+        }
+
+        if (remote != null) {
+            throw remote;
+        }
+
+        if (local != null) {
+            throw local;
+        }
+    }
+
+    void startServer(boolean newThread) throws Exception {
+        if (newThread) {
+            serverThread = new Thread() {
+                public void run() {
+                    try {
+                        doServerSide();
+                    } catch (Exception e) {
+                        /*
+                         * Our server thread just died.
+                         *
+                         * Release the client, if not active already...
+                         */
+                        System.err.println("Server died...");
+                        serverReady = true;
+                        serverException = e;
+                    }
+                }
+            };
+            serverThread.start();
+        } else {
+            try {
+                doServerSide();
+            } catch (Exception e) {
+                serverException = e;
+            } finally {
+                serverReady = true;
+            }
+        }
+    }
+
+    void startClient(boolean newThread) throws Exception {
+        if (newThread) {
+            clientThread = new Thread() {
+                public void run() {
+                    try {
+                        doClientSide();
+                    } catch (Exception e) {
+                        /*
+                         * Our client thread just died.
+                         */
+                        System.err.println("Client died...");
+                        clientException = e;
+                    }
+                }
+            };
+            clientThread.start();
+        } else {
+            try {
+                doClientSide();
+            } catch (Exception e) {
+                clientException = e;
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/sun/security/ssl/javax/net/ssl/ServerName/SSLSocketExplorerMatchedSNI.java	Thu Oct 18 01:14:00 2012 -0700
@@ -0,0 +1,435 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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 7068321
+ * @summary Support TLS Server Name Indication (SNI) Extension in JSSE Server
+ * @library ../../../../templates
+ * @build SSLCapabilities SSLExplorer
+ * @run main/othervm SSLSocketExplorerMatchedSNI www.example.com
+ *     www\.example\.com
+ * @run main/othervm SSLSocketExplorerMatchedSNI www.example.com
+ *     www\.example\.(com|org)
+ * @run main/othervm SSLSocketExplorerMatchedSNI example.com
+ *     (.*\.)*example\.(com|org)
+ * @run main/othervm SSLSocketExplorerMatchedSNI www.example.com
+ *     (.*\.)*example\.(com|org)
+ * @run main/othervm SSLSocketExplorerMatchedSNI www.us.example.com
+ *     (.*\.)*example\.(com|org)
+ */
+
+import java.io.*;
+import java.nio.*;
+import java.nio.channels.*;
+import java.util.*;
+import java.net.*;
+import javax.net.ssl.*;
+
+public class SSLSocketExplorerMatchedSNI {
+
+    /*
+     * =============================================================
+     * Set the various variables needed for the tests, then
+     * specify what tests to run on each side.
+     */
+
+    /*
+     * Should we run the client or server in a separate thread?
+     * Both sides can throw exceptions, but do you have a preference
+     * as to which side should be the main thread.
+     */
+    static boolean separateServerThread = false;
+
+    /*
+     * Where do we find the keystores?
+     */
+    static String pathToStores = "../../../../etc";
+    static String keyStoreFile = "keystore";
+    static String trustStoreFile = "truststore";
+    static String passwd = "passphrase";
+
+    /*
+     * Is the server ready to serve?
+     */
+    volatile static boolean serverReady = false;
+
+    /*
+     * Turn on SSL debugging?
+     */
+    static boolean debug = false;
+
+    /*
+     * If the client or server is doing some kind of object creation
+     * that the other side depends on, and that thread prematurely
+     * exits, you may experience a hang.  The test harness will
+     * terminate all hung threads after its timeout has expired,
+     * currently 3 minutes by default, but you might try to be
+     * smart about it....
+     */
+
+    /*
+     * Define the server side of the test.
+     *
+     * If the server prematurely exits, serverReady will be set to true
+     * to avoid infinite hangs.
+     */
+    void doServerSide() throws Exception {
+
+        ServerSocket serverSocket = new ServerSocket(serverPort);
+
+        // Signal Client, we're ready for his connect.
+        serverPort = serverSocket.getLocalPort();
+        serverReady = true;
+
+        Socket socket = serverSocket.accept();
+        InputStream ins = socket.getInputStream();
+
+        byte[] buffer = new byte[0xFF];
+        int position = 0;
+        SSLCapabilities capabilities = null;
+
+        // Read the header of TLS record
+        while (position < SSLExplorer.RECORD_HEADER_SIZE) {
+            int count = SSLExplorer.RECORD_HEADER_SIZE - position;
+            int n = ins.read(buffer, position, count);
+            if (n < 0) {
+                throw new Exception("unexpected end of stream!");
+            }
+            position += n;
+        }
+
+        int recordLength = SSLExplorer.getRequiredSize(buffer, 0, position);
+        if (buffer.length < recordLength) {
+            buffer = Arrays.copyOf(buffer, recordLength);
+        }
+
+        while (position < recordLength) {
+            int count = recordLength - position;
+            int n = ins.read(buffer, position, count);
+            if (n < 0) {
+                throw new Exception("unexpected end of stream!");
+            }
+            position += n;
+        }
+
+        capabilities = SSLExplorer.explore(buffer, 0, recordLength);;
+        if (capabilities != null) {
+            System.out.println("Record version: " +
+                    capabilities.getRecordVersion());
+            System.out.println("Hello version: " +
+                    capabilities.getHelloVersion());
+        }
+
+        SSLSocketFactory sslsf =
+            (SSLSocketFactory) SSLSocketFactory.getDefault();
+        ByteArrayInputStream bais =
+            new ByteArrayInputStream(buffer, 0, position);
+        SSLSocket sslSocket = (SSLSocket)sslsf.createSocket(socket, bais, true);
+
+        SNIMatcher matcher = SNIHostName.createSNIMatcher(
+                                                serverAcceptableHostname);
+        Collection<SNIMatcher> matchers = new ArrayList<>(1);
+        matchers.add(matcher);
+        SSLParameters params = sslSocket.getSSLParameters();
+        params.setSNIMatchers(matchers);
+        sslSocket.setSSLParameters(params);
+
+        InputStream sslIS = sslSocket.getInputStream();
+        OutputStream sslOS = sslSocket.getOutputStream();
+
+        sslIS.read();
+        sslOS.write(85);
+        sslOS.flush();
+
+        ExtendedSSLSession session = (ExtendedSSLSession)sslSocket.getSession();
+        checkCapabilities(capabilities, session);
+
+        sslSocket.close();
+        serverSocket.close();
+    }
+
+
+    /*
+     * Define the client side of the test.
+     *
+     * If the server prematurely exits, serverReady will be set to true
+     * to avoid infinite hangs.
+     */
+    void doClientSide() throws Exception {
+
+        /*
+         * Wait for server to get started.
+         */
+        while (!serverReady) {
+            Thread.sleep(50);
+        }
+
+        SSLSocketFactory sslsf =
+            (SSLSocketFactory) SSLSocketFactory.getDefault();
+        SSLSocket sslSocket = (SSLSocket)
+            sslsf.createSocket("localhost", serverPort);
+
+        SNIHostName serverName = new SNIHostName(clientRequestedHostname);
+        List<SNIServerName> serverNames = new ArrayList<>(1);
+        serverNames.add(serverName);
+        SSLParameters params = sslSocket.getSSLParameters();
+        params.setServerNames(serverNames);
+        sslSocket.setSSLParameters(params);
+
+        InputStream sslIS = sslSocket.getInputStream();
+        OutputStream sslOS = sslSocket.getOutputStream();
+
+        sslOS.write(280);
+        sslOS.flush();
+        sslIS.read();
+
+        ExtendedSSLSession session = (ExtendedSSLSession)sslSocket.getSession();
+        checkSNIInSession(session);
+
+        sslSocket.close();
+    }
+
+
+    void checkCapabilities(SSLCapabilities capabilities,
+            ExtendedSSLSession session) throws Exception {
+
+        List<SNIServerName> sessionSNI = session.getRequestedServerNames();
+        if (!sessionSNI.equals(capabilities.getServerNames())) {
+            for (SNIServerName sni : sessionSNI) {
+                System.out.println("SNI in session is " + sni);
+            }
+
+            List<SNIServerName> capaSNI = capabilities.getServerNames();
+            for (SNIServerName sni : capaSNI) {
+                System.out.println("SNI in session is " + sni);
+            }
+
+            throw new Exception(
+                    "server name indication does not match capabilities");
+        }
+
+        checkSNIInSession(session);
+    }
+
+    void checkSNIInSession(ExtendedSSLSession session) throws Exception {
+        List<SNIServerName> sessionSNI = session.getRequestedServerNames();
+        if (sessionSNI.isEmpty()) {
+            throw new Exception(
+                    "unexpected empty request server name indication");
+        }
+
+        if (sessionSNI.size() != 1) {
+            throw new Exception(
+                    "unexpected request server name indication");
+        }
+
+        SNIServerName serverName = sessionSNI.get(0);
+        if (!(serverName instanceof SNIHostName)) {
+            throw new Exception(
+                    "unexpected instance of request server name indication");
+        }
+
+        String hostname = ((SNIHostName)serverName).getAsciiName();
+        if (!clientRequestedHostname.equalsIgnoreCase(hostname)) {
+            throw new Exception(
+                    "unexpected request server name indication value");
+        }
+    }
+
+    private static String clientRequestedHostname;
+    private static String serverAcceptableHostname;
+
+    private static void parseArguments(String[] args) {
+        clientRequestedHostname = args[0];
+        serverAcceptableHostname = args[1];
+    }
+
+
+    /*
+     * =============================================================
+     * The remainder is just support stuff
+     */
+
+    // use any free port by default
+    volatile int serverPort = 0;
+
+    volatile Exception serverException = null;
+    volatile Exception clientException = null;
+
+
+    public static void main(String[] args) throws Exception {
+        String keyFilename =
+            System.getProperty("test.src", ".") + "/" + pathToStores +
+                "/" + keyStoreFile;
+        String trustFilename =
+            System.getProperty("test.src", ".") + "/" + pathToStores +
+                "/" + trustStoreFile;
+
+        System.setProperty("javax.net.ssl.keyStore", keyFilename);
+        System.setProperty("javax.net.ssl.keyStorePassword", passwd);
+        System.setProperty("javax.net.ssl.trustStore", trustFilename);
+        System.setProperty("javax.net.ssl.trustStorePassword", passwd);
+
+        if (debug)
+            System.setProperty("javax.net.debug", "all");
+
+        /*
+         * Get the customized arguments.
+         */
+        parseArguments(args);
+
+        /*
+         * Start the tests.
+         */
+        new SSLSocketExplorerMatchedSNI();
+    }
+
+    Thread clientThread = null;
+    Thread serverThread = null;
+
+    /*
+     * Primary constructor, used to drive remainder of the test.
+     *
+     * Fork off the other side, then do your work.
+     */
+    SSLSocketExplorerMatchedSNI() throws Exception {
+        try {
+            if (separateServerThread) {
+                startServer(true);
+                startClient(false);
+            } else {
+                startClient(true);
+                startServer(false);
+            }
+        } catch (Exception e) {
+            // swallow for now.  Show later
+        }
+
+        /*
+         * Wait for other side to close down.
+         */
+        if (separateServerThread) {
+            serverThread.join();
+        } else {
+            clientThread.join();
+        }
+
+        /*
+         * When we get here, the test is pretty much over.
+         * Which side threw the error?
+         */
+        Exception local;
+        Exception remote;
+        String whichRemote;
+
+        if (separateServerThread) {
+            remote = serverException;
+            local = clientException;
+            whichRemote = "server";
+        } else {
+            remote = clientException;
+            local = serverException;
+            whichRemote = "client";
+        }
+
+        /*
+         * If both failed, return the curthread's exception, but also
+         * print the remote side Exception
+         */
+        if ((local != null) && (remote != null)) {
+            System.out.println(whichRemote + " also threw:");
+            remote.printStackTrace();
+            System.out.println();
+            throw local;
+        }
+
+        if (remote != null) {
+            throw remote;
+        }
+
+        if (local != null) {
+            throw local;
+        }
+    }
+
+    void startServer(boolean newThread) throws Exception {
+        if (newThread) {
+            serverThread = new Thread() {
+                public void run() {
+                    try {
+                        doServerSide();
+                    } catch (Exception e) {
+                        /*
+                         * Our server thread just died.
+                         *
+                         * Release the client, if not active already...
+                         */
+                        System.err.println("Server died...");
+                        serverReady = true;
+                        serverException = e;
+                    }
+                }
+            };
+            serverThread.start();
+        } else {
+            try {
+                doServerSide();
+            } catch (Exception e) {
+                serverException = e;
+            } finally {
+                serverReady = true;
+            }
+        }
+    }
+
+    void startClient(boolean newThread) throws Exception {
+        if (newThread) {
+            clientThread = new Thread() {
+                public void run() {
+                    try {
+                        doClientSide();
+                    } catch (Exception e) {
+                        /*
+                         * Our client thread just died.
+                         */
+                        System.err.println("Client died...");
+                        clientException = e;
+                    }
+                }
+            };
+            clientThread.start();
+        } else {
+            try {
+                doClientSide();
+            } catch (Exception e) {
+                clientException = e;
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/sun/security/ssl/javax/net/ssl/ServerName/SSLSocketExplorerUnmatchedSNI.java	Thu Oct 18 01:14:00 2012 -0700
@@ -0,0 +1,392 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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 7068321
+ * @summary Support TLS Server Name Indication (SNI) Extension in JSSE Server
+ * @library ../../../../templates
+ * @build SSLCapabilities SSLExplorer
+ * @run main/othervm SSLSocketExplorerUnmatchedSNI www.example.com
+ *                                                 www\.example\.org
+ */
+
+import java.io.*;
+import java.nio.*;
+import java.nio.channels.*;
+import java.util.*;
+import java.net.*;
+import javax.net.ssl.*;
+
+public class SSLSocketExplorerUnmatchedSNI {
+
+    /*
+     * =============================================================
+     * Set the various variables needed for the tests, then
+     * specify what tests to run on each side.
+     */
+
+    /*
+     * Should we run the client or server in a separate thread?
+     * Both sides can throw exceptions, but do you have a preference
+     * as to which side should be the main thread.
+     */
+    static boolean separateServerThread = false;
+
+    /*
+     * Where do we find the keystores?
+     */
+    static String pathToStores = "../../../../etc";
+    static String keyStoreFile = "keystore";
+    static String trustStoreFile = "truststore";
+    static String passwd = "passphrase";
+
+    /*
+     * Is the server ready to serve?
+     */
+    volatile static boolean serverReady = false;
+
+    /*
+     * Turn on SSL debugging?
+     */
+    static boolean debug = false;
+
+    /*
+     * If the client or server is doing some kind of object creation
+     * that the other side depends on, and that thread prematurely
+     * exits, you may experience a hang.  The test harness will
+     * terminate all hung threads after its timeout has expired,
+     * currently 3 minutes by default, but you might try to be
+     * smart about it....
+     */
+
+    /*
+     * Define the server side of the test.
+     *
+     * If the server prematurely exits, serverReady will be set to true
+     * to avoid infinite hangs.
+     */
+    void doServerSide() throws Exception {
+
+        ServerSocket serverSocket = new ServerSocket(serverPort);
+
+        // Signal Client, we're ready for his connect.
+        serverPort = serverSocket.getLocalPort();
+        serverReady = true;
+
+        Socket socket = serverSocket.accept();
+        InputStream ins = socket.getInputStream();
+
+        byte[] buffer = new byte[0xFF];
+        int position = 0;
+        SSLCapabilities capabilities = null;
+
+        // Read the header of TLS record
+        while (position < SSLExplorer.RECORD_HEADER_SIZE) {
+            int count = SSLExplorer.RECORD_HEADER_SIZE - position;
+            int n = ins.read(buffer, position, count);
+            if (n < 0) {
+                throw new Exception("unexpected end of stream!");
+            }
+            position += n;
+        }
+
+        int recordLength = SSLExplorer.getRequiredSize(buffer, 0, position);
+        if (buffer.length < recordLength) {
+            buffer = Arrays.copyOf(buffer, recordLength);
+        }
+
+        while (position < recordLength) {
+            int count = recordLength - position;
+            int n = ins.read(buffer, position, count);
+            if (n < 0) {
+                throw new Exception("unexpected end of stream!");
+            }
+            position += n;
+        }
+
+        capabilities = SSLExplorer.explore(buffer, 0, recordLength);;
+        if (capabilities != null) {
+            System.out.println("Record version: " +
+                    capabilities.getRecordVersion());
+            System.out.println("Hello version: " +
+                    capabilities.getHelloVersion());
+        }
+
+        SSLSocketFactory sslsf =
+            (SSLSocketFactory) SSLSocketFactory.getDefault();
+        ByteArrayInputStream bais =
+            new ByteArrayInputStream(buffer, 0, position);
+        SSLSocket sslSocket = (SSLSocket)sslsf.createSocket(socket, bais, true);
+
+        SNIMatcher matcher = SNIHostName.createSNIMatcher(
+                                                serverAcceptableHostname);
+        Collection<SNIMatcher> matchers = new ArrayList<>(1);
+        matchers.add(matcher);
+        SSLParameters params = sslSocket.getSSLParameters();
+        params.setSNIMatchers(matchers);
+        sslSocket.setSSLParameters(params);
+
+        InputStream sslIS = sslSocket.getInputStream();
+        OutputStream sslOS = sslSocket.getOutputStream();
+
+        try {
+            sslIS.read();
+            sslOS.write(85);
+            sslOS.flush();
+
+            throw new Exception(
+                "Mismatched server name indication was accepted");
+        } catch (SSLHandshakeException sslhe) {
+            // the expected unrecognized server name indication exception
+        } catch (IOException ioe) {
+            // the peer may have closed the socket because of the unmatched
+            // server name indication.
+        } finally {
+            sslSocket.close();
+            serverSocket.close();
+        }
+    }
+
+
+    /*
+     * Define the client side of the test.
+     *
+     * If the server prematurely exits, serverReady will be set to true
+     * to avoid infinite hangs.
+     */
+    void doClientSide() throws Exception {
+
+        /*
+         * Wait for server to get started.
+         */
+        while (!serverReady) {
+            Thread.sleep(50);
+        }
+
+        SSLSocketFactory sslsf =
+            (SSLSocketFactory) SSLSocketFactory.getDefault();
+        SSLSocket sslSocket = (SSLSocket)
+            sslsf.createSocket("localhost", serverPort);
+
+        SNIHostName serverName = new SNIHostName(clientRequestedHostname);
+        List<SNIServerName> serverNames = new ArrayList<>(1);
+        serverNames.add(serverName);
+        SSLParameters params = sslSocket.getSSLParameters();
+        params.setServerNames(serverNames);
+        sslSocket.setSSLParameters(params);
+
+        InputStream sslIS = sslSocket.getInputStream();
+        OutputStream sslOS = sslSocket.getOutputStream();
+
+        try {
+            sslOS.write(280);
+            sslOS.flush();
+            sslIS.read();
+
+            throw new Exception(
+                "Mismatched server name indication was accepted");
+        } catch (SSLHandshakeException sslhe) {
+            // the expected unrecognized server name indication exception
+        } catch (IOException ioe) {
+            // the peer may have closed the socket because of the unmatched
+            // server name indication.
+        } finally {
+            sslSocket.close();
+        }
+    }
+
+    private static String clientRequestedHostname;
+    private static String serverAcceptableHostname;
+
+    private static void parseArguments(String[] args) {
+        clientRequestedHostname = args[0];
+        serverAcceptableHostname = args[1];
+    }
+
+    /*
+     * =============================================================
+     * The remainder is just support stuff
+     */
+
+    // use any free port by default
+    volatile int serverPort = 0;
+
+    volatile Exception serverException = null;
+    volatile Exception clientException = null;
+
+    public static void main(String[] args) throws Exception {
+        String keyFilename =
+            System.getProperty("test.src", ".") + "/" + pathToStores +
+                "/" + keyStoreFile;
+        String trustFilename =
+            System.getProperty("test.src", ".") + "/" + pathToStores +
+                "/" + trustStoreFile;
+
+        System.setProperty("javax.net.ssl.keyStore", keyFilename);
+        System.setProperty("javax.net.ssl.keyStorePassword", passwd);
+        System.setProperty("javax.net.ssl.trustStore", trustFilename);
+        System.setProperty("javax.net.ssl.trustStorePassword", passwd);
+
+        if (debug)
+            System.setProperty("javax.net.debug", "all");
+
+        /*
+         * Get the customized arguments.
+         */
+        parseArguments(args);
+
+        /*
+         * Start the tests.
+         */
+        new SSLSocketExplorerUnmatchedSNI();
+    }
+
+    Thread clientThread = null;
+    Thread serverThread = null;
+
+    /*
+     * Primary constructor, used to drive remainder of the test.
+     *
+     * Fork off the other side, then do your work.
+     */
+    SSLSocketExplorerUnmatchedSNI() throws Exception {
+        try {
+            if (separateServerThread) {
+                startServer(true);
+                startClient(false);
+            } else {
+                startClient(true);
+                startServer(false);
+            }
+        } catch (Exception e) {
+            // swallow for now.  Show later
+        }
+
+        /*
+         * Wait for other side to close down.
+         */
+        if (separateServerThread) {
+            serverThread.join();
+        } else {
+            clientThread.join();
+        }
+
+        /*
+         * When we get here, the test is pretty much over.
+         * Which side threw the error?
+         */
+        Exception local;
+        Exception remote;
+        String whichRemote;
+
+        if (separateServerThread) {
+            remote = serverException;
+            local = clientException;
+            whichRemote = "server";
+        } else {
+            remote = clientException;
+            local = serverException;
+            whichRemote = "client";
+        }
+
+        /*
+         * If both failed, return the curthread's exception, but also
+         * print the remote side Exception
+         */
+        if ((local != null) && (remote != null)) {
+            System.out.println(whichRemote + " also threw:");
+            remote.printStackTrace();
+            System.out.println();
+            throw local;
+        }
+
+        if (remote != null) {
+            throw remote;
+        }
+
+        if (local != null) {
+            throw local;
+        }
+    }
+
+    void startServer(boolean newThread) throws Exception {
+        if (newThread) {
+            serverThread = new Thread() {
+                public void run() {
+                    try {
+                        doServerSide();
+                    } catch (Exception e) {
+                        /*
+                         * Our server thread just died.
+                         *
+                         * Release the client, if not active already...
+                         */
+                        System.err.println("Server died...");
+                        serverReady = true;
+                        serverException = e;
+                    }
+                }
+            };
+            serverThread.start();
+        } else {
+            try {
+                doServerSide();
+            } catch (Exception e) {
+                serverException = e;
+            } finally {
+                serverReady = true;
+            }
+        }
+    }
+
+    void startClient(boolean newThread) throws Exception {
+        if (newThread) {
+            clientThread = new Thread() {
+                public void run() {
+                    try {
+                        doClientSide();
+                    } catch (Exception e) {
+                        /*
+                         * Our client thread just died.
+                         */
+                        System.err.println("Client died...");
+                        clientException = e;
+                    }
+                }
+            };
+            clientThread.start();
+        } else {
+            try {
+                doClientSide();
+            } catch (Exception e) {
+                clientException = e;
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/sun/security/ssl/javax/net/ssl/ServerName/SSLSocketExplorerWithCliSNI.java	Thu Oct 18 01:14:00 2012 -0700
@@ -0,0 +1,407 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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 7068321
+ * @summary Support TLS Server Name Indication (SNI) Extension in JSSE Server
+ * @library ../../../../templates
+ * @build SSLCapabilities SSLExplorer
+ * @run main/othervm SSLSocketExplorerWithCliSNI
+ */
+
+import java.io.*;
+import java.nio.*;
+import java.nio.channels.*;
+import java.util.*;
+import java.net.*;
+import javax.net.ssl.*;
+
+public class SSLSocketExplorerWithCliSNI {
+
+    /*
+     * =============================================================
+     * Set the various variables needed for the tests, then
+     * specify what tests to run on each side.
+     */
+
+    /*
+     * Should we run the client or server in a separate thread?
+     * Both sides can throw exceptions, but do you have a preference
+     * as to which side should be the main thread.
+     */
+    static boolean separateServerThread = true;
+
+    /*
+     * Where do we find the keystores?
+     */
+    static String pathToStores = "../../../../etc";
+    static String keyStoreFile = "keystore";
+    static String trustStoreFile = "truststore";
+    static String passwd = "passphrase";
+
+    /*
+     * Is the server ready to serve?
+     */
+    volatile static boolean serverReady = false;
+
+    /*
+     * Turn on SSL debugging?
+     */
+    static boolean debug = false;
+
+    /*
+     * If the client or server is doing some kind of object creation
+     * that the other side depends on, and that thread prematurely
+     * exits, you may experience a hang.  The test harness will
+     * terminate all hung threads after its timeout has expired,
+     * currently 3 minutes by default, but you might try to be
+     * smart about it....
+     */
+
+    /*
+     * Define the server side of the test.
+     *
+     * If the server prematurely exits, serverReady will be set to true
+     * to avoid infinite hangs.
+     */
+    void doServerSide() throws Exception {
+
+        ServerSocket serverSocket = new ServerSocket(serverPort);
+
+        // Signal Client, we're ready for his connect.
+        serverPort = serverSocket.getLocalPort();
+        serverReady = true;
+
+        Socket socket = serverSocket.accept();
+        InputStream ins = socket.getInputStream();
+
+        byte[] buffer = new byte[0xFF];
+        int position = 0;
+        SSLCapabilities capabilities = null;
+
+        // Read the header of TLS record
+        while (position < SSLExplorer.RECORD_HEADER_SIZE) {
+            int count = SSLExplorer.RECORD_HEADER_SIZE - position;
+            int n = ins.read(buffer, position, count);
+            if (n < 0) {
+                throw new Exception("unexpected end of stream!");
+            }
+            position += n;
+        }
+
+        int recordLength = SSLExplorer.getRequiredSize(buffer, 0, position);
+        if (buffer.length < recordLength) {
+            buffer = Arrays.copyOf(buffer, recordLength);
+        }
+
+        while (position < recordLength) {
+            int count = recordLength - position;
+            int n = ins.read(buffer, position, count);
+            if (n < 0) {
+                throw new Exception("unexpected end of stream!");
+            }
+            position += n;
+        }
+
+        capabilities = SSLExplorer.explore(buffer, 0, recordLength);;
+        if (capabilities != null) {
+            System.out.println("Record version: " +
+                    capabilities.getRecordVersion());
+            System.out.println("Hello version: " +
+                    capabilities.getHelloVersion());
+        }
+
+        SSLSocketFactory sslsf =
+            (SSLSocketFactory) SSLSocketFactory.getDefault();
+        ByteArrayInputStream bais =
+            new ByteArrayInputStream(buffer, 0, position);
+        SSLSocket sslSocket = (SSLSocket)sslsf.createSocket(socket, bais, true);
+
+        InputStream sslIS = sslSocket.getInputStream();
+        OutputStream sslOS = sslSocket.getOutputStream();
+
+        sslIS.read();
+        sslOS.write(85);
+        sslOS.flush();
+
+        ExtendedSSLSession session = (ExtendedSSLSession)sslSocket.getSession();
+        checkCapabilities(capabilities, session);
+
+        sslSocket.close();
+        serverSocket.close();
+    }
+
+
+    /*
+     * Define the client side of the test.
+     *
+     * If the server prematurely exits, serverReady will be set to true
+     * to avoid infinite hangs.
+     */
+    void doClientSide() throws Exception {
+
+        /*
+         * Wait for server to get started.
+         */
+        while (!serverReady) {
+            Thread.sleep(50);
+        }
+
+        SSLSocketFactory sslsf =
+            (SSLSocketFactory) SSLSocketFactory.getDefault();
+        SSLSocket sslSocket = (SSLSocket)
+            sslsf.createSocket("localhost", serverPort);
+
+        SNIHostName serverName = new SNIHostName(clientRequestedHostname);
+        List<SNIServerName> serverNames = new ArrayList<>(1);
+        serverNames.add(serverName);
+        SSLParameters params = sslSocket.getSSLParameters();
+        params.setServerNames(serverNames);
+        sslSocket.setSSLParameters(params);
+
+        InputStream sslIS = sslSocket.getInputStream();
+        OutputStream sslOS = sslSocket.getOutputStream();
+
+        sslOS.write(280);
+        sslOS.flush();
+        sslIS.read();
+
+        ExtendedSSLSession session = (ExtendedSSLSession)sslSocket.getSession();
+        checkSNIInSession(session);
+
+        sslSocket.close();
+    }
+
+    private static String clientRequestedHostname = "www.example.com";
+    private static String serverAcceptableHostname =
+                                                "www\\.example\\.(com|org)";
+
+    void checkCapabilities(SSLCapabilities capabilities,
+            ExtendedSSLSession session) throws Exception {
+
+        List<SNIServerName> sessionSNI = session.getRequestedServerNames();
+        if (!sessionSNI.equals(capabilities.getServerNames())) {
+            for (SNIServerName sni : sessionSNI) {
+                System.out.println("SNI in session is " + sni);
+            }
+
+            List<SNIServerName> capaSNI = capabilities.getServerNames();
+            for (SNIServerName sni : capaSNI) {
+                System.out.println("SNI in session is " + sni);
+            }
+
+            throw new Exception(
+                    "server name indication does not match capabilities");
+        }
+
+        checkSNIInSession(session);
+    }
+
+    void checkSNIInSession(ExtendedSSLSession session) throws Exception {
+        List<SNIServerName> sessionSNI = session.getRequestedServerNames();
+        if (sessionSNI.isEmpty()) {
+            throw new Exception(
+                    "unexpected empty request server name indication");
+        }
+
+        if (sessionSNI.size() != 1) {
+            throw new Exception(
+                    "unexpected request server name indication");
+        }
+
+        SNIServerName serverName = sessionSNI.get(0);
+        if (!(serverName instanceof SNIHostName)) {
+            throw new Exception(
+                    "unexpected instance of request server name indication");
+        }
+
+        String hostname = ((SNIHostName)serverName).getAsciiName();
+        if (!clientRequestedHostname.equalsIgnoreCase(hostname)) {
+            throw new Exception(
+                    "unexpected request server name indication value");
+        }
+    }
+
+
+    /*
+     * =============================================================
+     * The remainder is just support stuff
+     */
+
+    // use any free port by default
+    volatile int serverPort = 0;
+
+    volatile Exception serverException = null;
+    volatile Exception clientException = null;
+
+    public static void main(String[] args) throws Exception {
+        String keyFilename =
+            System.getProperty("test.src", ".") + "/" + pathToStores +
+                "/" + keyStoreFile;
+        String trustFilename =
+            System.getProperty("test.src", ".") + "/" + pathToStores +
+                "/" + trustStoreFile;
+
+        System.setProperty("javax.net.ssl.keyStore", keyFilename);
+        System.setProperty("javax.net.ssl.keyStorePassword", passwd);
+        System.setProperty("javax.net.ssl.trustStore", trustFilename);
+        System.setProperty("javax.net.ssl.trustStorePassword", passwd);
+
+        if (debug)
+            System.setProperty("javax.net.debug", "all");
+
+        /*
+         * Start the tests.
+         */
+        new SSLSocketExplorerWithCliSNI();
+    }
+
+    Thread clientThread = null;
+    Thread serverThread = null;
+
+    /*
+     * Primary constructor, used to drive remainder of the test.
+     *
+     * Fork off the other side, then do your work.
+     */
+    SSLSocketExplorerWithCliSNI() throws Exception {
+        try {
+            if (separateServerThread) {
+                startServer(true);
+                startClient(false);
+            } else {
+                startClient(true);
+                startServer(false);
+            }
+        } catch (Exception e) {
+            // swallow for now.  Show later
+        }
+
+        /*
+         * Wait for other side to close down.
+         */
+        if (separateServerThread) {
+            serverThread.join();
+        } else {
+            clientThread.join();
+        }
+
+        /*
+         * When we get here, the test is pretty much over.
+         * Which side threw the error?
+         */
+        Exception local;
+        Exception remote;
+        String whichRemote;
+
+        if (separateServerThread) {
+            remote = serverException;
+            local = clientException;
+            whichRemote = "server";
+        } else {
+            remote = clientException;
+            local = serverException;
+            whichRemote = "client";
+        }
+
+        /*
+         * If both failed, return the curthread's exception, but also
+         * print the remote side Exception
+         */
+        if ((local != null) && (remote != null)) {
+            System.out.println(whichRemote + " also threw:");
+            remote.printStackTrace();
+            System.out.println();
+            throw local;
+        }
+
+        if (remote != null) {
+            throw remote;
+        }
+
+        if (local != null) {
+            throw local;
+        }
+    }
+
+    void startServer(boolean newThread) throws Exception {
+        if (newThread) {
+            serverThread = new Thread() {
+                public void run() {
+                    try {
+                        doServerSide();
+                    } catch (Exception e) {
+                        /*
+                         * Our server thread just died.
+                         *
+                         * Release the client, if not active already...
+                         */
+                        System.err.println("Server died...");
+                        serverReady = true;
+                        serverException = e;
+                    }
+                }
+            };
+            serverThread.start();
+        } else {
+            try {
+                doServerSide();
+            } catch (Exception e) {
+                serverException = e;
+            } finally {
+                serverReady = true;
+            }
+        }
+    }
+
+    void startClient(boolean newThread) throws Exception {
+        if (newThread) {
+            clientThread = new Thread() {
+                public void run() {
+                    try {
+                        doClientSide();
+                    } catch (Exception e) {
+                        /*
+                         * Our client thread just died.
+                         */
+                        System.err.println("Client died...");
+                        clientException = e;
+                    }
+                }
+            };
+            clientThread.start();
+        } else {
+            try {
+                doClientSide();
+            } catch (Exception e) {
+                clientException = e;
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/sun/security/ssl/javax/net/ssl/ServerName/SSLSocketExplorerWithSrvSNI.java	Thu Oct 18 01:14:00 2012 -0700
@@ -0,0 +1,390 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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 7068321
+ * @summary Support TLS Server Name Indication (SNI) Extension in JSSE Server
+ * @library ../../../../templates
+ * @build SSLCapabilities SSLExplorer
+ * @run main/othervm SSLSocketExplorerWithSrvSNI
+ */
+
+import java.io.*;
+import java.nio.*;
+import java.nio.channels.*;
+import java.util.*;
+import java.net.*;
+import javax.net.ssl.*;
+
+public class SSLSocketExplorerWithSrvSNI {
+
+    /*
+     * =============================================================
+     * Set the various variables needed for the tests, then
+     * specify what tests to run on each side.
+     */
+
+    /*
+     * Should we run the client or server in a separate thread?
+     * Both sides can throw exceptions, but do you have a preference
+     * as to which side should be the main thread.
+     */
+    static boolean separateServerThread = true;
+
+    /*
+     * Where do we find the keystores?
+     */
+    static String pathToStores = "../../../../etc";
+    static String keyStoreFile = "keystore";
+    static String trustStoreFile = "truststore";
+    static String passwd = "passphrase";
+
+    /*
+     * Is the server ready to serve?
+     */
+    volatile static boolean serverReady = false;
+
+    /*
+     * Turn on SSL debugging?
+     */
+    static boolean debug = false;
+
+    /*
+     * If the client or server is doing some kind of object creation
+     * that the other side depends on, and that thread prematurely
+     * exits, you may experience a hang.  The test harness will
+     * terminate all hung threads after its timeout has expired,
+     * currently 3 minutes by default, but you might try to be
+     * smart about it....
+     */
+
+    /*
+     * Define the server side of the test.
+     *
+     * If the server prematurely exits, serverReady will be set to true
+     * to avoid infinite hangs.
+     */
+    void doServerSide() throws Exception {
+
+        ServerSocket serverSocket = new ServerSocket(serverPort);
+
+        // Signal Client, we're ready for his connect.
+        serverPort = serverSocket.getLocalPort();
+        serverReady = true;
+
+        Socket socket = serverSocket.accept();
+        InputStream ins = socket.getInputStream();
+
+        byte[] buffer = new byte[0xFF];
+        int position = 0;
+        SSLCapabilities capabilities = null;
+
+        // Read the header of TLS record
+        while (position < SSLExplorer.RECORD_HEADER_SIZE) {
+            int count = SSLExplorer.RECORD_HEADER_SIZE - position;
+            int n = ins.read(buffer, position, count);
+            if (n < 0) {
+                throw new Exception("unexpected end of stream!");
+            }
+            position += n;
+        }
+
+        int recordLength = SSLExplorer.getRequiredSize(buffer, 0, position);
+        if (buffer.length < recordLength) {
+            buffer = Arrays.copyOf(buffer, recordLength);
+        }
+
+        while (position < recordLength) {
+            int count = recordLength - position;
+            int n = ins.read(buffer, position, count);
+            if (n < 0) {
+                throw new Exception("unexpected end of stream!");
+            }
+            position += n;
+        }
+
+        capabilities = SSLExplorer.explore(buffer, 0, recordLength);;
+        if (capabilities != null) {
+            System.out.println("Record version: " +
+                    capabilities.getRecordVersion());
+            System.out.println("Hello version: " +
+                    capabilities.getHelloVersion());
+        }
+
+        SSLSocketFactory sslsf =
+            (SSLSocketFactory) SSLSocketFactory.getDefault();
+        ByteArrayInputStream bais =
+            new ByteArrayInputStream(buffer, 0, position);
+        SSLSocket sslSocket = (SSLSocket)sslsf.createSocket(socket, bais, true);
+
+        SNIMatcher matcher = SNIHostName.createSNIMatcher(
+                                                serverAcceptableHostname);
+        Collection<SNIMatcher> matchers = new ArrayList<>(1);
+        matchers.add(matcher);
+        SSLParameters params = sslSocket.getSSLParameters();
+        params.setSNIMatchers(matchers);
+        sslSocket.setSSLParameters(params);
+
+        InputStream sslIS = sslSocket.getInputStream();
+        OutputStream sslOS = sslSocket.getOutputStream();
+
+        sslIS.read();
+        sslOS.write(85);
+        sslOS.flush();
+
+        ExtendedSSLSession session = (ExtendedSSLSession)sslSocket.getSession();
+        checkCapabilities(capabilities, session);
+
+        sslSocket.close();
+        serverSocket.close();
+    }
+
+
+    /*
+     * Define the client side of the test.
+     *
+     * If the server prematurely exits, serverReady will be set to true
+     * to avoid infinite hangs.
+     */
+    void doClientSide() throws Exception {
+
+        /*
+         * Wait for server to get started.
+         */
+        while (!serverReady) {
+            Thread.sleep(50);
+        }
+
+        SSLSocketFactory sslsf =
+            (SSLSocketFactory) SSLSocketFactory.getDefault();
+        SSLSocket sslSocket = (SSLSocket)
+            sslsf.createSocket("localhost", serverPort);
+
+        InputStream sslIS = sslSocket.getInputStream();
+        OutputStream sslOS = sslSocket.getOutputStream();
+
+        sslOS.write(280);
+        sslOS.flush();
+        sslIS.read();
+
+        ExtendedSSLSession session = (ExtendedSSLSession)sslSocket.getSession();
+        checkSNIInSession(session);
+
+        sslSocket.close();
+    }
+
+    private static String clientRequestedHostname = "www.example.com";
+    private static String serverAcceptableHostname =
+                                                "www\\.example\\.(com|org)";
+
+    void checkCapabilities(SSLCapabilities capabilities,
+            ExtendedSSLSession session) throws Exception {
+
+        List<SNIServerName> sessionSNI = session.getRequestedServerNames();
+        if (!sessionSNI.equals(capabilities.getServerNames())) {
+            for (SNIServerName sni : sessionSNI) {
+                System.out.println("SNI in session is " + sni);
+            }
+
+            List<SNIServerName> capaSNI = capabilities.getServerNames();
+            for (SNIServerName sni : capaSNI) {
+                System.out.println("SNI in session is " + sni);
+            }
+
+            throw new Exception(
+                    "server name indication does not match capabilities");
+        }
+
+        checkSNIInSession(session);
+    }
+
+    void checkSNIInSession(ExtendedSSLSession session) throws Exception {
+        List<SNIServerName> sessionSNI = session.getRequestedServerNames();
+        if (!sessionSNI.isEmpty()) {
+            throw new Exception(
+                    "should be empty request server name indication");
+        }
+    }
+
+    /*
+     * =============================================================
+     * The remainder is just support stuff
+     */
+
+    // use any free port by default
+    volatile int serverPort = 0;
+
+    volatile Exception serverException = null;
+    volatile Exception clientException = null;
+
+    public static void main(String[] args) throws Exception {
+        String keyFilename =
+            System.getProperty("test.src", ".") + "/" + pathToStores +
+                "/" + keyStoreFile;
+        String trustFilename =
+            System.getProperty("test.src", ".") + "/" + pathToStores +
+                "/" + trustStoreFile;
+
+        System.setProperty("javax.net.ssl.keyStore", keyFilename);
+        System.setProperty("javax.net.ssl.keyStorePassword", passwd);
+        System.setProperty("javax.net.ssl.trustStore", trustFilename);
+        System.setProperty("javax.net.ssl.trustStorePassword", passwd);
+
+        if (debug)
+            System.setProperty("javax.net.debug", "all");
+
+        /*
+         * Start the tests.
+         */
+        new SSLSocketExplorerWithSrvSNI();
+    }
+
+    Thread clientThread = null;
+    Thread serverThread = null;
+
+    /*
+     * Primary constructor, used to drive remainder of the test.
+     *
+     * Fork off the other side, then do your work.
+     */
+    SSLSocketExplorerWithSrvSNI() throws Exception {
+        try {
+            if (separateServerThread) {
+                startServer(true);
+                startClient(false);
+            } else {
+                startClient(true);
+                startServer(false);
+            }
+        } catch (Exception e) {
+            // swallow for now.  Show later
+        }
+
+        /*
+         * Wait for other side to close down.
+         */
+        if (separateServerThread) {
+            serverThread.join();
+        } else {
+            clientThread.join();
+        }
+
+        /*
+         * When we get here, the test is pretty much over.
+         * Which side threw the error?
+         */
+        Exception local;
+        Exception remote;
+        String whichRemote;
+
+        if (separateServerThread) {
+            remote = serverException;
+            local = clientException;
+            whichRemote = "server";
+        } else {
+            remote = clientException;
+            local = serverException;
+            whichRemote = "client";
+        }
+
+        /*
+         * If both failed, return the curthread's exception, but also
+         * print the remote side Exception
+         */
+        if ((local != null) && (remote != null)) {
+            System.out.println(whichRemote + " also threw:");
+            remote.printStackTrace();
+            System.out.println();
+            throw local;
+        }
+
+        if (remote != null) {
+            throw remote;
+        }
+
+        if (local != null) {
+            throw local;
+        }
+    }
+
+    void startServer(boolean newThread) throws Exception {
+        if (newThread) {
+            serverThread = new Thread() {
+                public void run() {
+                    try {
+                        doServerSide();
+                    } catch (Exception e) {
+                        /*
+                         * Our server thread just died.
+                         *
+                         * Release the client, if not active already...
+                         */
+                        System.err.println("Server died...");
+                        serverReady = true;
+                        serverException = e;
+                    }
+                }
+            };
+            serverThread.start();
+        } else {
+            try {
+                doServerSide();
+            } catch (Exception e) {
+                serverException = e;
+            } finally {
+                serverReady = true;
+            }
+        }
+    }
+
+    void startClient(boolean newThread) throws Exception {
+        if (newThread) {
+            clientThread = new Thread() {
+                public void run() {
+                    try {
+                        doClientSide();
+                    } catch (Exception e) {
+                        /*
+                         * Our client thread just died.
+                         */
+                        System.err.println("Client died...");
+                        clientException = e;
+                    }
+                }
+            };
+            clientThread.start();
+        } else {
+            try {
+                doClientSide();
+            } catch (Exception e) {
+                clientException = e;
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/sun/security/ssl/javax/net/ssl/ServerName/SSLSocketInconsistentSNI.java	Thu Oct 18 01:14:00 2012 -0700
@@ -0,0 +1,340 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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 7068321
+ * @summary Support TLS Server Name Indication (SNI) Extension in JSSE Server
+ * @run main/othervm SSLSocketInconsistentSNI
+ */
+
+import java.io.*;
+import java.nio.*;
+import java.nio.channels.*;
+import java.util.*;
+import java.net.*;
+import javax.net.ssl.*;
+
+public class SSLSocketInconsistentSNI {
+
+    /*
+     * =============================================================
+     * Set the various variables needed for the tests, then
+     * specify what tests to run on each side.
+     */
+
+    /*
+     * Should we run the client or server in a separate thread?
+     * Both sides can throw exceptions, but do you have a preference
+     * as to which side should be the main thread.
+     */
+    static boolean separateServerThread = true;
+
+    /*
+     * Where do we find the keystores?
+     */
+    static String pathToStores = "../../../../etc";
+    static String keyStoreFile = "keystore";
+    static String trustStoreFile = "truststore";
+    static String passwd = "passphrase";
+
+    /*
+     * Is the server ready to serve?
+     */
+    volatile static boolean serverReady = false;
+
+    /*
+     * Turn on SSL debugging?
+     */
+    static boolean debug = false;
+
+    /*
+     * If the client or server is doing some kind of object creation
+     * that the other side depends on, and that thread prematurely
+     * exits, you may experience a hang.  The test harness will
+     * terminate all hung threads after its timeout has expired,
+     * currently 3 minutes by default, but you might try to be
+     * smart about it....
+     */
+
+    /*
+     * Define the server side of the test.
+     *
+     * If the server prematurely exits, serverReady will be set to true
+     * to avoid infinite hangs.
+     */
+    void doServerSide() throws Exception {
+        SSLServerSocketFactory sslssf =
+            (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
+        SSLServerSocket sslServerSocket =
+            (SSLServerSocket) sslssf.createServerSocket(serverPort);
+
+        SNIMatcher matcher = SNIHostName.createSNIMatcher(
+                                                serverAcceptableHostname);
+        Collection<SNIMatcher> matchers = new ArrayList<>(1);
+        matchers.add(matcher);
+        SSLParameters params = sslServerSocket.getSSLParameters();
+        params.setSNIMatchers(matchers);
+        sslServerSocket.setSSLParameters(params);
+
+        serverPort = sslServerSocket.getLocalPort();
+
+        /*
+         * Signal Client, we're ready for his connect.
+         */
+        serverReady = true;
+
+        SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
+        try {
+            InputStream sslIS = sslSocket.getInputStream();
+            OutputStream sslOS = sslSocket.getOutputStream();
+
+            sslIS.read();
+            sslOS.write(85);
+            sslOS.flush();
+
+            throw new Exception(
+                "Mismatched server name indication was accepted");
+        } catch (SSLHandshakeException sslhe) {
+            // the expected unrecognized server name indication exception
+        } catch (IOException ioe) {
+            // the peer may have closed the socket because of the unmatched
+            // server name indication.
+        } finally {
+            sslSocket.close();
+            sslServerSocket.close();
+        }
+    }
+
+
+    /*
+     * Define the client side of the test.
+     *
+     * If the server prematurely exits, serverReady will be set to true
+     * to avoid infinite hangs.
+     */
+    void doClientSide() throws Exception {
+
+        /*
+         * Wait for server to get started.
+         */
+        while (!serverReady) {
+            Thread.sleep(50);
+        }
+
+        SSLSocketFactory sslsf =
+            (SSLSocketFactory) SSLSocketFactory.getDefault();
+        SSLSocket sslSocket = (SSLSocket)
+            sslsf.createSocket("localhost", serverPort);
+
+        SNIHostName serverName = new SNIHostName(clientRequestedHostname);
+        List<SNIServerName> serverNames = new ArrayList<>(1);
+        serverNames.add(serverName);
+        SSLParameters params = sslSocket.getSSLParameters();
+        params.setServerNames(serverNames);
+        sslSocket.setSSLParameters(params);
+
+        try {
+            InputStream sslIS = sslSocket.getInputStream();
+            OutputStream sslOS = sslSocket.getOutputStream();
+
+            sslOS.write(280);
+            sslOS.flush();
+            sslIS.read();
+
+            throw new Exception(
+                "Mismatched server name indication was accepted");
+        } catch (SSLHandshakeException sslhe) {
+            // the expected unrecognized server name indication exception
+        } catch (IOException ioe) {
+            // the peer may have closed the socket because of the unmatched
+            // server name indication.
+        } finally {
+            sslSocket.close();
+        }
+    }
+
+    private static String clientRequestedHostname = "www.example.com";
+    private static String serverAcceptableHostname = "www\\.example\\.org";
+
+    /*
+     * =============================================================
+     * The remainder is just support stuff
+     */
+
+    // use any free port by default
+    volatile int serverPort = 0;
+
+    volatile Exception serverException = null;
+    volatile Exception clientException = null;
+
+    public static void main(String[] args) throws Exception {
+        String keyFilename =
+            System.getProperty("test.src", ".") + "/" + pathToStores +
+                "/" + keyStoreFile;
+        String trustFilename =
+            System.getProperty("test.src", ".") + "/" + pathToStores +
+                "/" + trustStoreFile;
+
+        System.setProperty("javax.net.ssl.keyStore", keyFilename);
+        System.setProperty("javax.net.ssl.keyStorePassword", passwd);
+        System.setProperty("javax.net.ssl.trustStore", trustFilename);
+        System.setProperty("javax.net.ssl.trustStorePassword", passwd);
+
+        if (debug)
+            System.setProperty("javax.net.debug", "all");
+
+        /*
+         * Start the tests.
+         */
+        new SSLSocketInconsistentSNI();
+    }
+
+    Thread clientThread = null;
+    Thread serverThread = null;
+
+    /*
+     * Primary constructor, used to drive remainder of the test.
+     *
+     * Fork off the other side, then do your work.
+     */
+    SSLSocketInconsistentSNI() throws Exception {
+        try {
+            if (separateServerThread) {
+                startServer(true);
+                startClient(false);
+            } else {
+                startClient(true);
+                startServer(false);
+            }
+        } catch (Exception e) {
+            // swallow for now.  Show later
+        }
+
+        /*
+         * Wait for other side to close down.
+         */
+        if (separateServerThread) {
+            serverThread.join();
+        } else {
+            clientThread.join();
+        }
+
+        /*
+         * When we get here, the test is pretty much over.
+         * Which side threw the error?
+         */
+        Exception local;
+        Exception remote;
+        String whichRemote;
+
+        if (separateServerThread) {
+            remote = serverException;
+            local = clientException;
+            whichRemote = "server";
+        } else {
+            remote = clientException;
+            local = serverException;
+            whichRemote = "client";
+        }
+
+        /*
+         * If both failed, return the curthread's exception, but also
+         * print the remote side Exception
+         */
+        if ((local != null) && (remote != null)) {
+            System.out.println(whichRemote + " also threw:");
+            remote.printStackTrace();
+            System.out.println();
+            throw local;
+        }
+
+        if (remote != null) {
+            throw remote;
+        }
+
+        if (local != null) {
+            throw local;
+        }
+    }
+
+    void startServer(boolean newThread) throws Exception {
+        if (newThread) {
+            serverThread = new Thread() {
+                public void run() {
+                    try {
+                        doServerSide();
+                    } catch (Exception e) {
+                        /*
+                         * Our server thread just died.
+                         *
+                         * Release the client, if not active already...
+                         */
+                        System.err.println("Server died...");
+                        serverReady = true;
+                        serverException = e;
+                    }
+                }
+            };
+            serverThread.start();
+        } else {
+            try {
+                doServerSide();
+            } catch (Exception e) {
+                serverException = e;
+            } finally {
+                serverReady = true;
+            }
+        }
+    }
+
+    void startClient(boolean newThread) throws Exception {
+        if (newThread) {
+            clientThread = new Thread() {
+                public void run() {
+                    try {
+                        doClientSide();
+                    } catch (Exception e) {
+                        /*
+                         * Our client thread just died.
+                         */
+                        System.err.println("Client died...");
+                        clientException = e;
+                    }
+                }
+            };
+            clientThread.start();
+        } else {
+            try {
+                doClientSide();
+            } catch (Exception e) {
+                clientException = e;
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/sun/security/ssl/javax/net/ssl/ServerName/SSLSocketSNISensitive.java	Thu Oct 18 01:14:00 2012 -0700
@@ -0,0 +1,564 @@
+/*
+ * Copyright (c) 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.
+ */
+
+//
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+//
+
+/*
+ * @test
+ * @bug 7068321
+ * @summary Support TLS Server Name Indication (SNI) Extension in JSSE Server
+ * @run main/othervm SSLSocketSNISensitive PKIX www.example.com
+ * @run main/othervm SSLSocketSNISensitive SunX509 www.example.com
+ * @run main/othervm SSLSocketSNISensitive PKIX www.example.net
+ * @run main/othervm SSLSocketSNISensitive SunX509 www.example.net
+ * @run main/othervm SSLSocketSNISensitive PKIX www.invalid.com
+ * @run main/othervm SSLSocketSNISensitive SunX509 www.invalid.com
+ */
+
+import java.net.*;
+import java.util.*;
+import java.io.*;
+import javax.net.ssl.*;
+import java.security.KeyStore;
+import java.security.KeyFactory;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.security.cert.CertificateFactory;
+import java.security.spec.*;
+import java.security.interfaces.*;
+import sun.misc.BASE64Decoder;
+
+
+public class SSLSocketSNISensitive {
+
+    /*
+     * =============================================================
+     * Set the various variables needed for the tests, then
+     * specify what tests to run on each side.
+     */
+
+    /*
+     * Should we run the client or server in a separate thread?
+     * Both sides can throw exceptions, but do you have a preference
+     * as to which side should be the main thread.
+     */
+    static boolean separateServerThread = false;
+
+    /*
+     * Where do we find the keystores?
+     */
+    // Certificates and key used in the test.
+    static String trustedCertStr =
+        "-----BEGIN CERTIFICATE-----\n" +
+        "MIICkjCCAfugAwIBAgIBADANBgkqhkiG9w0BAQQFADA7MQswCQYDVQQGEwJVUzEN\n" +
+        "MAsGA1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2UwHhcN\n" +
+        "MTIwNDE3MTIwNjA3WhcNMzMwMzI4MTIwNjA3WjA7MQswCQYDVQQGEwJVUzENMAsG\n" +
+        "A1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2UwgZ8wDQYJ\n" +
+        "KoZIhvcNAQEBBQADgY0AMIGJAoGBANY+7Enp+1S566kLcKk+qe4Ki6BxaHGZ+v7r\n" +
+        "vLksx9IQZCbAEf4YLbrZhKzKD3SPIJXyxPFwknAknIh3Knk8mViOZks7T8L3GnJr\n" +
+        "TBaVvDyTzDJum/QYiahfO2qpfN/Oya2UILmqsBAeLyWpzbQsAyWBXfoUtkOUgnzK\n" +
+        "fk6QAKYrAgMBAAGjgaUwgaIwHQYDVR0OBBYEFEtmQi7jT1ijXOafPsfkrLwSVu9e\n" +
+        "MGMGA1UdIwRcMFqAFEtmQi7jT1ijXOafPsfkrLwSVu9eoT+kPTA7MQswCQYDVQQG\n" +
+        "EwJVUzENMAsGA1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2\n" +
+        "Y2WCAQAwDwYDVR0TAQH/BAUwAwEB/zALBgNVHQ8EBAMCAQYwDQYJKoZIhvcNAQEE\n" +
+        "BQADgYEAkKWxMc4+ODk5WwLXXweB8/IKfVfrizNn0KLEgsZ6xNXFIXDpiPGAFcgl\n" +
+        "MzFO424JgyvUulsUc/X16Cnuwwntkk6KUG7vEV7h4o9sAV7Cax3gfQE/EZFb4ybn\n" +
+        "aBm1UsujMKd/ovqbbbxJbmOWzCeo0QfIGleDEyh3NBBZ0i11Kiw=\n" +
+        "-----END CERTIFICATE-----";
+
+    // web server certificate, www.example.com
+    static String targetCertStr_A =
+        "-----BEGIN CERTIFICATE-----\n" +
+        "MIICVTCCAb6gAwIBAgIBAjANBgkqhkiG9w0BAQQFADA7MQswCQYDVQQGEwJVUzEN\n" +
+        "MAsGA1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2UwHhcN\n" +
+        "MTIwNDE3MTIwNjA4WhcNMzIwMTAzMTIwNjA4WjBVMQswCQYDVQQGEwJVUzENMAsG\n" +
+        "A1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2UxGDAWBgNV\n" +
+        "BAMTD3d3dy5leGFtcGxlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA\n" +
+        "4zFp3PZNzsd3ZwG6FNNWO9eSN+UBymlf8oCwpKJM2tIinmMWvWIXnlx/2UXIfSAq\n" +
+        "QEG3aXkAFyEiGGpQlBbqcfrESsHsiz2pnnm5dG2v/eS0Bwz1jmcuNmwnh3UQw2Vl\n" +
+        "+BLk8ukdrLjiCT8jARiHExYf1Xg+wUqQ9y8NV26hdaUCAwEAAaNPME0wCwYDVR0P\n" +
+        "BAQDAgPoMB0GA1UdDgQWBBQwtx+gqzn2w4y82brXlp7tqBYEZDAfBgNVHSMEGDAW\n" +
+        "gBRLZkIu409Yo1zmnz7H5Ky8ElbvXjANBgkqhkiG9w0BAQQFAAOBgQAJWo8B6Ud+\n" +
+        "/OU+UcZLihlfMX02OSlK2ZB7mfqpj2G3JT9yb0A+VbY3uuajmaYYIIxl3kXGz/n8\n" +
+        "M2Q/Ux/MDxG+IFKHC26Kuj4dAQgzjq2pILVPTE2QnaQTNCsgVZtTaC47SG9FRSoC\n" +
+        "qvnIvn/oTpKSqus76I1cR4joDtiV2OEuVw==\n" +
+        "-----END CERTIFICATE-----";
+
+    // Private key in the format of PKCS#8
+    static String targetPrivateKey_A =
+        "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAOMxadz2Tc7Hd2cB\n" +
+        "uhTTVjvXkjflAcppX/KAsKSiTNrSIp5jFr1iF55cf9lFyH0gKkBBt2l5ABchIhhq\n" +
+        "UJQW6nH6xErB7Is9qZ55uXRtr/3ktAcM9Y5nLjZsJ4d1EMNlZfgS5PLpHay44gk/\n" +
+        "IwEYhxMWH9V4PsFKkPcvDVduoXWlAgMBAAECgYAqX2nuIyXp3fvgA0twXOYlbRRB\n" +
+        "Rn3qAXM6qFPJsNeCrFR2k+aG1cev6nKR1FkLNTeMGnWZv06MAcr5IML8i7WXyG4C\n" +
+        "LY/C0gedn94FDKFlln+bTENwQTGjn4lKysDA+IuNpasTeMCajbic+dPByhIdTOjZ\n" +
+        "iMCyxbLfpk40zQopVQJBAPyfGmkeHB3GjdbdgujWCGKb2UxBa4O8dy3O4l2yizTn\n" +
+        "uUqMGcwGY4ciNSVvZQ7jKo4vDmkSuYib4/woPChaNfMCQQDmO0BQuSWYGNtSwV35\n" +
+        "lafZfX1dNCLKm1iNA6A12evXgvQiE9WT4mqionig0VZW16HtiY4/BkHOcos/K9Um\n" +
+        "ARQHAkA8mkaRtSF1my5nv1gqVz5Hua+VdZQ/VDUbDiiL5cszc+ulkJqXsWirAG/T\n" +
+        "fTe3LJQG7A7+8fkEZrF4yoY0AAA1AkEAotokezULj5N9iAL5SzL9wIzQYV4ggfny\n" +
+        "YATBjXXxKccakwQ+ndWZIiMUeoS4ssLialhTgucVI0fIkU2a/r/ifwJAc6e+5Pvh\n" +
+        "MghQj/U788Od/v6rgqz/NGsduZ7uilCMcWiwA73OR2MHMH/OIuoofuEPrfuV9isV\n" +
+        "xVXhgpKfP/pdOA==";
+
+    // web server certificate, www.example.net
+    static String targetCertStr_B =
+        "-----BEGIN CERTIFICATE-----\n" +
+        "MIICVTCCAb6gAwIBAgIBBDANBgkqhkiG9w0BAQQFADA7MQswCQYDVQQGEwJVUzEN\n" +
+        "MAsGA1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2UwHhcN\n" +
+        "MTIwNDE3MTIwNjA5WhcNMzIwMTAzMTIwNjA5WjBVMQswCQYDVQQGEwJVUzENMAsG\n" +
+        "A1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2UxGDAWBgNV\n" +
+        "BAMTD3d3dy5leGFtcGxlLm5ldDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA\n" +
+        "2VlzF1fvWYczDChrUeJiLJ1M/dIShCaOTfYGiXfQGEZCAWTacUclwr+rVMnZ75/c\n" +
+        "wwg5pNdXRijxMil8DBTS1gFcIFQhosLHvzIAe6ULlg/xB+/L6KBz+NTWfo/2KF6t\n" +
+        "xatmcToNrCcwi7eUOfbzQje65Tizs56jJYem2m7Rk0ECAwEAAaNPME0wCwYDVR0P\n" +
+        "BAQDAgPoMB0GA1UdDgQWBBQT/FR0cAWcZQ7h0X79KGki34OSQjAfBgNVHSMEGDAW\n" +
+        "gBRLZkIu409Yo1zmnz7H5Ky8ElbvXjANBgkqhkiG9w0BAQQFAAOBgQB67cPIT6fz\n" +
+        "6Ws8fBpYgW2ad4ci66i1WduBD9CpGFE+jRK2feRj6hvYBXocKj0AMWUFIEB2E3hA\n" +
+        "oIjxcf1GxIpHVl9DjlhxqXbA0Ktl7/NGNRlDSLTizOTl3FB1mMTlOGvXDVmpcFhl\n" +
+        "HuoP1hYvhTsBwPx5igGNchuPtDIUzL2mXw==\n" +
+        "-----END CERTIFICATE-----";
+
+    static String targetPrivateKey_B =
+        "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBANlZcxdX71mHMwwo\n" +
+        "a1HiYiydTP3SEoQmjk32Bol30BhGQgFk2nFHJcK/q1TJ2e+f3MMIOaTXV0Yo8TIp\n" +
+        "fAwU0tYBXCBUIaLCx78yAHulC5YP8Qfvy+igc/jU1n6P9ihercWrZnE6DawnMIu3\n" +
+        "lDn280I3uuU4s7OeoyWHptpu0ZNBAgMBAAECgYEAl19H26sfhD+32rDPxZCgBShs\n" +
+        "dZ33zVe45i0Bcn4iTLWpxKTDyf7eGps4rO2DvfKdYqt40ggzvSZIjUH9JcDe8GmG\n" +
+        "d3m0ILB7pg4jsFlpyeHpTO8grPLxA1G9s3o0DoFpz/rooqgFfe/DrRDmRoOSkgfV\n" +
+        "/gseIbgJHRO/Ctyvdh0CQQD6uFd0HxhH1jl/JzvPzIH4LSnPcdEh9zsMEb6uzh75\n" +
+        "9qL+IHD5N2I/pYZTKqDFIwhJf701+LKag55AX/zrDt7rAkEA3e00AbnwanDMa6Wj\n" +
+        "+gFekUQveSVra38LiihzCkyVvQpFjbiF1rUhSNQ0dpU5/hmrYF0C6H9VXAesfkUY\n" +
+        "WhpDgwJAYjgZOop77piDycZK7isFt32p5XSHIzFBVocVFlH1XKM8UyXOXDNQL/Le\n" +
+        "XnJSrSf+NRzvuNcG0PVC56Ey6brXpQJAY4M4vcltt5zq3R5CQBmbGRJ1IyKXX3Vx\n" +
+        "bDslEqoyvri7ZYgnY5aG3UxiVgYmIf3KrgQnCLAIS6MZQumiuMxsFwJAK5pEG063\n" +
+        "9ngUof4fDMvZphqZjZR1zMKz/V/9ge0DWBINaqFgsgebNu+MyImsC8C6WKjGmV/2\n" +
+        "f1MY0D7sC2vU/Q==";
+
+    // web server certificate, www.invalid.com
+    static String targetCertStr_C =
+        "-----BEGIN CERTIFICATE-----\n" +
+        "MIICVTCCAb6gAwIBAgIBAzANBgkqhkiG9w0BAQQFADA7MQswCQYDVQQGEwJVUzEN\n" +
+        "MAsGA1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2UwHhcN\n" +
+        "MTIwNDE3MTIwNjA5WhcNMzIwMTAzMTIwNjA5WjBVMQswCQYDVQQGEwJVUzENMAsG\n" +
+        "A1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2UxGDAWBgNV\n" +
+        "BAMTD3d3dy5pbnZhbGlkLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA\n" +
+        "q6MyQwzCr2nJ41l0frmHL0qULSyW51MhevBC+1W28i0LE/efrmpwV3LdnlQEGFak\n" +
+        "DLDwtnff3iru8dSMcA7KdWVkivsE7ZTP+qFDaWBAy7XXiSsv6yZ2Nh4jJb0YcD28\n" +
+        "45zk2nAl5Az1/PuoTi1vpQxzFZKuBm1HGgz3MEZvBvMCAwEAAaNPME0wCwYDVR0P\n" +
+        "BAQDAgPoMB0GA1UdDgQWBBRRMifrND015Nm8N6gV5X7cg1YjjjAfBgNVHSMEGDAW\n" +
+        "gBRLZkIu409Yo1zmnz7H5Ky8ElbvXjANBgkqhkiG9w0BAQQFAAOBgQBjkUO6Ri/B\n" +
+        "uDC2gDMIyL5+NTe/1dPPQYM4HhCNa/KQYvU5lzCKO9Vpa+i+nyrUNNXUu8Tkyq4Y\n" +
+        "A+aGSm6+FT/i9rFwkYUdorBtD3KfQiwTIWrVERXBkWI5iZNaVZhx0TFy4vUpf65d\n" +
+        "QtwkbHpC66fdKc2EdLXkuY9KkmtZZJJ7YA==\n" +
+        "-----END CERTIFICATE-----";
+
+    static String targetPrivateKey_C =
+        "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAKujMkMMwq9pyeNZ\n" +
+        "dH65hy9KlC0sludTIXrwQvtVtvItCxP3n65qcFdy3Z5UBBhWpAyw8LZ3394q7vHU\n" +
+        "jHAOynVlZIr7BO2Uz/qhQ2lgQMu114krL+smdjYeIyW9GHA9vOOc5NpwJeQM9fz7\n" +
+        "qE4tb6UMcxWSrgZtRxoM9zBGbwbzAgMBAAECgYASJDK40Y12Wvki1Z6xkkyOnBRj\n" +
+        "XfYpRykfxGtgA2RN3qLwHlk7Zzaul46DIKA6LlYynTUkJDF+Ww1cdDnP0lBlwcmM\n" +
+        "iD0ck3zYyYBLhQHuVbkK3SYE+ANRhM0icvvqANP2at/U4awQcPNEae/KCiecLNu3\n" +
+        "CJGqyhPDdrEAqPuJGQJBAN46pQC6l3yrcSYE2s53jSmsm2HVVOFlFXjU6k/RMTxG\n" +
+        "FfDJtGUAOQ37rPQ06ugr/gjLAmmPp+FXozaBdA32D80CQQDFuGRgv3WYqbglIcRL\n" +
+        "JRs6xlj9w1F97s/aiUenuwhIPNiUoRbV7mnNuZ/sGF0svOVE7SazRjuFX6UqL9Y9\n" +
+        "HzG/AkEA170pCI8cl4w8eUNHRB9trGKEKjMXhwVCFh7lJf2ZBcGodSzr8w2HVhrZ\n" +
+        "Ke7hiemDYffrbJ1oxmv05+o+x3r0lQJBAL6adVm2+FyFMFnLZXmzeb59O4jWY5bt\n" +
+        "Qz6/HG6bpO5OidMuP99YCHMkQQDOs/PO3Y5GuAoW6IY4n/Y9S2B80+0CQBl1/H9/\n" +
+        "0n/vrb6vW6Azds49tuS82RFAnOhtwTyBEajs08WF8rZQ3WD2RHJnH0+jjfL0anIp\n" +
+        "dQBSeNN7s7b6rRk=";
+
+    // This is a certificate for client
+    static String targetCertStr_D=
+        "-----BEGIN CERTIFICATE-----\n" +
+        "MIICVDCCAb2gAwIBAgIBBTANBgkqhkiG9w0BAQQFADA7MQswCQYDVQQGEwJVUzEN\n" +
+        "MAsGA1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2UwHhcN\n" +
+        "MTIwNDE3MTIwNjEwWhcNMzIwMTAzMTIwNjEwWjBUMQswCQYDVQQGEwJVUzENMAsG\n" +
+        "A1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2UxFzAVBgNV\n" +
+        "BAMTDkludGVyT3AgVGVzdGVyMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDo\n" +
+        "Q/KoAIAC2ljFfW2KwjnxTzi4NQJeUuk2seqKpsAY8x4O5dvixzUl6142zmljapqi\n" +
+        "bJloQVpfB+CEc5/l4h5gzGRVzkuqP1oPzDrpZ5GsvmvuHenV/TzCIgX1cLETzQVt\n" +
+        "6Rk06okoBPnw3hDJEJiEc1Rv7HCE8p/p+SaiHrskwwIDAQABo08wTTALBgNVHQ8E\n" +
+        "BAMCA+gwHQYDVR0OBBYEFPr91O33RIGfFSqza2AwQIgE4QswMB8GA1UdIwQYMBaA\n" +
+        "FEtmQi7jT1ijXOafPsfkrLwSVu9eMA0GCSqGSIb3DQEBBAUAA4GBANIDFYgAhoj3\n" +
+        "B8u1YpqeoEp2Lt9TwrYBshaIrbmBPCwCGio0JIsoov3n8BCSg5F+8MnOtPl+TjeO\n" +
+        "0Ug+7guPdCk/wg8YNxLHgSsQlpcNJDjWiErqmUPVrg5BPPQb65qMund6KTmMN0y6\n" +
+        "4EbSmxRpZO/N0/5oK4umTk0EeXKNekBj\n" +
+        "-----END CERTIFICATE-----";
+
+    static String targetPrivateKey_D =
+        "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAOhD8qgAgALaWMV9\n" +
+        "bYrCOfFPOLg1Al5S6Tax6oqmwBjzHg7l2+LHNSXrXjbOaWNqmqJsmWhBWl8H4IRz\n" +
+        "n+XiHmDMZFXOS6o/Wg/MOulnkay+a+4d6dX9PMIiBfVwsRPNBW3pGTTqiSgE+fDe\n" +
+        "EMkQmIRzVG/scITyn+n5JqIeuyTDAgMBAAECgYBw37yIKp4LRONJLnhSq6sO+0n8\n" +
+        "Mz6waiiN/Q6XTQwj09pysQAYCGlqwSRrDAqpVsBJWO+Ae+oYLrLMi4hUZnwN75v3\n" +
+        "pe1nXlrD11RmPLXwBxqFxNSvAs2FgLHZEtwHI7Bn8KybT/8bGkQ8csLceInYtMDD\n" +
+        "MuTyy2KRk/pj60zIKQJBAPgebQiAH6viFQ88AwHaNvQhlUfwmSC1i6f8LVoeqaHC\n" +
+        "lnP0LJBwlyDeeEInhHrCR2ibnCB6I/Pig+49XQgabK8CQQDvpJwuGEbsOO+3rkJJ\n" +
+        "OpOw4toG0QJZdRnT6l8I6BlboQRZSfFh+lGGahvFXkxc4KdUpJ7QPtXU7HHk6Huk\n" +
+        "8RYtAkA9CW8VGj+wTuuTVdX/jKjcIa7RhbSFwWNbrcOSWdys+Gt+luCnn6rt4QyA\n" +
+        "aaxDbquWZkFgE+voQR7nap0KM0XtAkAznd0WAJymHM1lXt9gLoHJQ9N6TGKZKiPa\n" +
+        "BU1a+cMcfV4WbVrUo7oTnZ9Fr73681iXXq3mZOJh7lvJ1llreZIxAkBEnbiTgEf4\n" +
+        "tvku68jHcRbRPmdS7CBSWNEBaHLOm4pUSTcxVTKKMHw7vmM5/UYUxJ8QNKCYxn6O\n" +
+        "+vtiBwBawwzN";
+
+    static String[] serverCerts = {targetCertStr_A,
+                                targetCertStr_B, targetCertStr_C};
+    static String[] serverKeys  = {targetPrivateKey_A,
+                                targetPrivateKey_B, targetPrivateKey_C};
+    static String[] clientCerts = {targetCertStr_D};
+    static String[] clientKeys  = {targetPrivateKey_D};
+
+    static char passphrase[] = "passphrase".toCharArray();
+
+    /*
+     * Is the server ready to serve?
+     */
+    volatile static boolean serverReady = false;
+
+    /*
+     * Turn on SSL debugging?
+     */
+    static boolean debug = false;
+
+    /*
+     * Define the server side of the test.
+     *
+     * If the server prematurely exits, serverReady will be set to true
+     * to avoid infinite hangs.
+     */
+    void doServerSide() throws Exception {
+        SSLContext context = generateSSLContext(false);
+        SSLServerSocketFactory sslssf = context.getServerSocketFactory();
+        SSLServerSocket sslServerSocket =
+            (SSLServerSocket)sslssf.createServerSocket(serverPort);
+        serverPort = sslServerSocket.getLocalPort();
+
+        /*
+         * Signal Client, we're ready for his connect.
+         */
+        serverReady = true;
+
+        SSLSocket sslSocket = (SSLSocket)sslServerSocket.accept();
+        try {
+            InputStream sslIS = sslSocket.getInputStream();
+            OutputStream sslOS = sslSocket.getOutputStream();
+
+            sslIS.read();
+            sslOS.write('A');
+            sslOS.flush();
+
+            SSLSession session = sslSocket.getSession();
+            checkCertificate(session.getLocalCertificates(),
+                                                clientRequestedHostname);
+        } finally {
+            sslSocket.close();
+            sslServerSocket.close();
+        }
+    }
+
+    /*
+     * Define the client side of the test.
+     *
+     * If the server prematurely exits, serverReady will be set to true
+     * to avoid infinite hangs.
+     */
+    void doClientSide() throws Exception {
+
+        /*
+         * Wait for server to get started.
+         */
+        while (!serverReady) {
+            Thread.sleep(50);
+        }
+
+        SSLContext context = generateSSLContext(true);
+        SSLSocketFactory sslsf = context.getSocketFactory();
+
+        SSLSocket sslSocket =
+            (SSLSocket)sslsf.createSocket("localhost", serverPort);
+
+        SNIHostName serverName = new SNIHostName(clientRequestedHostname);
+        List<SNIServerName> serverNames = new ArrayList<>(1);
+        serverNames.add(serverName);
+        SSLParameters params = sslSocket.getSSLParameters();
+        params.setServerNames(serverNames);
+        sslSocket.setSSLParameters(params);
+
+        try {
+            InputStream sslIS = sslSocket.getInputStream();
+            OutputStream sslOS = sslSocket.getOutputStream();
+
+            sslOS.write('B');
+            sslOS.flush();
+            sslIS.read();
+
+            SSLSession session = sslSocket.getSession();
+            checkCertificate(session.getPeerCertificates(),
+                                                clientRequestedHostname);
+        } finally {
+            sslSocket.close();
+        }
+    }
+
+    private static void checkCertificate(Certificate[] certs,
+            String hostname) throws Exception {
+        if (certs != null && certs.length != 0) {
+            X509Certificate x509Cert = (X509Certificate)certs[0];
+
+            String subject = x509Cert.getSubjectX500Principal().getName();
+
+            if (!subject.contains(hostname)) {
+                throw new Exception(
+                        "Not the expected certificate: " + subject);
+            }
+        }
+    }
+
+    /*
+     * =============================================================
+     * The remainder is just support stuff
+     */
+    private static String tmAlgorithm;             // trust manager
+    private static String clientRequestedHostname; // server name indication
+
+    private static void parseArguments(String[] args) {
+        tmAlgorithm = args[0];
+        clientRequestedHostname = args[1];
+    }
+
+    private static SSLContext generateSSLContext(boolean isClient)
+            throws Exception {
+
+        // generate certificate from cert string
+        CertificateFactory cf = CertificateFactory.getInstance("X.509");
+
+        // create a key store
+        KeyStore ks = KeyStore.getInstance("JKS");
+        ks.load(null, null);
+
+        // import the trused cert
+        ByteArrayInputStream is =
+                    new ByteArrayInputStream(trustedCertStr.getBytes());
+        Certificate trusedCert = cf.generateCertificate(is);
+        is.close();
+
+        ks.setCertificateEntry("RSA Export Signer", trusedCert);
+
+        String[] certStrs = null;
+        String[] keyStrs = null;
+        if (isClient) {
+            certStrs = clientCerts;
+            keyStrs = clientKeys;
+        } else {
+            certStrs = serverCerts;
+            keyStrs = serverKeys;
+        }
+
+        for (int i = 0; i < certStrs.length; i++) {
+            // generate the private key.
+            String keySpecStr = keyStrs[i];
+            PKCS8EncodedKeySpec priKeySpec = new PKCS8EncodedKeySpec(
+                                new BASE64Decoder().decodeBuffer(keySpecStr));
+            KeyFactory kf = KeyFactory.getInstance("RSA");
+            RSAPrivateKey priKey =
+                    (RSAPrivateKey)kf.generatePrivate(priKeySpec);
+
+            // generate certificate chain
+            String keyCertStr = certStrs[i];
+            is = new ByteArrayInputStream(keyCertStr.getBytes());
+            Certificate keyCert = cf.generateCertificate(is);
+            is.close();
+
+            Certificate[] chain = new Certificate[2];
+            chain[0] = keyCert;
+            chain[1] = trusedCert;
+
+            // import the key entry.
+            ks.setKeyEntry("key-entry-" + i, priKey, passphrase, chain);
+        }
+
+        // create SSL context
+        TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmAlgorithm);
+        tmf.init(ks);
+
+        SSLContext ctx = SSLContext.getInstance("TLS");
+        KeyManagerFactory kmf = KeyManagerFactory.getInstance("NewSunX509");
+        kmf.init(ks, passphrase);
+
+        ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
+        ks = null;
+
+        return ctx;
+    }
+
+    // use any free port by default
+    volatile int serverPort = 0;
+
+    volatile Exception serverException = null;
+    volatile Exception clientException = null;
+
+    public static void main(String[] args) throws Exception {
+        if (debug)
+            System.setProperty("javax.net.debug", "all");
+
+        /*
+         * Get the customized arguments.
+         */
+        parseArguments(args);
+
+        /*
+         * Start the tests.
+         */
+        new SSLSocketSNISensitive();
+    }
+
+    Thread clientThread = null;
+    Thread serverThread = null;
+
+    /*
+     * Primary constructor, used to drive remainder of the test.
+     *
+     * Fork off the other side, then do your work.
+     */
+    SSLSocketSNISensitive() throws Exception {
+        try {
+            if (separateServerThread) {
+                startServer(true);
+                startClient(false);
+            } else {
+                startClient(true);
+                startServer(false);
+            }
+        } catch (Exception e) {
+            // swallow for now.  Show later
+        }
+
+        /*
+         * Wait for other side to close down.
+         */
+        if (separateServerThread) {
+            serverThread.join();
+        } else {
+            clientThread.join();
+        }
+
+        /*
+         * When we get here, the test is pretty much over.
+         * Which side threw the error?
+         */
+        Exception local;
+        Exception remote;
+        String whichRemote;
+
+        if (separateServerThread) {
+            remote = serverException;
+            local = clientException;
+            whichRemote = "server";
+        } else {
+            remote = clientException;
+            local = serverException;
+            whichRemote = "client";
+        }
+
+        /*
+         * If both failed, return the curthread's exception, but also
+         * print the remote side Exception
+         */
+        if ((local != null) && (remote != null)) {
+            System.out.println(whichRemote + " also threw:");
+            remote.printStackTrace();
+            System.out.println();
+            throw local;
+        }
+
+        if (remote != null) {
+            throw remote;
+        }
+
+        if (local != null) {
+            throw local;
+        }
+    }
+
+    void startServer(boolean newThread) throws Exception {
+        if (newThread) {
+            serverThread = new Thread() {
+                public void run() {
+                    try {
+                        doServerSide();
+                    } catch (Exception e) {
+                        /*
+                         * Our server thread just died.
+                         *
+                         * Release the client, if not active already...
+                         */
+                        System.err.println("Server died...");
+                        serverReady = true;
+                        serverException = e;
+                    }
+                }
+            };
+            serverThread.start();
+        } else {
+            try {
+                doServerSide();
+            } catch (Exception e) {
+                serverException = e;
+            } finally {
+                serverReady = true;
+            }
+        }
+    }
+
+    void startClient(boolean newThread) throws Exception {
+        if (newThread) {
+            clientThread = new Thread() {
+                public void run() {
+                    try {
+                        doClientSide();
+                    } catch (Exception e) {
+                        /*
+                         * Our client thread just died.
+                         */
+                        System.err.println("Client died...");
+                        clientException = e;
+                    }
+                }
+            };
+            clientThread.start();
+        } else {
+            try {
+                doClientSide();
+            } catch (Exception e) {
+                clientException = e;
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/sun/security/ssl/templates/SSLCapabilities.java	Thu Oct 18 01:14:00 2012 -0700
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 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.
+ */
+
+import java.util.List;
+import javax.net.ssl.SNIServerName;
+
+/**
+ * Encapsulates the security capabilities of an SSL/TLS connection.
+ * <P>
+ * The security capabilities are the list of ciphersuites to be accepted in
+ * an SSL/TLS handshake, the record version, the hello version, and server
+ * name indication, etc., of an SSL/TLS connection.
+ * <P>
+ * <code>SSLCapabilities</code> can be retrieved by exploring the network
+ * data of an SSL/TLS connection via {@link SSLExplorer#explore(ByteBuffer)}
+ * or {@link SSLExplorer#explore(byte[], int, int)}.
+ *
+ * @see SSLExplorer
+ */
+public abstract class SSLCapabilities {
+
+    /**
+     * Returns the record version of an SSL/TLS connection
+     *
+     * @return a non-null record version
+     */
+    public abstract String getRecordVersion();
+
+    /**
+     * Returns the hello version of an SSL/TLS connection
+     *
+     * @return a non-null hello version
+     */
+    public abstract String getHelloVersion();
+
+    /**
+     * Returns a <code>List</code> containing all {@link SNIServerName}s
+     * of the server name indication.
+     *
+     * @return a non-null immutable list of {@link SNIServerName}s
+     *         of the server name indication parameter, may be empty
+     *         if no server name indication.
+     *
+     * @see SNIServerName
+     */
+    public abstract List<SNIServerName> getServerNames();
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/sun/security/ssl/templates/SSLExplorer.java	Thu Oct 18 01:14:00 2012 -0700
@@ -0,0 +1,615 @@
+/*
+ * Copyright (c) 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.
+ */
+
+import java.nio.ByteBuffer;
+import java.nio.BufferUnderflowException;
+import java.io.IOException;
+import javax.net.ssl.*;
+import java.util.*;
+
+import sun.misc.HexDumpEncoder;
+
+/**
+ * Instances of this class acts as an explorer of the network data of an
+ * SSL/TLS connection.
+ */
+public final class SSLExplorer {
+
+    // Private constructor prevents construction outside this class.
+    private SSLExplorer() {
+    }
+
+    /**
+     * The header size of TLS/SSL records.
+     * <P>
+     * The value of this constant is {@value}.
+     */
+    public final static int RECORD_HEADER_SIZE = 0x05;
+
+    /**
+     * Returns the required number of bytes in the {@code source}
+     * {@link ByteBuffer} necessary to explore SSL/TLS connection.
+     * <P>
+     * This method tries to parse as few bytes as possible from
+     * {@code source} byte buffer to get the length of an
+     * SSL/TLS record.
+     * <P>
+     * This method accesses the {@code source} parameter in read-only
+     * mode, and does not update the buffer's properties such as capacity,
+     * limit, position, and mark values.
+     *
+     * @param  source
+     *         a {@link ByteBuffer} containing
+     *         inbound or outbound network data for an SSL/TLS connection.
+     * @throws BufferUnderflowException if less than {@code RECORD_HEADER_SIZE}
+     *         bytes remaining in {@code source}
+     * @return the required size in byte to explore an SSL/TLS connection
+     */
+    public final static int getRequiredSize(ByteBuffer source) {
+
+        ByteBuffer input = source.duplicate();
+
+        // Do we have a complete header?
+        if (input.remaining() < RECORD_HEADER_SIZE) {
+            throw new BufferUnderflowException();
+        }
+
+        // Is it a handshake message?
+        byte firstByte = input.get();
+        byte secondByte = input.get();
+        byte thirdByte = input.get();
+        if ((firstByte & 0x80) != 0 && thirdByte == 0x01) {
+            // looks like a V2ClientHello
+            // return (((firstByte & 0x7F) << 8) | (secondByte & 0xFF)) + 2;
+            return RECORD_HEADER_SIZE;   // Only need the header fields
+        } else {
+            return (((input.get() & 0xFF) << 8) | (input.get() & 0xFF)) + 5;
+        }
+    }
+
+    /**
+     * Returns the required number of bytes in the {@code source} byte array
+     * necessary to explore SSL/TLS connection.
+     * <P>
+     * This method tries to parse as few bytes as possible from
+     * {@code source} byte array to get the length of an
+     * SSL/TLS record.
+     *
+     * @param  source
+     *         a byte array containing inbound or outbound network data for
+     *         an SSL/TLS connection.
+     * @param  offset
+     *         the start offset in array {@code source} at which the
+     *         network data is read from.
+     * @param  length
+     *         the maximum number of bytes to read.
+     *
+     * @throws BufferUnderflowException if less than {@code RECORD_HEADER_SIZE}
+     *         bytes remaining in {@code source}
+     * @return the required size in byte to explore an SSL/TLS connection
+     */
+    public final static int getRequiredSize(byte[] source,
+            int offset, int length) throws IOException {
+
+        ByteBuffer byteBuffer =
+            ByteBuffer.wrap(source, offset, length).asReadOnlyBuffer();
+        return getRequiredSize(byteBuffer);
+    }
+
+    /**
+     * Launch and explore the security capabilities from byte buffer.
+     * <P>
+     * This method tries to parse as few records as possible from
+     * {@code source} byte buffer to get the {@link SSLCapabilities}
+     * of an SSL/TLS connection.
+     * <P>
+     * Please NOTE that this method must be called before any handshaking
+     * occurs.  The behavior of this method is not defined in this release
+     * if the handshake has begun, or has completed.
+     * <P>
+     * This method accesses the {@code source} parameter in read-only
+     * mode, and does not update the buffer's properties such as capacity,
+     * limit, position, and mark values.
+     *
+     * @param  source
+     *         a {@link ByteBuffer} containing
+     *         inbound or outbound network data for an SSL/TLS connection.
+     *
+     * @throws IOException on network data error
+     * @throws BufferUnderflowException if not enough source bytes available
+     *         to make a complete exploration.
+     *
+     * @return the explored {@link SSLCapabilities} of the SSL/TLS
+     *         connection
+     */
+    public final static SSLCapabilities explore(ByteBuffer source)
+            throws IOException {
+
+        ByteBuffer input = source.duplicate();
+
+        // Do we have a complete header?
+        if (input.remaining() < RECORD_HEADER_SIZE) {
+            throw new BufferUnderflowException();
+        }
+
+        // Is it a handshake message?
+        byte firstByte = input.get();
+        byte secondByte = input.get();
+        byte thirdByte = input.get();
+        if ((firstByte & 0x80) != 0 && thirdByte == 0x01) {
+            // looks like a V2ClientHello
+            return exploreV2HelloRecord(input,
+                                    firstByte, secondByte, thirdByte);
+        } else if (firstByte == 22) {   // 22: handshake record
+            return exploreTLSRecord(input,
+                                    firstByte, secondByte, thirdByte);
+        } else {
+            throw new SSLException("Not handshake record");
+        }
+    }
+
+    /**
+     * Launch and explore the security capabilities from byte array.
+     * <P>
+     * Please NOTE that this method must be called before any handshaking
+     * occurs.  The behavior of this method is not defined in this release
+     * if the handshake has begun, or has completed.  Once handshake has
+     * begun, or has completed, the security capabilities can not and
+     * should not be launched with this method.
+     *
+     * @param  source
+     *         a byte array containing inbound or outbound network data for
+     *         an SSL/TLS connection.
+     * @param  offset
+     *         the start offset in array {@code source} at which the
+     *         network data is read from.
+     * @param  length
+     *         the maximum number of bytes to read.
+     *
+     * @throws IOException on network data error
+     * @throws BufferUnderflowException if not enough source bytes available
+     *         to make a complete exploration.
+     * @return the explored {@link SSLCapabilities} of the SSL/TLS
+     *         connection
+     *
+     * @see #explore(ByteBuffer)
+     */
+    public final static SSLCapabilities explore(byte[] source,
+            int offset, int length) throws IOException {
+        ByteBuffer byteBuffer =
+            ByteBuffer.wrap(source, offset, length).asReadOnlyBuffer();
+        return explore(byteBuffer);
+    }
+
+    /*
+     * uint8 V2CipherSpec[3];
+     * struct {
+     *     uint16 msg_length;         // The highest bit MUST be 1;
+     *                                // the remaining bits contain the length
+     *                                // of the following data in bytes.
+     *     uint8 msg_type;            // MUST be 1
+     *     Version version;
+     *     uint16 cipher_spec_length; // It cannot be zero and MUST be a
+     *                                // multiple of the V2CipherSpec length.
+     *     uint16 session_id_length;  // This field MUST be empty.
+     *     uint16 challenge_length;   // SHOULD use a 32-byte challenge
+     *     V2CipherSpec cipher_specs[V2ClientHello.cipher_spec_length];
+     *     opaque session_id[V2ClientHello.session_id_length];
+     *     opaque challenge[V2ClientHello.challenge_length;
+     * } V2ClientHello;
+     */
+    private static SSLCapabilities exploreV2HelloRecord(
+            ByteBuffer input, byte firstByte, byte secondByte,
+            byte thirdByte) throws IOException {
+
+        // We only need the header. We have already had enough source bytes.
+        // int recordLength = (firstByte & 0x7F) << 8) | (secondByte & 0xFF);
+        try {
+            // Is it a V2ClientHello?
+            if (thirdByte != 0x01) {
+                throw new SSLException(
+                        "Unsupported or Unrecognized SSL record");
+            }
+
+            // What's the hello version?
+            byte helloVersionMajor = input.get();
+            byte helloVersionMinor = input.get();
+
+            // 0x00: major version of SSLv20
+            // 0x02: minor version of SSLv20
+            //
+            // SNIServerName is an extension, SSLv20 doesn't support extension.
+            return new SSLCapabilitiesImpl((byte)0x00, (byte)0x02,
+                        helloVersionMajor, helloVersionMinor,
+                        Collections.<SNIServerName>emptyList());
+        } catch (BufferUnderflowException bufe) {
+            throw new SSLProtocolException(
+                        "Invalid handshake record");
+        }
+    }
+
+    /*
+     * struct {
+     *     uint8 major;
+     *     uint8 minor;
+     * } ProtocolVersion;
+     *
+     * enum {
+     *     change_cipher_spec(20), alert(21), handshake(22),
+     *     application_data(23), (255)
+     * } ContentType;
+     *
+     * struct {
+     *     ContentType type;
+     *     ProtocolVersion version;
+     *     uint16 length;
+     *     opaque fragment[TLSPlaintext.length];
+     * } TLSPlaintext;
+     */
+    private static SSLCapabilities exploreTLSRecord(
+            ByteBuffer input, byte firstByte, byte secondByte,
+            byte thirdByte) throws IOException {
+
+        // Is it a handshake message?
+        if (firstByte != 22) {        // 22: handshake record
+            throw new SSLException("Not handshake record");
+        }
+
+        // We need the record version to construct SSLCapabilities.
+        byte recordMajorVersion = secondByte;
+        byte recordMinorVersion = thirdByte;
+
+        // Is there enough data for a full record?
+        int recordLength = getInt16(input);
+        if (recordLength > input.remaining()) {
+            throw new BufferUnderflowException();
+        }
+
+        // We have already had enough source bytes.
+        try {
+            return exploreHandshake(input,
+                recordMajorVersion, recordMinorVersion, recordLength);
+        } catch (BufferUnderflowException bufe) {
+            throw new SSLProtocolException(
+                        "Invalid handshake record");
+        }
+    }
+
+    /*
+     * enum {
+     *     hello_request(0), client_hello(1), server_hello(2),
+     *     certificate(11), server_key_exchange (12),
+     *     certificate_request(13), server_hello_done(14),
+     *     certificate_verify(15), client_key_exchange(16),
+     *     finished(20)
+     *     (255)
+     * } HandshakeType;
+     *
+     * struct {
+     *     HandshakeType msg_type;
+     *     uint24 length;
+     *     select (HandshakeType) {
+     *         case hello_request:       HelloRequest;
+     *         case client_hello:        ClientHello;
+     *         case server_hello:        ServerHello;
+     *         case certificate:         Certificate;
+     *         case server_key_exchange: ServerKeyExchange;
+     *         case certificate_request: CertificateRequest;
+     *         case server_hello_done:   ServerHelloDone;
+     *         case certificate_verify:  CertificateVerify;
+     *         case client_key_exchange: ClientKeyExchange;
+     *         case finished:            Finished;
+     *     } body;
+     * } Handshake;
+     */
+    private static SSLCapabilities exploreHandshake(
+            ByteBuffer input, byte recordMajorVersion,
+            byte recordMinorVersion, int recordLength) throws IOException {
+
+        // What is the handshake type?
+        byte handshakeType = input.get();
+        if (handshakeType != 0x01) {   // 0x01: client_hello message
+            throw new IllegalStateException("Not initial handshaking");
+        }
+
+        // What is the handshake body length?
+        int handshakeLength = getInt24(input);
+
+        // Theoretically, a single handshake message might span multiple
+        // records, but in practice this does not occur.
+        if (handshakeLength > (recordLength - 4)) { // 4: handshake header size
+            throw new SSLException("Handshake message spans multiple records");
+        }
+
+        input = input.duplicate();
+        input.limit(handshakeLength + input.position());
+        return exploreClientHello(input,
+                                    recordMajorVersion, recordMinorVersion);
+    }
+
+    /*
+     * struct {
+     *     uint32 gmt_unix_time;
+     *     opaque random_bytes[28];
+     * } Random;
+     *
+     * opaque SessionID<0..32>;
+     *
+     * uint8 CipherSuite[2];
+     *
+     * enum { null(0), (255) } CompressionMethod;
+     *
+     * struct {
+     *     ProtocolVersion client_version;
+     *     Random random;
+     *     SessionID session_id;
+     *     CipherSuite cipher_suites<2..2^16-2>;
+     *     CompressionMethod compression_methods<1..2^8-1>;
+     *     select (extensions_present) {
+     *         case false:
+     *             struct {};
+     *         case true:
+     *             Extension extensions<0..2^16-1>;
+     *     };
+     * } ClientHello;
+     */
+    private static SSLCapabilities exploreClientHello(
+            ByteBuffer input,
+            byte recordMajorVersion,
+            byte recordMinorVersion) throws IOException {
+
+        List<SNIServerName> snList = Collections.<SNIServerName>emptyList();
+
+        // client version
+        byte helloMajorVersion = input.get();
+        byte helloMinorVersion = input.get();
+
+        // ignore random
+        int position = input.position();
+        input.position(position + 32);  // 32: the length of Random
+
+        // ignore session id
+        ignoreByteVector8(input);
+
+        // ignore cipher_suites
+        ignoreByteVector16(input);
+
+        // ignore compression methods
+        ignoreByteVector8(input);
+
+        if (input.remaining() > 0) {
+            snList = exploreExtensions(input);
+        }
+
+        return new SSLCapabilitiesImpl(
+                recordMajorVersion, recordMinorVersion,
+                helloMajorVersion, helloMinorVersion, snList);
+    }
+
+    /*
+     * struct {
+     *     ExtensionType extension_type;
+     *     opaque extension_data<0..2^16-1>;
+     * } Extension;
+     *
+     * enum {
+     *     server_name(0), max_fragment_length(1),
+     *     client_certificate_url(2), trusted_ca_keys(3),
+     *     truncated_hmac(4), status_request(5), (65535)
+     * } ExtensionType;
+     */
+    private static List<SNIServerName> exploreExtensions(ByteBuffer input)
+            throws IOException {
+
+        int length = getInt16(input);           // length of extensions
+        while (length > 0) {
+            int extType = getInt16(input);      // extenson type
+            int extLen = getInt16(input);       // length of extension data
+
+            if (extType == 0x00) {      // 0x00: type of server name indication
+                return exploreSNIExt(input, extLen);
+            } else {                    // ignore other extensions
+                ignoreByteVector(input, extLen);
+            }
+
+            length -= extLen + 4;
+        }
+
+        return Collections.<SNIServerName>emptyList();
+    }
+
+    /*
+     * struct {
+     *     NameType name_type;
+     *     select (name_type) {
+     *         case host_name: HostName;
+     *     } name;
+     * } ServerName;
+     *
+     * enum {
+     *     host_name(0), (255)
+     * } NameType;
+     *
+     * opaque HostName<1..2^16-1>;
+     *
+     * struct {
+     *     ServerName server_name_list<1..2^16-1>
+     * } ServerNameList;
+     */
+    private static List<SNIServerName> exploreSNIExt(ByteBuffer input,
+            int extLen) throws IOException {
+
+        Map<Integer, SNIServerName> sniMap = new LinkedHashMap<>();
+
+        int remains = extLen;
+        if (extLen >= 2) {     // "server_name" extension in ClientHello
+            int listLen = getInt16(input);     // length of server_name_list
+            if (listLen == 0 || listLen + 2 != extLen) {
+                throw new SSLProtocolException(
+                    "Invalid server name indication extension");
+            }
+
+            remains -= 2;     // 0x02: the length field of server_name_list
+            while (remains > 0) {
+                int code = getInt8(input);      // name_type
+                int snLen = getInt16(input);    // length field of server name
+                if (snLen > remains) {
+                    throw new SSLProtocolException(
+                        "Not enough data to fill declared vector size");
+                }
+                byte[] encoded = new byte[snLen];
+                input.get(encoded);
+
+                SNIServerName serverName;
+                switch (code) {
+                    case StandardConstants.SNI_HOST_NAME:
+                        if (encoded.length == 0) {
+                            throw new SSLProtocolException(
+                                "Empty HostName in server name indication");
+                        }
+                        serverName = new SNIHostName(encoded);
+                        break;
+                    default:
+                        serverName = new UnknownServerName(code, encoded);
+                }
+                // check for duplicated server name type
+                if (sniMap.put(serverName.getType(), serverName) != null) {
+                    throw new SSLProtocolException(
+                            "Duplicated server name of type " +
+                            serverName.getType());
+                }
+
+                remains -= encoded.length + 3;  // NameType: 1 byte
+                                                // HostName length: 2 bytes
+            }
+        } else if (extLen == 0) {     // "server_name" extension in ServerHello
+            throw new SSLProtocolException(
+                        "Not server name indication extension in client");
+        }
+
+        if (remains != 0) {
+            throw new SSLProtocolException(
+                        "Invalid server name indication extension");
+        }
+
+        return Collections.<SNIServerName>unmodifiableList(
+                                            new ArrayList<>(sniMap.values()));
+    }
+
+    private static int getInt8(ByteBuffer input) {
+        return input.get();
+    }
+
+    private static int getInt16(ByteBuffer input) {
+        return ((input.get() & 0xFF) << 8) | (input.get() & 0xFF);
+    }
+
+    private static int getInt24(ByteBuffer input) {
+        return ((input.get() & 0xFF) << 16) | ((input.get() & 0xFF) << 8) |
+                (input.get() & 0xFF);
+    }
+
+    private static void ignoreByteVector8(ByteBuffer input) {
+        ignoreByteVector(input, getInt8(input));
+    }
+
+    private static void ignoreByteVector16(ByteBuffer input) {
+        ignoreByteVector(input, getInt16(input));
+    }
+
+    private static void ignoreByteVector24(ByteBuffer input) {
+        ignoreByteVector(input, getInt24(input));
+    }
+
+    private static void ignoreByteVector(ByteBuffer input, int length) {
+        if (length != 0) {
+            int position = input.position();
+            input.position(position + length);
+        }
+    }
+
+    private static class UnknownServerName extends SNIServerName {
+        UnknownServerName(int code, byte[] encoded) {
+            super(code, encoded);
+        }
+    }
+
+    private static final class SSLCapabilitiesImpl extends SSLCapabilities {
+        private final static Map<Integer, String> versionMap = new HashMap<>(5);
+
+        private final String recordVersion;
+        private final String helloVersion;
+        List<SNIServerName> sniNames;
+
+        static {
+            versionMap.put(0x0002, "SSLv2Hello");
+            versionMap.put(0x0300, "SSLv3");
+            versionMap.put(0x0301, "TLSv1");
+            versionMap.put(0x0302, "TLSv1.1");
+            versionMap.put(0x0303, "TLSv1.2");
+        }
+
+        SSLCapabilitiesImpl(byte recordMajorVersion, byte recordMinorVersion,
+                byte helloMajorVersion, byte helloMinorVersion,
+                List<SNIServerName> sniNames) {
+
+            int version = (recordMajorVersion << 8) | recordMinorVersion;
+            this.recordVersion = versionMap.get(version) != null ?
+                        versionMap.get(version) :
+                        unknownVersion(recordMajorVersion, recordMinorVersion);
+
+            version = (helloMajorVersion << 8) | helloMinorVersion;
+            this.helloVersion = versionMap.get(version) != null ?
+                        versionMap.get(version) :
+                        unknownVersion(helloMajorVersion, helloMinorVersion);
+
+            this.sniNames = sniNames;
+        }
+
+        @Override
+        public String getRecordVersion() {
+            return recordVersion;
+        }
+
+        @Override
+        public String getHelloVersion() {
+            return helloVersion;
+        }
+
+        @Override
+        public List<SNIServerName> getServerNames() {
+            if (!sniNames.isEmpty()) {
+                return Collections.<SNIServerName>unmodifiableList(sniNames);
+            }
+
+            return sniNames;
+        }
+
+        private static String unknownVersion(byte major, byte minor) {
+            return "Unknown-" + ((int)major) + "." + ((int)minor);
+        }
+    }
+}
+