jdk/src/share/classes/sun/security/ssl/SSLSocketImpl.java
changeset 14194 971f46db533d
parent 13815 2de30ecf335e
child 14664 e71aa0962e70
--- a/jdk/src/share/classes/sun/security/ssl/SSLSocketImpl.java	Wed Oct 17 13:22:39 2012 -0700
+++ b/jdk/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();